MutableMethodImplementation.java revision 5ff4ee9a3fc898dbe9a67386e984f14c21338391
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 32package org.jf.dexlib2.builder; 33 34import com.google.common.base.Function; 35import com.google.common.collect.Iterables; 36import com.google.common.collect.Lists; 37import com.google.common.collect.Sets; 38import org.jf.dexlib2.DebugItemType; 39import org.jf.dexlib2.Opcode; 40import org.jf.dexlib2.builder.debug.*; 41import org.jf.dexlib2.builder.instruction.*; 42import org.jf.dexlib2.iface.ExceptionHandler; 43import org.jf.dexlib2.iface.MethodImplementation; 44import org.jf.dexlib2.iface.TryBlock; 45import org.jf.dexlib2.iface.debug.*; 46import org.jf.dexlib2.iface.instruction.Instruction; 47import org.jf.dexlib2.iface.instruction.SwitchElement; 48import org.jf.dexlib2.iface.instruction.formats.*; 49import org.jf.dexlib2.iface.reference.TypeReference; 50import org.jf.util.ExceptionWithContext; 51 52import javax.annotation.Nonnull; 53import javax.annotation.Nullable; 54import java.util.*; 55 56public 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 instructions must be converted last, so that any switch statements that refer to them have 100 // 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(debugLocation, 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<Instruction> getInstructions() { 140 if (fixInstructions) { 141 fixInstructions(); 142 } 143 144 return new AbstractList<Instruction>() { 145 @Override public Instruction 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<? extends TryBlock<? extends ExceptionHandler>> 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 217 instructionList.add(index, new MethodLocation(instruction, codeAddress, index)); 218 codeAddress += instruction.getCodeUnits(); 219 220 for (int i=index+1; i<instructionList.size(); i++) { 221 MethodLocation location = instructionList.get(i); 222 location.index++; 223 location.codeAddress = codeAddress; 224 if (location.instruction != null) { 225 codeAddress += location.instruction.getCodeUnits(); 226 } else { 227 // only the last MethodLocation should have a null instruction 228 assert i == instructionList.size()-1; 229 } 230 } 231 232 this.fixInstructions = true; 233 } 234 235 public void addInstruction(@Nonnull BuilderInstruction instruction) { 236 MethodLocation last = instructionList.get(instructionList.size()-1); 237 last.instruction = instruction; 238 instruction.location = last; 239 240 int nextCodeAddress = last.codeAddress + instruction.getCodeUnits(); 241 instructionList.add(new MethodLocation(null, nextCodeAddress, instructionList.size())); 242 243 this.fixInstructions = true; 244 } 245 246 public void replaceInstruction(int index, @Nonnull BuilderInstruction replacementInstruction) { 247 if (index >= instructionList.size() - 1) { 248 throw new IndexOutOfBoundsException(); 249 } 250 251 MethodLocation replaceLocation = instructionList.get(index); 252 replacementInstruction.location = replaceLocation; 253 BuilderInstruction old = replaceLocation.instruction; 254 assert old != null; 255 old.location = null; 256 replaceLocation.instruction = replacementInstruction; 257 258 // TODO: factor out index/address fix up loop 259 int codeAddress = replaceLocation.codeAddress + replaceLocation.instruction.getCodeUnits(); 260 for (int i=index+1; i<instructionList.size(); i++) { 261 MethodLocation location = instructionList.get(i); 262 location.codeAddress = codeAddress; 263 264 Instruction instruction = location.getInstruction(); 265 if (instruction != null) { 266 codeAddress += instruction.getCodeUnits(); 267 } else { 268 assert i == instructionList.size() - 1; 269 } 270 } 271 272 this.fixInstructions = true; 273 } 274 275 public void removeInstruction(int index) { 276 if (index >= instructionList.size() - 1) { 277 throw new IndexOutOfBoundsException(); 278 } 279 280 MethodLocation toRemove = instructionList.get(index); 281 toRemove.instruction = null; 282 MethodLocation next = instructionList.get(index+1); 283 toRemove.mergeInto(next); 284 285 instructionList.remove(index); 286 int codeAddress = toRemove.codeAddress; 287 for (int i=index; i<instructionList.size(); i++) { 288 MethodLocation location = instructionList.get(i); 289 location.index = i; 290 location.codeAddress = codeAddress; 291 292 Instruction instruction = location.getInstruction(); 293 if (instruction != null) { 294 codeAddress += instruction.getCodeUnits(); 295 } else { 296 assert i == instructionList.size() - 1; 297 } 298 } 299 300 this.fixInstructions = true; 301 } 302 303 public void swapInstructions(int index1, int index2) { 304 if (index1 >= instructionList.size() - 1 || index2 >= instructionList.size() - 1) { 305 throw new IndexOutOfBoundsException(); 306 } 307 MethodLocation first = instructionList.get(index1); 308 MethodLocation second = instructionList.get(index2); 309 310 // only the last MethodLocation may have a null instruction 311 assert first.instruction != null; 312 assert second.instruction != null; 313 314 first.instruction.location = second; 315 second.instruction.location = first; 316 317 { 318 BuilderInstruction tmp = second.instruction; 319 second.instruction = first.instruction; 320 first.instruction = tmp; 321 } 322 323 if (index2 < index1) { 324 int tmp = index2; 325 index2 = index1; 326 index1 = tmp; 327 } 328 329 int codeAddress = first.codeAddress + first.instruction.getCodeUnits(); 330 for (int i=index1+1; i<=index2; i++) { 331 MethodLocation location = instructionList.get(i); 332 location.codeAddress = codeAddress; 333 334 Instruction instruction = location.instruction; 335 assert instruction != null; 336 codeAddress += location.instruction.getCodeUnits(); 337 } 338 339 this.fixInstructions = true; 340 } 341 342 @Nullable 343 private BuilderInstruction getFirstNonNop(int startIndex) { 344 345 for (int i=startIndex; i<instructionList.size()-1; i++) { 346 BuilderInstruction instruction = instructionList.get(i).instruction; 347 assert instruction != null; 348 if (instruction.getOpcode() != Opcode.NOP) { 349 return instruction; 350 } 351 } 352 return null; 353 } 354 355 private void fixInstructions() { 356 HashSet<MethodLocation> payloadLocations = Sets.newHashSet(); 357 358 for (MethodLocation location: instructionList) { 359 BuilderInstruction instruction = location.instruction; 360 if (instruction != null) { 361 switch (instruction.getOpcode()) { 362 case SPARSE_SWITCH: 363 case PACKED_SWITCH: { 364 MethodLocation targetLocation = 365 ((BuilderOffsetInstruction)instruction).getTarget().getLocation(); 366 BuilderInstruction targetInstruction = targetLocation.instruction; 367 if (targetInstruction == null) { 368 throw new IllegalStateException(String.format("Switch instruction at address/index " + 369 "0x%x/%d points to the end of the method.", location.codeAddress, location.index)); 370 } 371 372 if (targetInstruction.getOpcode() == Opcode.NOP) { 373 targetInstruction = getFirstNonNop(targetLocation.index+1); 374 } 375 if (targetInstruction == null || !(targetInstruction instanceof BuilderSwitchPayload)) { 376 throw new IllegalStateException(String.format("Switch instruction at address/index " + 377 "0x%x/%d does not refer to a payload instruction.", 378 location.codeAddress, location.index)); 379 } 380 if ((instruction.opcode == Opcode.PACKED_SWITCH && 381 targetInstruction.getOpcode() != Opcode.PACKED_SWITCH_PAYLOAD) || 382 (instruction.opcode == Opcode.SPARSE_SWITCH && 383 targetInstruction.getOpcode() != Opcode.SPARSE_SWITCH_PAYLOAD)) { 384 throw new IllegalStateException(String.format("Switch instruction at address/index " + 385 "0x%x/%d refers to the wrong type of payload instruction.", 386 location.codeAddress, location.index)); 387 } 388 389 if (!payloadLocations.add(targetLocation)) { 390 throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + 391 "This is not currently supported. Please file a bug :)"); 392 } 393 394 ((BuilderSwitchPayload)targetInstruction).referrer = location; 395 break; 396 } 397 } 398 } 399 } 400 401 boolean madeChanges; 402 do { 403 madeChanges = false; 404 405 for (int index=0; index<instructionList.size(); index++) { 406 MethodLocation location = instructionList.get(index); 407 BuilderInstruction instruction = location.instruction; 408 if (instruction != null) { 409 switch (instruction.getOpcode()) { 410 case GOTO: { 411 int offset = ((BuilderOffsetInstruction)instruction).internalGetCodeOffset(); 412 if (offset < Byte.MIN_VALUE || offset > Byte.MAX_VALUE) { 413 BuilderOffsetInstruction replacement; 414 if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { 415 replacement = new BuilderInstruction30t(Opcode.GOTO_32, 416 ((BuilderOffsetInstruction)instruction).getTarget()); 417 } else { 418 replacement = new BuilderInstruction20t(Opcode.GOTO_16, 419 ((BuilderOffsetInstruction)instruction).getTarget()); 420 } 421 replaceInstruction(location.index, replacement); 422 madeChanges = true; 423 } 424 break; 425 } 426 case GOTO_16: { 427 int offset = ((BuilderOffsetInstruction)instruction).internalGetCodeOffset(); 428 if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { 429 BuilderOffsetInstruction replacement = new BuilderInstruction30t(Opcode.GOTO_32, 430 ((BuilderOffsetInstruction)instruction).getTarget()); 431 replaceInstruction(location.index, replacement); 432 madeChanges = true; 433 } 434 break; 435 } 436 case SPARSE_SWITCH_PAYLOAD: 437 case PACKED_SWITCH_PAYLOAD: 438 case ARRAY_PAYLOAD: { 439 if ((location.codeAddress & 0x01) != 0) { 440 int previousIndex = location.index - 1; 441 MethodLocation previousLocation = instructionList.get(previousIndex); 442 Instruction previousInstruction = previousLocation.instruction; 443 assert previousInstruction != null; 444 if (previousInstruction.getOpcode() == Opcode.NOP) { 445 removeInstruction(previousIndex); 446 index--; 447 } else { 448 addInstruction(location.index, new BuilderInstruction10x(Opcode.NOP)); 449 index++; 450 } 451 madeChanges = true; 452 } 453 break; 454 } 455 } 456 } 457 } 458 } while (madeChanges); 459 460 fixInstructions = false; 461 } 462 463 private int mapCodeAddressToIndex(@Nonnull int[] codeAddressToIndex, int codeAddress) { 464 int index; 465 do { 466 index = codeAddressToIndex[codeAddress]; 467 if (index < 0) { 468 codeAddress--; 469 } else { 470 return index; 471 } 472 } while (true); 473 } 474 475 @Nonnull 476 private Label newLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) { 477 MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress)); 478 return referent.addNewLabel(); 479 } 480 481 private static class SwitchPayloadReferenceLabel extends Label { 482 @Nonnull public MethodLocation switchLocation; 483 } 484 485 @Nonnull 486 public Label newSwitchPayloadReferenceLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) { 487 MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress)); 488 Label label = new SwitchPayloadReferenceLabel(); 489 referent.getLabels().add(label); 490 return label; 491 } 492 493 private void setInstruction(@Nonnull MethodLocation location, @Nonnull BuilderInstruction instruction) { 494 location.instruction = instruction; 495 instruction.location = location; 496 } 497 498 private void convertAndSetInstruction(@Nonnull MethodLocation location, int[] codeAddressToIndex, 499 @Nonnull Instruction instruction) { 500 switch (instruction.getOpcode().format) { 501 case Format10t: 502 setInstruction(location, newBuilderInstruction10t(location.codeAddress, codeAddressToIndex, 503 (Instruction10t)instruction)); 504 return; 505 case Format10x: 506 setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction)); 507 return; 508 case Format11n: 509 setInstruction(location, newBuilderInstruction11n((Instruction11n)instruction)); 510 return; 511 case Format11x: 512 setInstruction(location, newBuilderInstruction11x((Instruction11x)instruction)); 513 return; 514 case Format12x: 515 setInstruction(location, newBuilderInstruction12x((Instruction12x)instruction)); 516 return; 517 case Format20bc: 518 setInstruction(location, newBuilderInstruction20bc((Instruction20bc)instruction)); 519 return; 520 case Format20t: 521 setInstruction(location, newBuilderInstruction20t(location.codeAddress, codeAddressToIndex, 522 (Instruction20t)instruction)); 523 return; 524 case Format21c: 525 setInstruction(location, newBuilderInstruction21c((Instruction21c)instruction)); 526 return; 527 case Format21ih: 528 setInstruction(location, newBuilderInstruction21ih((Instruction21ih)instruction)); 529 return; 530 case Format21lh: 531 setInstruction(location, newBuilderInstruction10x((Instruction10x)instruction)); 532 return; 533 case Format21s: 534 setInstruction(location, newBuilderInstruction21s((Instruction21s)instruction)); 535 return; 536 case Format21t: 537 setInstruction(location, newBuilderInstruction21t(location.codeAddress, codeAddressToIndex, 538 (Instruction21t)instruction)); 539 return; 540 case Format22b: 541 setInstruction(location, newBuilderInstruction22b((Instruction22b)instruction)); 542 return; 543 case Format22c: 544 setInstruction(location, newBuilderInstruction22c((Instruction22c)instruction)); 545 return; 546 case Format22s: 547 setInstruction(location, newBuilderInstruction22s((Instruction22s)instruction)); 548 return; 549 case Format22t: 550 setInstruction(location, newBuilderInstruction22t(location.codeAddress, codeAddressToIndex, 551 (Instruction22t)instruction)); 552 return; 553 case Format22x: 554 setInstruction(location, newBuilderInstruction22x((Instruction22x)instruction)); 555 return; 556 case Format23x: 557 setInstruction(location, newBuilderInstruction23x((Instruction23x)instruction)); 558 return; 559 case Format30t: 560 setInstruction(location, newBuilderInstruction30t(location.codeAddress, codeAddressToIndex, 561 (Instruction30t)instruction)); 562 return; 563 case Format31c: 564 setInstruction(location, newBuilderInstruction31c((Instruction31c)instruction)); 565 return; 566 case Format31i: 567 setInstruction(location, newBuilderInstruction31i((Instruction31i)instruction)); 568 return; 569 case Format31t: 570 setInstruction(location, newBuilderInstruction31t(location.codeAddress, codeAddressToIndex, 571 (Instruction31t)instruction)); 572 return; 573 case Format32x: 574 setInstruction(location, newBuilderInstruction32x((Instruction32x)instruction)); 575 return; 576 case Format35c: 577 setInstruction(location, newBuilderInstruction35c((Instruction35c)instruction)); 578 return; 579 case Format3rc: 580 setInstruction(location, newBuilderInstruction3rc((Instruction3rc)instruction)); 581 return; 582 case Format51l: 583 setInstruction(location, newBuilderInstruction51l((Instruction51l)instruction)); 584 return; 585 case PackedSwitchPayload: 586 setInstruction(location, 587 newBuilderPackedSwitchPayload(location, codeAddressToIndex, (PackedSwitchPayload)instruction)); 588 return; 589 case SparseSwitchPayload: 590 setInstruction(location, 591 newBuilderSparseSwitchPayload(location, codeAddressToIndex, (SparseSwitchPayload)instruction)); 592 case ArrayPayload: 593 setInstruction(location, newBuilderArrayPayload((ArrayPayload)instruction)); 594 default: 595 throw new ExceptionWithContext("Instruction format %s not supported", instruction.getOpcode().format); 596 } 597 } 598 599 @Nonnull 600 private BuilderInstruction10t newBuilderInstruction10t(int codeAddress, int[] codeAddressToIndex, 601 @Nonnull Instruction10t instruction) { 602 return new BuilderInstruction10t( 603 instruction.getOpcode(), 604 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 605 } 606 607 @Nonnull 608 private BuilderInstruction10x newBuilderInstruction10x(@Nonnull Instruction10x instruction) { 609 return new BuilderInstruction10x( 610 instruction.getOpcode()); 611 } 612 613 @Nonnull 614 private BuilderInstruction11n newBuilderInstruction11n(@Nonnull Instruction11n instruction) { 615 return new BuilderInstruction11n( 616 instruction.getOpcode(), 617 instruction.getRegisterA(), 618 instruction.getNarrowLiteral()); 619 } 620 621 @Nonnull 622 private BuilderInstruction11x newBuilderInstruction11x(@Nonnull Instruction11x instruction) { 623 return new BuilderInstruction11x( 624 instruction.getOpcode(), 625 instruction.getRegisterA()); 626 } 627 628 @Nonnull 629 private BuilderInstruction12x newBuilderInstruction12x(@Nonnull Instruction12x instruction) { 630 return new BuilderInstruction12x( 631 instruction.getOpcode(), 632 instruction.getRegisterA(), 633 instruction.getRegisterB()); 634 } 635 636 @Nonnull 637 private BuilderInstruction20bc newBuilderInstruction20bc(@Nonnull Instruction20bc instruction) { 638 return new BuilderInstruction20bc( 639 instruction.getOpcode(), 640 instruction.getVerificationError(), 641 instruction.getReference()); 642 } 643 644 @Nonnull 645 private BuilderInstruction20t newBuilderInstruction20t(int codeAddress, int[] codeAddressToIndex, 646 @Nonnull Instruction20t instruction) { 647 return new BuilderInstruction20t( 648 instruction.getOpcode(), 649 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 650 } 651 652 @Nonnull 653 private BuilderInstruction21c newBuilderInstruction21c(@Nonnull Instruction21c instruction) { 654 return new BuilderInstruction21c( 655 instruction.getOpcode(), 656 instruction.getRegisterA(), 657 instruction.getReference()); 658 } 659 660 @Nonnull 661 private BuilderInstruction21ih newBuilderInstruction21ih(@Nonnull Instruction21ih instruction) { 662 return new BuilderInstruction21ih( 663 instruction.getOpcode(), 664 instruction.getRegisterA(), 665 instruction.getHatLiteral()); 666 } 667 668 @Nonnull 669 private BuilderInstruction21lh newBuilderInstruction21lh(@Nonnull Instruction21lh instruction) { 670 return new BuilderInstruction21lh( 671 instruction.getOpcode(), 672 instruction.getRegisterA(), 673 instruction.getHatLiteral()); 674 } 675 676 @Nonnull 677 private BuilderInstruction21s newBuilderInstruction21s(@Nonnull Instruction21s instruction) { 678 return new BuilderInstruction21s( 679 instruction.getOpcode(), 680 instruction.getRegisterA(), 681 instruction.getNarrowLiteral()); 682 } 683 684 @Nonnull 685 private BuilderInstruction21t newBuilderInstruction21t(int codeAddress, int[] codeAddressToIndex, 686 @Nonnull Instruction21t instruction) { 687 return new BuilderInstruction21t( 688 instruction.getOpcode(), 689 instruction.getRegisterA(), 690 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 691 } 692 693 @Nonnull 694 private BuilderInstruction22b newBuilderInstruction22b(@Nonnull Instruction22b instruction) { 695 return new BuilderInstruction22b( 696 instruction.getOpcode(), 697 instruction.getRegisterA(), 698 instruction.getRegisterB(), 699 instruction.getNarrowLiteral()); 700 } 701 702 @Nonnull 703 private BuilderInstruction22c newBuilderInstruction22c(@Nonnull Instruction22c instruction) { 704 return new BuilderInstruction22c( 705 instruction.getOpcode(), 706 instruction.getRegisterA(), 707 instruction.getRegisterB(), 708 instruction.getReference()); 709 } 710 711 @Nonnull 712 private BuilderInstruction22s newBuilderInstruction22s(@Nonnull Instruction22s instruction) { 713 return new BuilderInstruction22s( 714 instruction.getOpcode(), 715 instruction.getRegisterA(), 716 instruction.getRegisterB(), 717 instruction.getNarrowLiteral()); 718 } 719 720 @Nonnull 721 private BuilderInstruction22t newBuilderInstruction22t(int codeAddress, int[] codeAddressToIndex, 722 @Nonnull Instruction22t instruction) { 723 return new BuilderInstruction22t( 724 instruction.getOpcode(), 725 instruction.getRegisterA(), 726 instruction.getRegisterB(), 727 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 728 } 729 730 @Nonnull 731 private BuilderInstruction22x newBuilderInstruction22x(@Nonnull Instruction22x instruction) { 732 return new BuilderInstruction22x( 733 instruction.getOpcode(), 734 instruction.getRegisterA(), 735 instruction.getRegisterB()); 736 } 737 738 @Nonnull 739 private BuilderInstruction23x newBuilderInstruction23x(@Nonnull Instruction23x instruction) { 740 return new BuilderInstruction23x( 741 instruction.getOpcode(), 742 instruction.getRegisterA(), 743 instruction.getRegisterB(), 744 instruction.getRegisterC()); 745 } 746 747 @Nonnull 748 private BuilderInstruction30t newBuilderInstruction30t(int codeAddress, int[] codeAddressToIndex, 749 @Nonnull Instruction30t instruction) { 750 return new BuilderInstruction30t( 751 instruction.getOpcode(), 752 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 753 } 754 755 @Nonnull 756 private BuilderInstruction31c newBuilderInstruction31c(@Nonnull Instruction31c instruction) { 757 return new BuilderInstruction31c( 758 instruction.getOpcode(), 759 instruction.getRegisterA(), 760 instruction.getReference()); 761 } 762 763 @Nonnull 764 private BuilderInstruction31i newBuilderInstruction31i(@Nonnull Instruction31i instruction) { 765 return new BuilderInstruction31i( 766 instruction.getOpcode(), 767 instruction.getRegisterA(), 768 instruction.getNarrowLiteral()); 769 } 770 771 @Nonnull 772 private BuilderInstruction31t newBuilderInstruction31t(int codeAddress, int[] codeAddressToIndex, 773 @Nonnull Instruction31t instruction) { 774 Label newLabel; 775 if (instruction.getOpcode() != Opcode.FILL_ARRAY_DATA) { 776 // if it's a sparse switch or packed switch 777 newLabel = newSwitchPayloadReferenceLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 778 } else { 779 newLabel = newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 780 } 781 return new BuilderInstruction31t( 782 instruction.getOpcode(), 783 instruction.getRegisterA(), 784 newLabel); 785 } 786 787 @Nonnull 788 private BuilderInstruction32x newBuilderInstruction32x(@Nonnull Instruction32x instruction) { 789 return new BuilderInstruction32x( 790 instruction.getOpcode(), 791 instruction.getRegisterA(), 792 instruction.getRegisterB()); 793 } 794 795 @Nonnull 796 private BuilderInstruction35c newBuilderInstruction35c(@Nonnull Instruction35c instruction) { 797 return new BuilderInstruction35c( 798 instruction.getOpcode(), 799 instruction.getRegisterCount(), 800 instruction.getRegisterC(), 801 instruction.getRegisterD(), 802 instruction.getRegisterE(), 803 instruction.getRegisterF(), 804 instruction.getRegisterG(), 805 instruction.getReference()); 806 } 807 808 @Nonnull 809 private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) { 810 return new BuilderInstruction3rc( 811 instruction.getOpcode(), 812 instruction.getStartRegister(), 813 instruction.getRegisterCount(), 814 instruction.getReference()); 815 } 816 817 @Nonnull 818 private BuilderInstruction51l newBuilderInstruction51l(@Nonnull Instruction51l instruction) { 819 return new BuilderInstruction51l( 820 instruction.getOpcode(), 821 instruction.getRegisterA(), 822 instruction.getWideLiteral()); 823 } 824 825 @Nullable 826 private MethodLocation findSwitchForPayload(@Nonnull MethodLocation payloadLocation) { 827 MethodLocation location = payloadLocation; 828 MethodLocation switchLocation = null; 829 do { 830 for (Label label: location.getLabels()) { 831 if (label instanceof SwitchPayloadReferenceLabel) { 832 if (switchLocation != null) { 833 throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + 834 "This is not currently supported. Please file a bug :)"); 835 } 836 switchLocation = ((SwitchPayloadReferenceLabel)label).switchLocation; 837 } 838 } 839 840 // A switch instruction can refer to the payload instruction itself, or to a nop before the payload 841 // instruction. 842 // We need to search for all occurrences of a switch reference, so we can detect when multiple switch 843 // statements refer to the same payload 844 // TODO: confirm that it could refer to the first NOP in a series of NOPs preceding the payload 845 if (location.index == 0) { 846 return switchLocation; 847 } 848 location = instructionList.get(location.index - 1); 849 if (location.instruction == null || location.instruction.getOpcode() != Opcode.NOP) { 850 return switchLocation; 851 } 852 } while (true); 853 } 854 855 @Nonnull 856 private BuilderPackedSwitchPayload newBuilderPackedSwitchPayload(@Nonnull MethodLocation location, 857 @Nonnull int[] codeAddressToIndex, 858 @Nonnull PackedSwitchPayload instruction) { 859 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 860 if (switchElements.size() == 0) { 861 return new BuilderPackedSwitchPayload(0, null); 862 } 863 864 MethodLocation switchLocation = findSwitchForPayload(location); 865 int baseAddress; 866 if (switchLocation == null) { 867 baseAddress = 0; 868 } else { 869 baseAddress = switchLocation.codeAddress; 870 } 871 872 List<Label> labels = Lists.newArrayList(); 873 for (SwitchElement element: switchElements) { 874 labels.add(newLabel(codeAddressToIndex, element.getOffset() - baseAddress)); 875 } 876 877 return new BuilderPackedSwitchPayload(switchElements.get(0).getKey(), labels); 878 } 879 880 @Nonnull 881 private BuilderSparseSwitchPayload newBuilderSparseSwitchPayload(@Nonnull MethodLocation location, 882 @Nonnull int[] codeAddressToIndex, 883 @Nonnull SparseSwitchPayload instruction) { 884 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 885 if (switchElements.size() == 0) { 886 return new BuilderSparseSwitchPayload(null); 887 } 888 889 MethodLocation switchLocation = findSwitchForPayload(location); 890 int baseAddress; 891 if (switchLocation == null) { 892 baseAddress = 0; 893 } else { 894 baseAddress = switchLocation.codeAddress; 895 } 896 897 List<SwitchLabelElement> labelElements = Lists.newArrayList(); 898 for (SwitchElement element: switchElements) { 899 labelElements.add(new SwitchLabelElement(element.getKey(), 900 newLabel(codeAddressToIndex, element.getOffset() - baseAddress))); 901 } 902 903 return new BuilderSparseSwitchPayload(labelElements); 904 } 905 906 @Nonnull 907 private BuilderArrayPayload newBuilderArrayPayload(@Nonnull ArrayPayload instruction) { 908 return new BuilderArrayPayload(instruction.getElementWidth(), instruction.getArrayElements()); 909 } 910 911 @Nonnull 912 private BuilderDebugItem convertDebugItem(@Nonnull MethodLocation location, @Nonnull DebugItem debugItem) { 913 switch (debugItem.getDebugItemType()) { 914 case DebugItemType.START_LOCAL: { 915 StartLocal startLocal = (StartLocal)debugItem; 916 return new BuilderStartLocal(location, startLocal.getRegister(), startLocal.getNameReference(), 917 startLocal.getTypeReference(), startLocal.getSignatureReference()); 918 } 919 case DebugItemType.END_LOCAL: { 920 EndLocal endLocal = (EndLocal)debugItem; 921 return new BuilderEndLocal(location, endLocal.getRegister()); 922 } 923 case DebugItemType.RESTART_LOCAL: { 924 RestartLocal restartLocal = (RestartLocal)debugItem; 925 return new BuilderRestartLocal(location, restartLocal.getRegister()); 926 } 927 case DebugItemType.PROLOGUE_END: 928 return new BuilderPrologueEnd(location); 929 case DebugItemType.EPILOGUE_BEGIN: 930 return new BuilderEpilogueBegin(location); 931 case DebugItemType.LINE_NUMBER: { 932 LineNumber lineNumber = (LineNumber)debugItem; 933 return new BuilderLineNumber(location, lineNumber.getLineNumber()); 934 } 935 case DebugItemType.SET_SOURCE_FILE: { 936 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 937 return new BuilderSetSourceFile(location, setSourceFile.getSourceFileReference()); 938 } 939 default: 940 throw new ExceptionWithContext("Invalid debug item type: " + debugItem.getDebugItemType()); 941 } 942 } 943} 944