MethodDefinition.java revision fda2e631ac0b1ca092973b7fff4b2f38d2c23437
11be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania/*
21be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * [The "BSD licence"]
31be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * Copyright (c) 2009 Ben Gruver
41be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * All rights reserved.
51be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *
61be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * Redistribution and use in source and binary forms, with or without
71be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * modification, are permitted provided that the following conditions
81be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * are met:
91be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * 1. Redistributions of source code must retain the above copyright
101be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *    notice, this list of conditions and the following disclaimer.
111be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * 2. Redistributions in binary form must reproduce the above copyright
121be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *    notice, this list of conditions and the following disclaimer in the
131be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *    documentation and/or other materials provided with the distribution.
141be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * 3. The name of the author may not be used to endorse or promote products
151be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *    derived from this software without specific prior written permission.
161be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania *
171be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
181be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
191be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
201be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
211be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
221be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
261be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania */
281be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
291be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniapackage org.jf.baksmali.Adaptors;
301be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
311be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.baksmali.Adaptors.Format.*;
321be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.baksmali.baksmali;
331be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.*;
341be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Debug.DebugInstructionIterator;
351be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Code.Format.*;
361be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Code.Instruction;
371be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Code.Opcode;
381be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Code.InstructionIterator;
391be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Code.OffsetInstruction;
401be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.jf.dexlib.Util.AccessFlags;
411be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.antlr.stringtemplate.StringTemplateGroup;
421be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport org.antlr.stringtemplate.StringTemplate;
431be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
441be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniaimport java.util.*;
451be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
461be2c9def7187e4e643c00a31dd9986395795d7dNicolas Cataniapublic class MethodDefinition {
471be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    public static StringTemplate makeTemplate(StringTemplateGroup stg, ClassDataItem.EncodedMethod encodedMethod,
481be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                                              AnnotationSetItem annotationSet,
491be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                                              AnnotationSetRefList parameterAnnotations) {
501be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
511be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        CodeItem codeItem = encodedMethod.codeItem;
521be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
531be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        StringTemplate template = stg.getInstanceOf("method");
541be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
551be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("AccessFlags", getAccessFlags(encodedMethod));
561be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("MethodName", encodedMethod.method.getMethodName().getStringValue());
571be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("Prototype", encodedMethod.method.getPrototype().getPrototypeString());
581be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("HasCode", codeItem != null);
591be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("RegistersDirective", baksmali.useLocalsDirective?".locals":".registers");
601be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("RegisterCount", codeItem==null?"0":Integer.toString(getRegisterCount(encodedMethod)));
611be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("Parameters", getParameters(stg, codeItem, parameterAnnotations));
621be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("Annotations", getAnnotations(stg, annotationSet));
631be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        template.setAttribute("MethodItems", getMethodItems(encodedMethod.method.getDexFile(), stg, codeItem));
641be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
651be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return template;
661be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
671be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
681be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static int getRegisterCount(ClassDataItem.EncodedMethod encodedMethod)
691be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    {
701be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        int totalRegisters = encodedMethod.codeItem.getRegisterCount();
711be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if (baksmali.useLocalsDirective) {
721be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            int parameterRegisters = encodedMethod.method.getPrototype().getParameterRegisterCount();
731be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if ((encodedMethod.accessFlags & AccessFlags.STATIC.getValue()) == 0) {
7441d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot                parameterRegisters++;
751be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
761be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            return totalRegisters - parameterRegisters;
771be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
781be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return totalRegisters;
791be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
8041d0579e8de9ef4ff178fc4991043c61a19943f7Brett Chabot
811be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static List<String> getAccessFlags(ClassDataItem.EncodedMethod encodedMethod) {
821be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<String> accessFlags = new ArrayList<String>();
831be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
841be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
851be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            accessFlags.add(accessFlag.toString());
861be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
871be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
881be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return accessFlags;
891be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
901be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
911be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static List<StringTemplate> getParameters(StringTemplateGroup stg, CodeItem codeItem,
921be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                                                               AnnotationSetRefList parameterAnnotations) {
931be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        DebugInfoItem debugInfoItem = null;
941be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if (codeItem != null) {
951be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            debugInfoItem = codeItem.getDebugInfo();
961be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
971be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
981be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        int parameterCount = 0;
991be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1001be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>();
1011be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if (parameterAnnotations != null) {
1021be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets();
1031be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (_annotations != null) {
1041be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                annotations.addAll(Arrays.asList(_annotations));
1051be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
1061be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1071be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            parameterCount = annotations.size();
108fc2de66453b0669c09eaca643b07d34443858b6fElliott Hughes        }
109fc2de66453b0669c09eaca643b07d34443858b6fElliott Hughes
1101be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<String> parameterNames = new ArrayList<String>();
1111be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if (debugInfoItem != null) {
1121be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            StringIdItem[] _parameterNames = debugInfoItem.getParameterNames();
1131be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (_parameterNames != null) {
1141be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                for (StringIdItem parameterName: _parameterNames) {
1151be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    parameterNames.add(parameterName==null?null:parameterName.getStringValue());
1161be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                }
1171be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
1181be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1191be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (parameterCount < parameterNames.size()) {
120fc2de66453b0669c09eaca643b07d34443858b6fElliott Hughes                parameterCount = parameterNames.size();
121fc2de66453b0669c09eaca643b07d34443858b6fElliott Hughes            }
1221be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1231be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1241be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<StringTemplate> parameters = new ArrayList<StringTemplate>();
1251be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        for (int i=0; i<parameterCount; i++) {
1261be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            AnnotationSetItem annotationSet = null;
1271be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (i < annotations.size()) {
1281be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                annotationSet = annotations.get(i);
1291be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
1301be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1311be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            String parameterName = null;
1321be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (i < parameterNames.size()) {
1331be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                parameterName = parameterNames.get(i);
1341be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
1351be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1361be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            parameters.add(ParameterAdaptor.makeTemplate(stg, parameterName, annotationSet));
1371be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1381be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1391be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return parameters;
1401be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
1411be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1421be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static List<StringTemplate> getAnnotations(StringTemplateGroup stg, AnnotationSetItem annotationSet) {
1431be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        if (annotationSet == null) {
1441be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            return null;
1451be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1461be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1471be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<StringTemplate> annotationAdaptors = new ArrayList<StringTemplate>();
1481be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1491be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
1501be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            annotationAdaptors.add(AnnotationAdaptor.makeTemplate(stg, annotationItem));
1511be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1521be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return annotationAdaptors;
1531be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
1541be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1551be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static List<MethodItem> getMethodItems(DexFile dexFile, StringTemplateGroup stg, CodeItem codeItem) {
1561be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        List<MethodItem> methodItems = new ArrayList<MethodItem>();
1571be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1581be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        MethodItemList methodItemList = new MethodItemList(dexFile, stg, codeItem);
1591be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        methodItemList.generateMethodItemList();
1601be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1611be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        for (LabelMethodItem labelMethodItem: methodItemList.labels.getLabels()) {
1621be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (labelMethodItem.isCommentedOut()) {
1631be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                methodItems.add(new CommentedOutMethodItem(stg, labelMethodItem));
1641be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            } else {
1651be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                methodItems.add(labelMethodItem);
1661be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
1671be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1681be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1691be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        methodItems.addAll(methodItemList.instructions);
1701be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        methodItems.addAll(methodItemList.blanks);
1711be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        methodItems.addAll(methodItemList.catches);
1721be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        methodItems.addAll(methodItemList.debugItems);
1731be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        Collections.sort(methodItems);
1741be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1751be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        return methodItems;
1761be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    }
1771be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1781be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1791be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania    private static class MethodItemList {
1801be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        private final DexFile dexFile;
1811be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        private final StringTemplateGroup stg;
1821be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        private final CodeItem codeItem;
1831be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1841be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public LabelCache labels = new LabelCache();
1851be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1861be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public List<MethodItem> instructions = new ArrayList<MethodItem>();
1871be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public List<BlankMethodItem> blanks = new ArrayList<BlankMethodItem>();
1881be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public List<CatchMethodItem> catches = new ArrayList<CatchMethodItem>();
1891be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public List<MethodItem> debugItems = new ArrayList<MethodItem>();
1901be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1911be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        private HashMap<Integer, Integer> packedSwitchMap = new HashMap<Integer, Integer>();
1921be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        private HashMap<Integer, Integer> sparseSwitchMap = new HashMap<Integer, Integer>();
1931be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
1941be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public MethodItemList(DexFile dexFile, StringTemplateGroup stg, CodeItem codeItem) {
1951be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            this.dexFile = dexFile;
1961be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            this.stg = stg;
1971be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            this.codeItem = codeItem;
1981be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        }
1991be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2001be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania        public void generateMethodItemList() {
2011be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (codeItem == null) {
2021be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                return;
2031be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            }
2041be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2051be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania            if (baksmali.deodexUtil != null && dexFile.isOdex()) {
2061be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                List<Instruction> instructions = baksmali.deodexUtil.deodexerizeCode(codeItem);
2071be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2081be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                int offset = 0;
2091be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                for (Instruction instruction: instructions) {
2101be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    if (instruction.opcode == Opcode.PACKED_SWITCH) {
2111be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                        Instruction31t ins = (Instruction31t)instruction;
2121be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                        packedSwitchMap.put(offset + ins.getOffset(), offset);
2131be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    } else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
2141be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                        Instruction31t ins = (Instruction31t)instruction;
2151be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                        sparseSwitchMap.put(offset + ins.getOffset(), offset);
2161be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    }
2171be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2181be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    offset += instruction.getSize(offset*2)/2;
2191be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                }
2201be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2211be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                offset = 0;
2221be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                for (Instruction instruction: instructions) {
2231be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    addMethodItemsForInstruction(offset, instruction, false);
2241be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    blanks.add(new BlankMethodItem(stg, offset));
2251be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2261be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                    offset += instruction.getSize(offset*2)/2;
2271be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                }
2281be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania
2291be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                /*
2301be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                 * Look for the last uncommented instruction. If it is an UnresolvedNullReference,
2311be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                 * then set IsLastInstruction, so a goto will be added after it, to avoid validation
2321be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                 * issues
2331be2c9def7187e4e643c00a31dd9986395795d7dNicolas Catania                 */
234                for (int i=this.instructions.size()-1; i>=0; i--) {
235                    MethodItem ins = this.instructions.get(i);
236                    if (ins instanceof UnresolvedNullReferenceMethodItem) {
237                        ((UnresolvedNullReferenceMethodItem)ins).setIsLastInstruction(true);
238                        break;
239                    }
240
241                    if (!(ins instanceof CommentedOutMethodItem)) {
242                        break;
243                    }
244                }
245            } else {
246                int currentCodeOffset = 0;
247                for (Instruction instruction: codeItem.getInstructions()) {
248                    if (instruction.opcode == Opcode.PACKED_SWITCH) {
249                        OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
250                        packedSwitchMap.put(currentCodeOffset/2 + offsetInstruction.getOffset(), currentCodeOffset/2);
251                    } else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
252                        OffsetInstruction offsetInstruction = (OffsetInstruction)instruction;
253                        sparseSwitchMap.put(currentCodeOffset/2 + offsetInstruction.getOffset(), currentCodeOffset/2);
254                    }
255
256                    currentCodeOffset += instruction.getSize(currentCodeOffset);
257                }
258
259                currentCodeOffset = 0;
260                for (Instruction instruction: codeItem.getInstructions()) {
261                    int offset = currentCodeOffset/2;
262                    addMethodItemsForInstruction(offset, instruction, false);
263                    blanks.add(new BlankMethodItem(stg, offset));
264                    currentCodeOffset += instruction.getSize(currentCodeOffset);
265                }
266
267            }
268
269            blanks.remove(blanks.size()-1);
270
271            addTries();
272
273            addDebugInfo();
274
275            if (baksmali.useIndexedLabels) {
276                setLabelIndexes();
277            }
278        }
279
280        private void addOffsetInstructionMethodItem(OffsetInstructionFormatMethodItem methodItem,
281                                                    boolean commentedOut) {
282            if (commentedOut) {
283                instructions.add(new CommentedOutMethodItem(stg, methodItem));
284            } else {
285                instructions.add(methodItem);
286                LabelMethodItem label = methodItem.getLabel();
287                label.setUncommented();
288            }
289        }
290
291
292        private void addInstructionMethodItem(InstructionFormatMethodItem methodItem, boolean commentedOut) {
293            if (commentedOut) {
294                instructions.add(new CommentedOutMethodItem(stg, methodItem));
295            } else {
296                instructions.add(methodItem);
297            }
298        }
299
300        private void addMethodItemsForInstruction(int offset, Instruction instruction, boolean commentedOut) {
301            switch (instruction.getFormat()) {
302                case Format10t:
303                    addOffsetInstructionMethodItem(
304                            new Instruction10tMethodItem(labels, codeItem, offset, stg,(Instruction10t)instruction),
305                            commentedOut);
306                    return;
307                case Format10x:
308                    addInstructionMethodItem(
309                            new Instruction10xMethodItem(codeItem, offset, stg, (Instruction10x)instruction),
310                            commentedOut);
311                    return;
312                case Format11n:
313                    addInstructionMethodItem(
314                            new Instruction11nMethodItem(codeItem, offset, stg, (Instruction11n)instruction),
315                            commentedOut);
316                    return;
317                case Format11x:
318                    addInstructionMethodItem(
319                            new Instruction11xMethodItem(codeItem, offset, stg, (Instruction11x)instruction),
320                            commentedOut);
321                    return;
322                case Format12x:
323                    addInstructionMethodItem(
324                            new Instruction12xMethodItem(codeItem, offset, stg, (Instruction12x)instruction),
325                            commentedOut);
326                    return;
327                case Format20t:
328                    addOffsetInstructionMethodItem(
329                            new Instruction20tMethodItem(labels, codeItem, offset, stg, (Instruction20t)instruction),
330                            commentedOut);
331                    return;
332                case Format21c:
333                    addInstructionMethodItem(
334                            new Instruction21cMethodItem(codeItem, offset, stg, (Instruction21c)instruction),
335                            commentedOut);
336                    return;
337                case Format21h:
338                    addInstructionMethodItem(
339                            new Instruction21hMethodItem(codeItem, offset, stg, (Instruction21h)instruction),
340                            commentedOut);
341                    return;
342                case Format21s:
343                    addInstructionMethodItem(
344                            new Instruction21sMethodItem(codeItem, offset, stg, (Instruction21s)instruction),
345                            commentedOut);
346                    return;
347                case Format21t:
348                    addOffsetInstructionMethodItem(
349                            new Instruction21tMethodItem(labels, codeItem, offset, stg, (Instruction21t)instruction),
350                            commentedOut);
351                    return;
352                case Format22b:
353                    addInstructionMethodItem(
354                            new Instruction22bMethodItem(codeItem, offset, stg, (Instruction22b)instruction),
355                            commentedOut);
356                    return;
357                case Format22c:
358                    addInstructionMethodItem(
359                            new Instruction22cMethodItem(codeItem, offset, stg, (Instruction22c)instruction),
360                            commentedOut);
361                    return;
362                case Format22cs:
363                    addInstructionMethodItem(
364                            new Instruction22csMethodItem(codeItem, offset, stg, (Instruction22cs)instruction),
365                            commentedOut);
366                    return;
367                case Format22csf:
368                    addInstructionMethodItem(
369                            new Instruction22csfMethodItem(codeItem, offset, stg, (Instruction22csf)instruction),
370                            commentedOut);
371                    return;
372                case Format22s:
373                    addInstructionMethodItem(
374                            new Instruction22sMethodItem(codeItem, offset, stg, (Instruction22s)instruction),
375                            commentedOut);
376                    return;
377                case Format22t:
378                    addOffsetInstructionMethodItem(
379                            new Instruction22tMethodItem(labels, codeItem, offset, stg, (Instruction22t)instruction),
380                            commentedOut);
381                    return;
382                case Format22x:
383                    addInstructionMethodItem(
384                            new Instruction22xMethodItem(codeItem, offset, stg, (Instruction22x)instruction),
385                            commentedOut);
386                    return;
387                case Format23x:
388                    addInstructionMethodItem(
389                            new Instruction23xMethodItem(codeItem, offset, stg, (Instruction23x)instruction),
390                            commentedOut);
391                    return;
392                case Format30t:
393                    addOffsetInstructionMethodItem(
394                            new Instruction30tMethodItem(labels, codeItem, offset, stg, (Instruction30t)instruction),
395                            commentedOut);
396                    return;
397                case Format31c:
398                    addInstructionMethodItem(
399                            new Instruction31cMethodItem(codeItem, offset, stg, (Instruction31c)instruction),
400                            commentedOut);
401                    return;
402                case Format31i:
403                    addInstructionMethodItem(
404                            new Instruction31iMethodItem(codeItem, offset, stg, (Instruction31i)instruction),
405                            commentedOut);
406                    return;
407                case Format31t:
408                    addOffsetInstructionMethodItem(
409                            new Instruction31tMethodItem(labels, codeItem, offset, stg, (Instruction31t)instruction),
410                            commentedOut);
411                    return;
412                case Format32x:
413                    addInstructionMethodItem(
414                            new Instruction32xMethodItem(codeItem, offset, stg, (Instruction32x)instruction),
415                            commentedOut);
416                    return;
417                case Format35c:
418                    addInstructionMethodItem(
419                            new Instruction35cMethodItem(codeItem, offset, stg, (Instruction35c)instruction),
420                            commentedOut);
421                    return;
422                case Format35s:
423                    addInstructionMethodItem(
424                            new Instruction35sMethodItem(codeItem, offset, stg, (Instruction35s)instruction),
425                            commentedOut);
426                    return;
427                case Format35sf:
428                    addInstructionMethodItem(
429                            new Instruction35sfMethodItem(codeItem, offset, stg, (Instruction35sf)instruction),
430                            commentedOut);
431                    return;
432                case Format35ms:
433                    addInstructionMethodItem(
434                            new Instruction35msMethodItem(codeItem, offset, stg, (Instruction35ms)instruction),
435                            commentedOut);
436                    return;
437                case Format35msf:
438                    addInstructionMethodItem(
439                            new Instruction35msfMethodItem(codeItem, offset, stg, (Instruction35msf)instruction),
440                            commentedOut);
441                    return;
442                case Format3rc:
443                    addInstructionMethodItem(
444                            new Instruction3rcMethodItem(codeItem, offset, stg, (Instruction3rc)instruction),
445                            commentedOut);
446                    return;
447                case Format3rms:
448                    addInstructionMethodItem(
449                            new Instruction3rmsMethodItem(codeItem, offset, stg, (Instruction3rms)instruction),
450                            commentedOut);
451                    return;
452                case Format3rmsf:
453                    addInstructionMethodItem(
454                            new Instruction3rmsfMethodItem(codeItem, offset, stg, (Instruction3rmsf)instruction),
455                            commentedOut);
456                    return;
457                case Format51l:
458                    addInstructionMethodItem(
459                            new Instruction51lMethodItem(codeItem, offset, stg, (Instruction51l)instruction),
460                            commentedOut);
461                    return;
462                case ArrayData:
463                    addInstructionMethodItem(
464                            new ArrayDataMethodItem(codeItem, offset, stg, (ArrayDataPseudoInstruction)instruction),
465                            commentedOut);
466                    return;
467                case PackedSwitchData:
468                {
469                    final Integer baseAddress = packedSwitchMap.get(offset);
470
471                    if (baseAddress != null) {
472                        PackedSwitchDataPseudoInstruction packedSwitchInstruction =
473                                (PackedSwitchDataPseudoInstruction)instruction;
474
475                        PackedSwitchMethodItem packedSwitch = new PackedSwitchMethodItem(labels, codeItem, offset, stg,
476                                packedSwitchInstruction, baseAddress);
477                        addInstructionMethodItem(packedSwitch, commentedOut);
478
479                        if (!commentedOut) {
480                            for (LabelMethodItem label: packedSwitch) {
481                                label.setUncommented();
482                            }
483                        }
484                    }
485                    return;
486                }
487                case SparseSwitchData:
488                {
489                    final Integer baseAddress = sparseSwitchMap.get(offset);
490
491                    if (baseAddress != null) {
492                        SparseSwitchDataPseudoInstruction sparseSwitchInstruction =
493                                (SparseSwitchDataPseudoInstruction)instruction;
494
495                        SparseSwitchMethodItem sparseSwitch = new SparseSwitchMethodItem(labels, codeItem, offset, stg,
496                                sparseSwitchInstruction, baseAddress);
497                        addInstructionMethodItem(sparseSwitch, commentedOut);
498
499                        if (!commentedOut) {
500                            for (LabelMethodItem label: sparseSwitch) {
501                                label.setUncommented();
502                            }
503                        }
504                    }
505                    return;
506                }
507                case UnresolvedNullReference:
508                {
509                    addInstructionMethodItem(new UnresolvedNullReferenceMethodItem(codeItem, offset, stg,
510                            (UnresolvedNullReference)instruction), commentedOut);
511                    addMethodItemsForInstruction(offset, ((UnresolvedNullReference)instruction).OriginalInstruction,
512                            true);
513                    return;
514                }
515                case DeadInstruction:
516                {
517                    //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
518                    addMethodItemsForInstruction(offset, ((DeadInstruction)instruction).OriginalInstruction, true);
519                    return;
520                }
521            }
522        }
523
524        private void addTries() {
525            if (codeItem.getTries() == null) {
526                return;
527            }
528            for (CodeItem.TryItem tryItem: codeItem.getTries()) {
529                int startAddress = tryItem.startAddress;
530                int endAddress = tryItem.startAddress + tryItem.instructionCount;
531
532                /**
533                 * The end address points to the address immediately after the end of the last
534                 * instruction that the try block covers. We want the .catch directive and end_try
535                 * label to be associated with the last covered instruction, so we need to get
536                 * the offset for that instruction
537                 */
538                int index = Collections.binarySearch(instructions, new BlankMethodItem(stg, endAddress));
539                if (index < 0) {
540                    index = (index * -1) - 1;
541                }
542                //index should never be 0, so this should be safe
543                if (index == instructions.size()) {
544                    //if the end address is the same as the address of the last instruction, then
545                    //this try item ends at the next to last instruction.
546                    //otherwise, if the end address is past the address of the last instruction,
547                    //thin this try item ends at the last instruction
548                    if (instructions.get(instructions.size() - 1).getOffset() == endAddress) {
549                        //get the address for the next to last instruction
550                        index -= 2;
551                    } else {
552                        //get the address for the last instruction
553                        index--;
554                    }
555                } else {
556                    index -= 2;
557                }
558
559                int lastInstructionOffset = instructions.get(index).getOffset();
560
561                //add the catch all handler if it exists
562                int catchAllAddress = tryItem.encodedCatchHandler.getCatchAllHandlerAddress();
563                if (catchAllAddress != -1) {
564                    CatchMethodItem catchMethodItem = new CatchMethodItem(labels, lastInstructionOffset, stg, null,
565                            startAddress, endAddress, catchAllAddress);
566                    catches.add(catchMethodItem);
567                }
568
569                //add the rest of the handlers
570                for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
571                    //use the offset from the last covered instruction
572                    CatchMethodItem catchMethodItem = new CatchMethodItem(labels, lastInstructionOffset, stg,
573                            handler.exceptionType, startAddress, endAddress, handler.getHandlerAddress());
574                    catches.add(catchMethodItem);
575                }
576            }
577        }
578
579        private void addDebugInfo() {
580            DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
581            if (debugInfoItem == null) {
582                return;
583            }
584
585            DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
586                    new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
587                        @Override
588                        public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
589                                                      TypeIdItem type) {
590                            debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", -1,
591                                    registerNum, name, type, null));
592                        }
593
594                        @Override
595                        public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum,
596                                                              StringIdItem name, TypeIdItem type,
597                                                              StringIdItem signature) {
598                            debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "StartLocal", -1,
599                                    registerNum, name, type, signature));
600                        }
601
602                        @Override
603                        public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name,
604                                                    TypeIdItem type, StringIdItem signature) {
605                            debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "EndLocal", -1,
606                                    registerNum, name, type, signature));
607                        }
608
609                        @Override
610                        public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
611                                                        TypeIdItem type, StringIdItem signature) {
612                            debugItems.add(new LocalDebugMethodItem(codeItem, codeAddress, stg, "RestartLocal", -1,
613                                    registerNum, name, type, signature));
614                        }
615
616                        @Override
617                        public void ProcessSetPrologueEnd(int codeAddress) {
618                            debugItems.add(new DebugMethodItem(codeAddress, stg, "EndPrologue", -4));
619                        }
620
621                        @Override
622                        public void ProcessSetEpilogueBegin(int codeAddress) {
623                            debugItems.add(new DebugMethodItem(codeAddress, stg, "StartEpilogue", -4));
624                        }
625
626                        @Override
627                        public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
628                            debugItems.add(new DebugMethodItem(codeAddress, stg, "SetFile", -3) {
629                                @Override
630                                protected void setAttributes(StringTemplate template) {
631                                    template.setAttribute("FileName", name.getStringValue());
632                                }
633                            });
634                        }
635
636                        @Override
637                        public void ProcessLineEmit(int codeAddress, final int line) {
638                             debugItems.add(new DebugMethodItem(codeAddress, stg, "Line", -2) {
639                                 @Override
640                                 protected void setAttributes(StringTemplate template) {
641                                     template.setAttribute("Line", line);
642                                 }
643                             });
644                        }
645                    });
646        }
647
648        private void setLabelIndexes() {
649            HashMap<String, Integer> nextLabelIndexByType = new HashMap<String, Integer>();
650            ArrayList<LabelMethodItem> sortedLabels = new ArrayList<LabelMethodItem>(labels.getLabels());
651
652            //sort the labels by their location in the method
653            Collections.sort(sortedLabels);
654
655            for (LabelMethodItem labelMethodItem: sortedLabels) {
656                Integer labelIndex = nextLabelIndexByType.get(labelMethodItem.getLabelPrefix());
657                if (labelIndex == null) {
658                    labelIndex = 0;
659                }
660                labelMethodItem.setLabelIndex(labelIndex);
661                nextLabelIndexByType.put(labelMethodItem.getLabelPrefix(), labelIndex + 1);
662            }
663        }
664    }
665
666    public static class LabelCache {
667        protected HashMap<LabelMethodItem, LabelMethodItem> labels = new HashMap<LabelMethodItem, LabelMethodItem>();
668
669        public LabelCache() {
670        }
671
672        public LabelMethodItem internLabel(LabelMethodItem labelMethodItem) {
673            LabelMethodItem internedLabelMethodItem = labels.get(labelMethodItem);
674            if (internedLabelMethodItem != null) {
675                if (!labelMethodItem.isCommentedOut()) {
676                    internedLabelMethodItem.setUncommented();
677                }
678                return internedLabelMethodItem;
679            }
680            labels.put(labelMethodItem, labelMethodItem);
681            return labelMethodItem;
682        }
683
684
685        public Collection<LabelMethodItem> getLabels() {
686            return labels.values();
687        }
688    }
689}
690