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