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 org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
     32 import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver;
     33 import org.jf.dexlib.Code.InstructionWithReference;
     34 import org.jf.util.IndentingWriter;
     35 import org.jf.baksmali.baksmali;
     36 import org.jf.dexlib.*;
     37 import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
     38 import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
     39 import org.jf.dexlib.Code.Analysis.ValidationException;
     40 import org.jf.dexlib.Code.Format.Format;
     41 import org.jf.dexlib.Code.Instruction;
     42 import org.jf.dexlib.Code.OffsetInstruction;
     43 import org.jf.dexlib.Code.Opcode;
     44 import org.jf.dexlib.Debug.DebugInstructionIterator;
     45 import org.jf.dexlib.Util.AccessFlags;
     46 import org.jf.dexlib.Util.ExceptionWithContext;
     47 import org.jf.dexlib.Util.SparseIntArray;
     48 
     49 import java.io.IOException;
     50 import java.util.*;
     51 
     52 public class MethodDefinition {
     53     private final ClassDataItem.EncodedMethod encodedMethod;
     54     private MethodAnalyzer methodAnalyzer;
     55 
     56     private final LabelCache labelCache = new LabelCache();
     57 
     58     private final SparseIntArray packedSwitchMap;
     59     private final SparseIntArray sparseSwitchMap;
     60     private final SparseIntArray instructionMap;
     61 
     62     public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod) {
     63 
     64 
     65         try {
     66             this.encodedMethod = encodedMethod;
     67 
     68             //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
     69 
     70             if (encodedMethod.codeItem != null) {
     71                 Instruction[] instructions = encodedMethod.codeItem.getInstructions();
     72 
     73                 packedSwitchMap = new SparseIntArray(1);
     74                 sparseSwitchMap = new SparseIntArray(1);
     75                 instructionMap = new SparseIntArray(instructions.length);
     76 
     77                 int currentCodeAddress = 0;
     78                 for (int i=0; i<instructions.length; i++) {
     79                     Instruction instruction = instructions[i];
     80                     if (instruction.opcode == Opcode.PACKED_SWITCH) {
     81                         packedSwitchMap.append(
     82                                 currentCodeAddress +
     83                                         ((OffsetInstruction)instruction).getTargetAddressOffset(),
     84                                 currentCodeAddress);
     85                     } else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
     86                         sparseSwitchMap.append(
     87                                 currentCodeAddress +
     88                                         ((OffsetInstruction)instruction).getTargetAddressOffset(),
     89                                 currentCodeAddress);
     90                     }
     91                     instructionMap.append(currentCodeAddress, i);
     92                     currentCodeAddress += instruction.getSize(currentCodeAddress);
     93                 }
     94             } else {
     95                 packedSwitchMap = null;
     96                 sparseSwitchMap = null;
     97                 instructionMap = null;
     98                 methodAnalyzer = null;
     99             }
    100         }catch (Exception ex) {
    101             throw ExceptionWithContext.withContext(ex, String.format("Error while processing method %s",
    102                     encodedMethod.method.getMethodString()));
    103         }
    104     }
    105 
    106     public void writeTo(IndentingWriter writer, AnnotationSetItem annotationSet,
    107                         AnnotationSetRefList parameterAnnotations) throws IOException {
    108         final CodeItem codeItem = encodedMethod.codeItem;
    109 
    110         writer.write(".method ");
    111         writeAccessFlags(writer, encodedMethod);
    112         writer.write(encodedMethod.method.getMethodName().getStringValue());
    113         writer.write(encodedMethod.method.getPrototype().getPrototypeString());
    114         writer.write('\n');
    115 
    116         writer.indent(4);
    117         if (codeItem != null) {
    118             if (baksmali.useLocalsDirective) {
    119                 writer.write(".locals ");
    120             } else {
    121                 writer.write(".registers ");
    122             }
    123             writer.printSignedIntAsDec(getRegisterCount(encodedMethod));
    124             writer.write('\n');
    125             writeParameters(writer, codeItem, parameterAnnotations);
    126             if (annotationSet != null) {
    127                 AnnotationFormatter.writeTo(writer, annotationSet);
    128             }
    129 
    130             writer.write('\n');
    131 
    132             for (MethodItem methodItem: getMethodItems()) {
    133                 if (methodItem.writeTo(writer)) {
    134                     writer.write('\n');
    135                 }
    136             }
    137         } else {
    138             writeParameters(writer, codeItem, parameterAnnotations);
    139             if (annotationSet != null) {
    140                 AnnotationFormatter.writeTo(writer, annotationSet);
    141             }
    142         }
    143         writer.deindent(4);
    144         writer.write(".end method\n");
    145     }
    146 
    147     private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod)
    148     {
    149         int totalRegisters = encodedMethod.codeItem.getRegisterCount();
    150         if (baksmali.useLocalsDirective) {
    151             int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount();
    152             if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
    153                 parameterRegisters++;
    154             }
    155             return totalRegisters - parameterRegisters;
    156         }
    157         return totalRegisters;
    158     }
    159 
    160     private static void writeAccessFlags(IndentingWriter writer, ClassDataItem.EncodedMethod encodedMethod)
    161                                                                                             throws IOException {
    162         for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
    163             writer.write(accessFlag.toString());
    164             writer.write(' ');
    165         }
    166     }
    167 
    168     private static void writeParameters(IndentingWriter writer, CodeItem codeItem,
    169                                         AnnotationSetRefList parameterAnnotations) throws IOException {
    170         DebugInfoItem debugInfoItem = null;
    171         if (baksmali.outputDebugInfo && codeItem != null) {
    172             debugInfoItem = codeItem.getDebugInfo();
    173         }
    174 
    175         int parameterCount = 0;
    176         AnnotationSetItem[] annotations;
    177         StringIdItem[] parameterNames = null;
    178 
    179         if (parameterAnnotations != null) {
    180             annotations = parameterAnnotations.getAnnotationSets();
    181             parameterCount = annotations.length;
    182         } else {
    183             annotations = new AnnotationSetItem[0];
    184         }
    185 
    186         if (debugInfoItem != null) {
    187             parameterNames = debugInfoItem.getParameterNames();
    188         }
    189         if (parameterNames == null) {
    190             parameterNames = new StringIdItem[0];
    191         }
    192 
    193         if (parameterCount < parameterNames.length) {
    194             parameterCount = parameterNames.length;
    195         }
    196 
    197         for (int i=0; i<parameterCount; i++) {
    198             AnnotationSetItem annotationSet = null;
    199             if (i < annotations.length) {
    200                 annotationSet = annotations[i];
    201             }
    202 
    203             StringIdItem parameterName = null;
    204             if (i < parameterNames.length) {
    205                 parameterName = parameterNames[i];
    206             }
    207 
    208             writer.write(".parameter");
    209 
    210             if (parameterName != null) {
    211                 writer.write(" \"");
    212                 writer.write(parameterName.getStringValue());
    213                 writer.write('"');
    214             }
    215 
    216             writer.write('\n');
    217             if (annotationSet != null) {
    218                 writer.indent(4);
    219                 AnnotationFormatter.writeTo(writer, annotationSet);
    220                 writer.deindent(4);
    221 
    222                 writer.write(".end parameter\n");
    223             }
    224         }
    225     }
    226 
    227     public LabelCache getLabelCache() {
    228         return labelCache;
    229     }
    230 
    231     public ValidationException getValidationException() {
    232         if (methodAnalyzer == null) {
    233             return null;
    234         }
    235 
    236         return methodAnalyzer.getValidationException();
    237     }
    238 
    239     public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
    240         int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
    241 
    242         if (packedSwitchBaseAddress == -1) {
    243             Instruction[] instructions = encodedMethod.codeItem.getInstructions();
    244             int index = instructionMap.get(packedSwitchDataAddress);
    245 
    246             if (instructions[index].opcode == Opcode.NOP) {
    247                 packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress+2, -1);
    248             }
    249         }
    250 
    251         return packedSwitchBaseAddress;
    252     }
    253 
    254     public int getSparseSwitchBaseAddress(int sparseSwitchDataAddress) {
    255         int sparseSwitchBaseAddress = this.sparseSwitchMap.get(sparseSwitchDataAddress, -1);
    256 
    257         if (sparseSwitchBaseAddress == -1) {
    258             Instruction[] instructions = encodedMethod.codeItem.getInstructions();
    259             int index = instructionMap.get(sparseSwitchDataAddress);
    260 
    261             if (instructions[index].opcode == Opcode.NOP) {
    262                 sparseSwitchBaseAddress = this.packedSwitchMap.get(sparseSwitchDataAddress+2, -1);
    263             }
    264         }
    265 
    266         return sparseSwitchBaseAddress;
    267     }
    268 
    269     /**
    270      * @param instructions The instructions array for this method
    271      * @param instruction The instruction
    272      * @return true if the specified instruction is a NOP, and the next instruction is one of the variable sized
    273      * switch/array data structures
    274      */
    275     private boolean isInstructionPaddingNop(List<AnalyzedInstruction> instructions, AnalyzedInstruction instruction) {
    276         if (instruction.getInstruction().opcode != Opcode.NOP ||
    277             instruction.getInstruction().getFormat().variableSizeFormat) {
    278 
    279             return false;
    280         }
    281 
    282         if (instruction.getInstructionIndex() == instructions.size()-1) {
    283             return false;
    284         }
    285 
    286         AnalyzedInstruction nextInstruction = instructions.get(instruction.getInstructionIndex()+1);
    287         if (nextInstruction.getInstruction().getFormat().variableSizeFormat) {
    288             return true;
    289         }
    290         return false;
    291     }
    292 
    293     private boolean needsAnalyzed() {
    294         for (Instruction instruction: encodedMethod.codeItem.getInstructions()) {
    295             if (instruction.opcode.odexOnly()) {
    296                 return true;
    297             }
    298         }
    299         return false;
    300     }
    301 
    302     private List<MethodItem> getMethodItems() {
    303         ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
    304 
    305         if (encodedMethod.codeItem == null) {
    306             return methodItems;
    307         }
    308 
    309         if ((baksmali.registerInfo != 0) || baksmali.verify ||
    310             (baksmali.deodex && needsAnalyzed())) {
    311             addAnalyzedInstructionMethodItems(methodItems);
    312         } else {
    313             addInstructionMethodItems(methodItems);
    314         }
    315 
    316         addTries(methodItems);
    317         if (baksmali.outputDebugInfo) {
    318             addDebugInfo(methodItems);
    319         }
    320 
    321         if (baksmali.useSequentialLabels) {
    322             setLabelSequentialNumbers();
    323         }
    324 
    325         for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
    326             methodItems.add(labelMethodItem);
    327         }
    328 
    329         Collections.sort(methodItems);
    330 
    331         return methodItems;
    332     }
    333 
    334     private void addInstructionMethodItems(List<MethodItem> methodItems) {
    335         Instruction[] instructions = encodedMethod.codeItem.getInstructions();
    336 
    337         int currentCodeAddress = 0;
    338         for (int i=0; i<instructions.length; i++) {
    339             Instruction instruction = instructions[i];
    340 
    341             MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
    342                     encodedMethod.codeItem, currentCodeAddress, instruction);
    343 
    344             methodItems.add(methodItem);
    345 
    346             if (i != instructions.length - 1) {
    347                 methodItems.add(new BlankMethodItem(currentCodeAddress));
    348             }
    349 
    350             if (baksmali.addCodeOffsets) {
    351                 methodItems.add(new MethodItem(currentCodeAddress) {
    352 
    353                     @Override
    354                     public double getSortOrder() {
    355                         return -1000;
    356                     }
    357 
    358                     @Override
    359                     public boolean writeTo(IndentingWriter writer) throws IOException {
    360                         writer.write("#@");
    361                         writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFF);
    362                         return true;
    363                     }
    364                 });
    365             }
    366 
    367             if (!baksmali.noAccessorComments && (instruction instanceof InstructionWithReference)) {
    368                 if (instruction.opcode == Opcode.INVOKE_STATIC || instruction.opcode == Opcode.INVOKE_STATIC_RANGE) {
    369                     MethodIdItem methodIdItem =
    370                             (MethodIdItem)((InstructionWithReference) instruction).getReferencedItem();
    371 
    372                     if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodIdItem)) {
    373                         SyntheticAccessorResolver.AccessedMember accessedMember =
    374                                 baksmali.syntheticAccessorResolver.getAccessedMember(methodIdItem);
    375                         if (accessedMember != null) {
    376                             methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress));
    377                         }
    378                     }
    379                 }
    380             }
    381 
    382             currentCodeAddress += instruction.getSize(currentCodeAddress);
    383         }
    384     }
    385 
    386     private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
    387         methodAnalyzer = new MethodAnalyzer(encodedMethod, baksmali.deodex, baksmali.inlineResolver);
    388 
    389         methodAnalyzer.analyze();
    390 
    391         ValidationException validationException = methodAnalyzer.getValidationException();
    392         if (validationException != null) {
    393             methodItems.add(new CommentMethodItem(
    394                     String.format("ValidationException: %s" ,validationException.getMessage()),
    395                     validationException.getCodeAddress(), Integer.MIN_VALUE));
    396         } else if (baksmali.verify) {
    397             methodAnalyzer.verify();
    398 
    399             validationException = methodAnalyzer.getValidationException();
    400             if (validationException != null) {
    401                 methodItems.add(new CommentMethodItem(
    402                         String.format("ValidationException: %s" ,validationException.getMessage()),
    403                         validationException.getCodeAddress(), Integer.MIN_VALUE));
    404             }
    405         }
    406 
    407         List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
    408 
    409         int currentCodeAddress = 0;
    410         for (int i=0; i<instructions.size(); i++) {
    411             AnalyzedInstruction instruction = instructions.get(i);
    412 
    413             MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
    414                     encodedMethod.codeItem, currentCodeAddress, instruction.getInstruction());
    415 
    416             methodItems.add(methodItem);
    417 
    418             if (instruction.getInstruction().getFormat() == Format.UnresolvedOdexInstruction) {
    419                 methodItems.add(new CommentedOutMethodItem(
    420                         InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
    421                                 encodedMethod.codeItem, currentCodeAddress, instruction.getOriginalInstruction())));
    422             }
    423 
    424             if (i != instructions.size() - 1) {
    425                 methodItems.add(new BlankMethodItem(currentCodeAddress));
    426             }
    427 
    428             if (baksmali.addCodeOffsets) {
    429                 methodItems.add(new MethodItem(currentCodeAddress) {
    430 
    431                     @Override
    432                     public double getSortOrder() {
    433                         return -1000;
    434                     }
    435 
    436                     @Override
    437                     public boolean writeTo(IndentingWriter writer) throws IOException {
    438                         writer.write("#@");
    439                         writer.printUnsignedLongAsHex(codeAddress & 0xFFFFFFFF);
    440                         return true;
    441                     }
    442                 });
    443             }
    444 
    445             if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) {
    446                 methodItems.add(
    447                         new PreInstructionRegisterInfoMethodItem(instruction, methodAnalyzer, currentCodeAddress));
    448 
    449                 methodItems.add(
    450                         new PostInstructionRegisterInfoMethodItem(instruction, methodAnalyzer, currentCodeAddress));
    451             }
    452 
    453             currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
    454         }
    455     }
    456 
    457     private void addTries(List<MethodItem> methodItems) {
    458         if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
    459             return;
    460         }
    461 
    462         Instruction[] instructions = encodedMethod.codeItem.getInstructions();
    463         int lastInstructionAddress = instructionMap.keyAt(instructionMap.size()-1);
    464         int codeSize = lastInstructionAddress + instructions[instructions.length - 1].getSize(lastInstructionAddress);
    465 
    466         for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
    467             int startAddress = tryItem.getStartCodeAddress();
    468             int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength();
    469 
    470             if (startAddress >= codeSize) {
    471                 throw new RuntimeException(String.format("Try start offset %d is past the end of the code block.",
    472                         startAddress));
    473             }
    474             // Note: not >=. endAddress == codeSize is valid, when the try covers the last instruction
    475             if (endAddress > codeSize) {
    476                 throw new RuntimeException(String.format("Try end offset %d is past the end of the code block.",
    477                         endAddress));
    478             }
    479 
    480             /**
    481              * The end address points to the address immediately after the end of the last
    482              * instruction that the try block covers. We want the .catch directive and end_try
    483              * label to be associated with the last covered instruction, so we need to get
    484              * the address for that instruction
    485              */
    486 
    487             int lastCoveredIndex = instructionMap.getClosestSmaller(endAddress-1);
    488             int lastCoveredAddress = instructionMap.keyAt(lastCoveredIndex);
    489 
    490             //add the catch all handler if it exists
    491             int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
    492             if (catchAllAddress != -1) {
    493                 if (catchAllAddress >= codeSize) {
    494                     throw new RuntimeException(String.format(
    495                             "Catch all handler offset %d is past the end of the code block.", catchAllAddress));
    496                 }
    497 
    498                 CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress, null,
    499                         startAddress, endAddress, catchAllAddress);
    500                 methodItems.add(catchAllMethodItem);
    501             }
    502 
    503             //add the rest of the handlers
    504             for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
    505                 if (handler.getHandlerAddress() >= codeSize) {
    506                     throw new RuntimeException(String.format(
    507                             "Exception handler offset %d is past the end of the code block.", catchAllAddress));
    508                 }
    509 
    510                 //use the address from the last covered instruction
    511                 CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastCoveredAddress,
    512                         handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
    513                 methodItems.add(catchMethodItem);
    514             }
    515         }
    516     }
    517 
    518     private void addDebugInfo(final List<MethodItem> methodItems) {
    519         if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) {
    520             return;
    521         }
    522 
    523         final CodeItem codeItem = encodedMethod.codeItem;
    524         DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
    525 
    526         DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
    527                 new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
    528                     @Override
    529                     public void ProcessStartLocal(final int codeAddress, final int length, final int registerNum,
    530                                                   final StringIdItem name, final TypeIdItem type) {
    531                         methodItems.add(new DebugMethodItem(codeAddress, -1) {
    532                             @Override
    533                             public boolean writeTo(IndentingWriter writer) throws IOException {
    534                                 writeStartLocal(writer, codeItem, registerNum, name, type, null);
    535                                 return true;
    536                             }
    537                         });
    538                     }
    539 
    540                     @Override
    541                     public void ProcessStartLocalExtended(final int codeAddress, final int length,
    542                                                           final int registerNum, final StringIdItem name,
    543                                                           final TypeIdItem type, final StringIdItem signature) {
    544                         methodItems.add(new DebugMethodItem(codeAddress, -1) {
    545                             @Override
    546                             public boolean writeTo(IndentingWriter writer) throws IOException {
    547                                 writeStartLocal(writer, codeItem, registerNum, name, type, signature);
    548                                 return true;
    549                             }
    550                         });
    551                     }
    552 
    553                     @Override
    554                     public void ProcessEndLocal(final int codeAddress, final int length, final int registerNum,
    555                                                 final StringIdItem name, final TypeIdItem type,
    556                                                 final StringIdItem signature) {
    557                         methodItems.add(new DebugMethodItem(codeAddress, -1) {
    558                             @Override
    559                             public boolean writeTo(IndentingWriter writer) throws IOException {
    560                                 writeEndLocal(writer, codeItem, registerNum, name, type, signature);
    561                                 return true;
    562                             }
    563                         });
    564                     }
    565 
    566                     @Override
    567                     public void ProcessRestartLocal(final int codeAddress, final int length, final int registerNum,
    568                                                 final StringIdItem name, final TypeIdItem type,
    569                                                 final StringIdItem signature) {
    570                         methodItems.add(new DebugMethodItem(codeAddress, -1) {
    571                             @Override
    572                             public boolean writeTo(IndentingWriter writer) throws IOException {
    573                                 writeRestartLocal(writer, codeItem, registerNum, name, type, signature);
    574                                 return true;
    575                             }
    576                         });
    577                     }
    578 
    579                     @Override
    580                     public void ProcessSetPrologueEnd(int codeAddress) {
    581                         methodItems.add(new DebugMethodItem(codeAddress, -4) {
    582                             @Override
    583                             public boolean writeTo(IndentingWriter writer) throws IOException {
    584                                 writeEndPrologue(writer);
    585                                 return true;
    586                             }
    587                         });
    588                     }
    589 
    590                     @Override
    591                     public void ProcessSetEpilogueBegin(int codeAddress) {
    592                         methodItems.add(new DebugMethodItem(codeAddress, -4) {
    593                             @Override
    594                             public boolean writeTo(IndentingWriter writer) throws IOException {
    595                                 writeBeginEpilogue(writer);
    596                                 return true;
    597                             }
    598                         });
    599                     }
    600 
    601                     @Override
    602                     public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
    603                         methodItems.add(new DebugMethodItem(codeAddress, -3) {
    604                             @Override
    605                             public boolean writeTo(IndentingWriter writer) throws IOException {
    606                                 writeSetFile(writer, name.getStringValue());
    607                                 return true;
    608                             }
    609                         });
    610                     }
    611 
    612                     @Override
    613                     public void ProcessLineEmit(int codeAddress, final int line) {
    614                         methodItems.add(new DebugMethodItem(codeAddress, -2) {
    615                             @Override
    616                             public boolean writeTo(IndentingWriter writer) throws IOException {
    617                                 writeLine(writer, line);
    618                                 return true;
    619                             }
    620                          });
    621                     }
    622                 });
    623     }
    624 
    625     private void setLabelSequentialNumbers() {
    626         HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>();
    627         ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels());
    628 
    629         //sort the labels by their location in the method
    630         Collections.sort(sortedLabels);
    631 
    632         for (LabelMethodItem labelMethodItem: sortedLabels) {
    633             Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix());
    634             if (labelSequence == null) {
    635                 labelSequence = 0;
    636             }
    637             labelMethodItem.setLabelSequence(labelSequence);
    638             nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1);
    639         }
    640     }
    641 
    642     public static class LabelCache {
    643         protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();
    644 
    645         public LabelCache() {
    646         }
    647 
    648         public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
    649             LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
    650             if (internedLabelMethodItem != null) {
    651                 return internedLabelMethodItem;
    652             }
    653             labels.put(labelMethodItem, labelMethodItem);
    654             return labelMethodItem;
    655         }
    656 
    657 
    658         public Collection<LabelMethodItem> getLabels() {
    659             return labels.values();
    660         }
    661     }
    662 }
    663