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