MethodDefinition.java revision b6547e8fd56242dde90275d9b0ba6f3639083a61
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.baksmali.Adaptors;
30
31import org.jf.baksmali.Adaptors.Format.*;
32import org.jf.dexlib.*;
33import org.jf.dexlib.code.Format.*;
34import org.jf.dexlib.code.Instruction;
35import org.jf.dexlib.code.InstructionField;
36import org.jf.dexlib.code.Opcode;
37import org.jf.dexlib.util.AccessFlags;
38import org.jf.dexlib.util.DebugInfoDecoder;
39
40import java.util.*;
41
42public class MethodDefinition {
43    private ClassDataItem.EncodedMethod encodedMethod;
44    private MethodIdItem methodIdItem;
45    private CodeItem codeItem;
46    private AnnotationSetItem annotationSet;
47
48    public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod, AnnotationSetItem annotationSet) {
49        this.encodedMethod = encodedMethod;
50        this.methodIdItem = encodedMethod.getMethod();
51        this.codeItem = encodedMethod.getCodeItem();
52        this.annotationSet = annotationSet;
53    }
54
55    private String methodName = null;
56    public String getMethodName() {
57        if (methodName == null) {
58            methodName = methodIdItem.getMethodName();
59        }
60        return methodName;
61    }
62
63    private List<String> accessFlags = null;
64    public List<String> getAccessFlags() {
65        if (accessFlags == null) {
66            accessFlags = new ArrayList<String>();
67
68            for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.getAccessFlags())) {
69                accessFlags.add(accessFlag.toString());
70            }
71        }
72        return accessFlags;
73    }
74
75    private String prototype = null;
76    public String getPrototype() {
77        if (prototype == null) {
78            prototype = methodIdItem.getPrototype().getPrototypeString();
79        }
80        return prototype;
81    }
82
83    private Boolean hasCode = null;
84    public boolean getHasCode() {
85        if (hasCode == null) {
86            hasCode = (codeItem != null);
87        }
88        return hasCode;
89    }
90
91    private String registerCount = null;
92    public String getRegisterCount() {
93        if (registerCount == null) {
94            if (codeItem == null) {
95                registerCount = "0";
96            } else {
97                registerCount = Integer.toString(codeItem.getRegisterCount());
98            }
99        }
100        return registerCount;
101    }
102
103    public List<AnnotationAdaptor> getAnnotations() {
104        if (annotationSet == null) {
105            return null;
106        }
107
108        List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>();
109
110        for (AnnotationItem annotationItem: annotationSet.getAnnotationItems()) {
111            annotationAdaptors.add(new AnnotationAdaptor(annotationItem));
112        }
113        return annotationAdaptors;
114    }
115
116
117    private List<MethodItem> methodItems = null;
118    public List<MethodItem> getMethodItems() {
119        if (methodItems == null) {
120            MethodItemList methodItemList = new MethodItemList();
121            methodItemList.generateMethodItemList();
122
123            methodItems = new ArrayList<MethodItem>();
124
125            methodItems.addAll(methodItemList.labels);
126            methodItems.addAll(methodItemList.instructions);
127            methodItems.addAll(methodItemList.blanks);
128            methodItems.addAll(methodItemList.catches);
129            methodItems.addAll(methodItemList.debugItems);
130            Collections.sort(methodItems);
131        }
132        return methodItems;
133    }
134
135
136    private class MethodItemList {
137        public HashSet<LabelMethodItem> labels = new HashSet<LabelMethodItem>();
138        public List<MethodItem> instructions = new ArrayList<MethodItem>();
139        public List<BlankMethodItem> blanks = new ArrayList<BlankMethodItem>();
140        public List<CatchMethodItem> catches = new ArrayList<CatchMethodItem>();
141        public List<MethodItem> debugItems = new ArrayList<MethodItem>();
142
143        private HashMap<Integer, Integer> packedSwitchMap = new HashMap<Integer, Integer>();
144        private HashMap<Integer, Integer> sparseSwitchMap = new HashMap<Integer, Integer>();
145
146
147        public void generateMethodItemList() {
148            if (codeItem == null) {
149                return;
150            }
151
152            int offset = 0;
153            for (InstructionField instructionField: codeItem.getInstructions()) {
154                Instruction instruction = instructionField.getInstruction();
155                if (instruction.getOpcode() == Opcode.PACKED_SWITCH) {
156                    packedSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset);
157                } else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) {
158                    sparseSwitchMap.put(offset+((Instruction31t)instruction).getOffset(), offset);
159                }
160
161                offset += instructionField.getSize(offset * 2) / 2;
162            }
163
164            offset = 0;
165            for (InstructionField instructionField: codeItem.getInstructions()) {
166                addMethodItemsForInstruction(offset, instructionField);
167                blanks.add(new BlankMethodItem(offset));
168                offset += instructionField.getSize(offset*2) / 2;
169            }
170            blanks.remove(blanks.size()-1);
171
172            addTries();
173
174            addDebugInfo();
175        }
176
177        private void addMethodItemsForInstruction(int offset, InstructionField instructionField) {
178            Instruction instruction = instructionField.getInstruction();
179
180            switch (instruction.getFormat()) {
181                case Format10t:
182                    instructions.add(new Instruction10tMethodItem(offset, (Instruction10t)instruction));
183                    labels.add(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), "goto_"));
184                    return;
185                case Format10x:
186                    instructions.add(new Instruction10xMethodItem(offset, (Instruction10x)instruction));
187                    return;
188                case Format11n:
189                    instructions.add(new Instruction11nMethodItem(offset, (Instruction11n)instruction));
190                    return;
191                case Format11x:
192                    instructions.add(new Instruction11xMethodItem(offset, (Instruction11x)instruction));
193                    return;
194                case Format12x:
195                    instructions.add(new Instruction12xMethodItem(offset, (Instruction12x)instruction));
196                    return;
197                case Format20t:
198                    instructions.add(new Instruction20tMethodItem(offset, (Instruction20t)instruction));
199                    labels.add(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), "goto_"));
200                    return;
201                case Format21c:
202                    instructions.add(new Instruction21cMethodItem(offset, (Instruction21c)instruction));
203                    return;
204                case Format21h:
205                    instructions.add(new Instruction21hMethodItem(offset, (Instruction21h)instruction));
206                    return;
207                case Format21s:
208                    instructions.add(new Instruction21sMethodItem(offset, (Instruction21s)instruction));
209                    return;
210                case Format21t:
211                    instructions.add(new Instruction21tMethodItem(offset, (Instruction21t)instruction));
212                    labels.add(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), "cond_"));
213                    return;
214                case Format22b:
215                    instructions.add(new Instruction22bMethodItem(offset, (Instruction22b)instruction));
216                    return;
217                case Format22c:
218                    instructions.add(new Instruction22cMethodItem(offset, (Instruction22c)instruction));
219                    return;
220                case Format22s:
221                    instructions.add(new Instruction22sMethodItem(offset, (Instruction22s)instruction));
222                    return;
223                case Format22t:
224                    instructions.add(new Instruction22tMethodItem(offset, (Instruction22t)instruction));
225                    labels.add(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), "cond_"));
226                    return;
227                case Format22x:
228                    instructions.add(new Instruction22xMethodItem(offset, (Instruction22x)instruction));
229                    return;
230                case Format23x:
231                    instructions.add(new Instruction23xMethodItem(offset, (Instruction23x)instruction));
232                    return;
233                case Format30t:
234                    instructions.add(new Instruction30tMethodItem(offset, (Instruction30t)instruction));
235                    labels.add(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), "goto_"));
236                    return;
237                case Format31c:
238                    instructions.add(new Instruction31cMethodItem(offset, (Instruction31c)instruction));
239                    return;
240                case Format31i:
241                    instructions.add(new Instruction31iMethodItem(offset, (Instruction31i)instruction));
242                    return;
243                case Format31t:
244                    instructions.add(new Instruction31tMethodItem(offset, (Instruction31t)instruction));
245                    if (instruction.getOpcode() == Opcode.FILL_ARRAY_DATA) {
246                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "array_"));
247                    } else if (instruction.getOpcode() == Opcode.PACKED_SWITCH) {
248                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "pswitch_data_"));
249                    } else if (instruction.getOpcode() == Opcode.SPARSE_SWITCH) {
250                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "sswitch_data_"));
251                    }
252                    return;
253                case Format32x:
254                    instructions.add(new Instruction32xMethodItem(offset, (Instruction32x)instruction));
255                    return;
256                case Format35c:
257                    instructions.add(new Instruction35cMethodItem(offset, (Instruction35c)instruction));
258                    return;
259                case Format3rc:
260                    instructions.add(new Instruction3rcMethodItem(offset, (Instruction3rc)instruction));
261                    return;
262                case Format51l:
263                    instructions.add(new Instruction51lMethodItem(offset, (Instruction51l)instruction));
264                    return;
265                case ArrayData:
266                    instructions.add(new ArrayDataMethodItem(offset, (ArrayDataPseudoInstruction)instruction));
267                    return;
268                case PackedSwitchData:
269                {
270                    Integer baseAddress = packedSwitchMap.get(offset);
271
272                    if (baseAddress != null) {
273                        instructions.add(new PackedSwitchMethodItem(offset,
274                                (PackedSwitchDataPseudoInstruction)instruction, baseAddress));
275                        for (int target: ((PackedSwitchDataPseudoInstruction)instruction).getTargets()) {
276                            labels.add(new LabelMethodItem(baseAddress + target, "pswitch_"));
277                        }
278                    }
279                    return;
280                }
281                case SparseSwitchData:
282                {
283                    Integer baseAddress = sparseSwitchMap.get(offset);
284                    if (baseAddress != null) {
285                        instructions.add(new SparseSwitchMethodItem(offset,
286                                (SparseSwitchDataPseudoInstruction)instruction, baseAddress));
287                        for (int target: ((SparseSwitchDataPseudoInstruction)instruction).getTargets()) {
288                            labels.add(new LabelMethodItem(baseAddress + target, "sswitch_"));
289                        }
290                    }
291                    return;
292                }
293            }
294        }
295
296        private void addTries() {
297            for (CodeItem.TryItem tryItem: codeItem.getTries()) {
298                int startAddress = tryItem.getStartAddress();
299                int endAddress = tryItem.getEndAddress();
300
301                /**
302                 * The end address points to the address immediately after the end of the last
303                 * instruction that the try block covers. We want the .catch directive and end_try
304                 * label to be associated with the last covered instruction, so we need to get
305                 * the offset for that instruction
306                 */
307                int index = Collections.binarySearch(instructions, new BlankMethodItem(endAddress));
308                if (index < 0) {
309                    index = (index * -1) - 1;
310                }
311                //index should never by 0, so this should be safe
312                if (index == instructions.size()) {
313                    index--;
314                } else {
315                    index -= 2;
316                }
317
318                int lastInstructionOffset = instructions.get(index).getOffset();
319
320                //add the catch all handler if it exists
321                int catchAllAddress = tryItem.getHandler().getCatchAllAddress();
322                if (catchAllAddress != -1) {
323                    CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, null, startAddress,
324                            endAddress, catchAllAddress) {
325                        public String getTemplate() {
326                            return "CatchAll";
327                        }
328                    };
329                    catches.add(catchMethodItem);
330
331                    labels.add(new LabelMethodItem(startAddress, "try_start_"));
332                    //use the offset from the last covered instruction, but make the label
333                    //name refer to the address of the next instruction
334                    labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress));
335                    labels.add(new LabelMethodItem(catchAllAddress, "handler_"));
336
337                }
338
339                //add the rest of the handlers
340                //TODO: find adjacent handlers for the same type and combine them
341                for (CodeItem.EncodedTypeAddrPair handler: tryItem.getHandler().getHandlers()) {
342                    //use the offset from the last covered instruction
343                    CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset,
344                            handler.getTypeReferenceField(), startAddress, endAddress, handler.getHandlerAddress());
345                    catches.add(catchMethodItem);
346
347                    labels.add(new LabelMethodItem(startAddress, "try_start_"));
348                    //use the offset from the last covered instruction, but make the label
349                    //name refer to the address of the next instruction
350                    labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress));
351                    labels.add(new LabelMethodItem(handler.getHandlerAddress(), "handler_"));
352                }
353            }
354        }
355
356        private void addDebugInfo() {
357            DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
358            if (debugInfoItem == null) {
359                return;
360            }
361
362            DebugInfoDecoder decoder = new DebugInfoDecoder(debugInfoItem, new DebugInfoDelegate(),
363                    codeItem.getRegisterCount());
364            decoder.decode();
365        }
366
367        private class DebugInfoDelegate implements DebugInfoDecoder.DebugInfoDelegate {
368
369            public void endPrologue(int address) {
370                debugItems.add(new DebugMethodItem(address, "EndPrologue", -4));
371            }
372
373            public void startEpilogue(int address) {
374                debugItems.add(new DebugMethodItem(address, "StartEpilogue", -4));
375            }
376
377            public void startLocal(int address, DebugInfoDecoder.Local local) {
378                debugItems.add(new LocalDebugMethodItem(address, "StartLocal", -1, local));
379            }
380
381            public void endLocal(int address, DebugInfoDecoder.Local local) {
382                debugItems.add(new LocalDebugMethodItem(address, "EndLocal", -1, local));
383            }
384
385            public void restartLocal(int address, DebugInfoDecoder.Local local) {
386                debugItems.add(new LocalDebugMethodItem(address, "RestartLocal", -1, local));
387            }
388
389            public void setFile(int address, final StringIdItem fileName) {
390                debugItems.add(new DebugMethodItem(address, "SetFile", -3) {
391                    public String getFileName() {
392                        return fileName.getStringValue();
393                    }
394                });
395            }
396
397            public void line(int address, final int line) {
398                debugItems.add(new DebugMethodItem(address, "Line", -2) {
399                    public int getLine() {
400                        return line;
401                    }
402                });
403            }
404        }
405    }
406}
407