1 /* 2 * Copyright 2013, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.dexlib2.builder; 33 34 import com.google.common.base.Function; 35 import com.google.common.collect.Iterables; 36 import com.google.common.collect.Lists; 37 import com.google.common.collect.Sets; 38 import org.jf.dexlib2.DebugItemType; 39 import org.jf.dexlib2.Opcode; 40 import org.jf.dexlib2.builder.debug.*; 41 import org.jf.dexlib2.builder.instruction.*; 42 import org.jf.dexlib2.iface.ExceptionHandler; 43 import org.jf.dexlib2.iface.MethodImplementation; 44 import org.jf.dexlib2.iface.TryBlock; 45 import org.jf.dexlib2.iface.debug.*; 46 import org.jf.dexlib2.iface.instruction.Instruction; 47 import org.jf.dexlib2.iface.instruction.SwitchElement; 48 import org.jf.dexlib2.iface.instruction.formats.*; 49 import org.jf.dexlib2.iface.reference.TypeReference; 50 import org.jf.util.ExceptionWithContext; 51 52 import javax.annotation.Nonnull; 53 import javax.annotation.Nullable; 54 import java.util.*; 55 56 public class MutableMethodImplementation implements MethodImplementation { 57 private final int registerCount; 58 final ArrayList<MethodLocation> instructionList = Lists.newArrayList(new MethodLocation(null, 0, 0)); 59 private final ArrayList<BuilderTryBlock> tryBlocks = Lists.newArrayList(); 60 private boolean fixInstructions = true; 61 62 public MutableMethodImplementation(@Nonnull MethodImplementation methodImplementation) { 63 this.registerCount = methodImplementation.getRegisterCount(); 64 65 int codeAddress = 0; 66 int index = 0; 67 68 for (Instruction instruction: methodImplementation.getInstructions()) { 69 codeAddress += instruction.getCodeUnits(); 70 index++; 71 72 instructionList.add(new MethodLocation(null, codeAddress, index)); 73 } 74 75 final int[] codeAddressToIndex = new int[codeAddress+1]; 76 Arrays.fill(codeAddressToIndex, -1); 77 78 for (int i=0; i<instructionList.size(); i++) { 79 codeAddressToIndex[instructionList.get(i).codeAddress] = i; 80 } 81 82 List<Task> switchPayloadTasks = Lists.newArrayList(); 83 index = 0; 84 for (final Instruction instruction: methodImplementation.getInstructions()) { 85 final MethodLocation location = instructionList.get(index); 86 final Opcode opcode = instruction.getOpcode(); 87 if (opcode == Opcode.PACKED_SWITCH_PAYLOAD || opcode == Opcode.SPARSE_SWITCH_PAYLOAD) { 88 switchPayloadTasks.add(new Task() { 89 @Override public void perform() { 90 convertAndSetInstruction(location, codeAddressToIndex, instruction); 91 } 92 }); 93 } else { 94 convertAndSetInstruction(location, codeAddressToIndex, instruction); 95 } 96 index++; 97 } 98 99 // the switch payload instructions must be converted last, so that any switch statements that refer to them 100 // have created the referring labels that we look for 101 for (Task switchPayloadTask: switchPayloadTasks) { 102 switchPayloadTask.perform(); 103 } 104 105 for (DebugItem debugItem: methodImplementation.getDebugItems()) { 106 int debugCodeAddress = debugItem.getCodeAddress(); 107 int locationIndex = mapCodeAddressToIndex(codeAddressToIndex, debugCodeAddress); 108 MethodLocation debugLocation = instructionList.get(locationIndex); 109 BuilderDebugItem builderDebugItem = convertDebugItem(debugItem); 110 debugLocation.getDebugItems().add(builderDebugItem); 111 builderDebugItem.location = debugLocation; 112 } 113 114 for (TryBlock<? extends ExceptionHandler> tryBlock: methodImplementation.getTryBlocks()) { 115 Label startLabel = newLabel(codeAddressToIndex, tryBlock.getStartCodeAddress()); 116 Label endLabel = newLabel(codeAddressToIndex, tryBlock.getStartCodeAddress() + tryBlock.getCodeUnitCount()); 117 118 for (ExceptionHandler exceptionHandler: tryBlock.getExceptionHandlers()) { 119 tryBlocks.add(new BuilderTryBlock(startLabel, endLabel, 120 exceptionHandler.getExceptionTypeReference(), 121 newLabel(codeAddressToIndex, exceptionHandler.getHandlerCodeAddress()))); 122 } 123 } 124 } 125 126 private interface Task { 127 void perform(); 128 } 129 130 public MutableMethodImplementation(int registerCount) { 131 this.registerCount = registerCount; 132 } 133 134 @Override public int getRegisterCount() { 135 return registerCount; 136 } 137 138 @Nonnull 139 public List<BuilderInstruction> getInstructions() { 140 if (fixInstructions) { 141 fixInstructions(); 142 } 143 144 return new AbstractList<BuilderInstruction>() { 145 @Override public BuilderInstruction get(int i) { 146 if (i >= size()) { 147 throw new IndexOutOfBoundsException(); 148 } 149 if (fixInstructions) { 150 fixInstructions(); 151 } 152 return instructionList.get(i).instruction; 153 } 154 155 @Override public int size() { 156 if (fixInstructions) { 157 fixInstructions(); 158 } 159 // don't include the last MethodLocation, which always has a null instruction 160 return instructionList.size() - 1; 161 } 162 }; 163 } 164 165 @Nonnull @Override public List<BuilderTryBlock> getTryBlocks() { 166 if (fixInstructions) { 167 fixInstructions(); 168 } 169 return Collections.unmodifiableList(tryBlocks); 170 } 171 172 @Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() { 173 if (fixInstructions) { 174 fixInstructions(); 175 } 176 return Iterables.concat( 177 Iterables.transform(instructionList, new Function<MethodLocation, Iterable<? extends DebugItem>>() { 178 @Nullable @Override public Iterable<? extends DebugItem> apply(@Nullable MethodLocation input) { 179 assert input != null; 180 if (fixInstructions) { 181 throw new IllegalStateException("This iterator was invalidated by a change to" + 182 " this MutableMethodImplementation."); 183 } 184 return input.getDebugItems(); 185 } 186 })); 187 } 188 189 public void addCatch(@Nullable TypeReference type, @Nonnull Label from, 190 @Nonnull Label to, @Nonnull Label handler) { 191 tryBlocks.add(new BuilderTryBlock(from, to, type, handler)); 192 } 193 194 public void addCatch(@Nullable String type, @Nonnull Label from, @Nonnull Label to, 195 @Nonnull Label handler) { 196 tryBlocks.add(new BuilderTryBlock(from, to, type, handler)); 197 } 198 199 public void addCatch(@Nonnull Label from, @Nonnull Label to, @Nonnull Label handler) { 200 tryBlocks.add(new BuilderTryBlock(from, to, handler)); 201 } 202 203 public void addInstruction(int index, BuilderInstruction instruction) { 204 // the end check here is intentially >= rather than >, because the list always includes an "empty" 205 // (null instruction) MethodLocation at the end. To add an instruction to the end of the list, the user would 206 // provide the index of this empty item, which would be size() - 1. 207 if (index >= instructionList.size()) { 208 throw new IndexOutOfBoundsException(); 209 } 210 211 if (index == instructionList.size() - 1) { 212 addInstruction(instruction); 213 return; 214 } 215 int codeAddress = instructionList.get(index).getCodeAddress(); 216 MethodLocation newLoc = new MethodLocation(instruction, codeAddress, index); 217 instructionList.add(index, newLoc); 218 instruction.location = newLoc; 219 220 codeAddress += instruction.getCodeUnits(); 221 222 for (int i=index+1; i<instructionList.size(); i++) { 223 MethodLocation location = instructionList.get(i); 224 location.index++; 225 location.codeAddress = codeAddress; 226 if (location.instruction != null) { 227 codeAddress += location.instruction.getCodeUnits(); 228 } else { 229 // only the last MethodLocation should have a null instruction 230 assert i == instructionList.size()-1; 231 } 232 } 233 234 this.fixInstructions = true; 235 } 236 237 public void addInstruction(@Nonnull BuilderInstruction instruction) { 238 MethodLocation last = instructionList.get(instructionList.size()-1); 239 last.instruction = instruction; 240 instruction.location = last; 241 242 int nextCodeAddress = last.codeAddress + instruction.getCodeUnits(); 243 instructionList.add(new MethodLocation(null, nextCodeAddress, instructionList.size())); 244 245 this.fixInstructions = true; 246 } 247 248 public void replaceInstruction(int index, @Nonnull BuilderInstruction replacementInstruction) { 249 if (index >= instructionList.size() - 1) { 250 throw new IndexOutOfBoundsException(); 251 } 252 253 MethodLocation replaceLocation = instructionList.get(index); 254 replacementInstruction.location = replaceLocation; 255 BuilderInstruction old = replaceLocation.instruction; 256 assert old != null; 257 old.location = null; 258 replaceLocation.instruction = replacementInstruction; 259 260 // TODO: factor out index/address fix up loop 261 int codeAddress = replaceLocation.codeAddress + replaceLocation.instruction.getCodeUnits(); 262 for (int i=index+1; i<instructionList.size(); i++) { 263 MethodLocation location = instructionList.get(i); 264 location.codeAddress = codeAddress; 265 266 Instruction instruction = location.getInstruction(); 267 if (instruction != null) { 268 codeAddress += instruction.getCodeUnits(); 269 } else { 270 assert i == instructionList.size() - 1; 271 } 272 } 273 274 this.fixInstructions = true; 275 } 276 277 public void removeInstruction(int index) { 278 if (index >= instructionList.size() - 1) { 279 throw new IndexOutOfBoundsException(); 280 } 281 282 MethodLocation toRemove = instructionList.get(index); 283 toRemove.instruction = null; 284 MethodLocation next = instructionList.get(index+1); 285 toRemove.mergeInto(next); 286 287 instructionList.remove(index); 288 int codeAddress = toRemove.codeAddress; 289 for (int i=index; i<instructionList.size(); i++) { 290 MethodLocation location = instructionList.get(i); 291 location.index = i; 292 location.codeAddress = codeAddress; 293 294 Instruction instruction = location.getInstruction(); 295 if (instruction != null) { 296 codeAddress += instruction.getCodeUnits(); 297 } else { 298 assert i == instructionList.size() - 1; 299 } 300 } 301 302 this.fixInstructions = true; 303 } 304 305 public void swapInstructions(int index1, int index2) { 306 if (index1 >= instructionList.size() - 1 || index2 >= instructionList.size() - 1) { 307 throw new IndexOutOfBoundsException(); 308 } 309 MethodLocation first = instructionList.get(index1); 310 MethodLocation second = instructionList.get(index2); 311 312 // only the last MethodLocation may have a null instruction 313 assert first.instruction != null; 314 assert second.instruction != null; 315 316 first.instruction.location = second; 317 second.instruction.location = first; 318 319 { 320 BuilderInstruction tmp = second.instruction; 321 second.instruction = first.instruction; 322 first.instruction = tmp; 323 } 324 325 if (index2 < index1) { 326 int tmp = index2; 327 index2 = index1; 328 index1 = tmp; 329 } 330 331 int codeAddress = first.codeAddress + first.instruction.getCodeUnits(); 332 for (int i=index1+1; i<=index2; i++) { 333 MethodLocation location = instructionList.get(i); 334 location.codeAddress = codeAddress; 335 336 Instruction instruction = location.instruction; 337 assert instruction != null; 338 codeAddress += location.instruction.getCodeUnits(); 339 } 340 341 this.fixInstructions = true; 342 } 343 344 @Nullable 345 private BuilderInstruction getFirstNonNop(int startIndex) { 346 347 for (int i=startIndex; i<instructionList.size()-1; i++) { 348 BuilderInstruction instruction = instructionList.get(i).instruction; 349 assert instruction != null; 350 if (instruction.getOpcode() != Opcode.NOP) { 351 return instruction; 352 } 353 } 354 return null; 355 } 356 357 private void fixInstructions() { 358 HashSet<MethodLocation> payloadLocations = Sets.newHashSet(); 359 360 for (MethodLocation location: instructionList) { 361 BuilderInstruction instruction = location.instruction; 362 if (instruction != null) { 363 switch (instruction.getOpcode()) { 364 case SPARSE_SWITCH: 365 case PACKED_SWITCH: { 366 MethodLocation targetLocation = 367 ((BuilderOffsetInstruction)instruction).getTarget().getLocation(); 368 BuilderInstruction targetInstruction = targetLocation.instruction; 369 if (targetInstruction == null) { 370 throw new IllegalStateException(String.format("Switch instruction at address/index " + 371 "0x%x/%d points to the end of the method.", location.codeAddress, location.index)); 372 } 373 374 if (targetInstruction.getOpcode() == Opcode.NOP) { 375 targetInstruction = getFirstNonNop(targetLocation.index+1); 376 } 377 if (targetInstruction == null || !(targetInstruction instanceof BuilderSwitchPayload)) { 378 throw new IllegalStateException(String.format("Switch instruction at address/index " + 379 "0x%x/%d does not refer to a payload instruction.", 380 location.codeAddress, location.index)); 381 } 382 if ((instruction.opcode == Opcode.PACKED_SWITCH && 383 targetInstruction.getOpcode() != Opcode.PACKED_SWITCH_PAYLOAD) || 384 (instruction.opcode == Opcode.SPARSE_SWITCH && 385 targetInstruction.getOpcode() != Opcode.SPARSE_SWITCH_PAYLOAD)) { 386 throw new IllegalStateException(String.format("Switch instruction at address/index " + 387 "0x%x/%d refers to the wrong type of payload instruction.", 388 location.codeAddress, location.index)); 389 } 390 391 if (!payloadLocations.add(targetLocation)) { 392 throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + 393 "This is not currently supported. Please file a bug :)"); 394 } 395 396 ((BuilderSwitchPayload)targetInstruction).referrer = location; 397 break; 398 } 399 } 400 } 401 } 402 403 boolean madeChanges; 404 do { 405 madeChanges = false; 406 407 for (int index=0; index<instructionList.size(); index++) { 408 MethodLocation location = instructionList.get(index); 409 BuilderInstruction instruction = location.instruction; 410 if (instruction != null) { 411 switch (instruction.getOpcode()) { 412 case GOTO: { 413 int offset = ((BuilderOffsetInstruction)instruction).internalGetCodeOffset(); 414 if (offset < Byte.MIN_VALUE || offset > Byte.MAX_VALUE) { 415 BuilderOffsetInstruction replacement; 416 if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { 417 replacement = new BuilderInstruction30t(Opcode.GOTO_32, 418 ((BuilderOffsetInstruction)instruction).getTarget()); 419 } else { 420 replacement = new BuilderInstruction20t(Opcode.GOTO_16, 421 ((BuilderOffsetInstruction)instruction).getTarget()); 422 } 423 replaceInstruction(location.index, replacement); 424 madeChanges = true; 425 } 426 break; 427 } 428 case GOTO_16: { 429 int offset = ((BuilderOffsetInstruction)instruction).internalGetCodeOffset(); 430 if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { 431 BuilderOffsetInstruction replacement = new BuilderInstruction30t(Opcode.GOTO_32, 432 ((BuilderOffsetInstruction)instruction).getTarget()); 433 replaceInstruction(location.index, replacement); 434 madeChanges = true; 435 } 436 break; 437 } 438 case SPARSE_SWITCH_PAYLOAD: 439 case PACKED_SWITCH_PAYLOAD: 440 if (((BuilderSwitchPayload)instruction).referrer == null) { 441 // if the switch payload isn't referenced, just remove it 442 removeInstruction(index); 443 index--; 444 madeChanges = true; 445 break; 446 } 447 // intentional fall-through 448 case ARRAY_PAYLOAD: { 449 if ((location.codeAddress & 0x01) != 0) { 450 int previousIndex = location.index - 1; 451 MethodLocation previousLocation = instructionList.get(previousIndex); 452 Instruction previousInstruction = previousLocation.instruction; 453 assert previousInstruction != null; 454 if (previousInstruction.getOpcode() == Opcode.NOP) { 455 removeInstruction(previousIndex); 456 index--; 457 } else { 458 addInstruction(location.index, new BuilderInstruction10x(Opcode.NOP)); 459 index++; 460 } 461 madeChanges = true; 462 } 463 break; 464 } 465 } 466 } 467 } 468 } while (madeChanges); 469 470 fixInstructions = false; 471 } 472 473 private int mapCodeAddressToIndex(@Nonnull int[] codeAddressToIndex, int codeAddress) { 474 int index; 475 do { 476 index = codeAddressToIndex[codeAddress]; 477 if (index < 0) { 478 codeAddress--; 479 } else { 480 return index; 481 } 482 } while (true); 483 } 484 485 private int mapCodeAddressToIndex(int codeAddress) { 486 float avgCodeUnitsPerInstruction = 1.9f; 487 488 int index = (int)(codeAddress/avgCodeUnitsPerInstruction); 489 if (index >= instructionList.size()) { 490 index = instructionList.size() - 1; 491 } 492 493 MethodLocation guessedLocation = instructionList.get(index); 494 495 if (guessedLocation.codeAddress == codeAddress) { 496 return index; 497 } else if (guessedLocation.codeAddress > codeAddress) { 498 do { 499 index--; 500 } while (instructionList.get(index).codeAddress > codeAddress); 501 return index; 502 } else { 503 do { 504 index++; 505 } while (index < instructionList.size() && instructionList.get(index).codeAddress <= codeAddress); 506 return index-1; 507 } 508 } 509 510 @Nonnull 511 public Label newLabelForAddress(int codeAddress) { 512 if (codeAddress < 0 || codeAddress > instructionList.get(instructionList.size()-1).codeAddress) { 513 throw new IndexOutOfBoundsException(String.format("codeAddress %d out of bounds", codeAddress)); 514 } 515 MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddress)); 516 return referent.addNewLabel(); 517 } 518 519 @Nonnull 520 public Label newLabelForIndex(int instructionIndex) { 521 if (instructionIndex < 0 || instructionIndex >= instructionList.size()) { 522 throw new IndexOutOfBoundsException(String.format("instruction index %d out of bounds", instructionIndex)); 523 } 524 MethodLocation referent = instructionList.get(instructionIndex); 525 return referent.addNewLabel(); 526 } 527 528 @Nonnull 529 private Label newLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) { 530 MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress)); 531 return referent.addNewLabel(); 532 } 533 534 private static class SwitchPayloadReferenceLabel extends Label { 535 @Nonnull public MethodLocation switchLocation; 536 } 537 538 @Nonnull 539 public Label newSwitchPayloadReferenceLabel(@Nonnull MethodLocation switchLocation, 540 @Nonnull int[] codeAddressToIndex, int codeAddress) { 541 MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress)); 542 SwitchPayloadReferenceLabel label = new SwitchPayloadReferenceLabel(); 543 label.switchLocation = switchLocation; 544 referent.getLabels().add(label); 545 return label; 546 } 547 548 private void setInstruction(@Nonnull MethodLocation location, @Nonnull BuilderInstruction instruction) { 549 location.instruction = instruction; 550 instruction.location = location; 551 } 552 553 private void convertAndSetInstruction(@Nonnull MethodLocation location, int[] codeAddressToIndex, 554 @Nonnull Instruction instruction) { 555 switch (instruction.getOpcode().format) { 556 case Format10t: 557 setInstruction(location, newBuilderInstruction10t(location.codeAddress, 558 codeAddressToIndex, 559 (Instruction10t) instruction)); 560 return; 561 case Format10x: 562 setInstruction(location, newBuilderInstruction10x((Instruction10x) instruction)); 563 return; 564 case Format11n: 565 setInstruction(location, newBuilderInstruction11n((Instruction11n) instruction)); 566 return; 567 case Format11x: 568 setInstruction(location, newBuilderInstruction11x((Instruction11x) instruction)); 569 return; 570 case Format12x: 571 setInstruction(location, newBuilderInstruction12x((Instruction12x) instruction)); 572 return; 573 case Format20bc: 574 setInstruction(location, newBuilderInstruction20bc((Instruction20bc) instruction)); 575 return; 576 case Format20t: 577 setInstruction(location, newBuilderInstruction20t(location.codeAddress, 578 codeAddressToIndex, 579 (Instruction20t) instruction)); 580 return; 581 case Format21c: 582 setInstruction(location, newBuilderInstruction21c((Instruction21c) instruction)); 583 return; 584 case Format21ih: 585 setInstruction(location, newBuilderInstruction21ih((Instruction21ih) instruction)); 586 return; 587 case Format21lh: 588 setInstruction(location, newBuilderInstruction21lh((Instruction21lh) instruction)); 589 return; 590 case Format21s: 591 setInstruction(location, newBuilderInstruction21s((Instruction21s) instruction)); 592 return; 593 case Format21t: 594 setInstruction(location, newBuilderInstruction21t(location.codeAddress, 595 codeAddressToIndex, 596 (Instruction21t) instruction)); 597 return; 598 case Format22b: 599 setInstruction(location, newBuilderInstruction22b((Instruction22b) instruction)); 600 return; 601 case Format22c: 602 setInstruction(location, newBuilderInstruction22c((Instruction22c) instruction)); 603 return; 604 case Format22cs: 605 setInstruction(location, newBuilderInstruction22cs((Instruction22cs) instruction)); 606 return; 607 case Format22s: 608 setInstruction(location, newBuilderInstruction22s((Instruction22s) instruction)); 609 return; 610 case Format22t: 611 setInstruction(location, newBuilderInstruction22t(location.codeAddress, 612 codeAddressToIndex, 613 (Instruction22t) instruction)); 614 return; 615 case Format22x: 616 setInstruction(location, newBuilderInstruction22x((Instruction22x) instruction)); 617 return; 618 case Format23x: 619 setInstruction(location, newBuilderInstruction23x((Instruction23x) instruction)); 620 return; 621 case Format30t: 622 setInstruction(location, newBuilderInstruction30t(location.codeAddress, 623 codeAddressToIndex, 624 (Instruction30t) instruction)); 625 return; 626 case Format31c: 627 setInstruction(location, newBuilderInstruction31c((Instruction31c) instruction)); 628 return; 629 case Format31i: 630 setInstruction(location, newBuilderInstruction31i((Instruction31i) instruction)); 631 return; 632 case Format31t: 633 setInstruction(location, newBuilderInstruction31t(location, codeAddressToIndex, 634 (Instruction31t) instruction)); 635 return; 636 case Format32x: 637 setInstruction(location, newBuilderInstruction32x((Instruction32x) instruction)); 638 return; 639 case Format35c: 640 setInstruction(location, newBuilderInstruction35c((Instruction35c) instruction)); 641 return; 642 case Format35mi: 643 setInstruction(location, newBuilderInstruction35mi((Instruction35mi) instruction)); 644 return; 645 case Format35ms: 646 setInstruction(location, newBuilderInstruction35ms((Instruction35ms) instruction)); 647 return; 648 case Format3rc: 649 setInstruction(location, newBuilderInstruction3rc((Instruction3rc)instruction)); 650 return; 651 case Format3rmi: 652 setInstruction(location, newBuilderInstruction3rmi((Instruction3rmi)instruction)); 653 return; 654 case Format3rms: 655 setInstruction(location, newBuilderInstruction3rms((Instruction3rms)instruction)); 656 return; 657 case Format51l: 658 setInstruction(location, newBuilderInstruction51l((Instruction51l)instruction)); 659 return; 660 case PackedSwitchPayload: 661 setInstruction(location, 662 newBuilderPackedSwitchPayload(location, codeAddressToIndex, (PackedSwitchPayload)instruction)); 663 return; 664 case SparseSwitchPayload: 665 setInstruction(location, 666 newBuilderSparseSwitchPayload(location, codeAddressToIndex, (SparseSwitchPayload)instruction)); 667 return; 668 case ArrayPayload: 669 setInstruction(location, newBuilderArrayPayload((ArrayPayload)instruction)); 670 return; 671 default: 672 throw new ExceptionWithContext("Instruction format %s not supported", instruction.getOpcode().format); 673 } 674 } 675 676 @Nonnull 677 private BuilderInstruction10t newBuilderInstruction10t(int codeAddress, int[] codeAddressToIndex, 678 @Nonnull Instruction10t instruction) { 679 return new BuilderInstruction10t( 680 instruction.getOpcode(), 681 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 682 } 683 684 @Nonnull 685 private BuilderInstruction10x newBuilderInstruction10x(@Nonnull Instruction10x instruction) { 686 return new BuilderInstruction10x( 687 instruction.getOpcode()); 688 } 689 690 @Nonnull 691 private BuilderInstruction11n newBuilderInstruction11n(@Nonnull Instruction11n instruction) { 692 return new BuilderInstruction11n( 693 instruction.getOpcode(), 694 instruction.getRegisterA(), 695 instruction.getNarrowLiteral()); 696 } 697 698 @Nonnull 699 private BuilderInstruction11x newBuilderInstruction11x(@Nonnull Instruction11x instruction) { 700 return new BuilderInstruction11x( 701 instruction.getOpcode(), 702 instruction.getRegisterA()); 703 } 704 705 @Nonnull 706 private BuilderInstruction12x newBuilderInstruction12x(@Nonnull Instruction12x instruction) { 707 return new BuilderInstruction12x( 708 instruction.getOpcode(), 709 instruction.getRegisterA(), 710 instruction.getRegisterB()); 711 } 712 713 @Nonnull 714 private BuilderInstruction20bc newBuilderInstruction20bc(@Nonnull Instruction20bc instruction) { 715 return new BuilderInstruction20bc( 716 instruction.getOpcode(), 717 instruction.getVerificationError(), 718 instruction.getReference()); 719 } 720 721 @Nonnull 722 private BuilderInstruction20t newBuilderInstruction20t(int codeAddress, int[] codeAddressToIndex, 723 @Nonnull Instruction20t instruction) { 724 return new BuilderInstruction20t( 725 instruction.getOpcode(), 726 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 727 } 728 729 @Nonnull 730 private BuilderInstruction21c newBuilderInstruction21c(@Nonnull Instruction21c instruction) { 731 return new BuilderInstruction21c( 732 instruction.getOpcode(), 733 instruction.getRegisterA(), 734 instruction.getReference()); 735 } 736 737 @Nonnull 738 private BuilderInstruction21ih newBuilderInstruction21ih(@Nonnull Instruction21ih instruction) { 739 return new BuilderInstruction21ih( 740 instruction.getOpcode(), 741 instruction.getRegisterA(), 742 instruction.getNarrowLiteral()); 743 } 744 745 @Nonnull 746 private BuilderInstruction21lh newBuilderInstruction21lh(@Nonnull Instruction21lh instruction) { 747 return new BuilderInstruction21lh( 748 instruction.getOpcode(), 749 instruction.getRegisterA(), 750 instruction.getWideLiteral()); 751 } 752 753 @Nonnull 754 private BuilderInstruction21s newBuilderInstruction21s(@Nonnull Instruction21s instruction) { 755 return new BuilderInstruction21s( 756 instruction.getOpcode(), 757 instruction.getRegisterA(), 758 instruction.getNarrowLiteral()); 759 } 760 761 @Nonnull 762 private BuilderInstruction21t newBuilderInstruction21t(int codeAddress, int[] codeAddressToIndex, 763 @Nonnull Instruction21t instruction) { 764 return new BuilderInstruction21t( 765 instruction.getOpcode(), 766 instruction.getRegisterA(), 767 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 768 } 769 770 @Nonnull 771 private BuilderInstruction22b newBuilderInstruction22b(@Nonnull Instruction22b instruction) { 772 return new BuilderInstruction22b( 773 instruction.getOpcode(), 774 instruction.getRegisterA(), 775 instruction.getRegisterB(), 776 instruction.getNarrowLiteral()); 777 } 778 779 @Nonnull 780 private BuilderInstruction22c newBuilderInstruction22c(@Nonnull Instruction22c instruction) { 781 return new BuilderInstruction22c( 782 instruction.getOpcode(), 783 instruction.getRegisterA(), 784 instruction.getRegisterB(), 785 instruction.getReference()); 786 } 787 788 @Nonnull 789 private BuilderInstruction22cs newBuilderInstruction22cs(@Nonnull Instruction22cs instruction) { 790 return new BuilderInstruction22cs( 791 instruction.getOpcode(), 792 instruction.getRegisterA(), 793 instruction.getRegisterB(), 794 instruction.getFieldOffset()); 795 } 796 797 @Nonnull 798 private BuilderInstruction22s newBuilderInstruction22s(@Nonnull Instruction22s instruction) { 799 return new BuilderInstruction22s( 800 instruction.getOpcode(), 801 instruction.getRegisterA(), 802 instruction.getRegisterB(), 803 instruction.getNarrowLiteral()); 804 } 805 806 @Nonnull 807 private BuilderInstruction22t newBuilderInstruction22t(int codeAddress, int[] codeAddressToIndex, 808 @Nonnull Instruction22t instruction) { 809 return new BuilderInstruction22t( 810 instruction.getOpcode(), 811 instruction.getRegisterA(), 812 instruction.getRegisterB(), 813 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 814 } 815 816 @Nonnull 817 private BuilderInstruction22x newBuilderInstruction22x(@Nonnull Instruction22x instruction) { 818 return new BuilderInstruction22x( 819 instruction.getOpcode(), 820 instruction.getRegisterA(), 821 instruction.getRegisterB()); 822 } 823 824 @Nonnull 825 private BuilderInstruction23x newBuilderInstruction23x(@Nonnull Instruction23x instruction) { 826 return new BuilderInstruction23x( 827 instruction.getOpcode(), 828 instruction.getRegisterA(), 829 instruction.getRegisterB(), 830 instruction.getRegisterC()); 831 } 832 833 @Nonnull 834 private BuilderInstruction30t newBuilderInstruction30t(int codeAddress, int[] codeAddressToIndex, 835 @Nonnull Instruction30t instruction) { 836 return new BuilderInstruction30t( 837 instruction.getOpcode(), 838 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 839 } 840 841 @Nonnull 842 private BuilderInstruction31c newBuilderInstruction31c(@Nonnull Instruction31c instruction) { 843 return new BuilderInstruction31c( 844 instruction.getOpcode(), 845 instruction.getRegisterA(), 846 instruction.getReference()); 847 } 848 849 @Nonnull 850 private BuilderInstruction31i newBuilderInstruction31i(@Nonnull Instruction31i instruction) { 851 return new BuilderInstruction31i( 852 instruction.getOpcode(), 853 instruction.getRegisterA(), 854 instruction.getNarrowLiteral()); 855 } 856 857 @Nonnull 858 private BuilderInstruction31t newBuilderInstruction31t(@Nonnull MethodLocation location , int[] codeAddressToIndex, 859 @Nonnull Instruction31t instruction) { 860 int codeAddress = location.getCodeAddress(); 861 Label newLabel; 862 if (instruction.getOpcode() != Opcode.FILL_ARRAY_DATA) { 863 // if it's a sparse switch or packed switch 864 newLabel = newSwitchPayloadReferenceLabel(location, codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 865 } else { 866 newLabel = newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 867 } 868 return new BuilderInstruction31t( 869 instruction.getOpcode(), 870 instruction.getRegisterA(), 871 newLabel); 872 } 873 874 @Nonnull 875 private BuilderInstruction32x newBuilderInstruction32x(@Nonnull Instruction32x instruction) { 876 return new BuilderInstruction32x( 877 instruction.getOpcode(), 878 instruction.getRegisterA(), 879 instruction.getRegisterB()); 880 } 881 882 @Nonnull 883 private BuilderInstruction35c newBuilderInstruction35c(@Nonnull Instruction35c instruction) { 884 return new BuilderInstruction35c( 885 instruction.getOpcode(), 886 instruction.getRegisterCount(), 887 instruction.getRegisterC(), 888 instruction.getRegisterD(), 889 instruction.getRegisterE(), 890 instruction.getRegisterF(), 891 instruction.getRegisterG(), 892 instruction.getReference()); 893 } 894 895 @Nonnull 896 private BuilderInstruction35mi newBuilderInstruction35mi(@Nonnull Instruction35mi instruction) { 897 return new BuilderInstruction35mi( 898 instruction.getOpcode(), 899 instruction.getRegisterCount(), 900 instruction.getRegisterC(), 901 instruction.getRegisterD(), 902 instruction.getRegisterE(), 903 instruction.getRegisterF(), 904 instruction.getRegisterG(), 905 instruction.getInlineIndex()); 906 } 907 908 @Nonnull 909 private BuilderInstruction35ms newBuilderInstruction35ms(@Nonnull Instruction35ms instruction) { 910 return new BuilderInstruction35ms( 911 instruction.getOpcode(), 912 instruction.getRegisterCount(), 913 instruction.getRegisterC(), 914 instruction.getRegisterD(), 915 instruction.getRegisterE(), 916 instruction.getRegisterF(), 917 instruction.getRegisterG(), 918 instruction.getVtableIndex()); 919 } 920 921 @Nonnull 922 private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) { 923 return new BuilderInstruction3rc( 924 instruction.getOpcode(), 925 instruction.getStartRegister(), 926 instruction.getRegisterCount(), 927 instruction.getReference()); 928 } 929 930 @Nonnull 931 private BuilderInstruction3rmi newBuilderInstruction3rmi(@Nonnull Instruction3rmi instruction) { 932 return new BuilderInstruction3rmi( 933 instruction.getOpcode(), 934 instruction.getStartRegister(), 935 instruction.getRegisterCount(), 936 instruction.getInlineIndex()); 937 } 938 939 @Nonnull 940 private BuilderInstruction3rms newBuilderInstruction3rms(@Nonnull Instruction3rms instruction) { 941 return new BuilderInstruction3rms( 942 instruction.getOpcode(), 943 instruction.getStartRegister(), 944 instruction.getRegisterCount(), 945 instruction.getVtableIndex()); 946 } 947 948 @Nonnull 949 private BuilderInstruction51l newBuilderInstruction51l(@Nonnull Instruction51l instruction) { 950 return new BuilderInstruction51l( 951 instruction.getOpcode(), 952 instruction.getRegisterA(), 953 instruction.getWideLiteral()); 954 } 955 956 @Nullable 957 private MethodLocation findSwitchForPayload(@Nonnull MethodLocation payloadLocation) { 958 MethodLocation location = payloadLocation; 959 MethodLocation switchLocation = null; 960 do { 961 for (Label label: location.getLabels()) { 962 if (label instanceof SwitchPayloadReferenceLabel) { 963 if (switchLocation != null) { 964 throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + 965 "This is not currently supported. Please file a bug :)"); 966 } 967 switchLocation = ((SwitchPayloadReferenceLabel)label).switchLocation; 968 } 969 } 970 971 // A switch instruction can refer to the payload instruction itself, or to a nop before the payload 972 // instruction. 973 // We need to search for all occurrences of a switch reference, so we can detect when multiple switch 974 // statements refer to the same payload 975 // TODO: confirm that it could refer to the first NOP in a series of NOPs preceding the payload 976 if (location.index == 0) { 977 return switchLocation; 978 } 979 location = instructionList.get(location.index - 1); 980 if (location.instruction == null || location.instruction.getOpcode() != Opcode.NOP) { 981 return switchLocation; 982 } 983 } while (true); 984 } 985 986 @Nonnull 987 private BuilderPackedSwitchPayload newBuilderPackedSwitchPayload(@Nonnull MethodLocation location, 988 @Nonnull int[] codeAddressToIndex, 989 @Nonnull PackedSwitchPayload instruction) { 990 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 991 if (switchElements.size() == 0) { 992 return new BuilderPackedSwitchPayload(0, null); 993 } 994 995 MethodLocation switchLocation = findSwitchForPayload(location); 996 int baseAddress; 997 if (switchLocation == null) { 998 baseAddress = 0; 999 } else { 1000 baseAddress = switchLocation.codeAddress; 1001 } 1002 1003 List<Label> labels = Lists.newArrayList(); 1004 for (SwitchElement element: switchElements) { 1005 labels.add(newLabel(codeAddressToIndex, element.getOffset() + baseAddress)); 1006 } 1007 1008 return new BuilderPackedSwitchPayload(switchElements.get(0).getKey(), labels); 1009 } 1010 1011 @Nonnull 1012 private BuilderSparseSwitchPayload newBuilderSparseSwitchPayload(@Nonnull MethodLocation location, 1013 @Nonnull int[] codeAddressToIndex, 1014 @Nonnull SparseSwitchPayload instruction) { 1015 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 1016 if (switchElements.size() == 0) { 1017 return new BuilderSparseSwitchPayload(null); 1018 } 1019 1020 MethodLocation switchLocation = findSwitchForPayload(location); 1021 int baseAddress; 1022 if (switchLocation == null) { 1023 baseAddress = 0; 1024 } else { 1025 baseAddress = switchLocation.codeAddress; 1026 } 1027 1028 List<SwitchLabelElement> labelElements = Lists.newArrayList(); 1029 for (SwitchElement element: switchElements) { 1030 labelElements.add(new SwitchLabelElement(element.getKey(), 1031 newLabel(codeAddressToIndex, element.getOffset() + baseAddress))); 1032 } 1033 1034 return new BuilderSparseSwitchPayload(labelElements); 1035 } 1036 1037 @Nonnull 1038 private BuilderArrayPayload newBuilderArrayPayload(@Nonnull ArrayPayload instruction) { 1039 return new BuilderArrayPayload(instruction.getElementWidth(), instruction.getArrayElements()); 1040 } 1041 1042 @Nonnull 1043 private BuilderDebugItem convertDebugItem(@Nonnull DebugItem debugItem) { 1044 switch (debugItem.getDebugItemType()) { 1045 case DebugItemType.START_LOCAL: { 1046 StartLocal startLocal = (StartLocal)debugItem; 1047 return new BuilderStartLocal(startLocal.getRegister(), startLocal.getNameReference(), 1048 startLocal.getTypeReference(), startLocal.getSignatureReference()); 1049 } 1050 case DebugItemType.END_LOCAL: { 1051 EndLocal endLocal = (EndLocal)debugItem; 1052 return new BuilderEndLocal(endLocal.getRegister()); 1053 } 1054 case DebugItemType.RESTART_LOCAL: { 1055 RestartLocal restartLocal = (RestartLocal)debugItem; 1056 return new BuilderRestartLocal(restartLocal.getRegister()); 1057 } 1058 case DebugItemType.PROLOGUE_END: 1059 return new BuilderPrologueEnd(); 1060 case DebugItemType.EPILOGUE_BEGIN: 1061 return new BuilderEpilogueBegin(); 1062 case DebugItemType.LINE_NUMBER: { 1063 LineNumber lineNumber = (LineNumber)debugItem; 1064 return new BuilderLineNumber(lineNumber.getLineNumber()); 1065 } 1066 case DebugItemType.SET_SOURCE_FILE: { 1067 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 1068 return new BuilderSetSourceFile(setSourceFile.getSourceFileReference()); 1069 } 1070 default: 1071 throw new ExceptionWithContext("Invalid debug item type: " + debugItem.getDebugItemType()); 1072 } 1073 } 1074 } 1075