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      */
Simulator(Machine machine, ConcreteMethod method, DexOptions dexOptions)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      */
simulate(ByteBlock bb, Frame frame)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      */
simulate(int offset, Frame frame)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      */
illegalTos()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      */
requiredArrayTypeFor(Type impliedType, Type foundArrayType)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          */
SimVisitor()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          */
setFrame(Frame frame)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
visitInvalid(int opcode, int offset, int length)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
visitNoArgs(int opcode, int offset, int length, Type type)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          */
checkReturnType(Type encountered)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
visitLocal(int opcode, int offset, int length, int idx, Type type, int value)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
visitConstant(int opcode, int offset, int length, Constant cst, int value)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
visitBranch(int opcode, int offset, int length, int target)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
visitSwitch(int opcode, int offset, int length, SwitchList cases, int padding)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
visitNewarray(int offset, int length, CstType type, ArrayList<Constant> initValues)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
setPreviousOffset(int offset)826         public void setPreviousOffset(int offset) {
827             previousOffset = offset;
828         }
829 
830         /** {@inheritDoc} */
831         @Override
getPreviousOffset()832         public int getPreviousOffset() {
833             return previousOffset;
834         }
835     }
836 
checkConstMethodHandleSupported(Constant cst)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 
checkInvokeDynamicSupported(int opcode)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 
checkInvokeInterfaceSupported(final int opcode, CstMethodRef callee)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 
checkInterfaceMethodDeclaration(ConcreteMethod declaredMethod)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 
checkInvokeSignaturePolymorphic(final int opcode)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 
fail(String reason)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 
warn(String reason)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