Home | History | Annotate | Download | only in Adaptors
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.baksmali.Adaptors;
     30 
     31 import com.google.common.collect.ImmutableList;
     32 import com.google.common.collect.Lists;
     33 import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
     34 import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
     35 import org.jf.baksmali.BaksmaliOptions;
     36 import org.jf.dexlib2.AccessFlags;
     37 import org.jf.dexlib2.Format;
     38 import org.jf.dexlib2.Opcode;
     39 import org.jf.dexlib2.ReferenceType;
     40 import org.jf.dexlib2.analysis.AnalysisException;
     41 import org.jf.dexlib2.analysis.AnalyzedInstruction;
     42 import org.jf.dexlib2.analysis.MethodAnalyzer;
     43 import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
     44 import org.jf.dexlib2.iface.*;
     45 import org.jf.dexlib2.iface.debug.DebugItem;
     46 import org.jf.dexlib2.iface.instruction.Instruction;
     47 import org.jf.dexlib2.iface.instruction.OffsetInstruction;
     48 import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
     49 import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
     50 import org.jf.dexlib2.iface.reference.MethodReference;
     51 import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t;
     52 import org.jf.dexlib2.util.InstructionOffsetMap;
     53 import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset;
     54 import org.jf.dexlib2.util.ReferenceUtil;
     55 import org.jf.dexlib2.util.SyntheticAccessorResolver;
     56 import org.jf.dexlib2.util.SyntheticAccessorResolver.AccessedMember;
     57 import org.jf.dexlib2.util.TypeUtils;
     58 import org.jf.util.ExceptionWithContext;
     59 import org.jf.util.IndentingWriter;
     60 import org.jf.util.SparseIntArray;
     61 
     62 import javax.annotation.Nonnull;
     63 import javax.annotation.Nullable;
     64 import java.io.IOException;
     65 import java.util.*;
     66 
     67 public class MethodDefinition {
     68     @Nonnull public final ClassDefinition classDef;
     69     @Nonnull public final Method method;
     70     @Nonnull public final MethodImplementation methodImpl;
     71     @Nonnull public final ImmutableList<Instruction> instructions;
     72     @Nonnull public final List<Instruction> effectiveInstructions;
     73 
     74     @Nonnull public final ImmutableList<MethodParameter> methodParameters;
     75     public RegisterFormatter registerFormatter;
     76 
     77     @Nonnull private final LabelCache labelCache = new LabelCache();
     78 
     79     @Nonnull private final SparseIntArray packedSwitchMap;
     80     @Nonnull private final SparseIntArray sparseSwitchMap;
     81     @Nonnull private final InstructionOffsetMap instructionOffsetMap;
     82 
     83     public MethodDefinition(@Nonnull ClassDefinition classDef, @Nonnull Method method,
     84                             @Nonnull MethodImplementation methodImpl) {
     85         this.classDef = classDef;
     86         this.method = method;
     87         this.methodImpl = methodImpl;
     88 
     89         try {
     90             //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
     91 
     92             instructions = ImmutableList.copyOf(methodImpl.getInstructions());
     93             methodParameters = ImmutableList.copyOf(method.getParameters());
     94 
     95             effectiveInstructions = Lists.newArrayList(instructions);
     96 
     97             packedSwitchMap = new SparseIntArray(0);
     98             sparseSwitchMap = new SparseIntArray(0);
     99             instructionOffsetMap = new InstructionOffsetMap(instructions);
    100 
    101             int endOffset = instructionOffsetMap.getInstructionCodeOffset(instructions.size()-1) +
    102                     instructions.get(instructions.size()-1).getCodeUnits();
    103 
    104             for (int i=0; i<instructions.size(); i++) {
    105                 Instruction instruction = instructions.get(i);
    106 
    107                 Opcode opcode = instruction.getOpcode();
    108                 if (opcode == Opcode.PACKED_SWITCH) {
    109                     boolean valid = true;
    110                     int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
    111                     int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
    112                     try {
    113                         targetOffset = findPayloadOffset(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
    114                     } catch (InvalidSwitchPayload ex) {
    115                         valid = false;
    116                     }
    117                     if (valid) {
    118                         if (packedSwitchMap.get(targetOffset, -1) != -1) {
    119                             Instruction payloadInstruction =
    120                                     findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
    121                             targetOffset = endOffset;
    122                             effectiveInstructions.set(i, new ImmutableInstruction31t(opcode,
    123                                     ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset));
    124                             effectiveInstructions.add(payloadInstruction);
    125                             endOffset += payloadInstruction.getCodeUnits();
    126                         }
    127                         packedSwitchMap.append(targetOffset, codeOffset);
    128                     }
    129                 } else if (opcode == Opcode.SPARSE_SWITCH) {
    130                     boolean valid = true;
    131                     int codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
    132                     int targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
    133                     try {
    134                         targetOffset = findPayloadOffset(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
    135                     } catch (InvalidSwitchPayload ex) {
    136                         valid = false;
    137                         // The offset to the payload instruction was invalid. Nothing to do, except that we won't
    138                         // add this instruction to the map.
    139                     }
    140                     if (valid) {
    141                         if (sparseSwitchMap.get(targetOffset, -1) != -1) {
    142                             Instruction payloadInstruction =
    143                                     findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
    144                             targetOffset = endOffset;
    145                             effectiveInstructions.set(i, new ImmutableInstruction31t(opcode,
    146                                     ((Instruction31t)instruction).getRegisterA(), targetOffset-codeOffset));
    147                             effectiveInstructions.add(payloadInstruction);
    148                             endOffset += payloadInstruction.getCodeUnits();
    149                         }
    150                         sparseSwitchMap.append(targetOffset, codeOffset);
    151                     }
    152                 }
    153             }
    154         } catch (Exception ex) {
    155             String methodString;
    156             try {
    157                 methodString = ReferenceUtil.getMethodDescriptor(method);
    158             } catch (Exception ex2) {
    159                 throw ExceptionWithContext.withContext(ex, "Error while processing method");
    160             }
    161             throw ExceptionWithContext.withContext(ex, "Error while processing method %s", methodString);
    162         }
    163     }
    164 
    165     public static void writeEmptyMethodTo(IndentingWriter writer, Method method,
    166                                           BaksmaliOptions options) throws IOException {
    167         writer.write(".method ");
    168         writeAccessFlags(writer, method.getAccessFlags());
    169         writer.write(method.getName());
    170         writer.write("(");
    171         ImmutableList<MethodParameter> methodParameters = ImmutableList.copyOf(method.getParameters());
    172         for (MethodParameter parameter: methodParameters) {
    173             writer.write(parameter.getType());
    174         }
    175         writer.write(")");
    176         writer.write(method.getReturnType());
    177         writer.write('\n');
    178 
    179         writer.indent(4);
    180         writeParameters(writer, method, methodParameters, options);
    181 
    182         String containingClass = null;
    183         if (options.implicitReferences) {
    184             containingClass = method.getDefiningClass();
    185         }
    186         AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
    187 
    188         writer.deindent(4);
    189         writer.write(".end method\n");
    190     }
    191 
    192     public void writeTo(IndentingWriter writer) throws IOException {
    193         int parameterRegisterCount = 0;
    194         if (!AccessFlags.STATIC.isSet(method.getAccessFlags())) {
    195             parameterRegisterCount++;
    196         }
    197 
    198         writer.write(".method ");
    199         writeAccessFlags(writer, method.getAccessFlags());
    200         writer.write(method.getName());
    201         writer.write("(");
    202         for (MethodParameter parameter: methodParameters) {
    203             String type = parameter.getType();
    204             writer.write(type);
    205             parameterRegisterCount++;
    206             if (TypeUtils.isWideType(type)) {
    207                 parameterRegisterCount++;
    208             }
    209         }
    210         writer.write(")");
    211         writer.write(method.getReturnType());
    212         writer.write('\n');
    213 
    214         writer.indent(4);
    215         if (classDef.options.localsDirective) {
    216             writer.write(".locals ");
    217             writer.printSignedIntAsDec(methodImpl.getRegisterCount() - parameterRegisterCount);
    218         } else {
    219             writer.write(".registers ");
    220             writer.printSignedIntAsDec(methodImpl.getRegisterCount());
    221         }
    222         writer.write('\n');
    223         writeParameters(writer, method, methodParameters, classDef.options);
    224 
    225         if (registerFormatter == null) {
    226             registerFormatter = new RegisterFormatter(classDef.options, methodImpl.getRegisterCount(),
    227                     parameterRegisterCount);
    228         }
    229 
    230         String containingClass = null;
    231         if (classDef.options.implicitReferences) {
    232             containingClass = method.getDefiningClass();
    233         }
    234         AnnotationFormatter.writeTo(writer, method.getAnnotations(), containingClass);
    235 
    236         writer.write('\n');
    237 
    238         List<MethodItem> methodItems = getMethodItems();
    239         for (MethodItem methodItem: methodItems) {
    240             if (methodItem.writeTo(writer)) {
    241                 writer.write('\n');
    242             }
    243         }
    244         writer.deindent(4);
    245         writer.write(".end method\n");
    246     }
    247 
    248     public Instruction findSwitchPayload(int targetOffset, Opcode type) {
    249         int targetIndex;
    250         try {
    251             targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
    252         } catch (InvalidInstructionOffset ex) {
    253             throw new InvalidSwitchPayload(targetOffset);
    254         }
    255 
    256         //TODO: does dalvik let you pad with multiple nops?
    257         //TODO: does dalvik let a switch instruction point to a non-payload instruction?
    258 
    259         Instruction instruction = instructions.get(targetIndex);
    260         if (instruction.getOpcode() != type) {
    261             // maybe it's pointing to a NOP padding instruction. Look at the next instruction
    262             if (instruction.getOpcode() == Opcode.NOP) {
    263                 targetIndex += 1;
    264                 if (targetIndex < instructions.size()) {
    265                     instruction = instructions.get(targetIndex);
    266                     if (instruction.getOpcode() == type) {
    267                         return instruction;
    268                     }
    269                 }
    270             }
    271             throw new InvalidSwitchPayload(targetOffset);
    272         } else {
    273             return instruction;
    274         }
    275     }
    276 
    277     public int findPayloadOffset(int targetOffset, Opcode type) {
    278         int targetIndex;
    279         try {
    280             targetIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(targetOffset);
    281         } catch (InvalidInstructionOffset ex) {
    282             throw new InvalidSwitchPayload(targetOffset);
    283         }
    284 
    285         //TODO: does dalvik let you pad with multiple nops?
    286         //TODO: does dalvik let a switch instruction point to a non-payload instruction?
    287 
    288         Instruction instruction = instructions.get(targetIndex);
    289         if (instruction.getOpcode() != type) {
    290             // maybe it's pointing to a NOP padding instruction. Look at the next instruction
    291             if (instruction.getOpcode() == Opcode.NOP) {
    292                 targetIndex += 1;
    293                 if (targetIndex < instructions.size()) {
    294                     instruction = instructions.get(targetIndex);
    295                     if (instruction.getOpcode() == type) {
    296                         return instructionOffsetMap.getInstructionCodeOffset(targetIndex);
    297                     }
    298                 }
    299             }
    300             throw new InvalidSwitchPayload(targetOffset);
    301         } else {
    302             return targetOffset;
    303         }
    304     }
    305 
    306     private static void writeAccessFlags(IndentingWriter writer, int accessFlags)
    307             throws IOException {
    308         for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(accessFlags)) {
    309             writer.write(accessFlag.toString());
    310             writer.write(' ');
    311         }
    312     }
    313 
    314     private static void writeParameters(IndentingWriter writer, Method method,
    315                                         List<? extends MethodParameter> parameters,
    316                                         BaksmaliOptions options) throws IOException {
    317         boolean isStatic = AccessFlags.STATIC.isSet(method.getAccessFlags());
    318         int registerNumber = isStatic?0:1;
    319         for (MethodParameter parameter: parameters) {
    320             String parameterType = parameter.getType();
    321             String parameterName = parameter.getName();
    322             Collection<? extends Annotation> annotations = parameter.getAnnotations();
    323             if ((options.debugInfo && parameterName != null) || annotations.size() != 0) {
    324                 writer.write(".param p");
    325                 writer.printSignedIntAsDec(registerNumber);
    326 
    327                 if (parameterName != null && options.debugInfo) {
    328                     writer.write(", ");
    329                     ReferenceFormatter.writeStringReference(writer, parameterName);
    330                 }
    331                 writer.write("    # ");
    332                 writer.write(parameterType);
    333                 writer.write("\n");
    334                 if (annotations.size() > 0) {
    335                     writer.indent(4);
    336 
    337                     String containingClass = null;
    338                     if (options.implicitReferences) {
    339                         containingClass = method.getDefiningClass();
    340                     }
    341                     AnnotationFormatter.writeTo(writer, annotations, containingClass);
    342                     writer.deindent(4);
    343                     writer.write(".end param\n");
    344                 }
    345             }
    346 
    347             registerNumber++;
    348             if (TypeUtils.isWideType(parameterType)) {
    349                 registerNumber++;
    350             }
    351         }
    352     }
    353 
    354     @Nonnull public LabelCache getLabelCache() {
    355         return labelCache;
    356     }
    357 
    358     public int getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset) {
    359         return packedSwitchMap.get(packedSwitchPayloadCodeOffset, -1);
    360     }
    361 
    362     public int getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset) {
    363         return sparseSwitchMap.get(sparseSwitchPayloadCodeOffset, -1);
    364     }
    365 
    366     private List<MethodItem> getMethodItems() {
    367         ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
    368 
    369         if ((classDef.options.registerInfo != 0) || (classDef.options.normalizeVirtualMethods) ||
    370                 (classDef.options.deodex && needsAnalyzed())) {
    371             addAnalyzedInstructionMethodItems(methodItems);
    372         } else {
    373             addInstructionMethodItems(methodItems);
    374         }
    375 
    376         addTries(methodItems);
    377         if (classDef.options.debugInfo) {
    378             addDebugInfo(methodItems);
    379         }
    380 
    381         if (classDef.options.sequentialLabels) {
    382             setLabelSequentialNumbers();
    383         }
    384 
    385         for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
    386             methodItems.add(labelMethodItem);
    387         }
    388 
    389         Collections.sort(methodItems);
    390 
    391         return methodItems;
    392     }
    393 
    394     private boolean needsAnalyzed() {
    395         for (Instruction instruction: methodImpl.getInstructions()) {
    396             if (instruction.getOpcode().odexOnly()) {
    397                 return true;
    398             }
    399         }
    400         return false;
    401     }
    402 
    403     private void addInstructionMethodItems(List<MethodItem> methodItems) {
    404         int currentCodeAddress = 0;
    405 
    406         for (int i=0; i<effectiveInstructions.size(); i++) {
    407             Instruction instruction = effectiveInstructions.get(i);
    408 
    409             MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
    410                     currentCodeAddress, instruction);
    411 
    412             methodItems.add(methodItem);
    413 
    414             if (i != effectiveInstructions.size() - 1) {
    415                 methodItems.add(new BlankMethodItem(currentCodeAddress));
    416             }
    417 
    418             if (classDef.options.codeOffsets) {
    419                 methodItems.add(new MethodItem(currentCodeAddress) {
    420 
    421                     @Override
    422                     public double getSortOrder() {
    423                         return -1000;
    424                     }
    425 
    426                     @Override
    427                     public boolean writeTo(IndentingWriter writer) throws IOException {
    428                         writer.write("#@");
    429                         writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL);
    430                         return true;
    431                     }
    432                 });
    433             }
    434 
    435             if (classDef.options.accessorComments && classDef.options.syntheticAccessorResolver != null &&
    436                     (instruction instanceof ReferenceInstruction)) {
    437                 Opcode opcode = instruction.getOpcode();
    438 
    439                 if (opcode.referenceType == ReferenceType.METHOD) {
    440                     MethodReference methodReference = null;
    441                     try {
    442                         methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference();
    443                     } catch (InvalidItemIndex ex) {
    444                         // just ignore it for now. We'll deal with it later, when processing the instructions
    445                         // themselves
    446                     }
    447 
    448                     if (methodReference != null &&
    449                             SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) {
    450                         AccessedMember accessedMember =
    451                                 classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference);
    452                         if (accessedMember != null) {
    453                             methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress));
    454                         }
    455                     }
    456                 }
    457             }
    458 
    459             currentCodeAddress += instruction.getCodeUnits();
    460         }
    461     }
    462 
    463     private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
    464         MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method,
    465                 classDef.options.inlineResolver, classDef.options.normalizeVirtualMethods);
    466 
    467         AnalysisException analysisException = methodAnalyzer.getAnalysisException();
    468         if (analysisException != null) {
    469             // TODO: need to keep track of whether any errors occurred, so we can exit with a non-zero result
    470             methodItems.add(new CommentMethodItem(
    471                     String.format("AnalysisException: %s", analysisException.getMessage()),
    472                     analysisException.codeAddress, Integer.MIN_VALUE));
    473             analysisException.printStackTrace(System.err);
    474         }
    475 
    476         List<AnalyzedInstruction> instructions = methodAnalyzer.getAnalyzedInstructions();
    477 
    478         int currentCodeAddress = 0;
    479         for (int i=0; i<instructions.size(); i++) {
    480             AnalyzedInstruction instruction = instructions.get(i);
    481 
    482             MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(
    483                     this, currentCodeAddress, instruction.getInstruction());
    484 
    485             methodItems.add(methodItem);
    486 
    487             if (instruction.getInstruction().getOpcode().format == Format.UnresolvedOdexInstruction) {
    488                 methodItems.add(new CommentedOutMethodItem(
    489                         InstructionMethodItemFactory.makeInstructionFormatMethodItem(
    490                                 this, currentCodeAddress, instruction.getOriginalInstruction())));
    491             }
    492 
    493             if (i != instructions.size() - 1) {
    494                 methodItems.add(new BlankMethodItem(currentCodeAddress));
    495             }
    496 
    497             if (classDef.options.codeOffsets) {
    498                 methodItems.add(new MethodItem(currentCodeAddress) {
    499 
    500                     @Override
    501                     public double getSortOrder() {
    502                         return -1000;
    503                     }
    504 
    505                     @Override
    506                     public boolean writeTo(IndentingWriter writer) throws IOException {
    507                         writer.write("#@");
    508                         writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFFL);
    509                         return true;
    510                     }
    511                 });
    512             }
    513 
    514             if (classDef.options.registerInfo != 0 &&
    515                     !instruction.getInstruction().getOpcode().format.isPayloadFormat) {
    516                 methodItems.add(
    517                         new PreInstructionRegisterInfoMethodItem(classDef.options.registerInfo,
    518                                 methodAnalyzer, registerFormatter, instruction, currentCodeAddress));
    519 
    520                 methodItems.add(
    521                         new PostInstructionRegisterInfoMethodItem(registerFormatter, instruction, currentCodeAddress));
    522             }
    523 
    524             currentCodeAddress += instruction.getInstruction().getCodeUnits();
    525         }
    526     }
    527 
    528     private void addTries(List<MethodItem> methodItems) {
    529         List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = methodImpl.getTryBlocks();
    530         if (tryBlocks.size() == 0) {
    531             return;
    532         }
    533 
    534         int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1);
    535         int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits();
    536 
    537         for (TryBlock<? extends ExceptionHandler> tryBlock: tryBlocks) {
    538             int startAddress = tryBlock.getStartCodeAddress();
    539             int endAddress = startAddress + tryBlock.getCodeUnitCount();
    540 
    541             if (startAddress >= codeSize) {
    542                 throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.",
    543                         startAddress));
    544             }
    545             // Note: not >=. endAddress == codeSize is valid, when the try covers the last instruction
    546             if (endAddress > codeSize) {
    547                 throw new RuntimeException(String.format("Try end offset %d is past the end of the code block.",
    548                         endAddress));
    549             }
    550 
    551             /**
    552              * The end address points to the address immediately after the end of the last
    553              * instruction that the try block covers. We want the .catch directive and end_try
    554              * label to be associated with the last covered instruction, so we need to get
    555              * the address for that instruction
    556              */
    557 
    558             int lastCoveredIndex = instructionOffsetMap.getInstructionIndexAtCodeOffset(endAddress - 1, false);
    559             int lastCoveredAddress = instructionOffsetMap.getInstructionCodeOffset(lastCoveredIndex);
    560 
    561             for (ExceptionHandler handler: tryBlock.getExceptionHandlers()) {
    562                 int handlerAddress = handler.getHandlerCodeAddress();
    563                 if (handlerAddress >= codeSize) {
    564                     throw new ExceptionWithContext(
    565                             "Exception handler offset %d is past the end of the code block.", handlerAddress);
    566                 }
    567 
    568                 //use the address from the last covered instruction
    569                 CatchMethodItem catchMethodItem = new CatchMethodItem(classDef.options, labelCache, lastCoveredAddress,
    570                         handler.getExceptionType(), startAddress, endAddress, handlerAddress);
    571                 methodItems.add(catchMethodItem);
    572             }
    573         }
    574     }
    575 
    576     private void addDebugInfo(final List<MethodItem> methodItems) {
    577         for (DebugItem debugItem: methodImpl.getDebugItems()) {
    578             methodItems.add(DebugMethodItem.build(registerFormatter, debugItem));
    579         }
    580     }
    581 
    582     private void setLabelSequentialNumbers() {
    583         HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>();
    584         ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels());
    585 
    586         //sort the labels by their location in the method
    587         Collections.sort(sortedLabels);
    588 
    589         for (LabelMethodItem labelMethodItem: sortedLabels) {
    590             Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix());
    591             if (labelSequence == null) {
    592                 labelSequence = 0;
    593             }
    594             labelMethodItem.setLabelSequence(labelSequence);
    595             nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1);
    596         }
    597     }
    598 
    599     @Nullable
    600     private String getContainingClassForImplicitReference() {
    601         if (classDef.options.implicitReferences) {
    602             return classDef.classDef.getType();
    603         }
    604         return null;
    605     }
    606 
    607     public static class LabelCache {
    608         protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();
    609 
    610         public LabelCache() {
    611         }
    612 
    613         public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
    614             LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
    615             if (internedLabelMethodItem != null) {
    616                 return internedLabelMethodItem;
    617             }
    618             labels.put(labelMethodItem, labelMethodItem);
    619             return labelMethodItem;
    620         }
    621 
    622 
    623         public Collection<LabelMethodItem> getLabels() {
    624             return labels.values();
    625         }
    626     }
    627 
    628     public static class InvalidSwitchPayload extends ExceptionWithContext {
    629         private final int payloadOffset;
    630 
    631         public InvalidSwitchPayload(int payloadOffset) {
    632             super("No switch payload at offset: %d", payloadOffset);
    633             this.payloadOffset = payloadOffset;
    634         }
    635 
    636         public int getPayloadOffset() {
    637             return payloadOffset;
    638         }
    639     }
    640 }
    641