MethodDefinition.java revision bf95959ae43ddd003936a01dfaecc612a438d4b5
1d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt/*
2d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * [The "BSD licence"]
3d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * Copyright (c) 2010 Ben Gruver (JesusFreke)
4d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * All rights reserved.
5d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *
6d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * Redistribution and use in source and binary forms, with or without
7d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * modification, are permitted provided that the following conditions
8d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * are met:
9d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * 1. Redistributions of source code must retain the above copyright
10d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *    notice, this list of conditions and the following disclaimer.
11d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * 2. Redistributions in binary form must reproduce the above copyright
12d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *    notice, this list of conditions and the following disclaimer in the
13d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *    documentation and/or other materials provided with the distribution.
14d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * 3. The name of the author may not be used to endorse or promote products
15d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *    derived from this software without specific prior written permission.
16d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt *
17d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21d5dc24eb5fbf0e0feff214c0260cae845721d5feDmitry Shmidt * 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 org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory;
32import org.jf.dexlib.Code.Analysis.SyntheticAccessorResolver;
33import org.jf.dexlib.Code.InstructionWithReference;
34import org.jf.util.IndentingWriter;
35import org.jf.baksmali.baksmali;
36import org.jf.dexlib.*;
37import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
38import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
39import org.jf.dexlib.Code.Analysis.ValidationException;
40import org.jf.dexlib.Code.Format.Format;
41import org.jf.dexlib.Code.Instruction;
42import org.jf.dexlib.Code.OffsetInstruction;
43import org.jf.dexlib.Code.Opcode;
44import org.jf.dexlib.Debug.DebugInstructionIterator;
45import org.jf.dexlib.Util.AccessFlags;
46import org.jf.util.ExceptionWithContext;
47import org.jf.dexlib.Util.SparseIntArray;
48
49import java.io.IOException;
50import java.util.*;
51
52public 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