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