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