Home | History | Annotate | Download | only in builder
      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