Simulator.java revision de75089fb7216d19e9c22cce4dc62a49513477d3
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.dx.rop.cst.Constant; 20import com.android.dx.rop.cst.CstFieldRef; 21import com.android.dx.rop.cst.CstInteger; 22import com.android.dx.rop.cst.CstInterfaceMethodRef; 23import com.android.dx.rop.cst.CstMethodRef; 24import com.android.dx.rop.cst.CstType; 25import com.android.dx.rop.cst.CstUtf8; 26import com.android.dx.rop.type.Prototype; 27import com.android.dx.rop.type.Type; 28import com.android.dx.rop.type.TypeBearer; 29import com.android.dx.rop.code.LocalItem; 30import com.android.dx.util.Hex; 31import com.android.dx.util.IntList; 32 33import java.util.List; 34import 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 */ 43public 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;} local variable information */ 59 private final LocalVariableList localVariables; 60 61 /** {@code non-null;} visitor instance to use */ 62 private final SimVisitor visitor; 63 64 /** 65 * Constructs an instance. 66 * 67 * @param machine {@code non-null;} machine to use when simulating 68 * @param method {@code non-null;} method data to use 69 */ 70 public Simulator(Machine machine, ConcreteMethod method) { 71 if (machine == null) { 72 throw new NullPointerException("machine == null"); 73 } 74 75 if (method == null) { 76 throw new NullPointerException("method == null"); 77 } 78 79 this.machine = machine; 80 this.code = method.getCode(); 81 this.localVariables = method.getLocalVariables(); 82 this.visitor = new SimVisitor(); 83 } 84 85 /** 86 * Simulates the effect of executing the given basic block. This modifies 87 * the passed-in frame to represent the end result. 88 * 89 * @param bb {@code non-null;} the basic block 90 * @param frame {@code non-null;} frame to operate on 91 */ 92 public void simulate(ByteBlock bb, Frame frame) { 93 int end = bb.getEnd(); 94 95 visitor.setFrame(frame); 96 97 try { 98 for (int off = bb.getStart(); off < end; /*off*/) { 99 int length = code.parseInstruction(off, visitor); 100 visitor.setPreviousOffset(off); 101 off += length; 102 } 103 } catch (SimException ex) { 104 frame.annotate(ex); 105 throw ex; 106 } 107 } 108 109 /** 110 * Simulates the effect of the instruction at the given offset, by 111 * making appropriate calls on the given frame. 112 * 113 * @param offset {@code >= 0;} offset of the instruction to simulate 114 * @param frame {@code non-null;} frame to operate on 115 * @return the length of the instruction, in bytes 116 */ 117 public int simulate(int offset, Frame frame) { 118 visitor.setFrame(frame); 119 return code.parseInstruction(offset, visitor); 120 } 121 122 /** 123 * Constructs an "illegal top-of-stack" exception, for the stack 124 * manipulation opcodes. 125 */ 126 private static SimException illegalTos() { 127 return new SimException("stack mismatch: illegal " + 128 "top-of-stack for opcode"); 129 } 130 131 /** 132 * Bytecode visitor used during simulation. 133 */ 134 private class SimVisitor implements BytecodeArray.Visitor { 135 /** 136 * {@code non-null;} machine instance to use (just to avoid excessive 137 * cross-object field access) 138 */ 139 private final Machine machine; 140 141 /** 142 * {@code null-ok;} frame to use; set with each call to 143 * {@link Simulator#simulate} 144 */ 145 private Frame frame; 146 147 /** offset of the previous bytecode */ 148 private int previousOffset; 149 150 /** 151 * Constructs an instance. 152 */ 153 public SimVisitor() { 154 this.machine = Simulator.this.machine; 155 this.frame = null; 156 } 157 158 /** 159 * Sets the frame to act on. 160 * 161 * @param frame {@code non-null;} the frame 162 */ 163 public void setFrame(Frame frame) { 164 if (frame == null) { 165 throw new NullPointerException("frame == null"); 166 } 167 168 this.frame = frame; 169 } 170 171 /** {@inheritDoc} */ 172 public void visitInvalid(int opcode, int offset, int length) { 173 throw new SimException("invalid opcode " + Hex.u1(opcode)); 174 } 175 176 /** {@inheritDoc} */ 177 public void visitNoArgs(int opcode, int offset, int length, 178 Type type) { 179 switch (opcode) { 180 case ByteOps.NOP: { 181 machine.clearArgs(); 182 break; 183 } 184 case ByteOps.INEG: { 185 machine.popArgs(frame, type); 186 break; 187 } 188 case ByteOps.I2L: 189 case ByteOps.I2F: 190 case ByteOps.I2D: 191 case ByteOps.I2B: 192 case ByteOps.I2C: 193 case ByteOps.I2S: { 194 machine.popArgs(frame, Type.INT); 195 break; 196 } 197 case ByteOps.L2I: 198 case ByteOps.L2F: 199 case ByteOps.L2D: { 200 machine.popArgs(frame, Type.LONG); 201 break; 202 } 203 case ByteOps.F2I: 204 case ByteOps.F2L: 205 case ByteOps.F2D: { 206 machine.popArgs(frame, Type.FLOAT); 207 break; 208 } 209 case ByteOps.D2I: 210 case ByteOps.D2L: 211 case ByteOps.D2F: { 212 machine.popArgs(frame, Type.DOUBLE); 213 break; 214 } 215 case ByteOps.RETURN: { 216 machine.clearArgs(); 217 checkReturnType(Type.VOID); 218 break; 219 } 220 case ByteOps.IRETURN: { 221 Type checkType = type; 222 if (type == Type.OBJECT) { 223 /* 224 * For an object return, use the best-known 225 * type of the popped value. 226 */ 227 checkType = frame.getStack().peekType(0); 228 } 229 machine.popArgs(frame, type); 230 checkReturnType(checkType); 231 break; 232 } 233 case ByteOps.POP: { 234 Type peekType = frame.getStack().peekType(0); 235 if (peekType.isCategory2()) { 236 throw illegalTos(); 237 } 238 machine.popArgs(frame, 1); 239 break; 240 } 241 case ByteOps.ARRAYLENGTH: { 242 Type arrayType = frame.getStack().peekType(0); 243 if (!arrayType.isArrayOrKnownNull()) { 244 throw new SimException("type mismatch: expected " + 245 "array type but encountered " + 246 arrayType.toHuman()); 247 } 248 machine.popArgs(frame, Type.OBJECT); 249 break; 250 } 251 case ByteOps.ATHROW: 252 case ByteOps.MONITORENTER: 253 case ByteOps.MONITOREXIT: { 254 machine.popArgs(frame, Type.OBJECT); 255 break; 256 } 257 case ByteOps.IALOAD: { 258 /* 259 * Change the type (which is to be pushed) to 260 * reflect the actual component type of the array 261 * being popped, unless it turns out to be a 262 * known-null, in which case we just use the type 263 * implied by the original instruction. 264 */ 265 Type foundArrayType = frame.getStack().peekType(1); 266 Type requireArrayType; 267 268 if (foundArrayType != Type.KNOWN_NULL) { 269 requireArrayType = foundArrayType; 270 type = foundArrayType.getComponentType(); 271 } else { 272 requireArrayType = type.getArrayType(); 273 } 274 275 machine.popArgs(frame, requireArrayType, Type.INT); 276 break; 277 } 278 case ByteOps.IADD: 279 case ByteOps.ISUB: 280 case ByteOps.IMUL: 281 case ByteOps.IDIV: 282 case ByteOps.IREM: 283 case ByteOps.IAND: 284 case ByteOps.IOR: 285 case ByteOps.IXOR: { 286 machine.popArgs(frame, type, type); 287 break; 288 } 289 case ByteOps.ISHL: 290 case ByteOps.ISHR: 291 case ByteOps.IUSHR: { 292 machine.popArgs(frame, type, Type.INT); 293 break; 294 } 295 case ByteOps.LCMP: { 296 machine.popArgs(frame, Type.LONG, Type.LONG); 297 break; 298 } 299 case ByteOps.FCMPL: 300 case ByteOps.FCMPG: { 301 machine.popArgs(frame, Type.FLOAT, Type.FLOAT); 302 break; 303 } 304 case ByteOps.DCMPL: 305 case ByteOps.DCMPG: { 306 machine.popArgs(frame, Type.DOUBLE, Type.DOUBLE); 307 break; 308 } 309 case ByteOps.IASTORE: { 310 /* 311 * Change the type (which is the type of the 312 * element) to reflect the actual component type 313 * of the array being popped, unless it turns out 314 * to be a known-null, in which case we just use 315 * the type implied by the original instruction. 316 * The category 1 vs. 2 thing here is that, if the 317 * element type is category 2, we have to skip over 318 * one extra stack slot to find the array. 319 */ 320 Type foundArrayType = 321 frame.getStack().peekType(type.isCategory1() ? 2 : 3); 322 Type requireArrayType; 323 324 if (foundArrayType != Type.KNOWN_NULL) { 325 requireArrayType = foundArrayType; 326 type = foundArrayType.getComponentType(); 327 } else { 328 requireArrayType = type.getArrayType(); 329 } 330 331 machine.popArgs(frame, requireArrayType, Type.INT, type); 332 break; 333 } 334 case ByteOps.POP2: 335 case ByteOps.DUP2: { 336 ExecutionStack stack = frame.getStack(); 337 int pattern; 338 339 if (stack.peekType(0).isCategory2()) { 340 // "form 2" in vmspec-2 341 machine.popArgs(frame, 1); 342 pattern = 0x11; 343 } else if (stack.peekType(1).isCategory1()) { 344 // "form 1" 345 machine.popArgs(frame, 2); 346 pattern = 0x2121; 347 } else { 348 throw illegalTos(); 349 } 350 351 if (opcode == ByteOps.DUP2) { 352 machine.auxIntArg(pattern); 353 } 354 break; 355 } 356 case ByteOps.DUP: { 357 Type peekType = frame.getStack().peekType(0); 358 359 if (peekType.isCategory2()) { 360 throw illegalTos(); 361 } 362 363 machine.popArgs(frame, 1); 364 machine.auxIntArg(0x11); 365 break; 366 } 367 case ByteOps.DUP_X1: { 368 ExecutionStack stack = frame.getStack(); 369 370 if (! (stack.peekType(0).isCategory1() && 371 stack.peekType(1).isCategory1())) { 372 throw illegalTos(); 373 } 374 375 machine.popArgs(frame, 2); 376 machine.auxIntArg(0x212); 377 break; 378 } 379 case ByteOps.DUP_X2: { 380 ExecutionStack stack = frame.getStack(); 381 382 if (stack.peekType(0).isCategory2()) { 383 throw illegalTos(); 384 } 385 386 if (stack.peekType(1).isCategory2()) { 387 // "form 2" in vmspec-2 388 machine.popArgs(frame, 2); 389 machine.auxIntArg(0x212); 390 } else if (stack.peekType(2).isCategory1()) { 391 // "form 1" 392 machine.popArgs(frame, 3); 393 machine.auxIntArg(0x3213); 394 } else { 395 throw illegalTos(); 396 } 397 break; 398 } 399 case ByteOps.DUP2_X1: { 400 ExecutionStack stack = frame.getStack(); 401 402 if (stack.peekType(0).isCategory2()) { 403 // "form 2" in vmspec-2 404 if (stack.peekType(2).isCategory2()) { 405 throw illegalTos(); 406 } 407 machine.popArgs(frame, 2); 408 machine.auxIntArg(0x212); 409 } else { 410 // "form 1" 411 if (stack.peekType(1).isCategory2() || 412 stack.peekType(2).isCategory2()) { 413 throw illegalTos(); 414 } 415 machine.popArgs(frame, 3); 416 machine.auxIntArg(0x32132); 417 } 418 break; 419 } 420 case ByteOps.DUP2_X2: { 421 ExecutionStack stack = frame.getStack(); 422 423 if (stack.peekType(0).isCategory2()) { 424 if (stack.peekType(2).isCategory2()) { 425 // "form 4" in vmspec-2 426 machine.popArgs(frame, 2); 427 machine.auxIntArg(0x212); 428 } else if (stack.peekType(3).isCategory1()) { 429 // "form 2" 430 machine.popArgs(frame, 3); 431 machine.auxIntArg(0x3213); 432 } else { 433 throw illegalTos(); 434 } 435 } else if (stack.peekType(1).isCategory1()) { 436 if (stack.peekType(2).isCategory2()) { 437 // "form 3" 438 machine.popArgs(frame, 3); 439 machine.auxIntArg(0x32132); 440 } else if (stack.peekType(3).isCategory1()) { 441 // "form 1" 442 machine.popArgs(frame, 4); 443 machine.auxIntArg(0x432143); 444 } else { 445 throw illegalTos(); 446 } 447 } else { 448 throw illegalTos(); 449 } 450 break; 451 } 452 case ByteOps.SWAP: { 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(0x12); 462 break; 463 } 464 default: { 465 visitInvalid(opcode, offset, length); 466 return; 467 } 468 } 469 470 machine.auxType(type); 471 machine.run(frame, offset, opcode); 472 } 473 474 /** 475 * Checks whether the prototype is compatible with returning the 476 * given type, and throws if not. 477 * 478 * @param encountered {@code non-null;} the encountered return type 479 */ 480 private void checkReturnType(Type encountered) { 481 Type returnType = machine.getPrototype().getReturnType(); 482 483 /* 484 * Check to see if the prototype's return type is 485 * possibly assignable from the type we encountered. This 486 * takes care of all the salient cases (types are the same, 487 * they're compatible primitive types, etc.). 488 */ 489 if (! Merger.isPossiblyAssignableFrom(returnType, encountered)) { 490 throw new SimException("return type mismatch: prototype " + 491 "indicates " + returnType.toHuman() + 492 ", but encountered type " + encountered.toHuman()); 493 } 494 } 495 496 /** {@inheritDoc} */ 497 public void visitLocal(int opcode, int offset, int length, 498 int idx, Type type, int value) { 499 /* 500 * Note that the "type" parameter is always the simplest 501 * type based on the original opcode, e.g., "int" for 502 * "iload" (per se) and "Object" for "aload". So, when 503 * possible, we replace the type with the one indicated in 504 * the local variable table, though we still need to check 505 * to make sure it's valid for the opcode. 506 * 507 * The reason we use (offset + length) for the localOffset 508 * for a store is because it is only after the store that 509 * the local type becomes valid. On the other hand, the 510 * type associated with a load is valid at the start of 511 * the instruction. 512 */ 513 int localOffset = 514 (opcode == ByteOps.ISTORE) ? (offset + length) : offset; 515 LocalVariableList.Item local = 516 localVariables.pcAndIndexToLocal(localOffset, idx); 517 Type localType; 518 519 if (local != null) { 520 localType = local.getType(); 521 if (localType.getBasicFrameType() != 522 type.getBasicFrameType()) { 523 BaseMachine.throwLocalMismatch(type, localType); 524 return; 525 } 526 } else { 527 localType = type; 528 } 529 530 switch (opcode) { 531 case ByteOps.ILOAD: 532 case ByteOps.RET: { 533 machine.localArg(frame, idx); 534 machine.auxType(type); 535 break; 536 } 537 case ByteOps.ISTORE: { 538 LocalItem item 539 = (local == null) ? null : local.getLocalItem(); 540 machine.popArgs(frame, type); 541 machine.auxType(type); 542 machine.localTarget(idx, localType, item); 543 break; 544 } 545 case ByteOps.IINC: { 546 LocalItem item 547 = (local == null) ? null : local.getLocalItem(); 548 machine.localArg(frame, idx); 549 machine.localTarget(idx, localType, item); 550 machine.auxType(type); 551 machine.auxIntArg(value); 552 machine.auxCstArg(CstInteger.make(value)); 553 break; 554 } 555 default: { 556 visitInvalid(opcode, offset, length); 557 return; 558 } 559 } 560 561 machine.run(frame, offset, opcode); 562 } 563 564 /** {@inheritDoc} */ 565 public void visitConstant(int opcode, int offset, int length, 566 Constant cst, int value) { 567 switch (opcode) { 568 case ByteOps.ANEWARRAY: { 569 machine.popArgs(frame, Type.INT); 570 break; 571 } 572 case ByteOps.PUTSTATIC: { 573 Type fieldType = ((CstFieldRef) cst).getType(); 574 machine.popArgs(frame, fieldType); 575 break; 576 } 577 case ByteOps.GETFIELD: 578 case ByteOps.CHECKCAST: 579 case ByteOps.INSTANCEOF: { 580 machine.popArgs(frame, Type.OBJECT); 581 break; 582 } 583 case ByteOps.PUTFIELD: { 584 Type fieldType = ((CstFieldRef) cst).getType(); 585 machine.popArgs(frame, Type.OBJECT, fieldType); 586 break; 587 } 588 case ByteOps.INVOKEINTERFACE: { 589 /* 590 * Convert the interface method ref into a normal 591 * method ref. 592 */ 593 cst = ((CstInterfaceMethodRef) cst).toMethodRef(); 594 // and fall through... 595 } 596 case ByteOps.INVOKEVIRTUAL: 597 case ByteOps.INVOKESPECIAL: { 598 /* 599 * Get the instance prototype, and use it to direct 600 * the machine. 601 */ 602 Prototype prototype = 603 ((CstMethodRef) cst).getPrototype(false); 604 machine.popArgs(frame, prototype); 605 break; 606 } 607 case ByteOps.INVOKESTATIC: { 608 /* 609 * Get the static prototype, and use it to direct 610 * the machine. 611 */ 612 Prototype prototype = 613 ((CstMethodRef) cst).getPrototype(true); 614 machine.popArgs(frame, prototype); 615 break; 616 } 617 case ByteOps.MULTIANEWARRAY: { 618 /* 619 * The "value" here is the count of dimensions to 620 * create. Make a prototype of that many "int" 621 * types, and tell the machine to pop them. This 622 * isn't the most efficient way in the world to do 623 * this, but then again, multianewarray is pretty 624 * darn rare and so not worth much effort 625 * optimizing for. 626 */ 627 Prototype prototype = 628 Prototype.internInts(Type.VOID, value); 629 machine.popArgs(frame, prototype); 630 break; 631 } 632 default: { 633 machine.clearArgs(); 634 break; 635 } 636 } 637 638 machine.auxIntArg(value); 639 machine.auxCstArg(cst); 640 machine.run(frame, offset, opcode); 641 } 642 643 /** {@inheritDoc} */ 644 public void visitBranch(int opcode, int offset, int length, 645 int target) { 646 switch (opcode) { 647 case ByteOps.IFEQ: 648 case ByteOps.IFNE: 649 case ByteOps.IFLT: 650 case ByteOps.IFGE: 651 case ByteOps.IFGT: 652 case ByteOps.IFLE: { 653 machine.popArgs(frame, Type.INT); 654 break; 655 } 656 case ByteOps.IFNULL: 657 case ByteOps.IFNONNULL: { 658 machine.popArgs(frame, Type.OBJECT); 659 break; 660 } 661 case ByteOps.IF_ICMPEQ: 662 case ByteOps.IF_ICMPNE: 663 case ByteOps.IF_ICMPLT: 664 case ByteOps.IF_ICMPGE: 665 case ByteOps.IF_ICMPGT: 666 case ByteOps.IF_ICMPLE: { 667 machine.popArgs(frame, Type.INT, Type.INT); 668 break; 669 } 670 case ByteOps.IF_ACMPEQ: 671 case ByteOps.IF_ACMPNE: { 672 machine.popArgs(frame, Type.OBJECT, Type.OBJECT); 673 break; 674 } 675 case ByteOps.GOTO: 676 case ByteOps.JSR: 677 case ByteOps.GOTO_W: 678 case ByteOps.JSR_W: { 679 machine.clearArgs(); 680 break; 681 } 682 default: { 683 visitInvalid(opcode, offset, length); 684 return; 685 } 686 } 687 688 machine.auxTargetArg(target); 689 machine.run(frame, offset, opcode); 690 } 691 692 /** {@inheritDoc} */ 693 public void visitSwitch(int opcode, int offset, int length, 694 SwitchList cases, int padding) { 695 machine.popArgs(frame, Type.INT); 696 machine.auxIntArg(padding); 697 machine.auxSwitchArg(cases); 698 machine.run(frame, offset, opcode); 699 } 700 701 /** {@inheritDoc} */ 702 public void visitNewarray(int offset, int length, CstType type, 703 ArrayList<Constant> initValues) { 704 machine.popArgs(frame, Type.INT); 705 machine.auxInitValues(initValues); 706 machine.auxCstArg(type); 707 machine.run(frame, offset, ByteOps.NEWARRAY); 708 } 709 710 /** {@inheritDoc} */ 711 public void setPreviousOffset(int offset) { 712 previousOffset = offset; 713 } 714 715 /** {@inheritDoc} */ 716 public int getPreviousOffset() { 717 return previousOffset; 718 } 719 } 720} 721