1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dexgen.dex.file;
18 
19 import com.android.dexgen.rop.annotation.Annotations;
20 import com.android.dexgen.rop.annotation.AnnotationsList;
21 import com.android.dexgen.rop.code.AccessFlags;
22 import com.android.dexgen.rop.cst.Constant;
23 import com.android.dexgen.rop.cst.CstArray;
24 import com.android.dexgen.rop.cst.CstFieldRef;
25 import com.android.dexgen.rop.cst.CstMethodRef;
26 import com.android.dexgen.rop.cst.CstType;
27 import com.android.dexgen.rop.cst.CstUtf8;
28 import com.android.dexgen.rop.type.StdTypeList;
29 import com.android.dexgen.rop.type.TypeList;
30 import com.android.dexgen.util.AnnotatedOutput;
31 import com.android.dexgen.util.Hex;
32 import com.android.dexgen.util.Writers;
33 
34 import java.io.PrintWriter;
35 import java.io.Writer;
36 import java.util.ArrayList;
37 import java.util.TreeSet;
38 
39 /**
40  * Representation of a Dalvik class, which is basically a set of
41  * members (fields or methods) along with a few more pieces of
42  * information.
43  */
44 public final class ClassDefItem extends IndexedItem {
45     /** size of instances when written out to a file, in bytes */
46     public static final int WRITE_SIZE = 32;
47 
48     /** {@code non-null;} type constant for this class */
49     private final CstType thisClass;
50 
51     /** access flags */
52     private final int accessFlags;
53 
54     /**
55      * {@code null-ok;} superclass or {@code null} if this class is a/the
56      * root class
57      */
58     private final CstType superclass;
59 
60     /** {@code null-ok;} list of implemented interfaces */
61     private TypeListItem interfaces;
62 
63     /** {@code null-ok;} source file name or {@code null} if unknown */
64     private final CstUtf8 sourceFile;
65 
66     /** {@code non-null;} associated class data object */
67     private final ClassDataItem classData;
68 
69     /**
70      * {@code null-ok;} item wrapper for the static values, initialized
71      * in {@link #addContents}
72      */
73     private EncodedArrayItem staticValuesItem;
74 
75     /** {@code non-null;} annotations directory */
76     private AnnotationsDirectoryItem annotationsDirectory;
77 
78     /**
79      * Constructs an instance. Its sets of members and annotations are
80      * initially empty.
81      *
82      * @param thisClass {@code non-null;} type constant for this class
83      * @param accessFlags access flags
84      * @param superclass {@code null-ok;} superclass or {@code null} if
85      * this class is a/the root class
86      * @param interfaces {@code non-null;} list of implemented interfaces
87      * @param sourceFile {@code null-ok;} source file name or
88      * {@code null} if unknown
89      */
ClassDefItem(CstType thisClass, int accessFlags, CstType superclass, TypeList interfaces, CstUtf8 sourceFile)90     public ClassDefItem(CstType thisClass, int accessFlags,
91             CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
92         if (thisClass == null) {
93             throw new NullPointerException("thisClass == null");
94         }
95 
96         /*
97          * TODO: Maybe check accessFlags and superclass, at
98          * least for easily-checked stuff?
99          */
100 
101         if (interfaces == null) {
102             throw new NullPointerException("interfaces == null");
103         }
104 
105         this.thisClass = thisClass;
106         this.accessFlags = accessFlags;
107         this.superclass = superclass;
108         this.interfaces =
109             (interfaces.size() == 0) ? null :  new TypeListItem(interfaces);
110         this.sourceFile = sourceFile;
111         this.classData = new ClassDataItem(thisClass);
112         this.staticValuesItem = null;
113         this.annotationsDirectory = new AnnotationsDirectoryItem();
114     }
115 
116     /** {@inheritDoc} */
117     @Override
itemType()118     public ItemType itemType() {
119         return ItemType.TYPE_CLASS_DEF_ITEM;
120     }
121 
122     /** {@inheritDoc} */
123     @Override
writeSize()124     public int writeSize() {
125         return WRITE_SIZE;
126     }
127 
128     /** {@inheritDoc} */
129     @Override
addContents(DexFile file)130     public void addContents(DexFile file) {
131         TypeIdsSection typeIds = file.getTypeIds();
132         MixedItemSection byteData = file.getByteData();
133         MixedItemSection wordData = file.getWordData();
134         MixedItemSection typeLists = file.getTypeLists();
135         StringIdsSection stringIds = file.getStringIds();
136 
137         typeIds.intern(thisClass);
138 
139         if (!classData.isEmpty()) {
140             MixedItemSection classDataSection = file.getClassData();
141             classDataSection.add(classData);
142 
143             CstArray staticValues = classData.getStaticValuesConstant();
144             if (staticValues != null) {
145                 staticValuesItem =
146                     byteData.intern(new EncodedArrayItem(staticValues));
147             }
148         }
149 
150         if (superclass != null) {
151             typeIds.intern(superclass);
152         }
153 
154         if (interfaces != null) {
155             interfaces = typeLists.intern(interfaces);
156         }
157 
158         if (sourceFile != null) {
159             stringIds.intern(sourceFile);
160         }
161 
162         if (! annotationsDirectory.isEmpty()) {
163             if (annotationsDirectory.isInternable()) {
164                 annotationsDirectory = wordData.intern(annotationsDirectory);
165             } else {
166                 wordData.add(annotationsDirectory);
167             }
168         }
169     }
170 
171     /** {@inheritDoc} */
172     @Override
writeTo(DexFile file, AnnotatedOutput out)173     public void writeTo(DexFile file, AnnotatedOutput out) {
174         boolean annotates = out.annotates();
175         TypeIdsSection typeIds = file.getTypeIds();
176         int classIdx = typeIds.indexOf(thisClass);
177         int superIdx = (superclass == null) ? -1 :
178             typeIds.indexOf(superclass);
179         int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
180         int annoOff = annotationsDirectory.isEmpty() ? 0 :
181             annotationsDirectory.getAbsoluteOffset();
182         int sourceFileIdx = (sourceFile == null) ? -1 :
183             file.getStringIds().indexOf(sourceFile);
184         int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
185         int staticValuesOff =
186             OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
187 
188         if (annotates) {
189             out.annotate(0, indexString() + ' ' + thisClass.toHuman());
190             out.annotate(4, "  class_idx:           " + Hex.u4(classIdx));
191             out.annotate(4, "  access_flags:        " +
192                          AccessFlags.classString(accessFlags));
193             out.annotate(4, "  superclass_idx:      " + Hex.u4(superIdx) +
194                          " // " + ((superclass == null) ? "<none>" :
195                           superclass.toHuman()));
196             out.annotate(4, "  interfaces_off:      " + Hex.u4(interOff));
197             if (interOff != 0) {
198                 TypeList list = interfaces.getList();
199                 int sz = list.size();
200                 for (int i = 0; i < sz; i++) {
201                     out.annotate(0, "    " + list.getType(i).toHuman());
202                 }
203             }
204             out.annotate(4, "  source_file_idx:     " + Hex.u4(sourceFileIdx) +
205                          " // " + ((sourceFile == null) ? "<none>" :
206                           sourceFile.toHuman()));
207             out.annotate(4, "  annotations_off:     " + Hex.u4(annoOff));
208             out.annotate(4, "  class_data_off:      " + Hex.u4(dataOff));
209             out.annotate(4, "  static_values_off:   " +
210                     Hex.u4(staticValuesOff));
211         }
212 
213         out.writeInt(classIdx);
214         out.writeInt(accessFlags);
215         out.writeInt(superIdx);
216         out.writeInt(interOff);
217         out.writeInt(sourceFileIdx);
218         out.writeInt(annoOff);
219         out.writeInt(dataOff);
220         out.writeInt(staticValuesOff);
221     }
222 
223     /**
224      * Gets the constant corresponding to this class.
225      *
226      * @return {@code non-null;} the constant
227      */
getThisClass()228     public CstType getThisClass() {
229         return thisClass;
230     }
231 
232     /**
233      * Gets the access flags.
234      *
235      * @return the access flags
236      */
getAccessFlags()237     public int getAccessFlags() {
238         return accessFlags;
239     }
240 
241     /**
242      * Gets the superclass.
243      *
244      * @return {@code null-ok;} the superclass or {@code null} if
245      * this class is a/the root class
246      */
getSuperclass()247     public CstType getSuperclass() {
248         return superclass;
249     }
250 
251     /**
252      * Gets the list of interfaces implemented.
253      *
254      * @return {@code non-null;} the interfaces list
255      */
getInterfaces()256     public TypeList getInterfaces() {
257         if (interfaces == null) {
258             return StdTypeList.EMPTY;
259         }
260 
261         return interfaces.getList();
262     }
263 
264     /**
265      * Gets the source file name.
266      *
267      * @return {@code null-ok;} the source file name or {@code null} if unknown
268      */
getSourceFile()269     public CstUtf8 getSourceFile() {
270         return sourceFile;
271     }
272 
273     /**
274      * Adds a static field.
275      *
276      * @param field {@code non-null;} the field to add
277      * @param value {@code null-ok;} initial value for the field, if any
278      */
addStaticField(EncodedField field, Constant value)279     public void addStaticField(EncodedField field, Constant value) {
280         classData.addStaticField(field, value);
281     }
282 
283     /**
284      * Adds an instance field.
285      *
286      * @param field {@code non-null;} the field to add
287      */
addInstanceField(EncodedField field)288     public void addInstanceField(EncodedField field) {
289         classData.addInstanceField(field);
290     }
291 
292     /**
293      * Adds a direct ({@code static} and/or {@code private}) method.
294      *
295      * @param method {@code non-null;} the method to add
296      */
addDirectMethod(EncodedMethod method)297     public void addDirectMethod(EncodedMethod method) {
298         classData.addDirectMethod(method);
299     }
300 
301     /**
302      * Adds a virtual method.
303      *
304      * @param method {@code non-null;} the method to add
305      */
addVirtualMethod(EncodedMethod method)306     public void addVirtualMethod(EncodedMethod method) {
307         classData.addVirtualMethod(method);
308     }
309 
310     /**
311      * Gets all the methods in this class. The returned list is not linked
312      * in any way to the underlying lists contained in this instance, but
313      * the objects contained in the list are shared.
314      *
315      * @return {@code non-null;} list of all methods
316      */
getMethods()317     public ArrayList<EncodedMethod> getMethods() {
318         return classData.getMethods();
319     }
320 
321     /**
322      * Sets the direct annotations on this class. These are annotations
323      * made on the class, per se, as opposed to on one of its members.
324      * It is only valid to call this method at most once per instance.
325      *
326      * @param annotations {@code non-null;} annotations to set for this class
327      */
setClassAnnotations(Annotations annotations)328     public void setClassAnnotations(Annotations annotations) {
329         annotationsDirectory.setClassAnnotations(annotations);
330     }
331 
332     /**
333      * Adds a field annotations item to this class.
334      *
335      * @param field {@code non-null;} field in question
336      * @param annotations {@code non-null;} associated annotations to add
337      */
addFieldAnnotations(CstFieldRef field, Annotations annotations)338     public void addFieldAnnotations(CstFieldRef field,
339             Annotations annotations) {
340         annotationsDirectory.addFieldAnnotations(field, annotations);
341     }
342 
343     /**
344      * Adds a method annotations item to this class.
345      *
346      * @param method {@code non-null;} method in question
347      * @param annotations {@code non-null;} associated annotations to add
348      */
addMethodAnnotations(CstMethodRef method, Annotations annotations)349     public void addMethodAnnotations(CstMethodRef method,
350             Annotations annotations) {
351         annotationsDirectory.addMethodAnnotations(method, annotations);
352     }
353 
354     /**
355      * Adds a parameter annotations item to this class.
356      *
357      * @param method {@code non-null;} method in question
358      * @param list {@code non-null;} associated list of annotation sets to add
359      */
addParameterAnnotations(CstMethodRef method, AnnotationsList list)360     public void addParameterAnnotations(CstMethodRef method,
361             AnnotationsList list) {
362         annotationsDirectory.addParameterAnnotations(method, list);
363     }
364 
365     /**
366      * Gets the method annotations for a given method, if any. This is
367      * meant for use by debugging / dumping code.
368      *
369      * @param method {@code non-null;} the method
370      * @return {@code null-ok;} the method annotations, if any
371      */
getMethodAnnotations(CstMethodRef method)372     public Annotations getMethodAnnotations(CstMethodRef method) {
373         return annotationsDirectory.getMethodAnnotations(method);
374     }
375 
376     /**
377      * Gets the parameter annotations for a given method, if any. This is
378      * meant for use by debugging / dumping code.
379      *
380      * @param method {@code non-null;} the method
381      * @return {@code null-ok;} the parameter annotations, if any
382      */
getParameterAnnotations(CstMethodRef method)383     public AnnotationsList getParameterAnnotations(CstMethodRef method) {
384         return annotationsDirectory.getParameterAnnotations(method);
385     }
386 
387     /**
388      * Prints out the contents of this instance, in a debugging-friendly
389      * way.
390      *
391      * @param out {@code non-null;} where to output to
392      * @param verbose whether to be verbose with the output
393      */
debugPrint(Writer out, boolean verbose)394     public void debugPrint(Writer out, boolean verbose) {
395         PrintWriter pw = Writers.printWriterFor(out);
396 
397         pw.println(getClass().getName() + " {");
398         pw.println("  accessFlags: " + Hex.u2(accessFlags));
399         pw.println("  superclass: " + superclass);
400         pw.println("  interfaces: " +
401                 ((interfaces == null) ? "<none>" : interfaces));
402         pw.println("  sourceFile: " +
403                 ((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
404 
405         classData.debugPrint(out, verbose);
406         annotationsDirectory.debugPrint(pw);
407 
408         pw.println("}");
409     }
410 }
411