ClassDataItem.java revision 83b80f81d311b233188c281059aad4a9f5e8b4e6
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.dexlib;
30
31import org.jf.dexlib.Util.*;
32
33public class ClassDataItem extends Item<ClassDataItem> {
34    private EncodedField[] staticFields;
35    private EncodedField[] instanceFields;
36    private EncodedMethod[] directMethods;
37    private EncodedMethod[] virtualMethods;
38
39    private ClassDefItem parent = null;
40
41    /**
42     * Creates a new uninitialized <code>ClassDataItem</code>
43     * @param dexFile The <code>DexFile</code> that this item belongs to
44     */
45    public ClassDataItem(final DexFile dexFile) {
46        super(dexFile);
47    }
48
49    /**
50     * Creates a new <code>ClassDataItem</code> with the given values
51     * @param dexFile The <code>DexFile</code> that this item belongs to
52     * @param staticFields The static fields for this class
53     * @param instanceFields The instance fields for this class
54     * @param directMethods The direct methods for this class
55     * @param virtualMethods The virtual methods for this class
56     */
57    private ClassDataItem(DexFile dexFile, EncodedField[] staticFields, EncodedField[] instanceFields,
58                         EncodedMethod[] directMethods, EncodedMethod[] virtualMethods) {
59        super(dexFile);
60        this.staticFields = staticFields==null?new EncodedField[0]:staticFields;
61        this.instanceFields = instanceFields==null?new EncodedField[0]:instanceFields;
62        this.directMethods = directMethods==null?new EncodedMethod[0]:directMethods;
63        this.virtualMethods = virtualMethods==null?new EncodedMethod[0]:virtualMethods;
64    }
65
66    /**
67     * Creates a new <code>ClassDataItem</code> with the given values
68     * @param dexFile The <code>DexFile</code> that this item belongs to
69     * @param staticFields The static fields for this class
70     * @param instanceFields The instance fields for this class
71     * @param directMethods The direct methods for this class
72     * @param virtualMethods The virtual methods for this class
73     * @return a new <code>ClassDataItem</code> with the given values
74     */
75    public static ClassDataItem getInternedClassDataItem(DexFile dexFile, EncodedField[] staticFields,
76                                                         EncodedField[] instanceFields, EncodedMethod[] directMethods,
77                                                         EncodedMethod[] virtualMethods) {
78        ClassDataItem classDataItem = new ClassDataItem(dexFile, staticFields, instanceFields, directMethods,
79                virtualMethods);
80        return dexFile.ClassDataSection.intern(classDataItem);
81    }
82
83    /** {@inheritDoc} */
84    protected void readItem(Input in, ReadContext readContext) {
85        staticFields = new EncodedField[in.readUnsignedLeb128()];
86        instanceFields = new EncodedField[in.readUnsignedLeb128()];
87        directMethods = new EncodedMethod[in.readUnsignedLeb128()];
88        virtualMethods = new EncodedMethod[in.readUnsignedLeb128()];
89
90        EncodedField previousEncodedField = null;
91        for (int i=0; i<staticFields.length; i++) {
92            staticFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
93        }
94
95        previousEncodedField = null;
96        for (int i=0; i<instanceFields.length; i++) {
97            instanceFields[i] = previousEncodedField = new EncodedField(dexFile, in, previousEncodedField);
98        }
99
100        EncodedMethod previousEncodedMethod = null;
101        for (int i=0; i<directMethods.length; i++) {
102            directMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
103                    previousEncodedMethod);
104        }
105
106        previousEncodedMethod = null;
107        for (int i=0; i<virtualMethods.length; i++) {
108            virtualMethods[i] = previousEncodedMethod = new EncodedMethod(dexFile, readContext, in,
109                    previousEncodedMethod);
110        }
111    }
112
113    /** {@inheritDoc} */
114    protected int placeItem(int offset) {
115        offset += Leb128Utils.unsignedLeb128Size(staticFields.length);
116        offset += Leb128Utils.unsignedLeb128Size(instanceFields.length);
117        offset += Leb128Utils.unsignedLeb128Size(directMethods.length);
118        offset += Leb128Utils.unsignedLeb128Size(virtualMethods.length);
119
120        EncodedField previousEncodedField = null;
121        for (EncodedField encodedField: staticFields) {
122            offset += encodedField.place(offset, previousEncodedField);
123            previousEncodedField = encodedField;
124        }
125
126        previousEncodedField = null;
127        for (EncodedField encodedField: instanceFields) {
128            offset += encodedField.place(offset, previousEncodedField);
129            previousEncodedField = encodedField;
130        }
131
132        EncodedMethod previousEncodedMethod = null;
133        for (EncodedMethod encodedMethod: directMethods) {
134            offset += encodedMethod.place(offset, previousEncodedMethod);
135            previousEncodedMethod = encodedMethod;
136        }
137
138        previousEncodedMethod = null;
139        for (EncodedMethod encodedMethod: virtualMethods) {
140            offset += encodedMethod.place(offset, previousEncodedMethod);
141            previousEncodedMethod = encodedMethod;
142        }
143
144        return offset;
145    }
146
147    /** {@inheritDoc} */
148    protected void writeItem(AnnotatedOutput out) {
149        if (out.annotates()) {
150            out.annotate("static_fields_size");
151            out.writeUnsignedLeb128(staticFields.length);
152            out.annotate("instance_fields_size");
153            out.writeUnsignedLeb128(instanceFields.length);
154            out.annotate("direct_methods_size");
155            out.writeUnsignedLeb128(directMethods.length);
156            out.annotate("virtual_methods_size");
157            out.writeUnsignedLeb128(virtualMethods.length);
158
159            EncodedField previousEncodedField = null;
160            for (EncodedField encodedField: staticFields) {
161                encodedField.writeTo(out, previousEncodedField);
162                previousEncodedField = encodedField;
163            }
164
165            previousEncodedField = null;
166            for (EncodedField encodedField: instanceFields) {
167                encodedField.writeTo(out, previousEncodedField);
168                previousEncodedField = encodedField;
169            }
170
171            EncodedMethod previousEncodedMethod = null;
172            for (EncodedMethod encodedMethod: directMethods) {
173                encodedMethod.writeTo(out, previousEncodedMethod);
174                previousEncodedMethod = encodedMethod;
175            }
176
177            previousEncodedMethod = null;
178            for (EncodedMethod encodedMethod: virtualMethods) {
179                encodedMethod.writeTo(out, previousEncodedMethod);
180                previousEncodedMethod = encodedMethod;
181            }
182        } else {
183            out.writeUnsignedLeb128(staticFields.length);
184            out.writeUnsignedLeb128(instanceFields.length);
185            out.writeUnsignedLeb128(directMethods.length);
186            out.writeUnsignedLeb128(virtualMethods.length);
187
188            EncodedField previousEncodedField = null;
189            for (EncodedField encodedField: staticFields) {
190                encodedField.writeTo(out, previousEncodedField);
191                previousEncodedField = encodedField;
192            }
193
194            previousEncodedField = null;
195            for (EncodedField encodedField: instanceFields) {
196                encodedField.writeTo(out, previousEncodedField);
197                previousEncodedField = encodedField;
198            }
199
200            EncodedMethod previousEncodedMethod = null;
201            for (EncodedMethod encodedMethod: directMethods) {
202                encodedMethod.writeTo(out, previousEncodedMethod);
203                previousEncodedMethod = encodedMethod;
204            }
205
206            previousEncodedMethod = null;
207            for (EncodedMethod encodedMethod: virtualMethods) {
208                encodedMethod.writeTo(out, previousEncodedMethod);
209                previousEncodedMethod = encodedMethod;
210            }
211        }
212    }
213
214    /** {@inheritDoc} */
215    public ItemType getItemType() {
216        return ItemType.TYPE_CLASS_DATA_ITEM;
217    }
218
219    /** {@inheritDoc} */
220    public String getConciseIdentity() {
221        return "class_data_item @0x" + Integer.toHexString(getOffset());
222    }
223
224    /** {@inheritDoc} */
225    public int compareTo(ClassDataItem other) {
226        if (parent == null) {
227            if (other.parent == null) {
228                return 0;
229            }
230            return -1;
231        }
232        if (other.parent == null) {
233            return 1;
234        }
235        return parent.compareTo(other.parent);
236    }
237
238    /**
239     * Sets the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with
240     * @param classDefItem the <code>ClassDefItem</code> that this <code>ClassDataItem</code> is associated with
241     */
242    protected void setParent(ClassDefItem classDefItem) {
243        this.parent = classDefItem;
244    }
245
246    /**
247     * @return the static fields for this class
248     */
249    public EncodedField[] getStaticFields() {
250        return staticFields;
251    }
252
253    /**
254     * @return the instance fields for this class
255     */
256    public EncodedField[] getInstanceFields() {
257        return instanceFields;
258    }
259
260    /**
261     * @return the direct methods for this class
262     */
263    public EncodedMethod[] getDirectMethods() {
264        return directMethods;
265    }
266
267    /**
268     * @return the virtual methods for this class
269     */
270    public EncodedMethod[] getVirtualMethods() {
271        return virtualMethods;
272    }
273
274    public static class EncodedField {
275        /**
276         * The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
277         */
278        public final FieldIdItem field;
279
280        /**
281         * The access flags for this field
282         */
283        public final int accessFlags;
284
285        /**
286         * Constructs a new <code>EncodedField</code> with the given values
287         * @param field The <code>FieldIdItem</code> that this <code>EncodedField</code> is associated with
288         * @param accessFlags The access flags for this field
289         */
290        public EncodedField(FieldIdItem field, int accessFlags) {
291            this.field = field;
292            this.accessFlags = accessFlags;
293        }
294
295        /**
296         * This is used internally to construct a new <code>EncodedField</code> while reading in a <code>DexFile</code>
297         * @param dexFile The <code>DexFile</code> that is being read in
298         * @param in the Input object to read the <code>EncodedField</code> from
299         * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
300         * <code>EncodedField</code>.
301         */
302        private EncodedField(DexFile dexFile, Input in, EncodedField previousEncodedField) {
303            int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
304            field = dexFile.FieldIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
305            accessFlags = in.readUnsignedLeb128();
306        }
307
308        /**
309         * Writes the <code>EncodedField</code> to the given <code>AnnotatedOutput</code> object
310         * @param out the <code>AnnotatedOutput</code> object to write to
311         * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
312         * <code>EncodedField</code>.
313         */
314        private void writeTo(AnnotatedOutput out, EncodedField previousEncodedField) {
315            int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
316
317            if (out.annotates()) {
318                out.annotate("field_idx_diff");
319                out.writeUnsignedLeb128(field.getIndex() - previousIndex);
320                out.annotate("access_flags");
321                out.writeUnsignedLeb128(accessFlags);
322            }else {
323                out.writeUnsignedLeb128(field.getIndex() - previousIndex);
324                out.writeUnsignedLeb128(accessFlags);
325            }
326        }
327
328        /**
329         * Calculates the size of this <code>EncodedField</code> and returns the offset
330         * immediately following it
331         * @param offset the offset of this <code>EncodedField</code> in the <code>DexFile</code>
332         * @param previousEncodedField The previous <code>EncodedField</code> in the list containing this
333         * <code>EncodedField</code>.
334         * @return the offset immediately following this <code>EncodedField</code>
335         */
336        private int place(int offset, EncodedField previousEncodedField) {
337            int previousIndex = previousEncodedField==null?0:previousEncodedField.field.getIndex();
338
339            offset += Leb128Utils.unsignedLeb128Size(field.getIndex() - previousIndex);
340            offset += Leb128Utils.unsignedLeb128Size(accessFlags);
341            return  offset;
342        }
343
344        /**
345         * Compares this <code>EncodedField</code> to another, based on the comparison of the associated
346         * <code>FieldIdItem</code>
347         * @param other The <code>EncodedField</code> to compare against
348         * @return a standard integer comparison value indicating the relationship
349         */
350        public int compareTo(EncodedField other)
351        {
352            return field.compareTo(other.field);
353        }
354
355        /**
356         * @return true if this is a static field
357         */
358        public boolean isStatic() {
359            return (accessFlags & AccessFlags.STATIC.getValue()) != 0;
360        }
361    }
362
363    public static class EncodedMethod {
364        /**
365         * The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
366         */
367        public final MethodIdItem method;
368
369        /**
370         * The access flags for this method
371         */
372        public final int accessFlags;
373
374        /**
375         * The <code>CodeItem</code> containing the code for this method, or null if there is no code for this method
376         * (i.e. an abstract method)
377         */
378        public final CodeItem codeItem;
379
380        /**
381         * Constructs a new <code>EncodedMethod</code> with the given values
382         * @param method The <code>MethodIdItem</code> that this <code>EncodedMethod</code> is associated with
383         * @param accessFlags The access flags for this method
384         * @param codeItem The <code>CodeItem</code> containing the code for this method, or null if there is no code
385         * for this method (i.e. an abstract method)
386         */
387        public EncodedMethod(MethodIdItem method, int accessFlags, CodeItem codeItem) {
388            this.method = method;
389            this.accessFlags = accessFlags;
390            this.codeItem = codeItem;
391            if (codeItem != null) {
392                codeItem.setParent(method);
393            }
394        }
395
396        /**
397         * This is used internally to construct a new <code>EncodedMethod</code> while reading in a <code>DexFile</code>
398         * @param dexFile The <code>DexFile</code> that is being read in
399         * @param readContext a <code>ReadContext</code> object to hold information that is only needed while reading
400         * in a file
401         * @param in the Input object to read the <code>EncodedMethod</code> from
402         * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
403         * <code>EncodedMethod</code>.
404         */
405        public EncodedMethod(DexFile dexFile, ReadContext readContext, Input in, EncodedMethod previousEncodedMethod) {
406            int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
407            method = dexFile.MethodIdsSection.getItemByIndex(in.readUnsignedLeb128() + previousIndex);
408            accessFlags = in.readUnsignedLeb128();
409            codeItem = (CodeItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_CODE_ITEM, in.readUnsignedLeb128());
410            if (codeItem != null) {
411                codeItem.setParent(method);
412            }
413        }
414
415        /**
416         * Writes the <code>EncodedMethod</code> to the given <code>AnnotatedOutput</code> object
417         * @param out the <code>AnnotatedOutput</code> object to write to
418         * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
419         * <code>EncodedMethod</code>.
420         */
421        private void writeTo(AnnotatedOutput out, EncodedMethod previousEncodedMethod) {
422            int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
423
424            if (out.annotates()) {
425                out.annotate("method_idx_diff");
426                out.writeUnsignedLeb128(method.getIndex() - previousIndex);
427                out.annotate("access_flags");
428                out.writeUnsignedLeb128(accessFlags);
429                out.annotate("code_off");
430                out.writeUnsignedLeb128(codeItem==null?0:codeItem.getIndex());
431            }else {
432                out.writeUnsignedLeb128(method.getIndex() - previousIndex);
433                out.writeUnsignedLeb128(accessFlags);
434                out.writeUnsignedLeb128(codeItem==null?0:codeItem.getIndex());
435            }
436        }
437
438        /**
439         * Calculates the size of this <code>EncodedMethod</code> and returns the offset
440         * immediately following it
441         * @param offset the offset of this <code>EncodedMethod</code> in the <code>DexFile</code>
442         * @param previousEncodedMethod The previous <code>EncodedMethod</code> in the list containing this
443         * <code>EncodedMethod</code>.
444         * @return the offset immediately following this <code>EncodedField</code>
445         */
446        private int place(int offset, EncodedMethod previousEncodedMethod) {
447            int previousIndex = previousEncodedMethod==null?0:previousEncodedMethod.method.getIndex();
448
449            offset += Leb128Utils.unsignedLeb128Size(method.getIndex() - previousIndex);
450            offset += Leb128Utils.unsignedLeb128Size(accessFlags);
451            offset += codeItem==null?1:Leb128Utils.unsignedLeb128Size(codeItem.getIndex());
452            return  offset;
453        }
454
455        /**
456         * Compares this <code>EncodedMethod</code> to another, based on the comparison of the associated
457         * <code>MethodIdItem</code>
458         * @param other The <code>EncodedMethod</code> to compare against
459         * @return a standard integer comparison value indicating the relationship
460         */
461        public int compareTo(EncodedMethod other) {
462            return method.compareTo(other.method);
463        }
464
465        /**
466         * @return true if this is a direct method
467         */
468        public boolean isDirect() {
469            return ((accessFlags & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
470                    AccessFlags.CONSTRUCTOR.getValue())) != 0);
471        }
472    }
473}
474