• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MethodDefinition(@onnull ClassDefinition classDef, @Nonnull Method method, @Nonnull MethodImplementation methodImpl)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 
writeEmptyMethodTo(IndentingWriter writer, Method method, BaksmaliOptions options)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 
writeTo(IndentingWriter writer)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 
findSwitchPayload(int targetOffset, Opcode type)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 
findPayloadOffset(int targetOffset, Opcode type)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 
writeAccessFlags(IndentingWriter writer, int accessFlags)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 
writeParameters(IndentingWriter writer, Method method, List<? extends MethodParameter> parameters, BaksmaliOptions options)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 
getLabelCache()354     @Nonnull public LabelCache getLabelCache() {
355         return labelCache;
356     }
357 
getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset)358     public int getPackedSwitchBaseAddress(int packedSwitchPayloadCodeOffset) {
359         return packedSwitchMap.get(packedSwitchPayloadCodeOffset, -1);
360     }
361 
getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset)362     public int getSparseSwitchBaseAddress(int sparseSwitchPayloadCodeOffset) {
363         return sparseSwitchMap.get(sparseSwitchPayloadCodeOffset, -1);
364     }
365 
getMethodItems()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 
needsAnalyzed()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 
addInstructionMethodItems(List<MethodItem> methodItems)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 
addAnalyzedInstructionMethodItems(List<MethodItem> methodItems)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 
addTries(List<MethodItem> methodItems)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 
addDebugInfo(final List<MethodItem> methodItems)576     private void addDebugInfo(final List<MethodItem> methodItems) {
577         for (DebugItem debugItem: methodImpl.getDebugItems()) {
578             methodItems.add(DebugMethodItem.build(registerFormatter, debugItem));
579         }
580     }
581 
setLabelSequentialNumbers()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
getContainingClassForImplicitReference()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 
LabelCache()610         public LabelCache() {
611         }
612 
internLabel(LabelMethodItem labelMethodItem)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 
getLabels()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 
InvalidSwitchPayload(int payloadOffset)631         public InvalidSwitchPayload(int payloadOffset) {
632             super("No switch payload at offset: %d", payloadOffset);
633             this.payloadOffset = payloadOffset;
634         }
635 
getPayloadOffset()636         public int getPayloadOffset() {
637             return payloadOffset;
638         }
639     }
640 }
641