Home | History | Annotate | Download | only in system
      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 
     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      */
     94     public final MethodType getMethodType() { return type; }
     95 
     96     /**
     97      * Returns the {@code MethodType} corresponding to the callsite of the
     98      */
     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 
    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 
    120         public static Range all(MethodType frameType) {
    121             return of(frameType, 0, frameType.parameterCount());
    122         }
    123 
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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 
    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          */
    329         public StackFrameAccessor attach(EmulatedStackFrame stackFrame) {
    330             return attach(stackFrame, 0 /* argumentIdx */, 0 /* referencesOffset */,
    331                     0 /* frameOffset */);
    332         }
    333 
    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 
    349         protected void checkType(Class<?> type) {
    350             if (argumentIdx >= numArgs || argumentIdx == (RETURN_VALUE_IDX + 1)) {
    351                 throw new IllegalArgumentException("Invalid argument index: " + argumentIdx);
    352             }
    353 
    354             final Class<?> expectedType = (argumentIdx == RETURN_VALUE_IDX) ?
    355                     frame.type.rtype() : frame.type.ptypes()[argumentIdx];
    356 
    357             if (expectedType != type) {
    358                 throw new IllegalArgumentException("Incorrect type: " + type +
    359                         ", expected: " + expectedType);
    360             }
    361         }
    362 
    363         /**
    364          * Positions the cursor at the return value location, either in the references array
    365          * or in the stack frame array. The next put* or next* call will result in a read or
    366          * write to the return value.
    367          */
    368         public void makeReturnValueAccessor() {
    369             Class<?> rtype = frame.type.rtype();
    370             argumentIdx = RETURN_VALUE_IDX;
    371 
    372             // Position the cursor appropriately. The return value is either the last element
    373             // of the references array, or the last 4 or 8 bytes of the stack frame.
    374             if (rtype.isPrimitive()) {
    375                 frameBuf.position(frameBuf.capacity() - getSize(rtype));
    376             } else {
    377                 referencesOffset = frame.references.length - 1;
    378             }
    379         }
    380 
    381         public static void copyNext(StackFrameReader reader, StackFrameWriter writer,
    382                                     Class<?> type) {
    383             if (!type.isPrimitive()) {
    384                 writer.putNextReference(reader.nextReference(type), type);
    385             } else if (type == boolean.class) {
    386                 writer.putNextBoolean(reader.nextBoolean());
    387             } else if (type == byte.class) {
    388                 writer.putNextByte(reader.nextByte());
    389             } else if (type == char.class) {
    390                 writer.putNextChar(reader.nextChar());
    391             } else if (type == short.class) {
    392                 writer.putNextShort(reader.nextShort());
    393             } else if (type == int.class) {
    394                 writer.putNextInt(reader.nextInt());
    395             } else if (type == long.class) {
    396                 writer.putNextLong(reader.nextLong());
    397             } else if (type == float.class) {
    398                 writer.putNextFloat(reader.nextFloat());
    399             } else if (type == double.class) {
    400                 writer.putNextDouble(reader.nextDouble());
    401             }
    402         }
    403     }
    404 
    405     /**
    406      * Provides sequential write access to an emulated stack frame. Allows writes to
    407      * argument slots as well as return value slots.
    408      */
    409     public static class StackFrameWriter extends StackFrameAccessor {
    410         public void putNextByte(byte value) {
    411             checkType(byte.class);
    412             argumentIdx++;
    413             frameBuf.putInt(value);
    414         }
    415 
    416         public void putNextInt(int value) {
    417             checkType(int.class);
    418             argumentIdx++;
    419             frameBuf.putInt(value);
    420         }
    421 
    422         public void putNextLong(long value) {
    423             checkType(long.class);
    424             argumentIdx++;
    425             frameBuf.putLong(value);
    426         }
    427 
    428         public void putNextChar(char value) {
    429             checkType(char.class);
    430             argumentIdx++;
    431             frameBuf.putInt((int) value);
    432         }
    433 
    434         public void putNextBoolean(boolean value) {
    435             checkType(boolean.class);
    436             argumentIdx++;
    437             frameBuf.putInt(value ? 1 : 0);
    438         }
    439 
    440         public void putNextShort(short value) {
    441             checkType(short.class);
    442             argumentIdx++;
    443             frameBuf.putInt((int) value);
    444         }
    445 
    446         public void putNextFloat(float value) {
    447             checkType(float.class);
    448             argumentIdx++;
    449             frameBuf.putFloat(value);
    450         }
    451 
    452         public void putNextDouble(double value) {
    453             checkType(double.class);
    454             argumentIdx++;
    455             frameBuf.putDouble(value);
    456         }
    457 
    458         public void putNextReference(Object value, Class<?> expectedType) {
    459             checkType(expectedType);
    460             argumentIdx++;
    461             frame.references[referencesOffset++] = value;
    462         }
    463     }
    464 
    465     /**
    466      * Provides sequential read access to an emulated stack frame. Allows reads to
    467      * argument slots as well as to return value slots.
    468      */
    469     public static class StackFrameReader extends StackFrameAccessor {
    470         public byte nextByte() {
    471             checkType(byte.class);
    472             argumentIdx++;
    473             return (byte) frameBuf.getInt();
    474         }
    475 
    476         public int nextInt() {
    477             checkType(int.class);
    478             argumentIdx++;
    479             return frameBuf.getInt();
    480         }
    481 
    482         public long nextLong() {
    483             checkType(long.class);
    484             argumentIdx++;
    485             return frameBuf.getLong();
    486         }
    487 
    488         public char nextChar() {
    489             checkType(char.class);
    490             argumentIdx++;
    491             return (char) frameBuf.getInt();
    492         }
    493 
    494         public boolean nextBoolean() {
    495             checkType(boolean.class);
    496             argumentIdx++;
    497             return (frameBuf.getInt() != 0);
    498         }
    499 
    500         public short nextShort() {
    501             checkType(short.class);
    502             argumentIdx++;
    503             return (short) frameBuf.getInt();
    504         }
    505 
    506         public float nextFloat() {
    507             checkType(float.class);
    508             argumentIdx++;
    509             return frameBuf.getFloat();
    510         }
    511 
    512         public double nextDouble() {
    513             checkType(double.class);
    514             argumentIdx++;
    515             return frameBuf.getDouble();
    516         }
    517 
    518         public <T> T nextReference(Class<T> expectedType) {
    519             checkType(expectedType);
    520             argumentIdx++;
    521             return (T) frame.references[referencesOffset++];
    522         }
    523     }
    524 }
    525