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.code.FillArrayDataInsn; 20import com.android.dx.rop.code.Insn; 21import com.android.dx.rop.code.PlainCstInsn; 22import com.android.dx.rop.code.PlainInsn; 23import com.android.dx.rop.code.RegOps; 24import com.android.dx.rop.code.RegisterSpec; 25import com.android.dx.rop.code.RegisterSpecList; 26import com.android.dx.rop.code.Rop; 27import com.android.dx.rop.code.Rops; 28import com.android.dx.rop.code.SourcePosition; 29import com.android.dx.rop.code.SwitchInsn; 30import com.android.dx.rop.code.ThrowingCstInsn; 31import com.android.dx.rop.code.ThrowingInsn; 32import com.android.dx.rop.code.TranslationAdvice; 33import com.android.dx.rop.cst.Constant; 34import com.android.dx.rop.cst.CstFieldRef; 35import com.android.dx.rop.cst.CstMethodRef; 36import com.android.dx.rop.cst.CstNat; 37import com.android.dx.rop.cst.CstType; 38import com.android.dx.rop.cst.CstUtf8; 39import com.android.dx.rop.type.Type; 40import com.android.dx.rop.type.TypeBearer; 41import com.android.dx.rop.type.TypeList; 42import com.android.dx.util.IntList; 43 44import java.util.ArrayList; 45 46/** 47 * Machine implementation for use by {@link Ropper}. 48 */ 49/*package*/ final class RopperMachine extends ValueAwareMachine { 50 /** non-null; array reflection class */ 51 private static final CstType ARRAY_REFLECT_TYPE = 52 new CstType(Type.internClassName("java/lang/reflect/Array")); 53 54 /** 55 * non-null; method constant for use in converting 56 * <code>multianewarray</code> instructions 57 */ 58 private static final CstMethodRef MULTIANEWARRAY_METHOD = 59 new CstMethodRef(ARRAY_REFLECT_TYPE, 60 new CstNat(new CstUtf8("newInstance"), 61 new CstUtf8("(Ljava/lang/Class;[I)" + 62 "Ljava/lang/Object;"))); 63 64 /** non-null; {@link Ropper} controlling this instance */ 65 private final Ropper ropper; 66 67 /** non-null; method being converted */ 68 private final ConcreteMethod method; 69 70 /** non-null; translation advice */ 71 private final TranslationAdvice advice; 72 73 /** max locals of the method */ 74 private final int maxLocals; 75 76 /** non-null; instructions for the rop basic block in-progress */ 77 private final ArrayList<Insn> insns; 78 79 /** non-null; catches for the block currently being processed */ 80 private TypeList catches; 81 82 /** whether the catches have been used in an instruction */ 83 private boolean catchesUsed; 84 85 /** whether the block contains a <code>return</code> */ 86 private boolean returns; 87 88 /** primary successor index */ 89 private int primarySuccessorIndex; 90 91 /** >= 0; number of extra basic blocks required */ 92 private int extraBlockCount; 93 94 /** true if last processed block ends with a jsr or jsr_W*/ 95 private boolean hasJsr; 96 97 /** true if an exception can be thrown by the last block processed */ 98 private boolean blockCanThrow; 99 100 /** 101 * If non-null, the ReturnAddress that was used by the terminating ret 102 * instruction. If null, there was no ret instruction encountered. 103 */ 104 105 private ReturnAddress returnAddress; 106 107 /** 108 * null-ok; the appropriate <code>return</code> op or <code>null</code> 109 * if it is not yet known 110 */ 111 private Rop returnOp; 112 113 /** 114 * null-ok; the source position for the return block or <code>null</code> 115 * if it is not yet known 116 */ 117 private SourcePosition returnPosition; 118 119 /** 120 * Constructs an instance. 121 * 122 * @param ropper non-null; ropper controlling this instance 123 * @param method non-null; method being converted 124 * @param advice non-null; translation advice to use 125 */ 126 public RopperMachine(Ropper ropper, ConcreteMethod method, 127 TranslationAdvice advice) { 128 super(method.getEffectiveDescriptor()); 129 130 if (ropper == null) { 131 throw new NullPointerException("ropper == null"); 132 } 133 134 if (advice == null) { 135 throw new NullPointerException("advice == null"); 136 } 137 138 this.ropper = ropper; 139 this.method = method; 140 this.advice = advice; 141 this.maxLocals = method.getMaxLocals(); 142 this.insns = new ArrayList<Insn>(25); 143 this.catches = null; 144 this.catchesUsed = false; 145 this.returns = false; 146 this.primarySuccessorIndex = -1; 147 this.extraBlockCount = 0; 148 this.blockCanThrow = false; 149 this.returnOp = null; 150 this.returnPosition = null; 151 } 152 153 /** 154 * Gets the instructions array. It is shared and gets modified by 155 * subsequent calls to this instance. 156 * 157 * @return non-null; the instructions array 158 */ 159 public ArrayList<Insn> getInsns() { 160 return insns; 161 } 162 163 /** 164 * Gets the return opcode encountered, if any. 165 * 166 * @return null-ok; the return opcode 167 */ 168 public Rop getReturnOp() { 169 return returnOp; 170 } 171 172 /** 173 * Gets the return position, if known. 174 * 175 * @return null-ok; the return position 176 */ 177 public SourcePosition getReturnPosition() { 178 return returnPosition; 179 } 180 181 /** 182 * Gets ready to start working on a new block. This will clear the 183 * {@link #insns} list, set {@link #catches}, reset whether it has 184 * been used, reset whether the block contains a 185 * <code>return</code>, and reset {@link #primarySuccessorIndex}. 186 */ 187 public void startBlock(TypeList catches) { 188 this.catches = catches; 189 190 insns.clear(); 191 catchesUsed = false; 192 returns = false; 193 primarySuccessorIndex = 0; 194 extraBlockCount = 0; 195 blockCanThrow = false; 196 hasJsr = false; 197 returnAddress = null; 198 } 199 200 /** 201 * Gets whether {@link #catches} was used. This indicates that the 202 * last instruction in the block is one of the ones that can throw. 203 * 204 * @return whether <code>catches</code> has been used 205 */ 206 public boolean wereCatchesUsed() { 207 return catchesUsed; 208 } 209 210 /** 211 * Gets whether the block just processed ended with a 212 * <code>return</code>. 213 * 214 * @return whether the block returns 215 */ 216 public boolean returns() { 217 return returns; 218 } 219 220 /** 221 * Gets the primary successor index. This is the index into the 222 * successors list where the primary may be found or 223 * <code>-1</code> if there are successors but no primary 224 * successor. This may return something other than 225 * <code>-1</code> in the case of an instruction with no 226 * successors at all (primary or otherwise). 227 * 228 * @return >= -1; the primary successor index 229 */ 230 public int getPrimarySuccessorIndex() { 231 return primarySuccessorIndex; 232 } 233 234 /** 235 * Gets how many extra blocks will be needed to represent the 236 * block currently being translated. Each extra block should consist 237 * of one instruction from the end of the original block. 238 * 239 * @return >= 0; the number of extra blocks needed 240 */ 241 public int getExtraBlockCount() { 242 return extraBlockCount; 243 } 244 245 /** 246 * @return true if at least one of the insn processed since the last 247 * call to startBlock() can throw. 248 */ 249 public boolean canThrow() { 250 return blockCanThrow; 251 } 252 253 /** 254 * @return true if a JSR has ben encountered since the last call to 255 * startBlock() 256 */ 257 public boolean hasJsr() { 258 return hasJsr; 259 } 260 261 /** 262 * @return true if a RET has ben encountered since the last call to 263 * startBlock() 264 */ 265 public boolean hasRet() { 266 return returnAddress != null; 267 } 268 269 /** 270 * @return null-ok; return address of a ret instruction if encountered 271 * since last call to startBlock(). null if no ret instruction encountered. 272 */ 273 public ReturnAddress getReturnAddress() { 274 return returnAddress; 275 } 276 277 /** {@inheritDoc} */ 278 @Override 279 public void run(Frame frame, int offset, int opcode) { 280 /* 281 * This is the stack pointer after the opcode's arguments have been 282 * popped. 283 */ 284 int stackPointer = maxLocals + frame.getStack().size(); 285 286 // The sources have to be retrieved before super.run() gets called. 287 RegisterSpecList sources = getSources(opcode, stackPointer); 288 int sourceCount = sources.size(); 289 290 super.run(frame, offset, opcode); 291 292 SourcePosition pos = method.makeSourcePosistion(offset); 293 RegisterSpec localTarget = getLocalTarget(); 294 int destCount = resultCount(); 295 RegisterSpec dest; 296 297 if (destCount == 0) { 298 dest = null; 299 switch (opcode) { 300 case ByteOps.POP: 301 case ByteOps.POP2: { 302 // These simply don't appear in the rop form. 303 return; 304 } 305 } 306 } else if (localTarget != null) { 307 dest = localTarget; 308 } else if (destCount == 1) { 309 dest = RegisterSpec.make(stackPointer, result(0)); 310 } else { 311 /* 312 * This clause only ever applies to the stack manipulation 313 * ops that have results (that is, dup* and swap but not 314 * pop*). 315 * 316 * What we do is first move all the source registers into 317 * the "temporary stack" area defined for the method, and 318 * then move stuff back down onto the main "stack" in the 319 * arrangement specified by the stack op pattern. 320 * 321 * Note: This code ends up emitting a lot of what will 322 * turn out to be superfluous moves (e.g., moving back and 323 * forth to the same local when doing a dup); however, 324 * that makes this code a bit easier (and goodness knows 325 * it doesn't need any extra complexity), and all the SSA 326 * stuff is going to want to deal with this sort of 327 * superfluous assignment anyway, so it should be a wash 328 * in the end. 329 */ 330 int scratchAt = ropper.getFirstTempStackReg(); 331 RegisterSpec[] scratchRegs = new RegisterSpec[sourceCount]; 332 333 for (int i = 0; i < sourceCount; i++) { 334 RegisterSpec src = sources.get(i); 335 TypeBearer type = src.getTypeBearer(); 336 RegisterSpec scratch = src.withReg(scratchAt); 337 insns.add(new PlainInsn(Rops.opMove(type), pos, scratch, src)); 338 scratchRegs[i] = scratch; 339 scratchAt += src.getCategory(); 340 } 341 342 for (int pattern = getAuxInt(); pattern != 0; pattern >>= 4) { 343 int which = (pattern & 0x0f) - 1; 344 RegisterSpec scratch = scratchRegs[which]; 345 TypeBearer type = scratch.getTypeBearer(); 346 insns.add(new PlainInsn(Rops.opMove(type), pos, 347 scratch.withReg(stackPointer), 348 scratch)); 349 stackPointer += type.getType().getCategory(); 350 } 351 return; 352 } 353 354 TypeBearer destType = (dest != null) ? dest : Type.VOID; 355 Constant cst = getAuxCst(); 356 int ropOpcode; 357 Rop rop; 358 Insn insn; 359 360 if (opcode == ByteOps.MULTIANEWARRAY) { 361 blockCanThrow = true; 362 363 // Add the extra instructions for handling multianewarray. 364 365 extraBlockCount = 6; 366 367 /* 368 * Add an array constructor for the int[] containing all the 369 * dimensions. 370 */ 371 RegisterSpec dimsReg = 372 RegisterSpec.make(dest.getNextReg(), Type.INT_ARRAY); 373 rop = Rops.opFilledNewArray(Type.INT_ARRAY, sourceCount); 374 insn = new ThrowingCstInsn(rop, pos, sources, catches, 375 CstType.INT_ARRAY); 376 insns.add(insn); 377 378 // Add a move-result for the new-filled-array 379 rop = Rops.opMoveResult(Type.INT_ARRAY); 380 insn = new PlainInsn(rop, pos, dimsReg, RegisterSpecList.EMPTY); 381 insns.add(insn); 382 383 /* 384 * Add a const-class instruction for the specified array 385 * class. 386 */ 387 388 /* 389 * Remove as many dimensions from the originally specified 390 * class as are given in the explicit list of dimensions, 391 * so as to pass the right component class to the standard 392 * Java library array constructor. 393 */ 394 Type componentType = ((CstType) cst).getClassType(); 395 for (int i = 0; i < sourceCount; i++) { 396 componentType = componentType.getComponentType(); 397 } 398 399 RegisterSpec classReg = 400 RegisterSpec.make(dest.getReg(), Type.CLASS); 401 402 if (componentType.isPrimitive()) { 403 /* 404 * The component type is primitive (e.g., int as opposed 405 * to Integer), so we have to fetch the corresponding 406 * TYPE class. 407 */ 408 CstFieldRef typeField = 409 CstFieldRef.forPrimitiveType(componentType); 410 insn = new ThrowingCstInsn(Rops.GET_STATIC_OBJECT, pos, 411 RegisterSpecList.EMPTY, 412 catches, typeField); 413 } else { 414 /* 415 * The component type is an object type, so just make a 416 * normal class reference. 417 */ 418 insn = new ThrowingCstInsn(Rops.CONST_OBJECT, pos, 419 RegisterSpecList.EMPTY, catches, 420 new CstType(componentType)); 421 } 422 423 insns.add(insn); 424 425 // Add a move-result-pseudo for the get-static or const 426 rop = Rops.opMoveResultPseudo(classReg.getType()); 427 insn = new PlainInsn(rop, pos, classReg, RegisterSpecList.EMPTY); 428 insns.add(insn); 429 430 /* 431 * Add a call to the "multianewarray method," that is, 432 * Array.newInstance(class, dims). Note: The result type 433 * of newInstance() is Object, which is why the last 434 * instruction in this sequence is a cast to the right 435 * type for the original instruction. 436 */ 437 438 RegisterSpec objectReg = 439 RegisterSpec.make(dest.getReg(), Type.OBJECT); 440 441 insn = new ThrowingCstInsn( 442 Rops.opInvokeStatic(MULTIANEWARRAY_METHOD.getPrototype()), 443 pos, RegisterSpecList.make(classReg, dimsReg), 444 catches, MULTIANEWARRAY_METHOD); 445 insns.add(insn); 446 447 // Add a move-result 448 rop = Rops.opMoveResult(MULTIANEWARRAY_METHOD.getPrototype() 449 .getReturnType()); 450 insn = new PlainInsn(rop, pos, objectReg, RegisterSpecList.EMPTY); 451 insns.add(insn); 452 453 /* 454 * And finally, set up for the remainder of this method to 455 * add an appropriate cast. 456 */ 457 458 opcode = ByteOps.CHECKCAST; 459 sources = RegisterSpecList.make(objectReg); 460 461 } else if (opcode == ByteOps.JSR) { 462 // JSR has no Rop instruction 463 hasJsr = true; 464 return; 465 } else if (opcode == ByteOps.RET) { 466 try { 467 returnAddress = (ReturnAddress)arg(0); 468 } catch (ClassCastException ex) { 469 throw new RuntimeException( 470 "Argument to RET was not a ReturnAddress", ex); 471 } 472 // RET has no Rop instruction. 473 return; 474 } 475 476 ropOpcode = jopToRopOpcode(opcode, cst); 477 478 rop = Rops.ropFor(ropOpcode, destType, sources, cst); 479 480 Insn moveResult = null; 481 if (dest != null && rop.isCallLike()) { 482 // We're going to want to have a move-result in the next basic block 483 extraBlockCount++; 484 485 moveResult = new PlainInsn( 486 Rops.opMoveResult(((CstMethodRef) cst).getPrototype() 487 .getReturnType()), pos, dest, RegisterSpecList.EMPTY); 488 489 dest = null; 490 } else if (dest != null && rop.canThrow()) { 491 // We're going to want to have a move-result-pseudo 492 // in the next basic block 493 extraBlockCount++; 494 495 moveResult = new PlainInsn( 496 Rops.opMoveResultPseudo(dest.getTypeBearer()), 497 pos, dest, RegisterSpecList.EMPTY); 498 499 dest = null; 500 } 501 if (ropOpcode == RegOps.NEW_ARRAY) { 502 /* 503 * In the original bytecode, this was either a primitive 504 * array constructor "newarray" or an object array 505 * constructor "anewarray". In the former case, there is 506 * no explicit constant, and in the latter, the constant 507 * is for the element type and not the array type. The rop 508 * instruction form for both of these is supposed to be 509 * the resulting array type, so we initialize / alter 510 * "cst" here, accordingly. Conveniently enough, the rop 511 * opcode already gets constructed with the proper array 512 * type. 513 */ 514 cst = CstType.intern(rop.getResult()); 515 } else if ((cst == null) && (sourceCount == 2)) { 516 TypeBearer lastType = sources.get(1).getTypeBearer(); 517 518 if (lastType.isConstant() 519 && advice.hasConstantOperation(rop, 520 sources.get(0), sources.get(1))) { 521 /* 522 * The target architecture has an instruction that can 523 * build in the constant found in the second argument, 524 * so pull it out of the sources and just use it as a 525 * constant here. 526 */ 527 cst = (Constant) lastType; 528 sources = sources.withoutLast(); 529 rop = Rops.ropFor(ropOpcode, destType, sources, cst); 530 } 531 } 532 533 SwitchList cases = getAuxCases(); 534 ArrayList<Constant> initValues = getInitValues(); 535 boolean canThrow = rop.canThrow(); 536 537 blockCanThrow |= canThrow; 538 539 if (cases != null) { 540 if (cases.size() == 0) { 541 // It's a default-only switch statement. It can happen! 542 insn = new PlainInsn(Rops.GOTO, pos, null, 543 RegisterSpecList.EMPTY); 544 primarySuccessorIndex = 0; 545 } else { 546 IntList values = cases.getValues(); 547 insn = new SwitchInsn(rop, pos, dest, sources, values); 548 primarySuccessorIndex = values.size(); 549 } 550 } else if (ropOpcode == RegOps.RETURN) { 551 /* 552 * Returns get turned into the combination of a move (if 553 * non-void and if the return doesn't already mention 554 * register 0) and a goto (to the return block). 555 */ 556 if (sources.size() != 0) { 557 RegisterSpec source = sources.get(0); 558 TypeBearer type = source.getTypeBearer(); 559 if (source.getReg() != 0) { 560 insns.add(new PlainInsn(Rops.opMove(type), pos, 561 RegisterSpec.make(0, type), 562 source)); 563 } 564 } 565 insn = new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY); 566 primarySuccessorIndex = 0; 567 updateReturnOp(rop, pos); 568 returns = true; 569 } else if (cst != null) { 570 if (canThrow) { 571 insn = 572 new ThrowingCstInsn(rop, pos, sources, catches, cst); 573 catchesUsed = true; 574 primarySuccessorIndex = catches.size(); 575 } else { 576 insn = new PlainCstInsn(rop, pos, dest, sources, cst); 577 } 578 } else if (canThrow) { 579 insn = new ThrowingInsn(rop, pos, sources, catches); 580 catchesUsed = true; 581 if (opcode == ByteOps.ATHROW) { 582 /* 583 * The op athrow is the only one where it's possible 584 * to have non-empty successors and yet not have a 585 * primary successor. 586 */ 587 primarySuccessorIndex = -1; 588 } else { 589 primarySuccessorIndex = catches.size(); 590 } 591 } else { 592 insn = new PlainInsn(rop, pos, dest, sources); 593 } 594 595 insns.add(insn); 596 597 if (moveResult != null) { 598 insns.add(moveResult); 599 } 600 601 /* 602 * If initValues is non-null, it means that the parser has seen a group 603 * of compatible constant initialization bytecodes that are applied to 604 * the current newarray. The action we take here is to convert these 605 * initialization bytecodes into a single fill-array-data ROP which lays 606 * out all the constant values in a table. 607 */ 608 if (initValues != null) { 609 extraBlockCount++; 610 insn = new FillArrayDataInsn(Rops.FILL_ARRAY_DATA, pos, 611 RegisterSpecList.make(moveResult.getResult()), initValues, 612 cst); 613 insns.add(insn); 614 } 615 } 616 617 /** 618 * Helper for {@link #run}, which gets the list of sources for the. 619 * instruction. 620 * 621 * @param opcode the opcode being translated 622 * @param stackPointer >= 0; the stack pointer after the instruction's 623 * arguments have been popped 624 * @return non-null; the sources 625 */ 626 private RegisterSpecList getSources(int opcode, int stackPointer) { 627 int count = argCount(); 628 629 if (count == 0) { 630 // We get an easy out if there aren't any sources. 631 return RegisterSpecList.EMPTY; 632 } 633 634 int localIndex = getLocalIndex(); 635 RegisterSpecList sources; 636 637 if (localIndex >= 0) { 638 // The instruction is operating on a local variable. 639 sources = new RegisterSpecList(1); 640 sources.set(0, RegisterSpec.make(localIndex, arg(0))); 641 } else { 642 sources = new RegisterSpecList(count); 643 int regAt = stackPointer; 644 for (int i = 0; i < count; i++) { 645 RegisterSpec spec = RegisterSpec.make(regAt, arg(i)); 646 sources.set(i, spec); 647 regAt += spec.getCategory(); 648 } 649 650 switch (opcode) { 651 case ByteOps.IASTORE: { 652 /* 653 * The Java argument order for array stores is 654 * (array, index, value), but the rop argument 655 * order is (value, array, index). The following 656 * code gets the right arguments in the right 657 * places. 658 */ 659 if (count != 3) { 660 throw new RuntimeException("shouldn't happen"); 661 } 662 RegisterSpec array = sources.get(0); 663 RegisterSpec index = sources.get(1); 664 RegisterSpec value = sources.get(2); 665 sources.set(0, value); 666 sources.set(1, array); 667 sources.set(2, index); 668 break; 669 } 670 case ByteOps.PUTFIELD: { 671 /* 672 * Similar to above: The Java argument order for 673 * putfield is (object, value), but the rop 674 * argument order is (value, object). 675 */ 676 if (count != 2) { 677 throw new RuntimeException("shouldn't happen"); 678 } 679 RegisterSpec obj = sources.get(0); 680 RegisterSpec value = sources.get(1); 681 sources.set(0, value); 682 sources.set(1, obj); 683 break; 684 } 685 } 686 } 687 688 sources.setImmutable(); 689 return sources; 690 } 691 692 /** 693 * Sets or updates the information about the return block. 694 * 695 * @param op non-null; the opcode to use 696 * @param pos non-null; the position to use 697 */ 698 private void updateReturnOp(Rop op, SourcePosition pos) { 699 if (op == null) { 700 throw new NullPointerException("op == null"); 701 } 702 703 if (pos == null) { 704 throw new NullPointerException("pos == null"); 705 } 706 707 if (returnOp == null) { 708 returnOp = op; 709 returnPosition = pos; 710 } else { 711 if (returnOp != op) { 712 throw new SimException("return op mismatch: " + op + ", " + 713 returnOp); 714 } 715 716 if (pos.getLine() > returnPosition.getLine()) { 717 // Pick the largest line number to be the "canonical" return. 718 returnPosition = pos; 719 } 720 } 721 } 722 723 /** 724 * Gets the register opcode for the given Java opcode. 725 * 726 * @param jop >= 0; the Java opcode 727 * @param cst null-ok; the constant argument, if any 728 * @return >= 0; the corresponding register opcode 729 */ 730 private int jopToRopOpcode(int jop, Constant cst) { 731 switch (jop) { 732 case ByteOps.POP: 733 case ByteOps.POP2: 734 case ByteOps.DUP: 735 case ByteOps.DUP_X1: 736 case ByteOps.DUP_X2: 737 case ByteOps.DUP2: 738 case ByteOps.DUP2_X1: 739 case ByteOps.DUP2_X2: 740 case ByteOps.SWAP: 741 case ByteOps.JSR: 742 case ByteOps.RET: 743 case ByteOps.MULTIANEWARRAY: { 744 // These need to be taken care of specially. 745 break; 746 } 747 case ByteOps.NOP: { 748 return RegOps.NOP; 749 } 750 case ByteOps.LDC: 751 case ByteOps.LDC2_W: { 752 return RegOps.CONST; 753 } 754 case ByteOps.ILOAD: 755 case ByteOps.ISTORE: { 756 return RegOps.MOVE; 757 } 758 case ByteOps.IALOAD: { 759 return RegOps.AGET; 760 } 761 case ByteOps.IASTORE: { 762 return RegOps.APUT; 763 } 764 case ByteOps.IADD: 765 case ByteOps.IINC: { 766 return RegOps.ADD; 767 } 768 case ByteOps.ISUB: { 769 return RegOps.SUB; 770 } 771 case ByteOps.IMUL: { 772 return RegOps.MUL; 773 } 774 case ByteOps.IDIV: { 775 return RegOps.DIV; 776 } 777 case ByteOps.IREM: { 778 return RegOps.REM; 779 } 780 case ByteOps.INEG: { 781 return RegOps.NEG; 782 } 783 case ByteOps.ISHL: { 784 return RegOps.SHL; 785 } 786 case ByteOps.ISHR: { 787 return RegOps.SHR; 788 } 789 case ByteOps.IUSHR: { 790 return RegOps.USHR; 791 } 792 case ByteOps.IAND: { 793 return RegOps.AND; 794 } 795 case ByteOps.IOR: { 796 return RegOps.OR; 797 } 798 case ByteOps.IXOR: { 799 return RegOps.XOR; 800 } 801 case ByteOps.I2L: 802 case ByteOps.I2F: 803 case ByteOps.I2D: 804 case ByteOps.L2I: 805 case ByteOps.L2F: 806 case ByteOps.L2D: 807 case ByteOps.F2I: 808 case ByteOps.F2L: 809 case ByteOps.F2D: 810 case ByteOps.D2I: 811 case ByteOps.D2L: 812 case ByteOps.D2F: { 813 return RegOps.CONV; 814 } 815 case ByteOps.I2B: { 816 return RegOps.TO_BYTE; 817 } 818 case ByteOps.I2C: { 819 return RegOps.TO_CHAR; 820 } 821 case ByteOps.I2S: { 822 return RegOps.TO_SHORT; 823 } 824 case ByteOps.LCMP: 825 case ByteOps.FCMPL: 826 case ByteOps.DCMPL: { 827 return RegOps.CMPL; 828 } 829 case ByteOps.FCMPG: 830 case ByteOps.DCMPG: { 831 return RegOps.CMPG; 832 } 833 case ByteOps.IFEQ: 834 case ByteOps.IF_ICMPEQ: 835 case ByteOps.IF_ACMPEQ: 836 case ByteOps.IFNULL: { 837 return RegOps.IF_EQ; 838 } 839 case ByteOps.IFNE: 840 case ByteOps.IF_ICMPNE: 841 case ByteOps.IF_ACMPNE: 842 case ByteOps.IFNONNULL: { 843 return RegOps.IF_NE; 844 } 845 case ByteOps.IFLT: 846 case ByteOps.IF_ICMPLT: { 847 return RegOps.IF_LT; 848 } 849 case ByteOps.IFGE: 850 case ByteOps.IF_ICMPGE: { 851 return RegOps.IF_GE; 852 } 853 case ByteOps.IFGT: 854 case ByteOps.IF_ICMPGT: { 855 return RegOps.IF_GT; 856 } 857 case ByteOps.IFLE: 858 case ByteOps.IF_ICMPLE: { 859 return RegOps.IF_LE; 860 } 861 case ByteOps.GOTO: { 862 return RegOps.GOTO; 863 } 864 case ByteOps.LOOKUPSWITCH: { 865 return RegOps.SWITCH; 866 } 867 case ByteOps.IRETURN: 868 case ByteOps.RETURN: { 869 return RegOps.RETURN; 870 } 871 case ByteOps.GETSTATIC: { 872 return RegOps.GET_STATIC; 873 } 874 case ByteOps.PUTSTATIC: { 875 return RegOps.PUT_STATIC; 876 } 877 case ByteOps.GETFIELD: { 878 return RegOps.GET_FIELD; 879 } 880 case ByteOps.PUTFIELD: { 881 return RegOps.PUT_FIELD; 882 } 883 case ByteOps.INVOKEVIRTUAL: { 884 return RegOps.INVOKE_VIRTUAL; 885 } 886 case ByteOps.INVOKESPECIAL: { 887 /* 888 * Determine whether the opcode should be 889 * INVOKE_DIRECT or INVOKE_SUPER. See vmspec-2 section 6 890 * on "invokespecial" as well as section 4.8.2 (7th 891 * bullet point) for the gory details. 892 */ 893 CstMethodRef ref = (CstMethodRef) cst; 894 if (ref.isInstanceInit() || 895 (ref.getDefiningClass() == method.getDefiningClass()) || 896 !method.getAccSuper()) { 897 return RegOps.INVOKE_DIRECT; 898 } 899 return RegOps.INVOKE_SUPER; 900 } 901 case ByteOps.INVOKESTATIC: { 902 return RegOps.INVOKE_STATIC; 903 } 904 case ByteOps.INVOKEINTERFACE: { 905 return RegOps.INVOKE_INTERFACE; 906 } 907 case ByteOps.NEW: { 908 return RegOps.NEW_INSTANCE; 909 } 910 case ByteOps.NEWARRAY: 911 case ByteOps.ANEWARRAY: { 912 return RegOps.NEW_ARRAY; 913 } 914 case ByteOps.ARRAYLENGTH: { 915 return RegOps.ARRAY_LENGTH; 916 } 917 case ByteOps.ATHROW: { 918 return RegOps.THROW; 919 } 920 case ByteOps.CHECKCAST: { 921 return RegOps.CHECK_CAST; 922 } 923 case ByteOps.INSTANCEOF: { 924 return RegOps.INSTANCE_OF; 925 } 926 case ByteOps.MONITORENTER: { 927 return RegOps.MONITOR_ENTER; 928 } 929 case ByteOps.MONITOREXIT: { 930 return RegOps.MONITOR_EXIT; 931 } 932 } 933 934 throw new RuntimeException("shouldn't happen"); 935 } 936} 937