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