MethodDefinition.java revision 1c56c7e7507dc24ae1ed2f693c793d94df814c76
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.baksmali.Adaptors;
30
31import org.jf.baksmali.Adaptors.Format.*;
32import org.jf.baksmali.baksmali;
33import org.jf.baksmali.main;
34import org.jf.dexlib.*;
35import org.jf.dexlib.Code.*;
36import org.jf.dexlib.Code.Analysis.AnalyzedInstruction;
37import org.jf.dexlib.Code.Analysis.MethodAnalyzer;
38import org.jf.dexlib.Code.Analysis.RegisterType;
39import org.jf.dexlib.Code.Analysis.ValidationException;
40import org.jf.dexlib.Debug.DebugInstructionIterator;
41import org.jf.dexlib.Util.AccessFlags;
42import org.antlr.stringtemplate.StringTemplateGroup;
43import org.antlr.stringtemplate.StringTemplate;
44import org.jf.dexlib.Util.SparseIntArray;
45
46import java.util.*;
47
48public class MethodDefinition {
49    private final StringTemplateGroup stg;
50    private final ClassDataItem.EncodedMethod encodedMethod;
51    private final MethodAnalyzer methodAnalyzer;
52
53    private final LabelCache labelCache = new LabelCache();
54
55    private final SparseIntArray packedSwitchMap;
56    private final SparseIntArray sparseSwitchMap;
57    private final SparseIntArray instructionMap;
58
59    private final int registerCount;
60
61    public MethodDefinition(StringTemplateGroup stg, ClassDataItem.EncodedMethod encodedMethod) {
62        this.stg = stg;
63        this.encodedMethod = encodedMethod;
64
65        //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
66
67        if (encodedMethod.codeItem != null) {
68            methodAnalyzer = new MethodAnalyzer(encodedMethod);
69            AnalyzedInstruction[] instructions = methodAnalyzer.makeInstructionArray();
70
71            packedSwitchMap = new SparseIntArray(1);
72            sparseSwitchMap = new SparseIntArray(1);
73            instructionMap = new SparseIntArray(instructions.length);
74
75            registerCount = encodedMethod.codeItem.getRegisterCount();
76
77            int currentCodeAddress = 0;
78            for (int i=0; i<instructions.length; i++) {
79                AnalyzedInstruction instruction = instructions[i];
80                if (instruction.instruction.opcode == Opcode.PACKED_SWITCH) {
81                    packedSwitchMap.append(
82                            currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
83                            currentCodeAddress);
84                } else if (instruction.instruction.opcode == Opcode.SPARSE_SWITCH) {
85                    sparseSwitchMap.append(
86                            currentCodeAddress + ((OffsetInstruction)instruction.instruction).getTargetAddressOffset(),
87                            currentCodeAddress);
88                }
89                instructionMap.append(currentCodeAddress, i);
90                currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
91            }
92        } else {
93            packedSwitchMap = null;
94            sparseSwitchMap = null;
95            instructionMap = null;
96            methodAnalyzer = null;
97            registerCount = 0;
98        }
99    }
100
101    public StringTemplate createTemplate(AnnotationSetItem annotationSet,
102                                                AnnotationSetRefList parameterAnnotations) {
103
104        CodeItem codeItem = encodedMethod.codeItem;
105
106        StringTemplate template = stg.getInstanceOf("method");
107
108        template.setAttribute("AccessFlags", getAccessFlags(encodedMethod));
109        template.setAttribute("MethodName", encodedMethod.method.getMethodName().getStringValue());
110        template.setAttribute("Prototype", encodedMethod.method.getPrototype().getPrototypeString());
111        template.setAttribute("HasCode", codeItem != null);
112        template.setAttribute("RegistersDirective", baksmali.useLocalsDirective?".locals":".registers");
113        template.setAttribute("RegisterCount", codeItem==null?"0":Integer.toString(getRegisterCount(encodedMethod)));
114        template.setAttribute("Parameters", getParameters(stg, codeItem, parameterAnnotations));
115        template.setAttribute("Annotations", getAnnotations(stg, annotationSet));
116        template.setAttribute("MethodItems", getMethodItems());
117
118        return template;
119    }
120
121    private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod)
122    {
123        int totalRegisters = encodedMethod.codeItem.getRegisterCount();
124        if (baksmali.useLocalsDirective) {
125            int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount();
126            if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
127                parameterRegisters++;
128            }
129            return totalRegisters - parameterRegisters;
130        }
131        return totalRegisters;
132    }
133
134    private static List<String> getAccessFlags(ClassDataItem.EncodedMethod encodedMethod) {
135        List<String> accessFlags = new ArrayList<String>();
136
137        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
138            accessFlags.add(accessFlag.toString());
139        }
140
141        return accessFlags;
142    }
143
144    private static List<StringTemplate> getParameters(StringTemplateGroup stg, CodeItem codeItem,
145                                                               AnnotationSetRefList parameterAnnotations) {
146        DebugInfoItem debugInfoItem = null;
147        if (baksmali.outputDebugInfo && codeItem != null) {
148            debugInfoItem = codeItem.getDebugInfo();
149        }
150
151        int parameterCount = 0;
152
153        List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>();
154        if (parameterAnnotations != null) {
155            AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets();
156            if (_annotations != null) {
157                annotations.addAll(Arrays.asList(_annotations));
158            }
159
160            parameterCount = annotations.size();
161        }
162
163        List<String> parameterNames = new ArrayList<String>();
164        if (debugInfoItem != null) {
165            StringIdItem[] _parameterNames = debugInfoItem.getParameterNames();
166            if (_parameterNames != null) {
167                for (StringIdItem parameterName: _parameterNames) {
168                    parameterNames.add(parameterName==null?null:parameterName.getStringValue());
169                }
170            }
171
172            if (parameterCount < parameterNames.size()) {
173                parameterCount = parameterNames.size();
174            }
175        }
176
177        List<StringTemplate> parameters = new ArrayList<StringTemplate>();
178        for (int i=0; i<parameterCount; i++) {
179            AnnotationSetItem annotationSet = null;
180            if (i < annotations.size()) {
181                annotationSet = annotations.get(i);
182            }
183
184            String parameterName = null;
185            if (i < parameterNames.size()) {
186                parameterName = parameterNames.get(i);
187            }
188
189            parameters.add(ParameterAdaptor.createTemplate(stg, parameterName, annotationSet));
190        }
191
192        return parameters;
193    }
194
195    public LabelCache getLabelCache() {
196        return labelCache;
197    }
198
199    public ValidationException getValidationException() {
200        if (methodAnalyzer == null) {
201            return null;
202        }
203
204        return methodAnalyzer.getValidationException();
205    }
206
207    public int getPackedSwitchBaseAddress(int packedSwitchDataAddress) {
208        int packedSwitchBaseAddress = this.packedSwitchMap.get(packedSwitchDataAddress, -1);
209
210        if (packedSwitchBaseAddress == -1) {
211            throw new RuntimeException("Could not find the packed switch statement corresponding to the packed " +
212                    "switch data at address " + packedSwitchDataAddress);
213        }
214
215        return packedSwitchBaseAddress;
216    }
217
218    public int getSparseSwitchBaseAddress(int sparseSwitchDataAddress) {
219        int sparseSwitchBaseAddress = this.sparseSwitchMap.get(sparseSwitchDataAddress, -1);
220
221        if (sparseSwitchBaseAddress == -1) {
222            throw new RuntimeException("Could not find the sparse switch statement corresponding to the sparse " +
223                    "switch data at address " + sparseSwitchDataAddress);
224        }
225
226        return sparseSwitchBaseAddress;
227    }
228
229    private static List<StringTemplate> getAnnotations(StringTemplateGroup stg, AnnotationSetItem annotationSet) {
230        if (annotationSet == null) {
231            return null;
232        }
233
234        List<StringTemplate> annotationAdaptors = new ArrayList<StringTemplate>();
235
236        for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
237            annotationAdaptors.add(AnnotationAdaptor.createTemplate(stg, annotationItem));
238        }
239        return annotationAdaptors;
240    }
241
242    private List<MethodItem> getMethodItems() {
243        List<MethodItem> methodItems = new ArrayList<MethodItem>();
244
245        if (encodedMethod.codeItem == null) {
246            return methodItems;
247        }
248
249        AnalyzedInstruction[] instructions;
250        if (baksmali.registerInfo != 0) {
251            instructions = methodAnalyzer.analyze();
252
253            ValidationException validationException = methodAnalyzer.getValidationException();
254            if (validationException != null) {
255                methodItems.add(new CommentMethodItem(stg,
256                        String.format("ValidationException: %s" ,validationException.getMessage()),
257                        validationException.getCodeAddress(), Integer.MIN_VALUE));
258            }
259        } else {
260            instructions = methodAnalyzer.makeInstructionArray();
261        }
262
263        BitSet printPreRegister = new BitSet(registerCount);
264        BitSet printPostRegister = new BitSet(registerCount);
265
266        int currentCodeAddress = 0;
267        for (int i=0; i<instructions.length; i++) {
268            AnalyzedInstruction instruction = instructions[i];
269
270            methodItems.add(InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
271                    encodedMethod.codeItem, currentCodeAddress, stg, instruction.instruction));
272
273            if (i != instructions.length - 1) {
274                methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
275            }
276
277            if (baksmali.registerInfo != 0) {
278                printPreRegister.clear();
279                printPostRegister.clear();
280
281                if ((baksmali.registerInfo & main.ALL) != 0) {
282                    printPreRegister.set(0, registerCount);
283                    printPostRegister.set(0, registerCount);
284                } else {
285                    if ((baksmali.registerInfo & main.ALLPRE) != 0) {
286                        printPreRegister.set(0, registerCount);
287                    } else {
288                        if ((baksmali.registerInfo & main.ARGS) != 0) {
289                            addArgsRegs(printPreRegister, instruction);
290                        }
291                        if ((baksmali.registerInfo & main.MERGE) != 0) {
292                            addMergeRegs(printPreRegister, instructions, i);
293                        } else if ((baksmali.registerInfo & main.FULLMERGE) != 0 &&
294                                (i == 0 || instruction.isBeginningInstruction())) {
295                            addParamRegs(printPreRegister);
296                        }
297                    }
298
299                    if ((baksmali.registerInfo & main.ALLPOST) != 0) {
300                        printPostRegister.set(0, registerCount);
301                    } else if ((baksmali.registerInfo & main.DEST) != 0) {
302                        addDestRegs(printPostRegister, instruction);
303                    }
304                }
305
306                if ((baksmali.registerInfo & main.FULLMERGE) != 0) {
307                    addFullMergeRegs(printPreRegister, methodItems, methodAnalyzer, i, currentCodeAddress);
308                }
309
310                if (!printPreRegister.isEmpty()) {
311                    methodItems.add(new CommentMethodItem(stg,
312                            getPreInstructionRegisterString(instruction, printPreRegister),
313                            currentCodeAddress, 99.9));
314                }
315
316                if (!printPostRegister.isEmpty()) {
317                    methodItems.add(new CommentMethodItem(stg,
318                            getPostInstructionRegisterString(instruction, printPostRegister),
319                            currentCodeAddress, 100.1));
320                }
321            }
322
323            currentCodeAddress += instruction.instruction.getSize(currentCodeAddress);
324        }
325
326        addTries(methodItems);
327        addDebugInfo(methodItems);
328
329        if (baksmali.useSequentialLabels) {
330            setLabelSequentialNumbers();
331        }
332
333
334        for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
335            if (labelMethodItem.isCommentedOut()) {
336                methodItems.add(new CommentedOutMethodItem(stg, labelMethodItem));
337            } else {
338                methodItems.add(labelMethodItem);
339            }
340        }
341
342        Collections.sort(methodItems);
343
344        return methodItems;
345    }
346
347    private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) {
348        if (analyzedInstruction.instruction instanceof RegisterRangeInstruction) {
349            RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.instruction;
350
351            printPreRegister.set(instruction.getStartRegister(),
352                    instruction.getStartRegister() + instruction.getRegCount());
353        } else if (analyzedInstruction.instruction instanceof FiveRegisterInstruction) {
354            FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.instruction;
355            int regCount = instruction.getRegCount();
356            switch (regCount) {
357                case 5:
358                    printPreRegister.set(instruction.getRegisterA());
359                    //fall through
360                case 4:
361                    printPreRegister.set(instruction.getRegisterG());
362                    //fall through
363                case 3:
364                    printPreRegister.set(instruction.getRegisterF());
365                    //fall through
366                case 2:
367                    printPreRegister.set(instruction.getRegisterE());
368                    //fall through
369                case 1:
370                    printPreRegister.set(instruction.getRegisterD());
371            }
372        } else if (analyzedInstruction.instruction instanceof ThreeRegisterInstruction) {
373            ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.instruction;
374            printPreRegister.set(instruction.getRegisterA());
375            printPreRegister.set(instruction.getRegisterB());
376            printPreRegister.set(instruction.getRegisterC());
377        } else if (analyzedInstruction.instruction instanceof TwoRegisterInstruction) {
378            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.instruction;
379            printPreRegister.set(instruction.getRegisterA());
380            printPreRegister.set(instruction.getRegisterB());
381        } else if (analyzedInstruction.instruction instanceof SingleRegisterInstruction) {
382            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.instruction;
383            printPreRegister.set(instruction.getRegisterA());
384        }
385    }
386
387    private void addFullMergeRegs(BitSet printPreRegister, List<MethodItem> methodItems, MethodAnalyzer methodAnalyzer,
388                                  int instructionNum, int currentCodeAddress) {
389        if (instructionNum == 0) {
390            return;
391        }
392
393        //MethodAnalyzer cached the analyzedInstructions, so we're not actually re-analyzing, just getting the
394        //instructions that have already been analyzed
395        AnalyzedInstruction[] instructions = methodAnalyzer.analyze();
396        AnalyzedInstruction instruction = instructions[instructionNum];
397
398        if (instruction.getPredecessorCount() <= 1) {
399            return;
400        }
401
402        StringBuffer sb = new StringBuffer();
403
404        for (int registerNum=0; registerNum<registerCount; registerNum++) {
405            sb.setLength(0);
406            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem, registerNum));
407            sb.append('=');
408            sb.append(instruction.getPreInstructionRegisterType(registerNum));
409            sb.append(":merge{");
410
411            RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum);
412            boolean addRegister = false;
413
414            boolean first = true;
415            for (AnalyzedInstruction predecessor: instruction.getPredecessors()) {
416                RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
417
418                if (!first) {
419                    sb.append(',');
420                    if (!addRegister && mergedRegisterType != predecessorRegisterType) {
421                        addRegister = true;
422                    }
423                }
424
425                if (predecessor.getInstructionIndex() == -1) {
426                    //the fake "StartOfMethod" instruction
427                    sb.append("Start:");
428                } else {
429                    sb.append("0x");
430                    sb.append(Integer.toHexString(methodAnalyzer.getInstructionAddress(predecessor)));
431                    sb.append(':');
432                }
433                sb.append(predecessor.getPostInstructionRegisterType(registerNum).toString());
434                first = false;
435            }
436
437            if (!addRegister) {
438                continue;
439            }
440
441            sb.append("}");
442
443            methodItems.add(new CommentMethodItem(stg, sb.toString(),  currentCodeAddress, 99.8));
444            printPreRegister.clear(registerNum);
445        }
446    }
447
448    private void addMergeRegs(BitSet printPreRegister, AnalyzedInstruction instructions[], int instructionNum) {
449        //MethodAnalyzer cached the analyzedInstructions, so we're not actually re-analyzing, just getting the
450        //instructions that have already been analyzed
451        AnalyzedInstruction instruction = instructions[instructionNum];
452
453        if (instructionNum == 0) {
454            addParamRegs(printPreRegister);
455        } else {
456            if (instruction.isBeginningInstruction()) {
457                addParamRegs(printPreRegister);
458            }
459
460            if (instruction.getPredecessorCount() <= 1) {
461                //in the common case of an instruction that only has a single predecessor which is the previous
462                //instruction, the pre-instruction registers will always match the previous instruction's
463                //post-instruction registers
464                return;
465            }
466
467            for (int registerNum=0; registerNum<registerCount; registerNum++) {
468                RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum);
469
470                for (AnalyzedInstruction predecessor: instruction.getPredecessors()) {
471                    if (predecessor.getPostInstructionRegisterType(registerNum) != mergedRegisterType) {
472                        printPreRegister.set(registerNum);
473                        continue;
474                    }
475                }
476            }
477        }
478    }
479
480    private void addParamRegs(BitSet printPreRegister) {
481        int registerCount = encodedMethod.codeItem.getRegisterCount();
482
483        int parameterRegisterCount = encodedMethod.method.getPrototype().getParameterRegisterCount();
484        if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
485            parameterRegisterCount++;
486        }
487
488        printPreRegister.set(registerCount-parameterRegisterCount, registerCount);
489    }
490
491    private void addDestRegs(BitSet printPostRegister, AnalyzedInstruction analyzedInstruction) {
492        for (int registerNum=0; registerNum<registerCount; registerNum++) {
493            if (analyzedInstruction.getPreInstructionRegisterType(registerNum) !=
494                    analyzedInstruction.getPostInstructionRegisterType(registerNum)) {
495                printPostRegister.set(registerNum);
496            }
497        }
498    }
499
500
501    private String getPreInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) {
502        StringBuilder sb = new StringBuilder();
503
504        for (int registerNum = registers.nextSetBit(0); registerNum >= 0;
505             registerNum = registers.nextSetBit(registerNum + 1)) {
506
507            RegisterType registerType = instruction.getPreInstructionRegisterType(registerNum);
508            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum));
509            sb.append("=");
510            if (registerType == null) {
511                sb.append("null");
512            } else {
513                sb.append(registerType.toString());
514            }
515            sb.append(";");
516        }
517
518        return sb.toString();
519    }
520
521    private String getPostInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) {
522        StringBuilder sb = new StringBuilder();
523
524        for (int registerNum = registers.nextSetBit(0); registerNum >= 0;
525             registerNum = registers.nextSetBit(registerNum + 1)) {
526
527            RegisterType registerType = instruction.getPostInstructionRegisterType(registerNum);
528            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum));
529            sb.append("=");
530            if (registerType == null) {
531                sb.append("null");
532            } else {
533                sb.append(registerType.toString());
534            }
535            sb.append(";");
536        }
537
538        return sb.toString();
539    }
540
541    private void addTries(List<MethodItem> methodItems) {
542        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
543            return;
544        }
545
546        Instruction[] instructions = encodedMethod.codeItem.getInstructions();
547
548        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
549            int startAddress = tryItem.getStartCodeAddress();
550            int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength();
551
552            /**
553             * The end address points to the address immediately after the end of the last
554             * instruction that the try block covers. We want the .catch directive and end_try
555             * label to be associated with the last covered instruction, so we need to get
556             * the address for that instruction
557             */
558
559            int index = instructionMap.get(endAddress, -1);
560            int lastInstructionAddress;
561
562            /**
563             * If we couldn't find the index, then the try block probably extends to the last instruction in the
564             * method, and so endAddress would be the address immediately after the end of the last instruction.
565             * Check to make sure this is the case, if not, throw an exception.
566             */
567            if (index == -1) {
568                Instruction lastInstruction = instructions[instructions.length - 1];
569                lastInstructionAddress = instructionMap.keyAt(instructionMap.size() - 1);
570
571                if (endAddress != lastInstructionAddress + lastInstruction.getSize(lastInstructionAddress)) {
572                    throw new RuntimeException("Invalid code offset " + endAddress + " for the try block end address");
573                }
574            } else {
575                if (index == 0) {
576                    throw new RuntimeException("Unexpected instruction index");
577                }
578                Instruction lastInstruction = instructions[index - 1];
579
580                if (lastInstruction.getFormat().variableSizeFormat) {
581                    throw new RuntimeException("This try block unexpectedly ends on a switch/array data block.");
582                }
583
584                //getSize for non-variable size formats should return the same size regardless of code address, so just
585                //use a dummy address of "0"
586                lastInstructionAddress = endAddress - lastInstruction.getSize(0);
587            }
588
589            //add the catch all handler if it exists
590            int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
591            if (catchAllAddress != -1) {
592                CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, null,
593                        startAddress, endAddress, catchAllAddress);
594                methodItems.add(catchAllMethodItem);
595            }
596
597            //add the rest of the handlers
598            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
599                //use the address from the last covered instruction
600                CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg,
601                        handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
602                methodItems.add(catchMethodItem);
603            }
604        }
605    }
606
607    private void addDebugInfo(final List<MethodItem> methodItems) {
608        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) {
609            return;
610        }
611
612        final CodeItem codeItem = encodedMethod.codeItem;
613        DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
614
615        DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
616                new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
617                    @Override
618                    public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
619                                                  TypeIdItem type) {
620                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal",
621                                -1, registerNum, name, type, null));
622                    }
623
624                    @Override
625                    public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum,
626                                                          StringIdItem name, TypeIdItem type,
627                                                          StringIdItem signature) {
628                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal",
629                                -1, registerNum, name, type, signature));
630                    }
631
632                    @Override
633                    public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name,
634                                                TypeIdItem type, StringIdItem signature) {
635                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1,
636                                registerNum, name, type, signature));
637                    }
638
639                    @Override
640                    public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
641                                                    TypeIdItem type, StringIdItem signature) {
642                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1,
643                                registerNum, name, type, signature));
644                    }
645
646                    @Override
647                    public void ProcessSetPrologueEnd(int codeAddress) {
648                        methodItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4));
649                    }
650
651                    @Override
652                    public void ProcessSetEpilogueBegin(int codeAddress) {
653                        methodItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4));
654                    }
655
656                    @Override
657                    public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
658                        methodItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) {
659                            @Override
660                            protected void setAttributes(StringTemplate template) {
661                                template.setAttribute("FileName", name.getStringValue());
662                            }
663                        });
664                    }
665
666                    @Override
667                    public void ProcessLineEmit(int codeAddress, final int line) {
668                         methodItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) {
669                             @Override
670                             protected void setAttributes(StringTemplate template) {
671                                 template.setAttribute("Line", line);
672                             }
673                         });
674                    }
675                });
676    }
677
678    private void setLabelSequentialNumbers() {
679        HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>();
680        ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels());
681
682        //sort the labels by their location in the method
683        Collections.sort(sortedLabels);
684
685        for (LabelMethodItem labelMethodItem: sortedLabels) {
686            Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix());
687            if (labelSequence == null) {
688                labelSequence = 0;
689            }
690            labelMethodItem.setLabelSequence(labelSequence);
691            nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1);
692        }
693    }
694
695    public static class LabelCache {
696        protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();
697
698        public LabelCache() {
699        }
700
701        public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
702            LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
703            if (internedLabelMethodItem != null) {
704                if (!labelMethodItem.isCommentedOut()) {
705                    internedLabelMethodItem.setUncommented();
706                }
707                return internedLabelMethodItem;
708            }
709            labels.put(labelMethodItem, labelMethodItem);
710            return labelMethodItem;
711        }
712
713
714        public Collection<LabelMethodItem> getLabels() {
715            return labels.values();
716        }
717    }
718}
719