MethodDefinition.java revision 814580d4cfe7de0c8848fa081e547146d87d3b0b
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.Debug.DebugInstructionIterator;
34import org.jf.dexlib.Code.Format.*;
35import org.jf.dexlib.Code.Instruction;
36import org.jf.dexlib.Code.Opcode;
37import org.jf.dexlib.Code.InstructionIterator;
38import org.jf.dexlib.Util.AccessFlags;
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    private AnnotationSetRefList parameterAnnotations;
48
49    public MethodDefinition(ClassDataItem.EncodedMethod encodedMethod, AnnotationSetItem annotationSet,
50                            AnnotationSetRefList parameterAnnotations) {
51        this.encodedMethod = encodedMethod;
52        this.methodIdItem = encodedMethod.method;
53        this.codeItem = encodedMethod.codeItem;
54        this.annotationSet = annotationSet;
55        this.parameterAnnotations = parameterAnnotations;
56    }
57
58    public String getMethodName() {
59        return methodIdItem.getMethodName().getStringValue();
60    }
61
62    private List<String> accessFlags = null;
63    public List<String> getAccessFlags() {
64        if (accessFlags == null) {
65            accessFlags = new ArrayList<String>();
66
67            for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForMethod(encodedMethod.accessFlags)) {
68                accessFlags.add(accessFlag.toString());
69            }
70        }
71        return accessFlags;
72    }
73
74    private String prototype = null;
75    public String getPrototype() {
76        if (prototype == null) {
77            prototype = methodIdItem.getPrototype().getPrototypeString();
78        }
79        return prototype;
80    }
81
82    private Boolean hasCode = null;
83    public boolean getHasCode() {
84        if (hasCode == null) {
85            hasCode = (codeItem != null);
86        }
87        return hasCode;
88    }
89
90    private String registerCount = null;
91    public String getRegisterCount() {
92        if (registerCount == null) {
93            if (codeItem == null) {
94                registerCount = "0";
95            } else {
96                registerCount = Integer.toString(codeItem.getRegisterCount());
97            }
98        }
99        return registerCount;
100    }
101
102    public List<AnnotationAdaptor> getAnnotations() {
103        if (annotationSet == null) {
104            return null;
105        }
106
107        List<AnnotationAdaptor> annotationAdaptors = new ArrayList<AnnotationAdaptor>();
108
109        for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
110            annotationAdaptors.add(new AnnotationAdaptor(annotationItem));
111        }
112        return annotationAdaptors;
113    }
114
115    public List<ParameterAdaptor> getParameters() {
116        DebugInfoItem debugInfoItem = null;
117        if (codeItem != null) {
118            debugInfoItem = codeItem.getDebugInfo();
119        }
120
121        int parameterCount = 0;
122
123        List<AnnotationSetItem> annotations = new ArrayList<AnnotationSetItem>();
124        if (parameterAnnotations != null) {
125            AnnotationSetItem[] _annotations = parameterAnnotations.getAnnotationSets();
126            if (_annotations != null) {
127                annotations.addAll(Arrays.asList(_annotations));
128            }
129
130            parameterCount = annotations.size();
131        }
132
133        List<String> parameterNames = new ArrayList<String>();
134        if (debugInfoItem != null) {
135            StringIdItem[] _parameterNames = debugInfoItem.getParameterNames();
136            if (_parameterNames != null) {
137                for (StringIdItem parameterName: _parameterNames) {
138                    parameterNames.add(parameterName==null?null:parameterName.getStringValue());
139                }
140            }
141
142            if (parameterCount < parameterNames.size()) {
143                parameterCount = parameterNames.size();
144            }
145        }
146
147        List<ParameterAdaptor> parameterAdaptors = new ArrayList<ParameterAdaptor>();
148        for (int i=0; i<parameterCount; i++) {
149            AnnotationSetItem annotationSet = null;
150            if (i < annotations.size()) {
151                annotationSet = annotations.get(i);
152            }
153
154            String parameterName = null;
155            if (i < parameterNames.size()) {
156                parameterName = parameterNames.get(i);
157            }
158
159            parameterAdaptors.add(new ParameterAdaptor(parameterName, annotationSet));
160        }
161
162        return parameterAdaptors;
163    }
164
165    public StringIdItem[] getParameterNames() {
166        if (codeItem == null) {
167            return null;
168        }
169
170        DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
171        if (debugInfoItem == null) {
172            return null;
173        }
174
175        return debugInfoItem.getParameterNames();
176    }
177
178    private List<MethodItem> methodItems = null;
179    public List<MethodItem> getMethodItems() {
180        if (methodItems == null) {
181            MethodItemList methodItemList = new MethodItemList();
182            methodItemList.generateMethodItemList();
183
184            methodItems = new ArrayList<MethodItem>();
185
186            methodItems.addAll(methodItemList.labels);
187            methodItems.addAll(methodItemList.instructions);
188            methodItems.addAll(methodItemList.blanks);
189            methodItems.addAll(methodItemList.catches);
190            methodItems.addAll(methodItemList.debugItems);
191            Collections.sort(methodItems);
192        }
193        return methodItems;
194    }
195
196
197    private class MethodItemList {
198        public HashSet<LabelMethodItem> labels = new HashSet<LabelMethodItem>();
199        public List<MethodItem> instructions = new ArrayList<MethodItem>();
200        public List<BlankMethodItem> blanks = new ArrayList<BlankMethodItem>();
201        public List<CatchMethodItem> catches = new ArrayList<CatchMethodItem>();
202        public List<MethodItem> debugItems = new ArrayList<MethodItem>();
203
204        private HashMap<Integer, Integer> packedSwitchMap = new HashMap<Integer, Integer>();
205        private HashMap<Integer, Integer> sparseSwitchMap = new HashMap<Integer, Integer>();
206
207
208        public void generateMethodItemList() {
209            if (codeItem == null) {
210                return;
211            }
212
213            final byte[] encodedInstructions = codeItem.getEncodedInstructions();
214
215            InstructionIterator.IterateInstructions(encodedInstructions,
216                    new InstructionIterator.ProcessRawInstructionDelegate() {
217                        public void ProcessNormalInstruction(Opcode opcode, int index) {
218                            if (opcode == Opcode.PACKED_SWITCH) {
219                                Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
220                                        methodIdItem.getDexFile(), opcode, encodedInstructions, index);
221                                packedSwitchMap.put(index/2 + ins.getOffset(), index/2);
222                            } else if (opcode == Opcode.SPARSE_SWITCH) {
223                                Instruction31t ins = (Instruction31t)opcode.format.Factory.makeInstruction(
224                                        methodIdItem.getDexFile(), opcode, encodedInstructions, index);
225                                sparseSwitchMap.put(index/2 + ins.getOffset(),  index/2);
226                            }
227                        }
228
229                        public void ProcessReferenceInstruction(Opcode opcode, int index) {
230                        }
231
232                        public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
233                        }
234
235                        public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
236                        }
237
238                        public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) {
239                        }
240                    });
241
242            InstructionIterator.IterateInstructions(methodIdItem.getDexFile(), encodedInstructions,
243                    new InstructionIterator.ProcessInstructionDelegate() {
244                        public void ProcessInstruction(int index, Instruction instruction) {
245                            int offset = index/2;
246                            addMethodItemsForInstruction(offset, instruction);
247                            blanks.add(new BlankMethodItem(offset));
248                        }
249                    });
250
251            blanks.remove(blanks.size()-1);
252
253            addTries();
254
255            addDebugInfo();
256        }
257
258        private void addMethodItemsForInstruction(int offset, Instruction instruction) {
259            switch (instruction.getFormat()) {
260                case Format10t:
261                    instructions.add(new Instruction10tMethodItem(offset, (Instruction10t)instruction));
262                    labels.add(new LabelMethodItem(offset + ((Instruction10t)instruction).getOffset(), "goto_"));
263                    return;
264                case Format10x:
265                    instructions.add(new Instruction10xMethodItem(offset, (Instruction10x)instruction));
266                    return;
267                case Format11n:
268                    instructions.add(new Instruction11nMethodItem(offset, (Instruction11n)instruction));
269                    return;
270                case Format11x:
271                    instructions.add(new Instruction11xMethodItem(offset, (Instruction11x)instruction));
272                    return;
273                case Format12x:
274                    instructions.add(new Instruction12xMethodItem(offset, (Instruction12x)instruction));
275                    return;
276                case Format20t:
277                    instructions.add(new Instruction20tMethodItem(offset, (Instruction20t)instruction));
278                    labels.add(new LabelMethodItem(offset + ((Instruction20t)instruction).getOffset(), "goto_"));
279                    return;
280                case Format21c:
281                    instructions.add(new Instruction21cMethodItem(offset, (Instruction21c)instruction));
282                    return;
283                case Format21h:
284                    instructions.add(new Instruction21hMethodItem(offset, (Instruction21h)instruction));
285                    return;
286                case Format21s:
287                    instructions.add(new Instruction21sMethodItem(offset, (Instruction21s)instruction));
288                    return;
289                case Format21t:
290                    instructions.add(new Instruction21tMethodItem(offset, (Instruction21t)instruction));
291                    labels.add(new LabelMethodItem(offset + ((Instruction21t)instruction).getOffset(), "cond_"));
292                    return;
293                case Format22b:
294                    instructions.add(new Instruction22bMethodItem(offset, (Instruction22b)instruction));
295                    return;
296                case Format22c:
297                    instructions.add(new Instruction22cMethodItem(offset, (Instruction22c)instruction));
298                    return;
299                case Format22s:
300                    instructions.add(new Instruction22sMethodItem(offset, (Instruction22s)instruction));
301                    return;
302                case Format22t:
303                    instructions.add(new Instruction22tMethodItem(offset, (Instruction22t)instruction));
304                    labels.add(new LabelMethodItem(offset + ((Instruction22t)instruction).getOffset(), "cond_"));
305                    return;
306                case Format22x:
307                    instructions.add(new Instruction22xMethodItem(offset, (Instruction22x)instruction));
308                    return;
309                case Format23x:
310                    instructions.add(new Instruction23xMethodItem(offset, (Instruction23x)instruction));
311                    return;
312                case Format30t:
313                    instructions.add(new Instruction30tMethodItem(offset, (Instruction30t)instruction));
314                    labels.add(new LabelMethodItem(offset + ((Instruction30t)instruction).getOffset(), "goto_"));
315                    return;
316                case Format31c:
317                    instructions.add(new Instruction31cMethodItem(offset, (Instruction31c)instruction));
318                    return;
319                case Format31i:
320                    instructions.add(new Instruction31iMethodItem(offset, (Instruction31i)instruction));
321                    return;
322                case Format31t:
323                    instructions.add(new Instruction31tMethodItem(offset, (Instruction31t)instruction));
324                    if (instruction.opcode == Opcode.FILL_ARRAY_DATA) {
325                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "array_"));
326                    } else if (instruction.opcode == Opcode.PACKED_SWITCH) {
327                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "pswitch_data_"));
328                    } else if (instruction.opcode == Opcode.SPARSE_SWITCH) {
329                        labels.add(new LabelMethodItem(offset + ((Instruction31t)instruction).getOffset(), "sswitch_data_"));
330                    }
331                    return;
332                case Format32x:
333                    instructions.add(new Instruction32xMethodItem(offset, (Instruction32x)instruction));
334                    return;
335                case Format35c:
336                    instructions.add(new Instruction35cMethodItem(offset, (Instruction35c)instruction));
337                    return;
338                case Format3rc:
339                    instructions.add(new Instruction3rcMethodItem(offset, (Instruction3rc)instruction));
340                    return;
341                case Format51l:
342                    instructions.add(new Instruction51lMethodItem(offset, (Instruction51l)instruction));
343                    return;
344                case ArrayData:
345                    instructions.add(new ArrayDataMethodItem(offset, (ArrayDataPseudoInstruction)instruction));
346                    return;
347                case PackedSwitchData:
348                {
349                    final Integer baseAddress = packedSwitchMap.get(offset);
350
351                    if (baseAddress != null) {
352                        PackedSwitchDataPseudoInstruction packedSwitchInstruction =
353                                (PackedSwitchDataPseudoInstruction)instruction;
354
355                        instructions.add(new PackedSwitchMethodItem(offset,
356                                packedSwitchInstruction, baseAddress));
357
358                        Iterator<PackedSwitchDataPseudoInstruction.PackedSwitchTarget> iterator =
359                                packedSwitchInstruction.getTargets();
360                        while (iterator.hasNext()) {
361                            PackedSwitchDataPseudoInstruction.PackedSwitchTarget target = iterator.next();
362                            labels.add(new LabelMethodItem(baseAddress + target.target, "pswitch_"));
363                        }
364                    }
365                    return;
366                }
367                case SparseSwitchData:
368                {
369                    final Integer baseAddress = sparseSwitchMap.get(offset);
370
371                    if (baseAddress != null) {
372                        SparseSwitchDataPseudoInstruction sparseSwitchInstruction =
373                                (SparseSwitchDataPseudoInstruction)instruction;
374
375                        instructions.add(new SparseSwitchMethodItem(offset,
376                                sparseSwitchInstruction, baseAddress));
377
378                        Iterator<SparseSwitchDataPseudoInstruction.SparseSwitchTarget> iterator =
379                                sparseSwitchInstruction.getTargets();
380                        while (iterator.hasNext()) {
381                            SparseSwitchDataPseudoInstruction.SparseSwitchTarget target = iterator.next();
382                            labels.add(new LabelMethodItem(baseAddress + target.target, "sswitch_"));
383                        }
384                    }
385                }
386            }
387        }
388
389        private void addTries() {
390            if (codeItem.getTries() == null) {
391                return;
392            }
393            for (CodeItem.TryItem tryItem: codeItem.getTries()) {
394                int startAddress = tryItem.startAddress;
395                int endAddress = tryItem.startAddress + tryItem.instructionCount;
396
397                /**
398                 * The end address points to the address immediately after the end of the last
399                 * instruction that the try block covers. We want the .catch directive and end_try
400                 * label to be associated with the last covered instruction, so we need to get
401                 * the offset for that instruction
402                 */
403                int index = Collections.binarySearch(instructions, new BlankMethodItem(endAddress));
404                if (index < 0) {
405                    index = (index * -1) - 1;
406                }
407                //index should never by 0, so this should be safe
408                if (index == instructions.size()) {
409                    //if the end address is the same as the address of the last instruction, then
410                    //this try item ends at the next to last instruction.
411                    //otherwise, if the end address is past the address of the last instruction,
412                    //thin this try item ends at the last instruction
413                    if (instructions.get(instructions.size() - 1).getOffset() == endAddress) {
414                        //get the address for the next to last instruction
415                        index -= 2;
416                    } else {
417                        //get the address for the last instruction
418                        index--;
419                    }
420                } else {
421                    index -= 2;
422                }
423
424                int lastInstructionOffset = instructions.get(index).getOffset();
425
426                //add the catch all handler if it exists
427                int catchAllAddress = tryItem.encodedCatchHandler.catchAllHandlerAddress;
428                if (catchAllAddress != -1) {
429                    CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset, null, startAddress,
430                            endAddress, catchAllAddress) {
431                        public String getTemplate() {
432                            return "CatchAll";
433                        }
434                    };
435                    catches.add(catchMethodItem);
436
437                    labels.add(new LabelMethodItem(startAddress, "try_start_"));
438                    //use the offset from the last covered instruction, but make the label
439                    //name refer to the address of the next instruction
440                    labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress));
441                    labels.add(new LabelMethodItem(catchAllAddress, "handler_"));
442
443                }
444
445                //add the rest of the handlers
446                //TODO: find adjacent handlers for the same type and combine them
447                for (CodeItem.EncodedTypeAddrPair handler: tryItem.encodedCatchHandler.handlers) {
448                    //use the offset from the last covered instruction
449                    CatchMethodItem catchMethodItem = new CatchMethodItem(lastInstructionOffset,
450                            handler.exceptionType, startAddress, endAddress, handler.handlerAddress);
451                    catches.add(catchMethodItem);
452
453                    labels.add(new LabelMethodItem(startAddress, "try_start_"));
454                    //use the offset from the last covered instruction, but make the label
455                    //name refer to the address of the next instruction
456                    labels.add(new EndTryLabelMethodItem(lastInstructionOffset, endAddress));
457                    labels.add(new LabelMethodItem(handler.handlerAddress, "handler_"));
458                }
459            }
460        }
461
462        private void addDebugInfo() {
463            DebugInfoItem debugInfoItem = codeItem.getDebugInfo();
464            if (debugInfoItem == null) {
465                return;
466            }
467
468            DebugInstructionIterator.DecodeInstructions(debugInfoItem, codeItem.getRegisterCount(),
469                    new DebugInstructionIterator.ProcessDecodedDebugInstructionDelegate() {
470                        @Override
471                        public void ProcessStartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
472                                                      TypeIdItem type) {
473                            debugItems.add(new LocalDebugMethodItem(codeAddress, "StartLocal", -1, registerNum, name,
474                                    type, null));
475                        }
476
477                        @Override
478                        public void ProcessStartLocalExtended(int codeAddress, int length, int registerNum,
479                                                              StringIdItem name, TypeIdItem type,
480                                                              StringIdItem signature) {
481                            debugItems.add(new LocalDebugMethodItem(codeAddress, "StartLocal", -1, registerNum, name,
482                                    type, signature));
483                        }
484
485                        @Override
486                        public void ProcessEndLocal(int codeAddress, int length, int registerNum, StringIdItem name,
487                                                    TypeIdItem type, StringIdItem signature) {
488                            debugItems.add(new LocalDebugMethodItem(codeAddress, "EndLocal", -1, registerNum, name,
489                                    type, signature));
490                        }
491
492                        @Override
493                        public void ProcessRestartLocal(int codeAddress, int length, int registerNum, StringIdItem name,
494                                                        TypeIdItem type, StringIdItem signature) {
495                            debugItems.add(new LocalDebugMethodItem(codeAddress, "RestartLocal", -1, registerNum, name,
496                                    type, signature));
497                        }
498
499                        @Override
500                        public void ProcessSetPrologueEnd(int codeAddress) {
501                            debugItems.add(new DebugMethodItem(codeAddress, "EndPrologue", -4));
502                        }
503
504                        @Override
505                        public void ProcessSetEpilogueBegin(int codeAddress) {
506                            debugItems.add(new DebugMethodItem(codeAddress, "StartEpilogue", -4));
507                        }
508
509                        @Override
510                        public void ProcessSetFile(int codeAddress, int length, final StringIdItem name) {
511                            debugItems.add(new DebugMethodItem(codeAddress, "SetFile", -3) {
512                                    public String getFileName() {
513                                        return name.getStringValue();
514                                    }
515                            });
516                        }
517
518                        @Override
519                        public void ProcessLineEmit(int codeAddress, final int line) {
520                             debugItems.add(new DebugMethodItem(codeAddress, "Line", -2) {
521                                public int getLine() {
522                                    return line;
523                                }
524                            });
525                        }
526                    });
527        }
528    }
529}
530