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