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