MethodDefinition.java revision 7bb8ace93bb02750a95d3a68fe99153980abdee8
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            List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
72
73            packedSwitchMap = new SparseIntArray(1);
74            sparseSwitchMap = new SparseIntArray(1);
75            instructionMap = new SparseIntArray(instructions.size());
76
77            registerCount = encodedMethod.codeItem.getRegisterCount();
78
79            int currentCodeAddress = 0;
80            for (int i=0; i<instructions.size(); i++) {
81                AnalyzedInstruction instruction = instructions.get(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        if (baksmali.registerInfo != 0 || baksmali.deodex || baksmali.verify) {
252            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            } else if (baksmali.verify) {
260                methodAnalyzer.verify();
261
262                validationException = methodAnalyzer.getValidationException();
263                if (validationException != null) {
264                    methodItems.add(new CommentMethodItem(stg,
265                            String.format("ValidationException: %s" ,validationException.getMessage()),
266                            validationException.getCodeAddress(), Integer.MIN_VALUE));
267                }
268            }
269        }
270        List<AnalyzedInstruction> instructions = methodAnalyzer.getInstructions();
271
272        AnalyzedInstruction lastInstruction = null;
273
274        for (int i=instructions.size()-1; i>=0; i--) {
275            AnalyzedInstruction instruction = instructions.get(i);
276
277            if (!instruction.isDead()) {
278                lastInstruction = instruction;
279                break;
280            }
281        }
282
283        BitSet printPreRegister = new BitSet(registerCount);
284        BitSet printPostRegister = new BitSet(registerCount);
285
286        int currentCodeAddress = 0;
287        for (int i=0; i<instructions.size(); i++) {
288            AnalyzedInstruction instruction = instructions.get(i);
289
290            MethodItem methodItem = InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
291                    encodedMethod.codeItem, currentCodeAddress, stg, instruction.getInstruction(),
292                    instruction == lastInstruction);
293
294            if (instruction.isDead()) {
295                methodItems.add(new CommentedOutMethodItem(stg, methodItem));
296            } else {
297                methodItems.add(methodItem);
298            }
299
300            if (instruction.getInstruction().getFormat() == Format.UnresolvedNullReference) {
301                methodItems.add(new CommentedOutMethodItem(stg, InstructionMethodItemFactory.makeInstructionFormatMethodItem(this,
302                        encodedMethod.codeItem, currentCodeAddress, stg, instruction.getOriginalInstruction(), false)));
303            }
304
305            if (i != instructions.size() - 1) {
306                methodItems.add(new BlankMethodItem(stg, currentCodeAddress));
307            }
308
309            if (baksmali.addCodeOffsets) {
310                methodItems.add(new CommentMethodItem(stg, String.format("@%x", currentCodeAddress),
311                        currentCodeAddress, -1000));
312            }
313
314            if (baksmali.registerInfo != 0 && !instruction.getInstruction().getFormat().variableSizeFormat) {
315                printPreRegister.clear();
316                printPostRegister.clear();
317
318                if ((baksmali.registerInfo & main.ALL) != 0) {
319                    printPreRegister.set(0, registerCount);
320                    printPostRegister.set(0, registerCount);
321                } else {
322                    if ((baksmali.registerInfo & main.ALLPRE) != 0) {
323                        printPreRegister.set(0, registerCount);
324                    } else {
325                        if ((baksmali.registerInfo & main.ARGS) != 0) {
326                            addArgsRegs(printPreRegister, instruction);
327                        }
328                        if ((baksmali.registerInfo & main.MERGE) != 0) {
329                            addMergeRegs(printPreRegister, instruction);
330                        } else if ((baksmali.registerInfo & main.FULLMERGE) != 0 &&
331                                (i == 0 || instruction.isBeginningInstruction())) {
332                            addParamRegs(printPreRegister);
333                        }
334                    }
335
336                    if ((baksmali.registerInfo & main.ALLPOST) != 0) {
337                        printPostRegister.set(0, registerCount);
338                    } else if ((baksmali.registerInfo & main.DEST) != 0) {
339                        addDestRegs(printPostRegister, instruction);
340                    }
341                }
342
343                if ((baksmali.registerInfo & main.FULLMERGE) != 0) {
344                    addFullMergeRegs(printPreRegister, methodItems, methodAnalyzer, instruction, currentCodeAddress);
345                }
346
347                if (!printPreRegister.isEmpty()) {
348                    String comment = getPreInstructionRegisterString(instruction, printPreRegister);
349                    if (comment != null && comment.length() > 0) {
350                        methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 99.9));
351                    }
352                }
353
354                if (!printPostRegister.isEmpty()) {
355                    String comment = getPostInstructionRegisterString(instruction, printPostRegister);
356                    if (comment != null && comment.length() > 0) {
357                        methodItems.add(new CommentMethodItem(stg, comment, currentCodeAddress, 100.1));
358                    }
359                }
360            }
361
362            currentCodeAddress += instruction.getInstruction().getSize(currentCodeAddress);
363        }
364
365        addTries(methodItems);
366        addDebugInfo(methodItems);
367
368        if (baksmali.useSequentialLabels) {
369            setLabelSequentialNumbers();
370        }
371
372
373        for (LabelMethodItem labelMethodItem: labelCache.getLabels()) {
374            if (labelMethodItem.isCommentedOut()) {
375                methodItems.add(new CommentedOutMethodItem(stg, labelMethodItem));
376            } else {
377                methodItems.add(labelMethodItem);
378            }
379        }
380
381        Collections.sort(methodItems);
382
383        return methodItems;
384    }
385
386    private void addArgsRegs(BitSet printPreRegister, AnalyzedInstruction analyzedInstruction) {
387        if (analyzedInstruction.getInstruction() instanceof RegisterRangeInstruction) {
388            RegisterRangeInstruction instruction = (RegisterRangeInstruction)analyzedInstruction.getInstruction();
389
390            printPreRegister.set(instruction.getStartRegister(),
391                    instruction.getStartRegister() + instruction.getRegCount());
392        } else if (analyzedInstruction.getInstruction() instanceof FiveRegisterInstruction) {
393            FiveRegisterInstruction instruction = (FiveRegisterInstruction)analyzedInstruction.getInstruction();
394            int regCount = instruction.getRegCount();
395            switch (regCount) {
396                case 5:
397                    printPreRegister.set(instruction.getRegisterA());
398                    //fall through
399                case 4:
400                    printPreRegister.set(instruction.getRegisterG());
401                    //fall through
402                case 3:
403                    printPreRegister.set(instruction.getRegisterF());
404                    //fall through
405                case 2:
406                    printPreRegister.set(instruction.getRegisterE());
407                    //fall through
408                case 1:
409                    printPreRegister.set(instruction.getRegisterD());
410            }
411        } else if (analyzedInstruction.getInstruction() instanceof ThreeRegisterInstruction) {
412            ThreeRegisterInstruction instruction = (ThreeRegisterInstruction)analyzedInstruction.getInstruction();
413            printPreRegister.set(instruction.getRegisterA());
414            printPreRegister.set(instruction.getRegisterB());
415            printPreRegister.set(instruction.getRegisterC());
416        } else if (analyzedInstruction.getInstruction() instanceof TwoRegisterInstruction) {
417            TwoRegisterInstruction instruction = (TwoRegisterInstruction)analyzedInstruction.getInstruction();
418            printPreRegister.set(instruction.getRegisterA());
419            printPreRegister.set(instruction.getRegisterB());
420        } else if (analyzedInstruction.getInstruction() instanceof SingleRegisterInstruction) {
421            SingleRegisterInstruction instruction = (SingleRegisterInstruction)analyzedInstruction.getInstruction();
422            printPreRegister.set(instruction.getRegisterA());
423        }
424    }
425
426    private void addFullMergeRegs(BitSet printPreRegister, List<MethodItem> methodItems, MethodAnalyzer methodAnalyzer,
427                                  AnalyzedInstruction instruction, int currentCodeAddress) {
428        if (instruction.getPredecessorCount() <= 1) {
429            return;
430        }
431
432        StringBuffer sb = new StringBuffer();
433
434        for (int registerNum=0; registerNum<registerCount; registerNum++) {
435            sb.setLength(0);
436            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem, registerNum));
437            sb.append('=');
438            sb.append(instruction.getPreInstructionRegisterType(registerNum));
439            sb.append(":merge{");
440
441            RegisterType mergedRegisterType = null;
442            boolean addRegister = false;
443
444            boolean first = true;
445            for (AnalyzedInstruction predecessor: instruction.getPredecessors()) {
446                RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
447
448                if (!first) {
449                    if (!addRegister) {
450                        sb.append(',');
451                        if (mergedRegisterType != predecessorRegisterType) {
452                            addRegister = true;
453                        }
454                        mergedRegisterType = mergedRegisterType.merge(predecessorRegisterType);
455                    }
456                } else {
457                    mergedRegisterType = predecessorRegisterType;
458                }
459
460                if (predecessor.getInstructionIndex() == -1) {
461                    //the fake "StartOfMethod" instruction
462                    sb.append("Start:");
463                } else {
464                    sb.append("0x");
465                    sb.append(Integer.toHexString(methodAnalyzer.getInstructionAddress(predecessor)));
466                    sb.append(':');
467                }
468                sb.append(predecessorRegisterType.toString());
469                first = false;
470            }
471
472            if (!addRegister) {
473                continue;
474            }
475
476            sb.append("}");
477
478            methodItems.add(new CommentMethodItem(stg, sb.toString(),  currentCodeAddress, 99.8));
479            printPreRegister.clear(registerNum);
480        }
481    }
482
483    private void addMergeRegs(BitSet printPreRegister, AnalyzedInstruction instruction) {
484        if (instruction.isBeginningInstruction()) {
485            addParamRegs(printPreRegister);
486        }
487
488        if (instruction.getPredecessorCount() <= 1) {
489            //in the common case of an instruction that only has a single predecessor which is the previous
490            //instruction, the pre-instruction registers will always match the previous instruction's
491            //post-instruction registers
492            return;
493        }
494
495        for (int registerNum=0; registerNum<registerCount; registerNum++) {
496            RegisterType mergedRegisterType = instruction.getPreInstructionRegisterType(registerNum);
497
498            for (AnalyzedInstruction predecessor: instruction.getPredecessors()) {
499                if (predecessor.getPostInstructionRegisterType(registerNum) != mergedRegisterType) {
500                    printPreRegister.set(registerNum);
501                    continue;
502                }
503            }
504        }
505    }
506
507    private void addParamRegs(BitSet printPreRegister) {
508        int registerCount = encodedMethod.codeItem.getRegisterCount();
509
510        int parameterRegisterCount = encodedMethod.method.getPrototype().getParameterRegisterCount();
511        if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
512            parameterRegisterCount++;
513        }
514
515        printPreRegister.set(registerCount-parameterRegisterCount, registerCount);
516    }
517
518    private void addDestRegs(BitSet printPostRegister, AnalyzedInstruction analyzedInstruction) {
519        for (int registerNum=0; registerNum<registerCount; registerNum++) {
520            if (analyzedInstruction.getPreInstructionRegisterType(registerNum) !=
521                    analyzedInstruction.getPostInstructionRegisterType(registerNum)) {
522                printPostRegister.set(registerNum);
523            }
524        }
525    }
526
527
528    private String getPreInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) {
529        StringBuilder sb = new StringBuilder();
530
531        for (int registerNum = registers.nextSetBit(0); registerNum >= 0;
532             registerNum = registers.nextSetBit(registerNum + 1)) {
533
534            RegisterType registerType = instruction.getPreInstructionRegisterType(registerNum);
535            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum));
536            sb.append("=");
537            if (registerType == null) {
538                sb.append("null");
539            } else {
540                sb.append(registerType.toString());
541            }
542            sb.append(";");
543        }
544
545        return sb.toString();
546    }
547
548    private String getPostInstructionRegisterString(AnalyzedInstruction instruction, BitSet registers) {
549        StringBuilder sb = new StringBuilder();
550
551        for (int registerNum = registers.nextSetBit(0); registerNum >= 0;
552             registerNum = registers.nextSetBit(registerNum + 1)) {
553
554            RegisterType registerType = instruction.getPostInstructionRegisterType(registerNum);
555            sb.append(RegisterFormatter.formatRegister(encodedMethod.codeItem,registerNum));
556            sb.append("=");
557            if (registerType == null) {
558                sb.append("null");
559            } else {
560                sb.append(registerType.toString());
561            }
562            sb.append(";");
563        }
564
565        return sb.toString();
566    }
567
568    private void addTries(List<MethodItem> methodItems) {
569        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getTries() == null) {
570            return;
571        }
572
573        Instruction[] instructions = encodedMethod.codeItem.getInstructions();
574
575        for (CodeItem.TryItem tryItem: encodedMethod.codeItem.getTries()) {
576            int startAddress = tryItem.getStartCodeAddress();
577            int endAddress = tryItem.getStartCodeAddress() + tryItem.getTryLength();
578
579            /**
580             * The end address points to the address immediately after the end of the last
581             * instruction that the try block covers. We want the .catch directive and end_try
582             * label to be associated with the last covered instruction, so we need to get
583             * the address for that instruction
584             */
585
586            int index = instructionMap.get(endAddress, -1);
587            int lastInstructionAddress;
588
589            /**
590             * If we couldn't find the index, then the try block probably extends to the last instruction in the
591             * method, and so endAddress would be the address immediately after the end of the last instruction.
592             * Check to make sure this is the case, if not, throw an exception.
593             */
594            if (index == -1) {
595                Instruction lastInstruction = instructions[instructions.length - 1];
596                lastInstructionAddress = instructionMap.keyAt(instructionMap.size() - 1);
597
598                if (endAddress != lastInstructionAddress + lastInstruction.getSize(lastInstructionAddress)) {
599                    throw new RuntimeException("Invalid code offset " + endAddress + " for the try block end address");
600                }
601            } else {
602                if (index == 0) {
603                    throw new RuntimeException("Unexpected instruction index");
604                }
605                Instruction lastInstruction = instructions[index - 1];
606
607                if (lastInstruction.getFormat().variableSizeFormat) {
608                    throw new RuntimeException("This try block unexpectedly ends on a switch/array data block.");
609                }
610
611                //getSize for non-variable size formats should return the same size regardless of code address, so just
612                //use a dummy address of "0"
613                lastInstructionAddress = endAddress - lastInstruction.getSize(0);
614            }
615
616            //add the catch all handler if it exists
617            int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
618            if (catchAllAddress != -1) {
619                CatchMethodItem catchAllMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg, null,
620                        startAddress, endAddress, catchAllAddress);
621                methodItems.add(catchAllMethodItem);
622            }
623
624            //add the rest of the handlers
625            for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
626                //use the address from the last covered instruction
627                CatchMethodItem catchMethodItem = new CatchMethodItem(labelCache, lastInstructionAddress, stg,
628                        handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
629                methodItems.add(catchMethodItem);
630            }
631        }
632    }
633
634    private void addDebugInfo(final List<MethodItem> methodItems) {
635        if (encodedMethod.codeItem == null || encodedMethod.codeItem.getDebugInfo() == null) {
636            return;
637        }
638
639        final CodeItem codeItem = encodedMethod.codeItem;
640        DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
641
642        DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
643                new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
644                    @Override
645                    public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
646                                                  TypeIdItem type) {
647                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal",
648                                -1, registerNum, name, type, null));
649                    }
650
651                    @Override
652                    public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum,
653                                                          StringIdItem name, TypeIdItem type,
654                                                          StringIdItem signature) {
655                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal",
656                                -1, registerNum, name, type, signature));
657                    }
658
659                    @Override
660                    public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name,
661                                                TypeIdItem type, StringIdItem signature) {
662                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1,
663                                registerNum, name, type, signature));
664                    }
665
666                    @Override
667                    public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
668                                                    TypeIdItem type, StringIdItem signature) {
669                        methodItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1,
670                                registerNum, name, type, signature));
671                    }
672
673                    @Override
674                    public void ProcessSetPrologueEnd(int codeAddress) {
675                        methodItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4));
676                    }
677
678                    @Override
679                    public void ProcessSetEpilogueBegin(int codeAddress) {
680                        methodItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4));
681                    }
682
683                    @Override
684                    public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
685                        methodItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) {
686                            @Override
687                            protected void setAttributes(StringTemplate template) {
688                                template.setAttribute("FileName", name.getStringValue());
689                            }
690                        });
691                    }
692
693                    @Override
694                    public void ProcessLineEmit(int codeAddress, final int line) {
695                         methodItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) {
696                             @Override
697                             protected void setAttributes(StringTemplate template) {
698                                 template.setAttribute("Line", line);
699                             }
700                         });
701                    }
702                });
703    }
704
705    private void setLabelSequentialNumbers() {
706        HashMap<String, Integer> nextLabelSequenceByType = new HashMap<String, Integer>();
707        ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labelCache.getLabels());
708
709        //sort the labels by their location in the method
710        Collections.sort(sortedLabels);
711
712        for (LabelMethodItem labelMethodItem: sortedLabels) {
713            Integer labelSequence = nextLabelSequenceByType.get(labelMethodItem.getLabelPrefix());
714            if (labelSequence == null) {
715                labelSequence = 0;
716            }
717            labelMethodItem.setLabelSequence(labelSequence);
718            nextLabelSequenceByType.put(labelMethodItem.getLabelPrefix(), labelSequence + 1);
719        }
720    }
721
722    public static class LabelCache {
723        protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();
724
725        public LabelCache() {
726        }
727
728        public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
729            LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
730            if (internedLabelMethodItem != null) {
731                if (!labelMethodItem.isCommentedOut()) {
732                    internedLabelMethodItem.setUncommented();
733                }
734                return internedLabelMethodItem;
735            }
736            labels.put(labelMethodItem, labelMethodItem);
737            return labelMethodItem;
738        }
739
740
741        public Collection<LabelMethodItem> getLabels() {
742            return labels.values();
743        }
744    }
745}
746