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