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