Home | History | Annotate | Download | only in code
      1 /*
      2  * Copyright (C) 2007 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 com.android.dx.cf.code;
     18 
     19 import com.android.dex.DexFormat;
     20 import com.android.dx.dex.DexOptions;
     21 import com.android.dx.rop.code.LocalItem;
     22 import com.android.dx.rop.cst.Constant;
     23 import com.android.dx.rop.cst.CstFieldRef;
     24 import com.android.dx.rop.cst.CstInteger;
     25 import com.android.dx.rop.cst.CstInterfaceMethodRef;
     26 import com.android.dx.rop.cst.CstInvokeDynamic;
     27 import com.android.dx.rop.cst.CstMethodHandle;
     28 import com.android.dx.rop.cst.CstMethodRef;
     29 import com.android.dx.rop.cst.CstProtoRef;
     30 import com.android.dx.rop.cst.CstType;
     31 import com.android.dx.rop.type.Prototype;
     32 import com.android.dx.rop.type.Type;
     33 import com.android.dx.util.Hex;
     34 import java.util.ArrayList;
     35 
     36 /**
     37  * Class which knows how to simulate the effects of executing bytecode.
     38  *
     39  * <p><b>Note:</b> This class is not thread-safe. If multiple threads
     40  * need to use a single instance, they must synchronize access explicitly
     41  * between themselves.</p>
     42  */
     43 public class Simulator {
     44     /**
     45      * {@code non-null;} canned error message for local variable
     46      * table mismatches
     47      */
     48     private static final String LOCAL_MISMATCH_ERROR =
     49         "This is symptomatic of .class transformation tools that ignore " +
     50         "local variable information.";
     51 
     52     /** {@code non-null;} machine to use when simulating */
     53     private final Machine machine;
     54 
     55     /** {@code non-null;} array of bytecode */
     56     private final BytecodeArray code;
     57 
     58     /** {@code non-null;} the method being simulated */
     59     private ConcreteMethod method;
     60 
     61     /** {@code non-null;} local variable information */
     62     private final LocalVariableList localVariables;
     63 
     64     /** {@code non-null;} visitor instance to use */
     65     private final SimVisitor visitor;
     66 
     67     /** {@code non-null;} options for dex output */
     68     private final DexOptions dexOptions;
     69 
     70     /**
     71      * Constructs an instance.
     72      *
     73      * @param machine {@code non-null;} machine to use when simulating
     74      * @param method {@code non-null;} method data to use
     75      * @param dexOptions {@code non-null;} options for dex output
     76      */
     77     public Simulator(Machine machine, ConcreteMethod method, DexOptions dexOptions) {
     78         if (machine == null) {
     79             throw new NullPointerException("machine == null");
     80         }
     81 
     82         if (method == null) {
     83             throw new NullPointerException("method == null");
     84         }
     85 
     86         if (dexOptions == null) {
     87             throw new NullPointerException("dexOptions == null");
     88         }
     89 
     90         this.machine = machine;
     91         this.code = method.getCode();
     92         this.method = method;
     93         this.localVariables = method.getLocalVariables();
     94         this.visitor = new SimVisitor();
     95         this.dexOptions = dexOptions;
     96 
     97         // This check assumes class is initialized (accesses dexOptions).
     98         if (method.isDefaultOrStaticInterfaceMethod()) {
     99             checkInterfaceMethodDeclaration(method);
    100         }
    101     }
    102 
    103     /**
    104      * Simulates the effect of executing the given basic block. This modifies
    105      * the passed-in frame to represent the end result.
    106      *
    107      * @param bb {@code non-null;} the basic block
    108      * @param frame {@code non-null;} frame to operate on
    109      */
    110     public void simulate(ByteBlock bb, Frame frame) {
    111         int end = bb.getEnd();
    112 
    113         visitor.setFrame(frame);
    114 
    115         try {
    116             for (int off = bb.getStart(); off < end; /*off*/) {
    117                 int length = code.parseInstruction(off, visitor);
    118                 visitor.setPreviousOffset(off);
    119                 off += length;
    120             }
    121         } catch (SimException ex) {
    122             frame.annotate(ex);
    123             throw ex;
    124         }
    125     }
    126 
    127     /**
    128      * Simulates the effect of the instruction at the given offset, by
    129      * making appropriate calls on the given frame.
    130      *
    131      * @param offset {@code offset >= 0;} offset of the instruction to simulate
    132      * @param frame {@code non-null;} frame to operate on
    133      * @return the length of the instruction, in bytes
    134      */
    135     public int simulate(int offset, Frame frame) {
    136         visitor.setFrame(frame);
    137         return code.parseInstruction(offset, visitor);
    138     }
    139 
    140     /**
    141      * Constructs an "illegal top-of-stack" exception, for the stack
    142      * manipulation opcodes.
    143      */
    144     private static SimException illegalTos() {
    145         return new SimException("stack mismatch: illegal " +
    146                 "top-of-stack for opcode");
    147     }
    148 
    149     /**
    150      * Returns the required array type for an array load or store
    151      * instruction, based on a given implied type and an observed
    152      * actual array type.
    153      *
    154      * <p>The interesting cases here have to do with object arrays,
    155      * <code>byte[]</code>s, <code>boolean[]</code>s, and
    156      * known-nulls.</p>
    157      *
    158      * <p>In the case of arrays of objects, we want to narrow the type
    159      * to the actual array present on the stack, as long as what is
    160      * present is an object type. Similarly, due to a quirk of the
    161      * original bytecode representation, the instructions for dealing
    162      * with <code>byte[]</code> and <code>boolean[]</code> are
    163      * undifferentiated, and we aim here to return whichever one was
    164      * actually present on the stack.</p>
    165      *
    166      * <p>In the case where there is a known-null on the stack where
    167      * an array is expected, our behavior depends on the implied type
    168      * of the instruction. When the implied type is a reference, we
    169      * don't attempt to infer anything, as we don't know the dimension
    170      * of the null constant and thus any explicit inferred type could
    171      * be wrong. When the implied type is a primitive, we fall back to
    172      * the implied type of the instruction. Due to the quirk described
    173      * above, this means that source code that uses
    174      * <code>boolean[]</code> might get translated surprisingly -- but
    175      * correctly -- into an instruction that specifies a
    176      * <code>byte[]</code>. It will be correct, because should the
    177      * code actually execute, it will necessarily throw a
    178      * <code>NullPointerException</code>, and it won't matter what
    179      * opcode variant is used to achieve that result.</p>
    180      *
    181      * @param impliedType {@code non-null;} type implied by the
    182      * instruction; is <i>not</i> an array type
    183      * @param foundArrayType {@code non-null;} type found on the
    184      * stack; is either an array type or a known-null
    185      * @return {@code non-null;} the array type that should be
    186      * required in this context
    187      */
    188     private static Type requiredArrayTypeFor(Type impliedType,
    189             Type foundArrayType) {
    190         if (foundArrayType == Type.KNOWN_NULL) {
    191             return impliedType.isReference()
    192                 ? Type.KNOWN_NULL
    193                 : impliedType.getArrayType();
    194         }
    195 
    196         if ((impliedType == Type.OBJECT)
    197                 && foundArrayType.isArray()
    198                 && foundArrayType.getComponentType().isReference()) {
    199             return foundArrayType;
    200         }
    201 
    202         if ((impliedType == Type.BYTE)
    203                 && (foundArrayType == Type.BOOLEAN_ARRAY)) {
    204             /*
    205              * Per above, an instruction with implied byte[] is also
    206              * allowed to be used on boolean[].
    207              */
    208             return Type.BOOLEAN_ARRAY;
    209         }
    210 
    211         return impliedType.getArrayType();
    212     }
    213 
    214     /**
    215      * Bytecode visitor used during simulation.
    216      */
    217     private class SimVisitor implements BytecodeArray.Visitor {
    218         /**
    219          * {@code non-null;} machine instance to use (just to avoid excessive
    220          * cross-object field access)
    221          */
    222         private final Machine machine;
    223 
    224         /**
    225          * {@code null-ok;} frame to use; set with each call to
    226          * {@link Simulator#simulate}
    227          */
    228         private Frame frame;
    229 
    230         /** offset of the previous bytecode */
    231         private int previousOffset;
    232 
    233         /**
    234          * Constructs an instance.
    235          */
    236         public SimVisitor() {
    237             this.machine = Simulator.this.machine;
    238             this.frame = null;
    239         }
    240 
    241         /**
    242          * Sets the frame to act on.
    243          *
    244          * @param frame {@code non-null;} the frame
    245          */
    246         public void setFrame(Frame frame) {
    247             if (frame == null) {
    248                 throw new NullPointerException("frame == null");
    249             }
    250 
    251             this.frame = frame;
    252         }
    253 
    254         /** {@inheritDoc} */
    255         @Override
    256         public void visitInvalid(int opcode, int offset, int length) {
    257             throw new SimException("invalid opcode " + Hex.u1(opcode));
    258         }
    259 
    260         /** {@inheritDoc} */
    261         @Override
    262         public void visitNoArgs(int opcode, int offset, int length,
    263                 Type type) {
    264             switch (opcode) {
    265                 case ByteOps.NOP: {
    266                     machine.clearArgs();
    267                     break;
    268                 }
    269                 case ByteOps.INEG: {
    270                     machine.popArgs(frame, type);
    271                     break;
    272                 }
    273                 case ByteOps.I2L:
    274                 case ByteOps.I2F:
    275                 case ByteOps.I2D:
    276                 case ByteOps.I2B:
    277                 case ByteOps.I2C:
    278                 case ByteOps.I2S: {
    279                     machine.popArgs(frame, Type.INT);
    280                     break;
    281                 }
    282                 case ByteOps.L2I:
    283                 case ByteOps.L2F:
    284                 case ByteOps.L2D: {
    285                     machine.popArgs(frame, Type.LONG);
    286                     break;
    287                 }
    288                 case ByteOps.F2I:
    289                 case ByteOps.F2L:
    290                 case ByteOps.F2D: {
    291                     machine.popArgs(frame, Type.FLOAT);
    292                     break;
    293                 }
    294                 case ByteOps.D2I:
    295                 case ByteOps.D2L:
    296                 case ByteOps.D2F: {
    297                     machine.popArgs(frame, Type.DOUBLE);
    298                     break;
    299                 }
    300                 case ByteOps.RETURN: {
    301                     machine.clearArgs();
    302                     checkReturnType(Type.VOID);
    303                     break;
    304                 }
    305                 case ByteOps.IRETURN: {
    306                     Type checkType = type;
    307                     if (type == Type.OBJECT) {
    308                         /*
    309                          * For an object return, use the best-known
    310                          * type of the popped value.
    311                          */
    312                         checkType = frame.getStack().peekType(0);
    313                     }
    314                     machine.popArgs(frame, type);
    315                     checkReturnType(checkType);
    316                     break;
    317                 }
    318                 case ByteOps.POP: {
    319                     Type peekType = frame.getStack().peekType(0);
    320                     if (peekType.isCategory2()) {
    321                         throw illegalTos();
    322                     }
    323                     machine.popArgs(frame, 1);
    324                     break;
    325                 }
    326                 case ByteOps.ARRAYLENGTH: {
    327                     Type arrayType = frame.getStack().peekType(0);
    328                     if (!arrayType.isArrayOrKnownNull()) {
    329                         fail("type mismatch: expected array type but encountered " +
    330                              arrayType.toHuman());
    331                     }
    332                     machine.popArgs(frame, Type.OBJECT);
    333                     break;
    334                 }
    335                 case ByteOps.ATHROW:
    336                 case ByteOps.MONITORENTER:
    337                 case ByteOps.MONITOREXIT: {
    338                     machine.popArgs(frame, Type.OBJECT);
    339                     break;
    340                 }
    341                 case ByteOps.IALOAD: {
    342                     /*
    343                      * See comment on requiredArrayTypeFor() for explanation
    344                      * about what's going on here.
    345                      */
    346                     Type foundArrayType = frame.getStack().peekType(1);
    347                     Type requiredArrayType =
    348                         requiredArrayTypeFor(type, foundArrayType);
    349 
    350                     // Make type agree with the discovered requiredArrayType.
    351                     type = (requiredArrayType == Type.KNOWN_NULL)
    352                         ? Type.KNOWN_NULL
    353                         : requiredArrayType.getComponentType();
    354 
    355                     machine.popArgs(frame, requiredArrayType, Type.INT);
    356                     break;
    357                 }
    358                 case ByteOps.IADD:
    359                 case ByteOps.ISUB:
    360                 case ByteOps.IMUL:
    361                 case ByteOps.IDIV:
    362                 case ByteOps.IREM:
    363                 case ByteOps.IAND:
    364                 case ByteOps.IOR:
    365                 case ByteOps.IXOR: {
    366                     machine.popArgs(frame, type, type);
    367                     break;
    368                 }
    369                 case ByteOps.ISHL:
    370                 case ByteOps.ISHR:
    371                 case ByteOps.IUSHR: {
    372                     machine.popArgs(frame, type, Type.INT);
    373                     break;
    374                 }
    375                 case ByteOps.LCMP: {
    376                     machine.popArgs(frame, Type.LONG, Type.LONG);
    377                     break;
    378                 }
    379                 case ByteOps.FCMPL:
    380                 case ByteOps.FCMPG: {
    381                     machine.popArgs(frame, Type.FLOAT, Type.FLOAT);
    382                     break;
    383                 }
    384                 case ByteOps.DCMPL:
    385                 case ByteOps.DCMPG: {
    386                     machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE);
    387                     break;
    388                 }
    389                 case ByteOps.IASTORE: {
    390                     /*
    391                      * See comment on requiredArrayTypeFor() for
    392                      * explanation about what's going on here. In
    393                      * addition to that, the category 1 vs. 2 thing
    394                      * below is to deal with the fact that, if the
    395                      * element type is category 2, we have to skip
    396                      * over one extra stack slot to find the array.
    397                      */
    398                     ExecutionStack stack = frame.getStack();
    399                     int peekDepth = type.isCategory1() ? 2 : 3;
    400                     Type foundArrayType = stack.peekType(peekDepth);
    401                     boolean foundArrayLocal = stack.peekLocal(peekDepth);
    402 
    403                     Type requiredArrayType =
    404                         requiredArrayTypeFor(type, foundArrayType);
    405 
    406                     /*
    407                      * Make type agree with the discovered requiredArrayType
    408                      * if it has local info.
    409                      */
    410                     if (foundArrayLocal) {
    411                         type = (requiredArrayType == Type.KNOWN_NULL)
    412                             ? Type.KNOWN_NULL
    413                             : requiredArrayType.getComponentType();
    414                     }
    415 
    416                     machine.popArgs(frame, requiredArrayType, Type.INT, type);
    417                     break;
    418                 }
    419                 case ByteOps.POP2:
    420                 case ByteOps.DUP2: {
    421                     ExecutionStack stack = frame.getStack();
    422                     int pattern;
    423 
    424                     if (stack.peekType(0).isCategory2()) {
    425                         // "form 2" in vmspec-2
    426                         machine.popArgs(frame, 1);
    427                         pattern = 0x11;
    428                     } else if (stack.peekType(1).isCategory1()) {
    429                         // "form 1"
    430                         machine.popArgs(frame, 2);
    431                         pattern = 0x2121;
    432                     } else {
    433                         throw illegalTos();
    434                     }
    435 
    436                     if (opcode == ByteOps.DUP2) {
    437                         machine.auxIntArg(pattern);
    438                     }
    439                     break;
    440                 }
    441                 case ByteOps.DUP: {
    442                     Type peekType = frame.getStack().peekType(0);
    443 
    444                     if (peekType.isCategory2()) {
    445                         throw illegalTos();
    446                     }
    447 
    448                     machine.popArgs(frame, 1);
    449                     machine.auxIntArg(0x11);
    450                     break;
    451                 }
    452                 case ByteOps.DUP_X1: {
    453                     ExecutionStack stack = frame.getStack();
    454 
    455                     if (!(stack.peekType(0).isCategory1() &&
    456                           stack.peekType(1).isCategory1())) {
    457                         throw illegalTos();
    458                     }
    459 
    460                     machine.popArgs(frame, 2);
    461                     machine.auxIntArg(0x212);
    462                     break;
    463                 }
    464                 case ByteOps.DUP_X2: {
    465                     ExecutionStack stack = frame.getStack();
    466 
    467                     if (stack.peekType(0).isCategory2()) {
    468                         throw illegalTos();
    469                     }
    470 
    471                     if (stack.peekType(1).isCategory2()) {
    472                         // "form 2" in vmspec-2
    473                         machine.popArgs(frame, 2);
    474                         machine.auxIntArg(0x212);
    475                     } else if (stack.peekType(2).isCategory1()) {
    476                         // "form 1"
    477                         machine.popArgs(frame, 3);
    478                         machine.auxIntArg(0x3213);
    479                     } else {
    480                         throw illegalTos();
    481                     }
    482                     break;
    483                 }
    484                 case ByteOps.DUP2_X1: {
    485                     ExecutionStack stack = frame.getStack();
    486 
    487                     if (stack.peekType(0).isCategory2()) {
    488                         // "form 2" in vmspec-2
    489                         if (stack.peekType(2).isCategory2()) {
    490                             throw illegalTos();
    491                         }
    492                         machine.popArgs(frame, 2);
    493                         machine.auxIntArg(0x212);
    494                     } else {
    495                         // "form 1"
    496                         if (stack.peekType(1).isCategory2() ||
    497                             stack.peekType(2).isCategory2()) {
    498                             throw illegalTos();
    499                         }
    500                         machine.popArgs(frame, 3);
    501                         machine.auxIntArg(0x32132);
    502                     }
    503                     break;
    504                 }
    505                 case ByteOps.DUP2_X2: {
    506                     ExecutionStack stack = frame.getStack();
    507 
    508                     if (stack.peekType(0).isCategory2()) {
    509                         if (stack.peekType(2).isCategory2()) {
    510                             // "form 4" in vmspec-2
    511                             machine.popArgs(frame, 2);
    512                             machine.auxIntArg(0x212);
    513                         } else if (stack.peekType(3).isCategory1()) {
    514                             // "form 2"
    515                             machine.popArgs(frame, 3);
    516                             machine.auxIntArg(0x3213);
    517                         } else {
    518                             throw illegalTos();
    519                         }
    520                     } else if (stack.peekType(1).isCategory1()) {
    521                         if (stack.peekType(2).isCategory2()) {
    522                             // "form 3"
    523                             machine.popArgs(frame, 3);
    524                             machine.auxIntArg(0x32132);
    525                         } else if (stack.peekType(3).isCategory1()) {
    526                             // "form 1"
    527                             machine.popArgs(frame, 4);
    528                             machine.auxIntArg(0x432143);
    529                         } else {
    530                             throw illegalTos();
    531                         }
    532                     } else {
    533                         throw illegalTos();
    534                     }
    535                     break;
    536                 }
    537                 case ByteOps.SWAP: {
    538                     ExecutionStack stack = frame.getStack();
    539 
    540                     if (!(stack.peekType(0).isCategory1() &&
    541                           stack.peekType(1).isCategory1())) {
    542                         throw illegalTos();
    543                     }
    544 
    545                     machine.popArgs(frame, 2);
    546                     machine.auxIntArg(0x12);
    547                     break;
    548                 }
    549                 default: {
    550                     visitInvalid(opcode, offset, length);
    551                     return;
    552                 }
    553             }
    554 
    555             machine.auxType(type);
    556             machine.run(frame, offset, opcode);
    557         }
    558 
    559         /**
    560          * Checks whether the prototype is compatible with returning the
    561          * given type, and throws if not.
    562          *
    563          * @param encountered {@code non-null;} the encountered return type
    564          */
    565         private void checkReturnType(Type encountered) {
    566             Type returnType = machine.getPrototype().getReturnType();
    567 
    568             /*
    569              * Check to see if the prototype's return type is
    570              * possibly assignable from the type we encountered. This
    571              * takes care of all the salient cases (types are the same,
    572              * they're compatible primitive types, etc.).
    573              */
    574             if (!Merger.isPossiblyAssignableFrom(returnType, encountered)) {
    575                 fail("return type mismatch: prototype " +
    576                      "indicates " + returnType.toHuman() +
    577                      ", but encountered type " + encountered.toHuman());
    578             }
    579         }
    580 
    581         /** {@inheritDoc} */
    582         @Override
    583         public void visitLocal(int opcode, int offset, int length,
    584                 int idx, Type type, int value) {
    585             /*
    586              * Note that the "type" parameter is always the simplest
    587              * type based on the original opcode, e.g., "int" for
    588              * "iload" (per se) and "Object" for "aload". So, when
    589              * possible, we replace the type with the one indicated in
    590              * the local variable table, though we still need to check
    591              * to make sure it's valid for the opcode.
    592              *
    593              * The reason we use (offset + length) for the localOffset
    594              * for a store is because it is only after the store that
    595              * the local type becomes valid. On the other hand, the
    596              * type associated with a load is valid at the start of
    597              * the instruction.
    598              */
    599             int localOffset =
    600                 (opcode == ByteOps.ISTORE) ? (offset + length) : offset;
    601             LocalVariableList.Item local =
    602                 localVariables.pcAndIndexToLocal(localOffset, idx);
    603             Type localType;
    604 
    605             if (local != null) {
    606                 localType = local.getType();
    607                 if (localType.getBasicFrameType() !=
    608                         type.getBasicFrameType()) {
    609                     // wrong type, ignore local variable info
    610                     local = null;
    611                     localType = type;
    612                 }
    613             } else {
    614                 localType = type;
    615             }
    616 
    617             switch (opcode) {
    618                 case ByteOps.ILOAD:
    619                 case ByteOps.RET: {
    620                     machine.localArg(frame, idx);
    621                     machine.localInfo(local != null);
    622                     machine.auxType(type);
    623                     break;
    624                 }
    625                 case ByteOps.ISTORE: {
    626                     LocalItem item
    627                             = (local == null) ? null : local.getLocalItem();
    628                     machine.popArgs(frame, type);
    629                     machine.auxType(type);
    630                     machine.localTarget(idx, localType, item);
    631                     break;
    632                 }
    633                 case ByteOps.IINC: {
    634                     LocalItem item
    635                             = (local == null) ? null : local.getLocalItem();
    636                     machine.localArg(frame, idx);
    637                     machine.localTarget(idx, localType, item);
    638                     machine.auxType(type);
    639                     machine.auxIntArg(value);
    640                     machine.auxCstArg(CstInteger.make(value));
    641                     break;
    642                 }
    643                 default: {
    644                     visitInvalid(opcode, offset, length);
    645                     return;
    646                 }
    647             }
    648 
    649             machine.run(frame, offset, opcode);
    650         }
    651 
    652         /** {@inheritDoc} */
    653         @Override
    654         public void visitConstant(int opcode, int offset, int length,
    655                 Constant cst, int value) {
    656             switch (opcode) {
    657                 case ByteOps.ANEWARRAY: {
    658                     machine.popArgs(frame, Type.INT);
    659                     break;
    660                 }
    661                 case ByteOps.PUTSTATIC: {
    662                     Type fieldType = ((CstFieldRef) cst).getType();
    663                     machine.popArgs(frame, fieldType);
    664                     break;
    665                 }
    666                 case ByteOps.GETFIELD:
    667                 case ByteOps.CHECKCAST:
    668                 case ByteOps.INSTANCEOF: {
    669                     machine.popArgs(frame, Type.OBJECT);
    670                     break;
    671                 }
    672                 case ByteOps.PUTFIELD: {
    673                     Type fieldType = ((CstFieldRef) cst).getType();
    674                     machine.popArgs(frame, Type.OBJECT, fieldType);
    675                     break;
    676                 }
    677                 case ByteOps.INVOKEINTERFACE:
    678                 case ByteOps.INVOKEVIRTUAL:
    679                 case ByteOps.INVOKESPECIAL:
    680                 case ByteOps.INVOKESTATIC: {
    681                     /*
    682                      * Convert the interface method ref into a normal
    683                      * method ref if necessary.
    684                      */
    685                     if (cst instanceof CstInterfaceMethodRef) {
    686                         cst = ((CstInterfaceMethodRef) cst).toMethodRef();
    687                         checkInvokeInterfaceSupported(opcode, (CstMethodRef) cst);
    688                     }
    689 
    690                     /*
    691                      * Check whether invoke-polymorphic is required and supported.
    692                      */
    693                     if (cst instanceof CstMethodRef) {
    694                         CstMethodRef methodRef = (CstMethodRef) cst;
    695                         if (methodRef.isSignaturePolymorphic()) {
    696                             checkInvokeSignaturePolymorphic(opcode);
    697                         }
    698                     }
    699 
    700                     /*
    701                      * Get the instance or static prototype, and use it to
    702                      * direct the machine.
    703                      */
    704                     boolean staticMethod = (opcode == ByteOps.INVOKESTATIC);
    705                     Prototype prototype
    706                         = ((CstMethodRef) cst).getPrototype(staticMethod);
    707                     machine.popArgs(frame, prototype);
    708                     break;
    709                 }
    710                 case ByteOps.INVOKEDYNAMIC: {
    711                     checkInvokeDynamicSupported(opcode);
    712                     CstInvokeDynamic invokeDynamicRef = (CstInvokeDynamic) cst;
    713                     Prototype prototype = invokeDynamicRef.getPrototype();
    714                     machine.popArgs(frame, prototype);
    715                     // Change the constant to be associated with instruction to
    716                     // a call site reference.
    717                     cst = invokeDynamicRef.addReference();
    718                     break;
    719                 }
    720                 case ByteOps.MULTIANEWARRAY: {
    721                     /*
    722                      * The "value" here is the count of dimensions to
    723                      * create. Make a prototype of that many "int"
    724                      * types, and tell the machine to pop them. This
    725                      * isn't the most efficient way in the world to do
    726                      * this, but then again, multianewarray is pretty
    727                      * darn rare and so not worth much effort
    728                      * optimizing for.
    729                      */
    730                     Prototype prototype =
    731                         Prototype.internInts(Type.VOID, value);
    732                     machine.popArgs(frame, prototype);
    733                     break;
    734                 }
    735                 case ByteOps.LDC:
    736                 case ByteOps.LDC_W: {
    737                     if ((cst instanceof CstMethodHandle || cst instanceof CstProtoRef)) {
    738                         checkConstMethodHandleSupported(cst);
    739                     }
    740                     machine.clearArgs();
    741                     break;
    742                 }
    743                 default: {
    744                     machine.clearArgs();
    745                     break;
    746                 }
    747             }
    748 
    749             machine.auxIntArg(value);
    750             machine.auxCstArg(cst);
    751             machine.run(frame, offset, opcode);
    752         }
    753 
    754         /** {@inheritDoc} */
    755         @Override
    756         public void visitBranch(int opcode, int offset, int length,
    757                 int target) {
    758             switch (opcode) {
    759                 case ByteOps.IFEQ:
    760                 case ByteOps.IFNE:
    761                 case ByteOps.IFLT:
    762                 case ByteOps.IFGE:
    763                 case ByteOps.IFGT:
    764                 case ByteOps.IFLE: {
    765                     machine.popArgs(frame, Type.INT);
    766                     break;
    767                 }
    768                 case ByteOps.IFNULL:
    769                 case ByteOps.IFNONNULL: {
    770                     machine.popArgs(frame, Type.OBJECT);
    771                     break;
    772                 }
    773                 case ByteOps.IF_ICMPEQ:
    774                 case ByteOps.IF_ICMPNE:
    775                 case ByteOps.IF_ICMPLT:
    776                 case ByteOps.IF_ICMPGE:
    777                 case ByteOps.IF_ICMPGT:
    778                 case ByteOps.IF_ICMPLE: {
    779                     machine.popArgs(frame, Type.INT, Type.INT);
    780                     break;
    781                 }
    782                 case ByteOps.IF_ACMPEQ:
    783                 case ByteOps.IF_ACMPNE: {
    784                     machine.popArgs(frame, Type.OBJECT, Type.OBJECT);
    785                     break;
    786                 }
    787                 case ByteOps.GOTO:
    788                 case ByteOps.JSR:
    789                 case ByteOps.GOTO_W:
    790                 case ByteOps.JSR_W: {
    791                     machine.clearArgs();
    792                     break;
    793                 }
    794                 default: {
    795                     visitInvalid(opcode, offset, length);
    796                     return;
    797                 }
    798             }
    799 
    800             machine.auxTargetArg(target);
    801             machine.run(frame, offset, opcode);
    802         }
    803 
    804         /** {@inheritDoc} */
    805         @Override
    806         public void visitSwitch(int opcode, int offset, int length,
    807                 SwitchList cases, int padding) {
    808             machine.popArgs(frame, Type.INT);
    809             machine.auxIntArg(padding);
    810             machine.auxSwitchArg(cases);
    811             machine.run(frame, offset, opcode);
    812         }
    813 
    814         /** {@inheritDoc} */
    815         @Override
    816         public void visitNewarray(int offset, int length, CstType type,
    817                 ArrayList<Constant> initValues) {
    818             machine.popArgs(frame, Type.INT);
    819             machine.auxInitValues(initValues);
    820             machine.auxCstArg(type);
    821             machine.run(frame, offset, ByteOps.NEWARRAY);
    822         }
    823 
    824         /** {@inheritDoc} */
    825         @Override
    826         public void setPreviousOffset(int offset) {
    827             previousOffset = offset;
    828         }
    829 
    830         /** {@inheritDoc} */
    831         @Override
    832         public int getPreviousOffset() {
    833             return previousOffset;
    834         }
    835     }
    836 
    837     private void checkConstMethodHandleSupported(Constant cst) throws SimException {
    838         if (!dexOptions.apiIsSupported(DexFormat.API_CONST_METHOD_HANDLE)) {
    839             fail(String.format("invalid constant type %s requires --min-sdk-version >= %d " +
    840                                "(currently %d)",
    841                                cst.typeName(), DexFormat.API_CONST_METHOD_HANDLE,
    842                                dexOptions.minSdkVersion));
    843         }
    844     }
    845 
    846     private void checkInvokeDynamicSupported(int opcode) throws SimException {
    847         if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
    848             fail(String.format("invalid opcode %02x - invokedynamic requires " +
    849                                "--min-sdk-version >= %d (currently %d)",
    850                                opcode, DexFormat.API_METHOD_HANDLES, dexOptions.minSdkVersion));
    851         }
    852     }
    853 
    854     private void checkInvokeInterfaceSupported(final int opcode, CstMethodRef callee) {
    855         if (opcode == ByteOps.INVOKEINTERFACE) {
    856             // Invoked in the tranditional way, this is fine.
    857             return;
    858         }
    859 
    860         if (dexOptions.apiIsSupported(DexFormat.API_INVOKE_INTERFACE_METHODS)) {
    861             // Running at the officially support API level for default
    862             // and static interface methods.
    863             return;
    864         }
    865 
    866         //
    867         // One might expect a hard API level for invoking interface
    868         // methods. It's either okay to have code invoking static (and
    869         // default) interface methods or not. Reality asks to be
    870         // prepared for a little compromise here because the
    871         // traditional guidance to Android developers when producing a
    872         // multi-API level DEX file is to guard the use of the newer
    873         // feature with an API level check, e.g.
    874         //
    875         // int x = (android.os.Build.VERSION.SDK_INT >= 038) ?
    876         //         DoJava8Thing() : Do JavaOtherThing();
    877         //
    878         // This is fine advice if the bytecodes and VM semantics never
    879         // change. Unfortunately, changes like Java 8 support
    880         // introduce new bytecodes and also additional semantics to
    881         // existing bytecodes. Invoking static and default interface
    882         // methods is one of these awkward VM transitions.
    883         //
    884         // Experimentally invoke-static of static interface methods
    885         // breaks on VMs running below API level 21. Invocations of
    886         // default interface methods may soft-fail verification but so
    887         // long as they are not called that's okay.
    888         //
    889         boolean softFail = dexOptions.allowAllInterfaceMethodInvokes;
    890         if (opcode == ByteOps.INVOKESTATIC) {
    891             softFail &= dexOptions.apiIsSupported(DexFormat.API_INVOKE_STATIC_INTERFACE_METHODS);
    892         } else {
    893             assert (opcode == ByteOps.INVOKESPECIAL) || (opcode == ByteOps.INVOKEVIRTUAL);
    894         }
    895 
    896         // Running below the officially supported API level. Fail hard
    897         // unless the user has explicitly allowed this with
    898         // "--allow-all-interface-method-invokes".
    899         String invokeKind = (opcode == ByteOps.INVOKESTATIC) ? "static" : "default";
    900         if (softFail) {
    901             // The code we are warning about here should have an API check
    902             // that protects it being used on API version < API_INVOKE_INTERFACE_METHODS.
    903             String reason =
    904                     String.format(
    905                         "invoking a %s interface method %s.%s strictly requires " +
    906                         "--min-sdk-version >= %d (experimental at current API level %d)",
    907                         invokeKind, callee.getDefiningClass().toHuman(), callee.getNat().toHuman(),
    908                         DexFormat.API_INVOKE_INTERFACE_METHODS, dexOptions.minSdkVersion);
    909             warn(reason);
    910         } else {
    911             String reason =
    912                     String.format(
    913                         "invoking a %s interface method %s.%s strictly requires " +
    914                         "--min-sdk-version >= %d (blocked at current API level %d)",
    915                     invokeKind, callee.getDefiningClass().toHuman(), callee.getNat().toHuman(),
    916                     DexFormat.API_INVOKE_INTERFACE_METHODS, dexOptions.minSdkVersion);
    917             fail(reason);
    918         }
    919     }
    920 
    921     private void checkInterfaceMethodDeclaration(ConcreteMethod declaredMethod) {
    922         if (!dexOptions.apiIsSupported(DexFormat.API_DEFINE_INTERFACE_METHODS)) {
    923             String reason
    924                 = String.format(
    925                     "defining a %s interface method requires --min-sdk-version >= %d (currently %d)"
    926                     + " for interface methods: %s.%s",
    927                     declaredMethod.isStaticMethod() ? "static" : "default",
    928                     DexFormat.API_DEFINE_INTERFACE_METHODS, dexOptions.minSdkVersion,
    929                     declaredMethod.getDefiningClass().toHuman(), declaredMethod.getNat().toHuman());
    930             warn(reason);
    931         }
    932     }
    933 
    934     private void checkInvokeSignaturePolymorphic(final int opcode) {
    935         if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
    936             fail(String.format(
    937                 "invoking a signature-polymorphic requires --min-sdk-version >= %d (currently %d)",
    938                 DexFormat.API_METHOD_HANDLES, dexOptions.minSdkVersion));
    939         } else if (opcode != ByteOps.INVOKEVIRTUAL) {
    940             fail("Unsupported signature polymorphic invocation (" + ByteOps.opName(opcode) + ")");
    941         }
    942     }
    943 
    944     private void fail(String reason) {
    945         String message = String.format("ERROR in %s.%s: %s", method.getDefiningClass().toHuman(),
    946                                        method.getNat().toHuman(), reason);
    947         throw new SimException(message);
    948     }
    949 
    950     private void warn(String reason) {
    951         String warning = String.format("WARNING in %s.%s: %s", method.getDefiningClass().toHuman(),
    952                                        method.getNat().toHuman(), reason);
    953         dexOptions.err.println(warning);
    954     }
    955 }
    956