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