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