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