ClassDataItem.java revision ec857fcecd0e0d03de6a6bf63625867d4ecaec1c
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.ArrayList;
34import java.util.Collections;
35import java.util.List;
36
37public class ClassDataItem extends OffsettedItem<ClassDataItem> {
38    private final ArrayList<EncodedField> staticFieldList = new ArrayList<EncodedField>();
39    private final ArrayList<EncodedField> instanceFieldList = new ArrayList<EncodedField>();
40    private final ArrayList<EncodedMethod> directMethodList = new ArrayList<EncodedMethod>();
41    private final ArrayList<EncodedMethod> virtualMethodList = new ArrayList<EncodedMethod>();
42
43    private final ListSizeField staticFieldsCountField;
44    private final ListSizeField instanceFieldsCountField;
45    private final ListSizeField directMethodsCountField;
46    private final ListSizeField virtualMethodsCountField;
47    private final EncodedMemberList<EncodedField> staticFieldsListField;
48    private final EncodedMemberList<EncodedField> instanceFieldsListField;
49    private final EncodedMemberList<EncodedMethod> directMethodsListField;
50    private final EncodedMemberList<EncodedMethod> virtualMethodsListField;
51
52    private ClassDefItem parent = null;
53
54    public ClassDataItem(final DexFile dexFile, int offset) {
55        super(offset);
56
57        fields = new Field[] {
58                staticFieldsCountField = new ListSizeField(staticFieldList,
59                        new Leb128Field("static_fields_size")),
60                instanceFieldsCountField = new ListSizeField(instanceFieldList,
61                        new Leb128Field("instance_fields_size")),
62                directMethodsCountField = new ListSizeField(directMethodList,
63                        new Leb128Field("direct_methods_size")),
64                virtualMethodsCountField = new ListSizeField(virtualMethodList,
65                        new Leb128Field("virtual_methods_size")),
66                staticFieldsListField = new EncodedMemberList<EncodedField>(staticFieldList, "static_fields") {
67                    protected EncodedField make(EncodedField previousField) {
68                        return new EncodedField(dexFile, previousField);
69                    }
70                },
71                instanceFieldsListField = new EncodedMemberList<EncodedField>(instanceFieldList, "instance_fields") {
72                    protected EncodedField make(EncodedField previousField) {
73                        return new EncodedField(dexFile, previousField);
74                    }
75                },
76                directMethodsListField = new EncodedMemberList<EncodedMethod>(directMethodList, "direct_methods") {
77                    protected EncodedMethod make(EncodedMethod previousMethod) {
78                        return new EncodedMethod(dexFile, previousMethod);
79                    }
80                },
81                virtualMethodsListField = new EncodedMemberList<EncodedMethod>(virtualMethodList, "virtual_methods") {
82                    protected EncodedMethod make(EncodedMethod previousMethod) {
83                        return new EncodedMethod(dexFile, previousMethod);
84                    }
85                }
86        };
87    }
88
89    public void addMethod(EncodedMethod encodedMethod) {
90        if (encodedMethod.isDirect()) {
91            directMethodList.add(encodedMethod);
92        } else {
93            virtualMethodList.add(encodedMethod);
94        }
95    }
96
97    public int addField(EncodedField encodedField) {
98        if (encodedField.isStatic()) {
99            int index = Collections.binarySearch(staticFieldList, encodedField);
100            if (index >= 0) {
101                throw new RuntimeException("A static field of that name and type is already present");
102            }
103            index = (index + 1) * -1;
104            staticFieldList.add(index, encodedField);
105            return index;
106        } else {
107            int index = Collections.binarySearch(instanceFieldList, encodedField);
108            if (index >= 0) {
109                throw new RuntimeException("An instance field of that name and type is already present");
110            }
111            index = (index + 1) * -1;
112            instanceFieldList.add(index, encodedField);
113            return index;
114        }
115    }
116
117    public List<EncodedField> getStaticFields() {
118        return Collections.unmodifiableList(staticFieldList);
119    }
120
121    public List<EncodedField> getInstanceFields() {
122        return Collections.unmodifiableList(instanceFieldList);
123    }
124
125    public List<EncodedMethod> getDirectMethods() {
126        return Collections.unmodifiableList(directMethodList);
127    }
128
129    public List<EncodedMethod> getVirtualMethods() {
130        return Collections.unmodifiableList(virtualMethodList);
131    }
132
133    private static abstract class EncodedMember<T extends EncodedMember<T>> extends CompositeField<T> implements Field<T>, Comparable<T>
134    {
135        public EncodedMember(String fieldName) {
136            super(fieldName);
137        }
138
139        protected abstract void setPreviousMember(T previousMember);
140    }
141
142    private static abstract class EncodedMemberList<T extends EncodedMember<T>>  implements Field<EncodedMemberList<T>> {
143        private final ArrayList<T> list;
144        private final String fieldName;
145
146        public EncodedMemberList(ArrayList<T> list, String fieldName) {
147            this.list = list;
148            this.fieldName = fieldName;
149        }
150
151        public void writeTo(AnnotatedOutput out) {
152            out.annotate(0, fieldName + ":");
153            int i=0;
154            for (T field: list) {
155                out.annotate(0, "[0x" + Integer.toHexString(i) + "]");
156                field.writeTo(out);
157                i++;
158            }
159        }
160
161        protected abstract T make(T previousField);
162
163        public void readFrom(Input in) {
164            for (int i = 0; i < list.size(); i++) {
165                T previousField = null;
166                if (i > 0) {
167                    previousField = list.get(i-1);
168                }
169                T field = make(previousField);
170                list.set(i, field);
171                field.readFrom(in);
172            }
173        }
174
175        public int place(int offset) {
176            Collections.sort(list);
177
178            T previousMember = null;
179            for (T encodedMember: list) {
180                encodedMember.setPreviousMember(previousMember);
181                offset = encodedMember.place(offset);
182                previousMember = encodedMember;
183            }
184            return offset;
185        }
186
187        public void copyTo(DexFile dexFile, EncodedMemberList<T> copy) {
188            copy.list.clear();
189            copy.list.ensureCapacity(list.size());
190            for (int i = 0; i < list.size(); i++) {
191                T previousField = null;
192                if (i > 0) {
193                    previousField = copy.list.get(i-1);
194                }
195                T fieldCopy = copy.make(previousField);
196                list.get(i).copyTo(dexFile, fieldCopy);
197                copy.list.add(fieldCopy);
198            }
199        }
200
201        public int hashCode() {
202            int h = 1;
203            for (int i = 0; i < list.size(); i++) {
204                h = h * 31 + list.get(i).hashCode();
205            }
206            return h;
207        }
208
209        public boolean equals(Object o) {
210            if (!(o instanceof EncodedMemberList)) {
211                return false;
212            }
213
214            EncodedMemberList<T> other = (EncodedMemberList<T>)o;
215            if (list.size() != other.list.size()) {
216                return false;
217            }
218
219            for (int i = 0; i < list.size(); i++) {
220                if (!list.get(i).equals(other.list.get(i))) {
221                    return false;
222                }
223            }
224            return true;
225        }
226    }
227
228    public static class EncodedField extends EncodedMember<EncodedField> {
229        private final IndexedItemReference<FieldIdItem> fieldReferenceField;
230        private final Leb128DeltaField fieldIndexField;
231        private final Leb128Field accessFlagsField;
232
233        public EncodedField(DexFile dexFile, final EncodedField previousField) {
234            super("encoded_field");
235            Leb128DeltaField previousIndexField = null;
236            if (previousField != null) {
237                previousIndexField = previousField.fieldIndexField;
238            }
239
240
241            fields = new Field[] {
242                    fieldReferenceField = new IndexedItemReference<FieldIdItem>(dexFile.FieldIdsSection,
243                            fieldIndexField = new Leb128DeltaField(previousIndexField, null), "field_idx_diff"),
244                    accessFlagsField = new Leb128Field("access_flags")
245            };
246        }
247
248        public EncodedField(DexFile dexFile, FieldIdItem field, int accessFlags) {
249            super("encoded_field");
250            fields = new Field[] {
251                    this.fieldReferenceField = new IndexedItemReference<FieldIdItem>(dexFile, field,
252                            fieldIndexField = new Leb128DeltaField(null), "field_idx_diff"),
253                    this.accessFlagsField = new Leb128Field(accessFlags, "access_flags")
254            };
255        }
256
257        protected void setPreviousMember(EncodedField previousField) {
258            if (previousField != null) {
259                fieldIndexField.setPreviousField(previousField.fieldIndexField);
260            } else {
261                fieldIndexField.setPreviousField(null);
262            }
263        }
264
265        public int compareTo(EncodedField other)
266        {
267            return fieldReferenceField.getReference().compareTo(other.fieldReferenceField.getReference());
268        }
269
270        public boolean isStatic() {
271            return (accessFlagsField.getCachedValue() & AccessFlags.STATIC.getValue()) != 0;
272        }
273
274        public FieldIdItem getField() {
275            return fieldReferenceField.getReference();
276        }
277
278        public int getAccessFlags() {
279            return accessFlagsField.getCachedValue();
280        }
281    }
282
283    public static class EncodedMethod extends EncodedMember<EncodedMethod> {
284        private final IndexedItemReference<MethodIdItem> methodReferenceField;
285        private final Leb128DeltaField methodIndexField;
286        private final Leb128Field accessFlagsField;
287        private final OffsettedItemReference<CodeItem> codeItemReferenceField;
288
289        public EncodedMethod(DexFile dexFile, final EncodedMethod previousMethod) {
290            super("encedod_method");
291            Leb128DeltaField previousIndexField = null;
292            if (previousMethod != null) {
293                previousIndexField = previousMethod.methodIndexField;
294            }
295
296            fields = new Field[] {
297                    methodReferenceField = new IndexedItemReference<MethodIdItem>(dexFile.MethodIdsSection,
298                            methodIndexField = new Leb128DeltaField(previousIndexField, null), "method_idx_diff"),
299                    accessFlagsField = new Leb128Field("access_flags"),
300                    codeItemReferenceField = new OffsettedItemReference<CodeItem>(dexFile.CodeItemsSection,
301                            new Leb128Field(null), "code_off")
302            };
303        }
304
305        public EncodedMethod(DexFile dexFile, MethodIdItem methodIdItem, int accessFlags, CodeItem codeItem) {
306            super("encoded_method");
307            fields = new Field[] {
308                    this.methodReferenceField = new IndexedItemReference<MethodIdItem>(dexFile, methodIdItem,
309                            methodIndexField = new Leb128DeltaField(null), "method_idx_diff"),
310                    this.accessFlagsField = new Leb128Field(accessFlags, "access_flags"),
311                    this.codeItemReferenceField = new OffsettedItemReference<CodeItem>(dexFile, codeItem,
312                            new Leb128Field(null), "code_off")
313            };
314
315            if (codeItem != null) {
316                codeItem.setParent(methodIdItem);
317            }
318        }
319
320        protected void setPreviousMember(EncodedMethod previousMethod) {
321            if (previousMethod != null) {
322                methodIndexField.setPreviousField(previousMethod.methodIndexField);
323            } else {
324                methodIndexField.setPreviousField(null);
325            }
326        }
327
328        public int compareTo(EncodedMethod other) {
329            return methodReferenceField.getReference().compareTo(other.methodReferenceField.getReference());
330        }
331
332        public boolean isDirect() {
333            return ((accessFlagsField.getCachedValue() & (AccessFlags.STATIC.getValue() | AccessFlags.PRIVATE.getValue() |
334                    AccessFlags.CONSTRUCTOR.getValue())) != 0);
335        }
336
337        public void readFrom(Input in) {
338            super.readFrom(in);
339            CodeItem codeItem = codeItemReferenceField.getReference();
340            if (codeItem != null) {
341                codeItem.setParent(methodReferenceField.getReference());
342            }
343        }
344
345        public int getAccessFlags() {
346            return accessFlagsField.getCachedValue();
347        }
348
349        public MethodIdItem getMethod() {
350            return methodReferenceField.getReference();
351        }
352
353        public CodeItem getCodeItem() {
354            return codeItemReferenceField.getReference();
355        }
356    }
357
358
359    /**
360     * An Leb128 integer that encodes its value as the difference between
361     * itself and the previous Leb128DeltaField in the list. The first
362     * item encodes the value as per normal
363     */
364    protected static class Leb128DeltaField extends Leb128Field {
365        private Leb128DeltaField previousField = null;
366
367        public Leb128DeltaField(String fieldName) {
368            super(fieldName);
369        }
370
371        public void readFrom(Input in) {
372            super.readFrom(in);
373            value += getPreviousValue();
374        }
375
376        public int place(int offset) {
377            return offset + Leb128Utils.unsignedLeb128Size(value - getPreviousValue());
378        }
379
380        private int getPreviousValue() {
381            if (previousField == null) {
382                return 0;
383            }
384            return previousField.value;
385        }
386
387        public void writeValue(Output out) {
388            out.writeUnsignedLeb128(value - getPreviousValue());
389        }
390
391        public Leb128DeltaField(Leb128DeltaField previousField, String fieldName) {
392            super(fieldName);
393            this.previousField = previousField;
394        }
395
396        public void setPreviousField(Leb128DeltaField previousField) {
397            this.previousField = previousField;
398        }
399    }
400
401    protected int getAlignment() {
402        return 1;
403    }
404
405    public ItemType getItemType() {
406        return ItemType.TYPE_CLASS_DATA_ITEM;
407    }
408
409    public String getConciseIdentity() {
410        return "class_data_item @0x" + Integer.toHexString(getOffset());
411    }
412
413    protected void setParent(ClassDefItem classDefItem) {
414        this.parent = classDefItem;
415    }
416
417    public int compareTo(ClassDataItem other) {
418        if (parent == null) {
419            if (other.parent == null) {
420                return 0;
421            }
422            return -1;
423        }
424        if (other.parent == null) {
425            return 1;
426        }
427        return parent.compareTo(other.parent);
428    }
429}
430