MethodDefinition.java revision 961c21be988e842cb552e8e55f59b69656bffc6c
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
29package org.jf.baksmali.Adaptors;
30
31import com.google.common.collect.ImmutableList;
32import com.google.common.collect.Lists;
33import org.jf.baksmali.Adaptors.Debug.DebugMethodItem;
34import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
35import org.jf.baksmali.BaksmaliOptions;
36import org.jf.dexlib2.AccessFlags;
37import org.jf.dexlib2.Format;
38import org.jf.dexlib2.Opcode;
39import org.jf.dexlib2.ReferenceType;
40import org.jf.dexlib2.analysis.AnalysisException;
41import org.jf.dexlib2.analysis.AnalyzedInstruction;
42import org.jf.dexlib2.analysis.MethodAnalyzer;
43import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex;
44import org.jf.dexlib2.iface.*;
45import org.jf.dexlib2.iface.debug.DebugItem;
46import org.jf.dexlib2.iface.instruction.Instruction;
47import org.jf.dexlib2.iface.instruction.OffsetInstruction;
48import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
49import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
50import org.jf.dexlib2.iface.reference.MethodReference;
51import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t;
52import org.jf.dexlib2.util.InstructionOffsetMap;
53import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset;
54import org.jf.dexlib2.util.ReferenceUtil;
55import org.jf.dexlib2.util.SyntheticAccessorResolver;
56import org.jf.dexlib2.util.SyntheticAccessorResolver.AccessedMember;
57import org.jf.dexlib2.util.TypeUtils;
58import org.jf.util.ExceptionWithContext;
59import org.jf.util.IndentingWriter;
60import org.jf.util.SparseIntArray;
61
62import javax.annotation.Nonnull;
63import javax.annotation.Nullable;
64import java.io.IOException;
65import java.util.*;
66
67public 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