MutableMethodImplementation.java revision 135631b11bfa2e52d78ba0d5a1581cf02ab4f7fa
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(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, newBuilderInstruction21lh((Instruction21lh)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 return; 593 case ArrayPayload: 594 setInstruction(location, newBuilderArrayPayload((ArrayPayload)instruction)); 595 return; 596 default: 597 throw new ExceptionWithContext("Instruction format %s not supported", instruction.getOpcode().format); 598 } 599 } 600 601 @Nonnull 602 private BuilderInstruction10t newBuilderInstruction10t(int codeAddress, int[] codeAddressToIndex, 603 @Nonnull Instruction10t instruction) { 604 return new BuilderInstruction10t( 605 instruction.getOpcode(), 606 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 607 } 608 609 @Nonnull 610 private BuilderInstruction10x newBuilderInstruction10x(@Nonnull Instruction10x instruction) { 611 return new BuilderInstruction10x( 612 instruction.getOpcode()); 613 } 614 615 @Nonnull 616 private BuilderInstruction11n newBuilderInstruction11n(@Nonnull Instruction11n instruction) { 617 return new BuilderInstruction11n( 618 instruction.getOpcode(), 619 instruction.getRegisterA(), 620 instruction.getNarrowLiteral()); 621 } 622 623 @Nonnull 624 private BuilderInstruction11x newBuilderInstruction11x(@Nonnull Instruction11x instruction) { 625 return new BuilderInstruction11x( 626 instruction.getOpcode(), 627 instruction.getRegisterA()); 628 } 629 630 @Nonnull 631 private BuilderInstruction12x newBuilderInstruction12x(@Nonnull Instruction12x instruction) { 632 return new BuilderInstruction12x( 633 instruction.getOpcode(), 634 instruction.getRegisterA(), 635 instruction.getRegisterB()); 636 } 637 638 @Nonnull 639 private BuilderInstruction20bc newBuilderInstruction20bc(@Nonnull Instruction20bc instruction) { 640 return new BuilderInstruction20bc( 641 instruction.getOpcode(), 642 instruction.getVerificationError(), 643 instruction.getReference()); 644 } 645 646 @Nonnull 647 private BuilderInstruction20t newBuilderInstruction20t(int codeAddress, int[] codeAddressToIndex, 648 @Nonnull Instruction20t instruction) { 649 return new BuilderInstruction20t( 650 instruction.getOpcode(), 651 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 652 } 653 654 @Nonnull 655 private BuilderInstruction21c newBuilderInstruction21c(@Nonnull Instruction21c instruction) { 656 return new BuilderInstruction21c( 657 instruction.getOpcode(), 658 instruction.getRegisterA(), 659 instruction.getReference()); 660 } 661 662 @Nonnull 663 private BuilderInstruction21ih newBuilderInstruction21ih(@Nonnull Instruction21ih instruction) { 664 return new BuilderInstruction21ih( 665 instruction.getOpcode(), 666 instruction.getRegisterA(), 667 instruction.getNarrowLiteral()); 668 } 669 670 @Nonnull 671 private BuilderInstruction21lh newBuilderInstruction21lh(@Nonnull Instruction21lh instruction) { 672 return new BuilderInstruction21lh( 673 instruction.getOpcode(), 674 instruction.getRegisterA(), 675 instruction.getWideLiteral()); 676 } 677 678 @Nonnull 679 private BuilderInstruction21s newBuilderInstruction21s(@Nonnull Instruction21s instruction) { 680 return new BuilderInstruction21s( 681 instruction.getOpcode(), 682 instruction.getRegisterA(), 683 instruction.getNarrowLiteral()); 684 } 685 686 @Nonnull 687 private BuilderInstruction21t newBuilderInstruction21t(int codeAddress, int[] codeAddressToIndex, 688 @Nonnull Instruction21t instruction) { 689 return new BuilderInstruction21t( 690 instruction.getOpcode(), 691 instruction.getRegisterA(), 692 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 693 } 694 695 @Nonnull 696 private BuilderInstruction22b newBuilderInstruction22b(@Nonnull Instruction22b instruction) { 697 return new BuilderInstruction22b( 698 instruction.getOpcode(), 699 instruction.getRegisterA(), 700 instruction.getRegisterB(), 701 instruction.getNarrowLiteral()); 702 } 703 704 @Nonnull 705 private BuilderInstruction22c newBuilderInstruction22c(@Nonnull Instruction22c instruction) { 706 return new BuilderInstruction22c( 707 instruction.getOpcode(), 708 instruction.getRegisterA(), 709 instruction.getRegisterB(), 710 instruction.getReference()); 711 } 712 713 @Nonnull 714 private BuilderInstruction22s newBuilderInstruction22s(@Nonnull Instruction22s instruction) { 715 return new BuilderInstruction22s( 716 instruction.getOpcode(), 717 instruction.getRegisterA(), 718 instruction.getRegisterB(), 719 instruction.getNarrowLiteral()); 720 } 721 722 @Nonnull 723 private BuilderInstruction22t newBuilderInstruction22t(int codeAddress, int[] codeAddressToIndex, 724 @Nonnull Instruction22t instruction) { 725 return new BuilderInstruction22t( 726 instruction.getOpcode(), 727 instruction.getRegisterA(), 728 instruction.getRegisterB(), 729 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 730 } 731 732 @Nonnull 733 private BuilderInstruction22x newBuilderInstruction22x(@Nonnull Instruction22x instruction) { 734 return new BuilderInstruction22x( 735 instruction.getOpcode(), 736 instruction.getRegisterA(), 737 instruction.getRegisterB()); 738 } 739 740 @Nonnull 741 private BuilderInstruction23x newBuilderInstruction23x(@Nonnull Instruction23x instruction) { 742 return new BuilderInstruction23x( 743 instruction.getOpcode(), 744 instruction.getRegisterA(), 745 instruction.getRegisterB(), 746 instruction.getRegisterC()); 747 } 748 749 @Nonnull 750 private BuilderInstruction30t newBuilderInstruction30t(int codeAddress, int[] codeAddressToIndex, 751 @Nonnull Instruction30t instruction) { 752 return new BuilderInstruction30t( 753 instruction.getOpcode(), 754 newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset())); 755 } 756 757 @Nonnull 758 private BuilderInstruction31c newBuilderInstruction31c(@Nonnull Instruction31c instruction) { 759 return new BuilderInstruction31c( 760 instruction.getOpcode(), 761 instruction.getRegisterA(), 762 instruction.getReference()); 763 } 764 765 @Nonnull 766 private BuilderInstruction31i newBuilderInstruction31i(@Nonnull Instruction31i instruction) { 767 return new BuilderInstruction31i( 768 instruction.getOpcode(), 769 instruction.getRegisterA(), 770 instruction.getNarrowLiteral()); 771 } 772 773 @Nonnull 774 private BuilderInstruction31t newBuilderInstruction31t(int codeAddress, int[] codeAddressToIndex, 775 @Nonnull Instruction31t instruction) { 776 Label newLabel; 777 if (instruction.getOpcode() != Opcode.FILL_ARRAY_DATA) { 778 // if it's a sparse switch or packed switch 779 newLabel = newSwitchPayloadReferenceLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 780 } else { 781 newLabel = newLabel(codeAddressToIndex, codeAddress + instruction.getCodeOffset()); 782 } 783 return new BuilderInstruction31t( 784 instruction.getOpcode(), 785 instruction.getRegisterA(), 786 newLabel); 787 } 788 789 @Nonnull 790 private BuilderInstruction32x newBuilderInstruction32x(@Nonnull Instruction32x instruction) { 791 return new BuilderInstruction32x( 792 instruction.getOpcode(), 793 instruction.getRegisterA(), 794 instruction.getRegisterB()); 795 } 796 797 @Nonnull 798 private BuilderInstruction35c newBuilderInstruction35c(@Nonnull Instruction35c instruction) { 799 return new BuilderInstruction35c( 800 instruction.getOpcode(), 801 instruction.getRegisterCount(), 802 instruction.getRegisterC(), 803 instruction.getRegisterD(), 804 instruction.getRegisterE(), 805 instruction.getRegisterF(), 806 instruction.getRegisterG(), 807 instruction.getReference()); 808 } 809 810 @Nonnull 811 private BuilderInstruction3rc newBuilderInstruction3rc(@Nonnull Instruction3rc instruction) { 812 return new BuilderInstruction3rc( 813 instruction.getOpcode(), 814 instruction.getStartRegister(), 815 instruction.getRegisterCount(), 816 instruction.getReference()); 817 } 818 819 @Nonnull 820 private BuilderInstruction51l newBuilderInstruction51l(@Nonnull Instruction51l instruction) { 821 return new BuilderInstruction51l( 822 instruction.getOpcode(), 823 instruction.getRegisterA(), 824 instruction.getWideLiteral()); 825 } 826 827 @Nullable 828 private MethodLocation findSwitchForPayload(@Nonnull MethodLocation payloadLocation) { 829 MethodLocation location = payloadLocation; 830 MethodLocation switchLocation = null; 831 do { 832 for (Label label: location.getLabels()) { 833 if (label instanceof SwitchPayloadReferenceLabel) { 834 if (switchLocation != null) { 835 throw new IllegalStateException("Multiple switch instructions refer to the same payload. " + 836 "This is not currently supported. Please file a bug :)"); 837 } 838 switchLocation = ((SwitchPayloadReferenceLabel)label).switchLocation; 839 } 840 } 841 842 // A switch instruction can refer to the payload instruction itself, or to a nop before the payload 843 // instruction. 844 // We need to search for all occurrences of a switch reference, so we can detect when multiple switch 845 // statements refer to the same payload 846 // TODO: confirm that it could refer to the first NOP in a series of NOPs preceding the payload 847 if (location.index == 0) { 848 return switchLocation; 849 } 850 location = instructionList.get(location.index - 1); 851 if (location.instruction == null || location.instruction.getOpcode() != Opcode.NOP) { 852 return switchLocation; 853 } 854 } while (true); 855 } 856 857 @Nonnull 858 private BuilderPackedSwitchPayload newBuilderPackedSwitchPayload(@Nonnull MethodLocation location, 859 @Nonnull int[] codeAddressToIndex, 860 @Nonnull PackedSwitchPayload instruction) { 861 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 862 if (switchElements.size() == 0) { 863 return new BuilderPackedSwitchPayload(0, null); 864 } 865 866 MethodLocation switchLocation = findSwitchForPayload(location); 867 int baseAddress; 868 if (switchLocation == null) { 869 baseAddress = 0; 870 } else { 871 baseAddress = switchLocation.codeAddress; 872 } 873 874 List<Label> labels = Lists.newArrayList(); 875 for (SwitchElement element: switchElements) { 876 labels.add(newLabel(codeAddressToIndex, element.getOffset() - baseAddress)); 877 } 878 879 return new BuilderPackedSwitchPayload(switchElements.get(0).getKey(), labels); 880 } 881 882 @Nonnull 883 private BuilderSparseSwitchPayload newBuilderSparseSwitchPayload(@Nonnull MethodLocation location, 884 @Nonnull int[] codeAddressToIndex, 885 @Nonnull SparseSwitchPayload instruction) { 886 List<? extends SwitchElement> switchElements = instruction.getSwitchElements(); 887 if (switchElements.size() == 0) { 888 return new BuilderSparseSwitchPayload(null); 889 } 890 891 MethodLocation switchLocation = findSwitchForPayload(location); 892 int baseAddress; 893 if (switchLocation == null) { 894 baseAddress = 0; 895 } else { 896 baseAddress = switchLocation.codeAddress; 897 } 898 899 List<SwitchLabelElement> labelElements = Lists.newArrayList(); 900 for (SwitchElement element: switchElements) { 901 labelElements.add(new SwitchLabelElement(element.getKey(), 902 newLabel(codeAddressToIndex, element.getOffset() - baseAddress))); 903 } 904 905 return new BuilderSparseSwitchPayload(labelElements); 906 } 907 908 @Nonnull 909 private BuilderArrayPayload newBuilderArrayPayload(@Nonnull ArrayPayload instruction) { 910 return new BuilderArrayPayload(instruction.getElementWidth(), instruction.getArrayElements()); 911 } 912 913 @Nonnull 914 private BuilderDebugItem convertDebugItem(@Nonnull DebugItem debugItem) { 915 switch (debugItem.getDebugItemType()) { 916 case DebugItemType.START_LOCAL: { 917 StartLocal startLocal = (StartLocal)debugItem; 918 return new BuilderStartLocal(startLocal.getRegister(), startLocal.getNameReference(), 919 startLocal.getTypeReference(), startLocal.getSignatureReference()); 920 } 921 case DebugItemType.END_LOCAL: { 922 EndLocal endLocal = (EndLocal)debugItem; 923 return new BuilderEndLocal(endLocal.getRegister()); 924 } 925 case DebugItemType.RESTART_LOCAL: { 926 RestartLocal restartLocal = (RestartLocal)debugItem; 927 return new BuilderRestartLocal(restartLocal.getRegister()); 928 } 929 case DebugItemType.PROLOGUE_END: 930 return new BuilderPrologueEnd(); 931 case DebugItemType.EPILOGUE_BEGIN: 932 return new BuilderEpilogueBegin(); 933 case DebugItemType.LINE_NUMBER: { 934 LineNumber lineNumber = (LineNumber)debugItem; 935 return new BuilderLineNumber(lineNumber.getLineNumber()); 936 } 937 case DebugItemType.SET_SOURCE_FILE: { 938 SetSourceFile setSourceFile = (SetSourceFile)debugItem; 939 return new BuilderSetSourceFile(setSourceFile.getSourceFileReference()); 940 } 941 default: 942 throw new ExceptionWithContext("Invalid debug item type: " + debugItem.getDebugItemType()); 943 } 944 } 945} 946