ClassDefinition.java revision 090e553f34a176bc558f0d70392181c0fbd83fe8
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.dexlib.EncodedValue.EncodedValue;
32import org.jf.dexlib.*;
33import org.jf.dexlib.Code.InstructionIterator;
34import org.jf.dexlib.Code.Opcode;
35import org.jf.dexlib.Code.Format.Format;
36import org.jf.dexlib.Code.Format.Instruction21c;
37import org.jf.dexlib.Util.AccessFlags;
38import org.jf.dexlib.Util.SparseArray;
39import org.antlr.stringtemplate.StringTemplate;
40import org.antlr.stringtemplate.StringTemplateGroup;
41
42import java.util.*;
43
44public class ClassDefinition {
45    private StringTemplateGroup stg;
46    private ClassDefItem classDefItem;
47    private ClassDataItem classDataItem;
48
49    private SparseArray<AnnotationSetItem> methodAnnotationsMap;
50    private SparseArray<AnnotationSetItem> fieldAnnotationsMap;
51    private SparseArray<AnnotationSetRefList> parameterAnnotationsMap;
52
53    private SparseArray<FieldIdItem> fieldsSetInStaticConstructor;
54
55    public ClassDefinition(StringTemplateGroup stg, ClassDefItem classDefItem) {
56        this.stg = stg;
57        this.classDefItem = classDefItem;
58        this.classDataItem = classDefItem.getClassData();
59        buildAnnotationMaps();
60        findFieldsSetInStaticConstructor();
61    }
62
63    public StringTemplate makeTemplate() {
64        StringTemplate template = stg.getInstanceOf("smaliFile");
65
66        template.setAttribute("AccessFlags", getAccessFlags());
67        template.setAttribute("ClassType", classDefItem.getClassType().getTypeDescriptor());
68        template.setAttribute("SuperType", getSuperType());
69        template.setAttribute("SourceFile", getSourceFile());
70        template.setAttribute("Interfaces", getInterfaces());
71        template.setAttribute("Annotations", getAnnotations());
72        template.setAttribute("StaticFields", getStaticFields());
73        template.setAttribute("InstanceFields", getInstanceFields());
74        template.setAttribute("DirectMethods", getDirectMethods());
75        template.setAttribute("VirtualMethods", getVirtualMethods());
76
77        return template;
78    }
79
80    private void buildAnnotationMaps() {
81        AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
82        if (annotationDirectory == null) {
83            methodAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
84            fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(0);
85            parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(0);
86            return;
87        }
88
89        methodAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getMethodAnnotationCount());
90        annotationDirectory.iterateMethodAnnotations(new AnnotationDirectoryItem.MethodAnnotationIteratorDelegate() {
91            public void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations) {
92                methodAnnotationsMap.put(method.getIndex(), methodAnnotations);
93            }
94        });
95
96        fieldAnnotationsMap = new SparseArray<AnnotationSetItem>(annotationDirectory.getFieldAnnotationCount());
97        annotationDirectory.iterateFieldAnnotations(new AnnotationDirectoryItem.FieldAnnotationIteratorDelegate() {
98            public void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations) {
99                fieldAnnotationsMap.put(field.getIndex(), fieldAnnotations);
100            }
101        });
102
103        parameterAnnotationsMap = new SparseArray<AnnotationSetRefList>(
104                annotationDirectory.getParameterAnnotationCount());
105        annotationDirectory.iterateParameterAnnotations(
106          new AnnotationDirectoryItem.ParameterAnnotationIteratorDelegate() {
107            public void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations) {
108                parameterAnnotationsMap.put(method.getIndex(), parameterAnnotations);
109            }
110        });
111    }
112
113    private void findFieldsSetInStaticConstructor() {
114        fieldsSetInStaticConstructor = new SparseArray<FieldIdItem>();
115
116        if (classDataItem == null) {
117            return;
118        }
119
120        for (ClassDataItem.EncodedMethod directMethod: classDataItem.getDirectMethods()) {
121            if (directMethod.method.getMethodName().getStringValue().equals("<clinit>")) {
122                final byte[] encodedInstructions = directMethod.codeItem.getEncodedInstructions();
123
124                InstructionIterator.IterateInstructions(encodedInstructions,
125                        new InstructionIterator.ProcessRawInstructionDelegate() {
126                            public void ProcessNormalInstruction(Opcode opcode, int index) {
127                            }
128
129                            public void ProcessReferenceInstruction(Opcode opcode, int index) {
130                                switch (opcode) {
131                                    case SPUT:
132                                    case SPUT_BOOLEAN:
133                                    case SPUT_BYTE:
134                                    case SPUT_CHAR:
135                                    case SPUT_OBJECT:
136                                    case SPUT_SHORT:
137                                    case SPUT_WIDE:
138                                        Instruction21c ins = (Instruction21c)Format.Format21c.Factory.makeInstruction(
139                                                classDefItem.getDexFile(), opcode, encodedInstructions, index);
140                                        FieldIdItem fieldIdItem = (FieldIdItem)ins.getReferencedItem();
141                                        fieldsSetInStaticConstructor.put(fieldIdItem.getIndex(), fieldIdItem);
142                                }
143                            }
144
145                            public void ProcessPackedSwitchInstruction(int index, int targetCount, int instructionLength) {
146                            }
147
148                            public void ProcessSparseSwitchInstruction(int index, int targetCount, int instructionLength) {
149                            }
150
151                            public void ProcessFillArrayDataInstruction(int index, int elementWidth, int elementCount, int instructionLength) {
152                            }
153                        });
154
155            }
156        }
157    }
158
159    private List<String> getAccessFlags() {
160        List<String> accessFlags = new ArrayList<String>();
161
162        for (AccessFlags accessFlag: AccessFlags.getAccessFlagsForClass(classDefItem.getAccessFlags())) {
163            accessFlags.add(accessFlag.toString());
164        }
165
166        return accessFlags;
167    }
168
169
170    private String getSuperType() {
171        TypeIdItem superClass = classDefItem.getSuperclass();
172        if (superClass != null) {
173            return superClass.getTypeDescriptor();
174        }
175        return null;
176    }
177
178    private String getSourceFile() {
179        StringIdItem sourceFile = classDefItem.getSourceFile();
180
181        if (sourceFile == null) {
182            return null;
183        }
184        return classDefItem.getSourceFile().getStringValue();
185    }
186
187    private List<String> getInterfaces() {
188        List<String> interfaces = new ArrayList<String>();
189
190        TypeListItem interfaceList = classDefItem.getInterfaces();
191
192        if (interfaceList != null) {
193            for (TypeIdItem typeIdItem: interfaceList.getTypes()) {
194                interfaces.add(typeIdItem.getTypeDescriptor());
195            }
196        }
197
198        return interfaces;
199    }
200
201    private List<StringTemplate> getAnnotations() {
202        AnnotationDirectoryItem annotationDirectory = classDefItem.getAnnotations();
203        if (annotationDirectory == null) {
204            return null;
205        }
206
207        AnnotationSetItem annotationSet = annotationDirectory.getClassAnnotations();
208        if (annotationSet == null) {
209            return null;
210        }
211
212        List<StringTemplate> annotations = new ArrayList<StringTemplate>();
213
214        for (AnnotationItem annotationItem: annotationSet.getAnnotations()) {
215            annotations.add(AnnotationAdaptor.makeTemplate(stg, annotationItem));
216        }
217        return annotations;
218    }
219
220    private List<StringTemplate> getStaticFields() {
221        List<StringTemplate> staticFields = new ArrayList<StringTemplate>();
222
223        if (classDataItem != null) {
224            EncodedArrayItem encodedStaticInitializers = classDefItem.getStaticFieldInitializers();
225
226            EncodedValue[] staticInitializers;
227            if (encodedStaticInitializers != null) {
228                staticInitializers = encodedStaticInitializers.getEncodedArray().values;
229            } else {
230                staticInitializers = new EncodedValue[0];
231            }
232
233            int i=0;
234            for (ClassDataItem.EncodedField field: classDataItem.getStaticFields()) {
235                EncodedValue encodedValue = null;
236                if (i < staticInitializers.length) {
237                    encodedValue = staticInitializers[i];
238                }
239                AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
240
241                boolean setInStaticConstructor =
242                        fieldsSetInStaticConstructor.get(field.field.getIndex()) != null;
243
244                staticFields.add(FieldDefinition.createTemplate(stg, field, encodedValue, annotationSet,
245                        setInStaticConstructor));
246                i++;
247            }
248        }
249        return staticFields;
250    }
251
252    private List<StringTemplate> getInstanceFields() {
253        List<StringTemplate> instanceFields = new ArrayList<StringTemplate>();
254
255        if (classDataItem != null) {
256            for (ClassDataItem.EncodedField field: classDataItem.getInstanceFields()) {
257                AnnotationSetItem annotationSet = fieldAnnotationsMap.get(field.field.getIndex());
258                instanceFields.add(FieldDefinition.createTemplate(stg, field, annotationSet));
259            }
260        }
261
262        return instanceFields;
263    }
264
265    private List<StringTemplate> getDirectMethods() {
266        List<StringTemplate> directMethods = new ArrayList<StringTemplate>();
267
268        if (classDataItem != null) {
269            for (ClassDataItem.EncodedMethod method: classDataItem.getDirectMethods()) {
270                AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
271                AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
272                directMethods.add(MethodDefinition.makeTemplate(stg, method, annotationSet, parameterAnnotationList));
273            }
274        }
275
276        return directMethods;
277    }
278
279    private List<StringTemplate> getVirtualMethods() {
280        List<StringTemplate> virtualMethods = new ArrayList<StringTemplate>();
281
282        if (classDataItem != null) {
283            for (ClassDataItem.EncodedMethod method: classDataItem.getVirtualMethods()) {
284                AnnotationSetItem annotationSet = methodAnnotationsMap.get(method.method.getIndex());
285                AnnotationSetRefList parameterAnnotationList = parameterAnnotationsMap.get(method.method.getIndex());
286                virtualMethods.add(MethodDefinition.makeTemplate(stg, method, annotationSet, parameterAnnotationList));
287            }
288        }
289
290        return virtualMethods;
291    }
292}
293