1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dalvik.system;
18 
19 import sun.invoke.util.Wrapper;
20 
21 import java.lang.invoke.MethodType;
22 import java.nio.ByteBuffer;
23 import java.nio.ByteOrder;
24 
25 /**
26  * Provides typed (read-only) access to method arguments and a slot to store a return value.
27  *
28  * Used to implement method handle transforms. See {@link java.lang.invoke.Transformers}.
29  *
30  * @hide
31  */
32 public class EmulatedStackFrame {
33     /**
34      * The type of this stack frame, i.e, the types of its arguments and the type of its
35      * return value.
36      */
37     private final MethodType type;
38 
39     /**
40      * All reference arguments and reference return values that belong to this argument array.
41      *
42      * If the return type is a reference, it will be the last element of this array.
43      */
44     private final Object[] references;
45 
46     /**
47      * Contains all primitive values on the stack. Primitive values always take 4 or 8 bytes of
48      * space and all {@code short}, {@code char} and {@code boolean} arguments are promoted to ints.
49      *
50      * Reference values do not appear on the stack frame but they appear (in order)
51      * in the {@code references} array. No additional slots or space for reference arguments or
52      * return values are reserved in the stackFrame.
53      *
54      * By convention, if the return value is a primitive, it will occupy the last 4 or 8 bytes
55      * of the stack frame, depending on the type.
56      *
57      * The size of this array is known at the time of creation of this {@code EmulatedStackFrame}
58      * and is determined by the {@code MethodType} of the frame.
59      *
60      * Example :
61      * <pre>
62      *     Function : String foo(String a, String b, int c, long d) { }
63      *
64      *     EmulatedStackFrame :
65      *     references = { a, b, [return_value] }
66      *     stackFrame = { c0, c1, c2, c3, d0, d1, d2, d3, d4, d5, d6, d7 }
67      *
68      *     Function : int foo(String a)
69      *
70      *     EmulatedStackFrame :
71      *     references = { a }
72      *     stackFrame = { rv0, rv1, rv2, rv3 }  // rv is the return value.
73      *
74      * </pre>
75      *
76      */
77     private final byte[] stackFrame;
78 
EmulatedStackFrame(MethodType type, Object[] references, byte[] stackFrame)79     private EmulatedStackFrame(MethodType type, Object[] references, byte[] stackFrame) {
80         this.type = type;
81         this.references = references;
82         this.stackFrame = stackFrame;
83     }
84 
85     /**
86      * Returns the {@code MethodType} that the frame was created for.
87      */
getMethodType()88     public final MethodType getMethodType() { return type; }
89 
90     /**
91      * Represents a range of arguments on an {@code EmulatedStackFrame}.
92      *
93      * @hide
94      */
95     public static final class Range {
96         public final int referencesStart;
97         public final int numReferences;
98 
99         public final int stackFrameStart;
100         public final int numBytes;
101 
102         private static Range EMPTY_RANGE = new Range(0, 0, 0, 0);
103 
Range(int referencesStart, int numReferences, int stackFrameStart, int numBytes)104         private Range(int referencesStart, int numReferences, int stackFrameStart, int numBytes) {
105             this.referencesStart = referencesStart;
106             this.numReferences = numReferences;
107             this.stackFrameStart = stackFrameStart;
108             this.numBytes = numBytes;
109         }
110 
111         /** Creates a {@code Range} spanning all arguments.
112          * @param frameType the type of the frame.
113          */
all(MethodType frameType)114         public static Range all(MethodType frameType) {
115             return of(frameType, 0, frameType.parameterCount());
116         }
117 
118         /** Creates a {@code Range} spanning specified arguments.
119          * @param frameType the type of the frame.
120          * @param startArg the first argument in the range to be created.
121          * @param endArg the argument ending the range to be created.
122          */
of(MethodType frameType, int startArg, int endArg)123         public static Range of(MethodType frameType, int startArg, int endArg) {
124             if (startArg >= endArg) {
125                 return EMPTY_RANGE;
126             }
127 
128             int referencesStart = 0;
129             int numReferences = 0;
130             int stackFrameStart = 0;
131             int numBytes = 0;
132 
133             final Class<?>[] ptypes = frameType.ptypes();
134             for (int i = 0; i < startArg; ++i) {
135                 Class<?> cl = ptypes[i];
136                 if (!cl.isPrimitive()) {
137                     referencesStart++;
138                 } else {
139                     stackFrameStart += getSize(cl);
140                 }
141             }
142 
143             for (int i = startArg; i < endArg; ++i) {
144                 Class<?> cl = ptypes[i];
145                 if (!cl.isPrimitive()) {
146                     numReferences++;
147                 } else {
148                     numBytes += getSize(cl);
149                 }
150             }
151 
152             return new Range(referencesStart, numReferences, stackFrameStart, numBytes);
153         }
154 
155         /** Creates a {@code Range} covering all arguments starting from specified position.
156          * @param frameType the type of the frame.
157          * @param startArg the first argument in the range to be created.
158          */
from(MethodType frameType, int startArg)159         public static Range from(MethodType frameType, int startArg) {
160             return of(frameType, startArg, frameType.parameterCount());
161         }
162     }
163 
164     /**
165      * Creates an emulated stack frame for a given {@code MethodType}.
166      */
create(MethodType frameType)167     public static EmulatedStackFrame create(MethodType frameType) {
168         int numRefs = 0;
169         int frameSize = 0;
170         for (Class<?> ptype : frameType.ptypes()) {
171             if (!ptype.isPrimitive()) {
172                 numRefs++;
173             } else {
174                 frameSize += getSize(ptype);
175             }
176         }
177 
178         final Class<?> rtype = frameType.rtype();
179         if (!rtype.isPrimitive()) {
180             numRefs++;
181         } else {
182             frameSize += getSize(rtype);
183         }
184 
185         return new EmulatedStackFrame(frameType, new Object[numRefs], new byte[frameSize]);
186     }
187 
188     /**
189      * Convert parameter index to index within references array.
190      */
getReferenceIndex(int parameterIndex)191     int getReferenceIndex(int parameterIndex) {
192         final Class [] ptypes = type.ptypes();
193         int refIndex = 0;
194         for (int i = 0; i < parameterIndex; ++i) {
195             if (!ptypes[i].isPrimitive()) {
196                 refIndex += 1;
197             }
198         }
199         return refIndex;
200     }
201 
202     /**
203      * Sets the {@code idx} to {@code reference}. Type checks are performed.
204      */
setReference(int idx, Object reference)205     public void setReference(int idx, Object reference) {
206         final Class<?>[] ptypes = type.ptypes();
207         if (idx < 0 || idx >= ptypes.length) {
208             throw new IllegalArgumentException("Invalid index: " + idx);
209         }
210         if (reference != null && !ptypes[idx].isInstance(reference)) {
211             throw new IllegalStateException("reference is not of type: " + type.ptypes()[idx]);
212         }
213         int referenceIndex = getReferenceIndex(idx);
214         references[referenceIndex] = reference;
215     }
216 
217     /**
218      * Gets the reference at {@code idx}, checking that it's of type {@code referenceType}.
219      */
getReference(int idx, Class<T> referenceType)220     public <T> T getReference(int idx, Class<T> referenceType) {
221         if (referenceType != type.ptypes()[idx]) {
222             throw new IllegalArgumentException("Argument: " + idx +
223                     " is of type " + type.ptypes()[idx] + " expected " + referenceType + "");
224         }
225         int referenceIndex = getReferenceIndex(idx);
226         return (T) references[referenceIndex];
227     }
228 
229     /**
230      * Copies a specified range of arguments, given by {@code fromRange} to a specified
231      * EmulatedStackFrame {@code other}, with references starting at {@code referencesStart}
232      * and primitives starting at {@code primitivesStart}.
233      */
copyRangeTo(EmulatedStackFrame other, Range fromRange, int referencesStart, int primitivesStart)234     public void copyRangeTo(EmulatedStackFrame other, Range fromRange, int referencesStart,
235                             int primitivesStart) {
236         if (fromRange.numReferences > 0) {
237             System.arraycopy(references, fromRange.referencesStart,
238                     other.references, referencesStart, fromRange.numReferences);
239         }
240 
241         if (fromRange.numBytes > 0) {
242             System.arraycopy(stackFrame, fromRange.stackFrameStart,
243                     other.stackFrame, primitivesStart, fromRange.numBytes);
244         }
245     }
246 
247     /**
248      * Copies the return value from this stack frame to {@code other}.
249      */
copyReturnValueTo(EmulatedStackFrame other)250     public void copyReturnValueTo(EmulatedStackFrame other) {
251         final Class<?> returnType = type.returnType();
252         if (!returnType.isPrimitive()) {
253             other.references[other.references.length - 1] = references[references.length - 1];
254         } else if (!is64BitPrimitive(returnType)) {
255             System.arraycopy(stackFrame, stackFrame.length - 4,
256                     other.stackFrame, other.stackFrame.length - 4, 4);
257         } else {
258             System.arraycopy(stackFrame, stackFrame.length - 8,
259                     other.stackFrame, other.stackFrame.length - 8, 8);
260         }
261     }
262 
setReturnValueTo(Object reference)263     public void setReturnValueTo(Object reference) {
264         final Class<?> returnType = type.returnType();
265         if (returnType.isPrimitive()) {
266             throw new IllegalStateException("return type is not a reference type: " + returnType);
267         }
268 
269         if (reference != null && !returnType.isInstance(reference)) {
270             throw new IllegalArgumentException("reference is not of type " + returnType);
271         }
272 
273         references[references.length - 1] = reference;
274     }
275 
276     /**
277      * Returns true iff. the input {@code type} needs 64 bits (8 bytes) of storage on an
278      * {@code EmulatedStackFrame}.
279      */
is64BitPrimitive(Class<?> type)280     private static boolean is64BitPrimitive(Class<?> type) {
281         return type == double.class || type == long.class;
282     }
283 
284     /**
285      * Returns the size (in bytes) occupied by a given primitive type on an
286      * {@code EmulatedStackFrame}.
287      */
getSize(Class<?> type)288     public static int getSize(Class<?> type) {
289         if (!type.isPrimitive()) {
290             throw new IllegalArgumentException("type.isPrimitive() == false: " + type);
291         }
292 
293         if (is64BitPrimitive(type)) {
294             return 8;
295         } else {
296             return 4;
297         }
298     }
299 
300     /**
301      * Base class for readers and writers to stack frames.
302      *
303      * @hide
304      */
305     public static class StackFrameAccessor {
306         /**
307          * The current offset into the references array.
308          */
309         protected int referencesOffset;
310 
311         /**
312          * The index of the current argument being processed. For a function of arity N,
313          * values [0, N) correspond to input arguments, and the special index {@code -2}
314          * maps to the return value. All other indices are invalid.
315          */
316         protected int argumentIdx;
317 
318         /**
319          * Wrapper for {@code EmulatedStackFrame.this.stackFrame}.
320          */
321         protected ByteBuffer frameBuf;
322 
323         /**
324          * The number of arguments that this stack frame expects.
325          */
326         private int numArgs;
327 
328         /**
329          * The stack frame we're currently accessing.
330          */
331         protected EmulatedStackFrame frame;
332 
333         /**
334          * The value of {@code argumentIdx} when this accessor's cursor is pointing to the
335          * frame's return value.
336          */
337         private static final int RETURN_VALUE_IDX = -2;
338 
StackFrameAccessor()339         protected StackFrameAccessor() {
340             referencesOffset = 0;
341             argumentIdx = 0;
342 
343             frameBuf = null;
344             numArgs = 0;
345         }
346 
347         /**
348          * Attaches this accessor to a given {@code EmulatedStackFrame} to read or write
349          * values to it. Also resets all state associated with the current accessor.
350          */
attach(EmulatedStackFrame stackFrame)351         public StackFrameAccessor attach(EmulatedStackFrame stackFrame) {
352             return attach(stackFrame, 0 /* argumentIdx */, 0 /* referencesOffset */,
353                     0 /* frameOffset */);
354         }
355 
attach(EmulatedStackFrame stackFrame, int argumentIdx, int referencesOffset, int frameOffset)356         public StackFrameAccessor attach(EmulatedStackFrame stackFrame, int argumentIdx,
357                                          int referencesOffset, int frameOffset) {
358             if (frame != stackFrame) {
359                 // Re-initialize storage if not re-attaching to the same stackFrame.
360                 frame = stackFrame;
361                 frameBuf = ByteBuffer.wrap(frame.stackFrame).order(ByteOrder.LITTLE_ENDIAN);
362                 numArgs = frame.type.ptypes().length;
363             }
364 
365             frameBuf.position(frameOffset);
366             this.referencesOffset = referencesOffset;
367             this.argumentIdx = argumentIdx;
368 
369             return this;
370         }
371 
getCurrentArgumentType()372         private Class<?> getCurrentArgumentType() {
373             if (argumentIdx >= numArgs || argumentIdx == (RETURN_VALUE_IDX + 1)) {
374                 throw new IllegalArgumentException("Invalid argument index: " + argumentIdx);
375             }
376             return (argumentIdx == RETURN_VALUE_IDX) ?
377                     frame.type.rtype() : frame.type.ptypes()[argumentIdx];
378         }
379 
checkAssignable(Class<?> expectedType, Class<?> actualType)380         private static void checkAssignable(Class<?> expectedType, Class<?> actualType) {
381             if (!expectedType.isAssignableFrom(actualType)) {
382                 throw new IllegalArgumentException("Incorrect type: " + actualType
383                                                    + ", expected: " + expectedType);
384             }
385         }
386 
checkWriteType(Class<?> type)387         protected void checkWriteType(Class<?> type) {
388             checkAssignable(getCurrentArgumentType(), type);
389         }
390 
checkReadType(Class<?> expectedType)391         protected void checkReadType(Class<?> expectedType) {
392             checkAssignable(expectedType, getCurrentArgumentType());
393         }
394 
395         /**
396          * Positions the cursor at the return value location, either in the references array
397          * or in the stack frame array. The next put* or next* call will result in a read or
398          * write to the return value.
399          */
makeReturnValueAccessor()400         public void makeReturnValueAccessor() {
401             Class<?> rtype = frame.type.rtype();
402             argumentIdx = RETURN_VALUE_IDX;
403 
404             // Position the cursor appropriately. The return value is either the last element
405             // of the references array, or the last 4 or 8 bytes of the stack frame.
406             if (rtype.isPrimitive()) {
407                 frameBuf.position(frameBuf.capacity() - getSize(rtype));
408             } else {
409                 referencesOffset = frame.references.length - 1;
410             }
411         }
412 
copyNext( StackFrameReader reader, StackFrameWriter writer, Class<?> type)413         public static void copyNext(
414                 StackFrameReader reader, StackFrameWriter writer, Class<?> type) {
415             switch (Wrapper.basicTypeChar(type)) {
416                 case 'L':
417                     writer.putNextReference(reader.nextReference(type), type);
418                     break;
419                 case 'Z':
420                     writer.putNextBoolean(reader.nextBoolean());
421                     break;
422                 case 'B':
423                     writer.putNextByte(reader.nextByte());
424                     break;
425                 case 'C':
426                     writer.putNextChar(reader.nextChar());
427                     break;
428                 case 'S':
429                     writer.putNextShort(reader.nextShort());
430                     break;
431                 case 'I':
432                     writer.putNextInt(reader.nextInt());
433                     break;
434                 case 'J':
435                     writer.putNextLong(reader.nextLong());
436                     break;
437                 case 'F':
438                     writer.putNextFloat(reader.nextFloat());
439                     break;
440                 case 'D':
441                     writer.putNextDouble(reader.nextDouble());
442                     break;
443             }
444         }
445     }
446 
447     /**
448      * Provides sequential write access to an emulated stack frame. Allows writes to
449      * argument slots as well as return value slots.
450      */
451     public static class StackFrameWriter extends StackFrameAccessor {
putNextByte(byte value)452         public void putNextByte(byte value) {
453             checkWriteType(byte.class);
454             argumentIdx++;
455             frameBuf.putInt(value);
456         }
457 
putNextInt(int value)458         public void putNextInt(int value) {
459             checkWriteType(int.class);
460             argumentIdx++;
461             frameBuf.putInt(value);
462         }
463 
putNextLong(long value)464         public void putNextLong(long value) {
465             checkWriteType(long.class);
466             argumentIdx++;
467             frameBuf.putLong(value);
468         }
469 
putNextChar(char value)470         public void putNextChar(char value) {
471             checkWriteType(char.class);
472             argumentIdx++;
473             frameBuf.putInt((int) value);
474         }
475 
putNextBoolean(boolean value)476         public void putNextBoolean(boolean value) {
477             checkWriteType(boolean.class);
478             argumentIdx++;
479             frameBuf.putInt(value ? 1 : 0);
480         }
481 
putNextShort(short value)482         public void putNextShort(short value) {
483             checkWriteType(short.class);
484             argumentIdx++;
485             frameBuf.putInt((int) value);
486         }
487 
putNextFloat(float value)488         public void putNextFloat(float value) {
489             checkWriteType(float.class);
490             argumentIdx++;
491             frameBuf.putFloat(value);
492         }
493 
putNextDouble(double value)494         public void putNextDouble(double value) {
495             checkWriteType(double.class);
496             argumentIdx++;
497             frameBuf.putDouble(value);
498         }
499 
putNextReference(Object value, Class<?> expectedType)500         public void putNextReference(Object value, Class<?> expectedType) {
501             checkWriteType(expectedType);
502             argumentIdx++;
503             frame.references[referencesOffset++] = value;
504         }
505     }
506 
507     /**
508      * Provides sequential read access to an emulated stack frame. Allows reads to
509      * argument slots as well as to return value slots.
510      */
511     public static class StackFrameReader extends StackFrameAccessor {
nextByte()512         public byte nextByte() {
513             checkReadType(byte.class);
514             argumentIdx++;
515             return (byte) frameBuf.getInt();
516         }
517 
nextInt()518         public int nextInt() {
519             checkReadType(int.class);
520             argumentIdx++;
521             return frameBuf.getInt();
522         }
523 
nextLong()524         public long nextLong() {
525             checkReadType(long.class);
526             argumentIdx++;
527             return frameBuf.getLong();
528         }
529 
nextChar()530         public char nextChar() {
531             checkReadType(char.class);
532             argumentIdx++;
533             return (char) frameBuf.getInt();
534         }
535 
nextBoolean()536         public boolean nextBoolean() {
537             checkReadType(boolean.class);
538             argumentIdx++;
539             return (frameBuf.getInt() != 0);
540         }
541 
nextShort()542         public short nextShort() {
543             checkReadType(short.class);
544             argumentIdx++;
545             return (short) frameBuf.getInt();
546         }
547 
nextFloat()548         public float nextFloat() {
549             checkReadType(float.class);
550             argumentIdx++;
551             return frameBuf.getFloat();
552         }
553 
nextDouble()554         public double nextDouble() {
555             checkReadType(double.class);
556             argumentIdx++;
557             return frameBuf.getDouble();
558         }
559 
nextReference(Class<T> expectedType)560         public <T> T nextReference(Class<T> expectedType) {
561             checkReadType(expectedType);
562             argumentIdx++;
563             return (T) frame.references[referencesOffset++];
564         }
565     }
566 
567     /**
568      * Provides sequential and non-sequential read access to an emulated stack frame. Allows reads
569      * to argument slots as well as to return value slots.
570      */
571     public static class RandomOrderStackFrameReader extends StackFrameReader {
572         int [] frameOffsets;
573         int [] referencesOffsets;
574 
buildTables(MethodType methodType)575         private void buildTables(MethodType methodType) {
576             final Class<?> [] ptypes = methodType.parameterArray();
577             frameOffsets = new int [ptypes.length];
578             referencesOffsets = new int [ptypes.length];
579             int frameOffset = 0;
580             int referenceOffset = 0;
581             for (int i = 0; i < ptypes.length; ++i) {
582                 frameOffsets[i] = frameOffset;
583                 referencesOffsets[i] = referenceOffset;
584 
585                 final Class<?> ptype = ptypes[i];
586                 if (ptype.isPrimitive()) {
587                     frameOffset += getSize(ptype);
588                 } else {
589                     referenceOffset += 1;
590                 }
591             }
592         }
593 
594         @Override
attach(EmulatedStackFrame stackFrame, int argumentIdx, int referencesOffset, int frameOffset)595         public StackFrameAccessor attach(EmulatedStackFrame stackFrame, int argumentIdx,
596                 int referencesOffset, int frameOffset) {
597             super.attach(stackFrame, argumentIdx, referencesOffset, frameOffset);
598             buildTables(stackFrame.getMethodType());
599             return this;
600         }
601 
602         /**
603          * Position to read argument at specific index.
604          * @param argumentIndex the index of the next argument to be read.
605          * @return this reader.
606          */
moveTo(int argumentIndex)607         public RandomOrderStackFrameReader moveTo(int argumentIndex) {
608             referencesOffset = referencesOffsets[argumentIndex];
609             frameBuf.position(frameOffsets[argumentIndex]);
610             argumentIdx = argumentIndex;
611             return this;
612         }
613     }
614 }
615