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.dexgen.dex.file;
18
19import com.android.dexgen.rop.annotation.Annotations;
20import com.android.dexgen.rop.annotation.AnnotationsList;
21import com.android.dexgen.rop.code.AccessFlags;
22import com.android.dexgen.rop.cst.Constant;
23import com.android.dexgen.rop.cst.CstArray;
24import com.android.dexgen.rop.cst.CstFieldRef;
25import com.android.dexgen.rop.cst.CstMethodRef;
26import com.android.dexgen.rop.cst.CstType;
27import com.android.dexgen.rop.cst.CstUtf8;
28import com.android.dexgen.rop.type.StdTypeList;
29import com.android.dexgen.rop.type.TypeList;
30import com.android.dexgen.util.AnnotatedOutput;
31import com.android.dexgen.util.Hex;
32import com.android.dexgen.util.Writers;
33
34import java.io.PrintWriter;
35import java.io.Writer;
36import java.util.ArrayList;
37import 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 */
44public 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     */
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
118    public ItemType itemType() {
119        return ItemType.TYPE_CLASS_DEF_ITEM;
120    }
121
122    /** {@inheritDoc} */
123    @Override
124    public int writeSize() {
125        return WRITE_SIZE;
126    }
127
128    /** {@inheritDoc} */
129    @Override
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
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     */
228    public CstType getThisClass() {
229        return thisClass;
230    }
231
232    /**
233     * Gets the access flags.
234     *
235     * @return the access flags
236     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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