DexBackedClassDef.java revision db389aa3a1d898d3a452f3f0b2220b334b23cb4c
1/*
2 * Copyright 2012, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.dexbacked;
33
34import com.google.common.collect.ImmutableList;
35import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
36import org.jf.dexlib2.dexbacked.util.FixedSizeList;
37import org.jf.dexlib2.dexbacked.util.StaticInitialValueIterator;
38import org.jf.dexlib2.dexbacked.util.VariableSizeListWithContext;
39import org.jf.dexlib2.iface.ClassDef;
40
41import javax.annotation.Nonnull;
42import javax.annotation.Nullable;
43import java.util.List;
44
45public class DexBackedClassDef implements ClassDef {
46    @Nonnull public final DexBuffer dexBuf;
47    private final int classDefOffset;
48
49    private int classDataOffset = -1;
50
51    @Nullable private AnnotationsDirectory annotationsDirectory;
52
53    //class_def_item offsets
54    private static final int CLASS_NAME_OFFSET = 0;
55    private static final int ACCESS_FLAGS_OFFSET = 4;
56    private static final int SUPERCLASS_OFFSET = 8;
57    private static final int INTERFACES_OFFSET = 12;
58    private static final int SOURCE_FILE_OFFSET = 16;
59    private static final int ANNOTATIONS_OFFSET = 20;
60    private static final int CLASS_DATA_OFFSET = 24;
61    private static final int STATIC_INITIAL_VALUES_OFFSET = 28;
62
63    public DexBackedClassDef(@Nonnull DexBuffer dexBuf,
64                             int classDefOffset) {
65        this.dexBuf = dexBuf;
66        this.classDefOffset = classDefOffset;
67    }
68
69    @Nonnull
70    @Override
71    public String getType() {
72        return dexBuf.getType(dexBuf.readSmallUint(classDefOffset + CLASS_NAME_OFFSET));
73    }
74
75    @Nullable
76    @Override
77    public String getSuperclass() {
78        return dexBuf.getOptionalType(dexBuf.readOptionalUint(classDefOffset + SUPERCLASS_OFFSET));
79    }
80
81    @Override
82    public int getAccessFlags() {
83        return dexBuf.readSmallUint(classDefOffset + ACCESS_FLAGS_OFFSET);
84    }
85
86    @Nullable
87    @Override
88    public String getSourceFile() {
89        return dexBuf.getOptionalString(dexBuf.readOptionalUint(classDefOffset + SOURCE_FILE_OFFSET));
90    }
91
92    @Nonnull
93    @Override
94    public List<String> getInterfaces() {
95        final int interfacesOffset = dexBuf.readSmallUint(classDefOffset + INTERFACES_OFFSET);
96        if (interfacesOffset > 0) {
97            final int size = dexBuf.readSmallUint(interfacesOffset);
98            return new FixedSizeList<String>() {
99                @Nonnull
100                @Override
101                public String readItem(int index) {
102                    return dexBuf.getType(dexBuf.readUshort(interfacesOffset + 4 + (2*index)));
103                }
104
105                @Override public int size() { return size; }
106            };
107        }
108        return ImmutableList.of();
109    }
110
111    @Nonnull
112    @Override
113    public List<? extends DexBackedAnnotation> getAnnotations() {
114        return getAnnotationsDirectory().getClassAnnotations();
115    }
116
117    @Nonnull
118    @Override
119    public List<? extends DexBackedField> getFields() {
120        int classDataOffset = getClassDataOffset();
121        if (getClassDataOffset() != 0) {
122            DexReader reader = dexBuf.readerAt(classDataOffset);
123            final int staticFieldCount = reader.readSmallUleb128();
124            int instanceFieldCount = reader.readSmallUleb128();
125            final int fieldCount = staticFieldCount + instanceFieldCount;
126            if (fieldCount > 0) {
127                reader.skipUleb128(); //direct_methods_size
128                reader.skipUleb128(); //virtual_methods_size
129
130                final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
131                final int staticInitialValuesOffset =
132                        dexBuf.readSmallUint(classDefOffset + STATIC_INITIAL_VALUES_OFFSET);
133                final int fieldsStartOffset = reader.getOffset();
134
135                return new VariableSizeListWithContext<DexBackedField>() {
136                    @Nonnull
137                    @Override
138                    public VariableSizeListIterator listIterator() {
139                        return new VariableSizeListIterator(dexBuf, fieldsStartOffset) {
140                            private int previousFieldIndex = 0;
141                            @Nonnull private final AnnotationsDirectory.AnnotationIterator annotationIterator =
142                                    annotationsDirectory.getFieldAnnotationIterator();
143                            @Nonnull private final StaticInitialValueIterator staticInitialValueIterator =
144                                    StaticInitialValueIterator.newOrEmpty(dexBuf, staticInitialValuesOffset);
145
146                            @Nonnull
147                            @Override
148                            protected DexBackedField readItem(@Nonnull DexReader reader, int index) {
149                                if (index == staticFieldCount) {
150                                    // We reached the end of the static field, restart the numbering for
151                                    // instance fields
152                                    previousFieldIndex = 0;
153                                    annotationIterator.reset();
154                                }
155                                DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this,
156                                        previousFieldIndex, staticInitialValueIterator, annotationIterator);
157                                previousFieldIndex = item.fieldIndex;
158                                return item;
159                            }
160
161                            @Override
162                            protected void skipItem(@Nonnull DexReader reader, int index) {
163                                if (index == staticFieldCount) {
164                                    // We reached the end of the static field, restart the numbering for
165                                    // instance fields
166                                    previousFieldIndex = 0;
167                                    annotationIterator.reset();
168                                }
169                                previousFieldIndex = DexBackedField.skipEncodedField(reader, previousFieldIndex);
170                                staticInitialValueIterator.skipNext();
171                            }
172                        };
173                    }
174
175                    @Override public int size() { return fieldCount; }
176                };
177            }
178        }
179        return ImmutableList.of();
180    }
181
182    @Nonnull
183    @Override
184    public List<? extends DexBackedMethod> getMethods() {
185        int classDataOffset = getClassDataOffset();
186        if (classDataOffset > 0) {
187            DexReader reader = dexBuf.readerAt(classDataOffset);
188            int staticFieldCount = reader.readSmallUleb128();
189            int instanceFieldCount = reader.readSmallUleb128();
190            final int directMethodCount = reader.readSmallUleb128();
191            int virtualMethodCount = reader.readSmallUleb128();
192            final int methodCount = directMethodCount + virtualMethodCount;
193            if (methodCount > 0) {
194                DexBackedField.skipAllFields(reader, staticFieldCount + instanceFieldCount);
195
196                final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
197                final int methodsStartOffset = reader.getOffset();
198
199                return new VariableSizeListWithContext<DexBackedMethod>() {
200                    @Nonnull
201                    @Override
202                    public VariableSizeListIterator listIterator() {
203                        return new VariableSizeListIterator(dexBuf, methodsStartOffset) {
204                            private int previousMethodIndex = 0;
205                            @Nonnull private final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
206                                    annotationsDirectory.getMethodAnnotationIterator();
207                            @Nonnull private final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
208                                    annotationsDirectory.getParameterAnnotationIterator();
209
210                            @Nonnull
211                            @Override
212                            protected DexBackedMethod readItem(@Nonnull DexReader reader, int index) {
213                                if (index == directMethodCount) {
214                                    // We reached the end of the direct methods, restart the numbering for
215                                    // virtual methods
216                                    previousMethodIndex = 0;
217                                    methodAnnotationIterator.reset();
218                                    parameterAnnotationIterator.reset();
219                                }
220                                DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
221                                        previousMethodIndex, methodAnnotationIterator, parameterAnnotationIterator);
222                                previousMethodIndex = item.methodIndex;
223                                return item;
224                            }
225
226                            @Override
227                            protected void skipItem(@Nonnull DexReader reader, int index) {
228                                if (index == directMethodCount) {
229                                    // We reached the end of the direct methods, restart the numbering for
230                                    // virtual methods
231                                    previousMethodIndex = 0;
232                                    methodAnnotationIterator.reset();
233                                    parameterAnnotationIterator.reset();
234                                }
235                                previousMethodIndex = DexBackedMethod.skipEncodedMethod(reader, previousMethodIndex);
236                            }
237                        };
238                    }
239
240                    @Override public int size() { return methodCount; }
241                };
242            }
243        }
244        return ImmutableList.of();
245    }
246
247    private int getClassDataOffset() {
248        if (classDataOffset == -1) {
249            classDataOffset = dexBuf.readSmallUint(classDefOffset + CLASS_DATA_OFFSET);
250        }
251        return classDataOffset;
252    }
253
254    private AnnotationsDirectory getAnnotationsDirectory() {
255        if (annotationsDirectory == null) {
256            int annotationsDirectoryOffset = dexBuf.readSmallUint(classDefOffset + ANNOTATIONS_OFFSET);
257            annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexBuf, annotationsDirectoryOffset);
258        }
259        return annotationsDirectory;
260    }
261}
262