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.ImmutableSet;
35import com.google.common.collect.Iterables;
36import org.jf.dexlib2.base.reference.BaseTypeReference;
37import org.jf.dexlib2.dexbacked.raw.ClassDefItem;
38import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
39import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
40import org.jf.dexlib2.dexbacked.util.StaticInitialValueIterator;
41import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator;
42import org.jf.dexlib2.iface.ClassDef;
43import org.jf.dexlib2.iface.reference.FieldReference;
44import org.jf.dexlib2.iface.reference.MethodReference;
45import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
46import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
47
48import javax.annotation.Nonnull;
49import javax.annotation.Nullable;
50import java.util.Iterator;
51import java.util.Set;
52
53public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
54    @Nonnull public final DexBackedDexFile dexFile;
55    private final int classDefOffset;
56
57    private final int staticFieldsOffset;
58    private int instanceFieldsOffset = 0;
59    private int directMethodsOffset = 0;
60    private int virtualMethodsOffset = 0;
61
62    private final int staticFieldCount;
63    private final int instanceFieldCount;
64    private final int directMethodCount;
65    private final int virtualMethodCount;
66
67    @Nullable private AnnotationsDirectory annotationsDirectory;
68
69    public DexBackedClassDef(@Nonnull DexBackedDexFile dexFile,
70                             int classDefOffset) {
71        this.dexFile = dexFile;
72        this.classDefOffset = classDefOffset;
73
74        int classDataOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
75        if (classDataOffset == 0) {
76            staticFieldsOffset = -1;
77            staticFieldCount = 0;
78            instanceFieldCount = 0;
79            directMethodCount = 0;
80            virtualMethodCount = 0;
81        } else {
82            DexReader reader = dexFile.readerAt(classDataOffset);
83            staticFieldCount = reader.readSmallUleb128();
84            instanceFieldCount = reader.readSmallUleb128();
85            directMethodCount = reader.readSmallUleb128();
86            virtualMethodCount = reader.readSmallUleb128();
87            staticFieldsOffset = reader.getOffset();
88        }
89
90    }
91
92    @Nonnull
93    @Override
94    public String getType() {
95        return dexFile.getType(dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_OFFSET));
96    }
97
98    @Nullable
99    @Override
100    public String getSuperclass() {
101        return dexFile.getOptionalType(dexFile.readOptionalUint(classDefOffset + ClassDefItem.SUPERCLASS_OFFSET));
102    }
103
104    @Override
105    public int getAccessFlags() {
106        return dexFile.readSmallUint(classDefOffset + ClassDefItem.ACCESS_FLAGS_OFFSET);
107    }
108
109    @Nullable
110    @Override
111    public String getSourceFile() {
112        return dexFile.getOptionalString(dexFile.readOptionalUint(classDefOffset + ClassDefItem.SOURCE_FILE_OFFSET));
113    }
114
115    @Nonnull
116    @Override
117    public Set<String> getInterfaces() {
118        final int interfacesOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.INTERFACES_OFFSET);
119        if (interfacesOffset > 0) {
120            final int size = dexFile.readSmallUint(interfacesOffset);
121            return new FixedSizeSet<String>() {
122                @Nonnull
123                @Override
124                public String readItem(int index) {
125                    return dexFile.getType(dexFile.readUshort(interfacesOffset + 4 + (2*index)));
126                }
127
128                @Override public int size() { return size; }
129            };
130        }
131        return ImmutableSet.of();
132    }
133
134    @Nonnull
135    @Override
136    public Set<? extends DexBackedAnnotation> getAnnotations() {
137        return getAnnotationsDirectory().getClassAnnotations();
138    }
139
140    @Nonnull
141    @Override
142    public Iterable<? extends DexBackedField> getStaticFields() {
143        return getStaticFields(true);
144    }
145
146    @Nonnull
147    public Iterable<? extends DexBackedField> getStaticFields(final boolean skipDuplicates) {
148        if (staticFieldCount > 0) {
149            DexReader reader = dexFile.readerAt(staticFieldsOffset);
150
151            final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
152            final int staticInitialValuesOffset =
153                    dexFile.readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET);
154            final int fieldsStartOffset = reader.getOffset();
155
156            return new Iterable<DexBackedField>() {
157                @Nonnull
158                @Override
159                public Iterator<DexBackedField> iterator() {
160                    final AnnotationsDirectory.AnnotationIterator annotationIterator =
161                            annotationsDirectory.getFieldAnnotationIterator();
162                    final StaticInitialValueIterator staticInitialValueIterator =
163                            StaticInitialValueIterator.newOrEmpty(dexFile, staticInitialValuesOffset);
164
165                    return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) {
166                        private int count;
167                        @Nullable private FieldReference previousField;
168                        private int previousIndex;
169
170                        @Nullable
171                        @Override
172                        protected DexBackedField readNextItem(@Nonnull DexReader reader) {
173                            while (true) {
174                                if (++count > staticFieldCount) {
175                                    instanceFieldsOffset = reader.getOffset();
176                                    return null;
177                                }
178
179                                DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this,
180                                        previousIndex, staticInitialValueIterator, annotationIterator);
181                                FieldReference currentField = previousField;
182                                FieldReference nextField = ImmutableFieldReference.of(item);
183
184                                previousField = nextField;
185                                previousIndex = item.fieldIndex;
186
187                                if (skipDuplicates && currentField != null && currentField.equals(nextField)) {
188                                    continue;
189                                }
190
191                                return item;
192                            }
193                        }
194                    };
195                }
196            };
197        } else {
198            instanceFieldsOffset = staticFieldsOffset;
199            return ImmutableSet.of();
200        }
201    }
202
203    @Nonnull
204    @Override
205    public Iterable<? extends DexBackedField> getInstanceFields() {
206        return getInstanceFields(true);
207    }
208
209    @Nonnull
210    public Iterable<? extends DexBackedField> getInstanceFields(final boolean skipDuplicates) {
211        if (instanceFieldCount > 0) {
212            DexReader reader = dexFile.readerAt(getInstanceFieldsOffset());
213
214            final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
215            final int fieldsStartOffset = reader.getOffset();
216
217            return new Iterable<DexBackedField>() {
218                @Nonnull
219                @Override
220                public Iterator<DexBackedField> iterator() {
221                    final AnnotationsDirectory.AnnotationIterator annotationIterator =
222                            annotationsDirectory.getFieldAnnotationIterator();
223
224                    return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) {
225                        private int count;
226                        @Nullable private FieldReference previousField;
227                        private int previousIndex;
228
229                        @Nullable
230                        @Override
231                        protected DexBackedField readNextItem(@Nonnull DexReader reader) {
232                            while (true) {
233                                if (++count > instanceFieldCount) {
234                                    directMethodsOffset = reader.getOffset();
235                                    return null;
236                                }
237
238                                DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this,
239                                        previousIndex, annotationIterator);
240                                FieldReference currentField = previousField;
241                                FieldReference nextField = ImmutableFieldReference.of(item);
242
243                                previousField = nextField;
244                                previousIndex = item.fieldIndex;
245
246                                if (skipDuplicates && currentField != null && currentField.equals(nextField)) {
247                                    continue;
248                                }
249
250                                return item;
251                            }
252                        }
253                    };
254                }
255            };
256        } else {
257            if (instanceFieldsOffset > 0) {
258                directMethodsOffset = instanceFieldsOffset;
259            }
260            return ImmutableSet.of();
261        }
262    }
263
264    @Nonnull
265    @Override
266    public Iterable<? extends DexBackedField> getFields() {
267        return Iterables.concat(getStaticFields(), getInstanceFields());
268    }
269
270    @Nonnull
271    @Override
272    public Iterable<? extends DexBackedMethod> getDirectMethods() {
273        return getDirectMethods(true);
274    }
275
276    @Nonnull
277    public Iterable<? extends DexBackedMethod> getDirectMethods(final boolean skipDuplicates) {
278        if (directMethodCount > 0) {
279            DexReader reader = dexFile.readerAt(getDirectMethodsOffset());
280
281            final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
282            final int methodsStartOffset = reader.getOffset();
283
284            return new Iterable<DexBackedMethod>() {
285                @Nonnull
286                @Override
287                public Iterator<DexBackedMethod> iterator() {
288                    final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
289                            annotationsDirectory.getMethodAnnotationIterator();
290                    final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
291                            annotationsDirectory.getParameterAnnotationIterator();
292
293                    return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) {
294                        private int count;
295                        @Nullable private MethodReference previousMethod;
296                        private int previousIndex;
297
298                        @Nullable
299                        @Override
300                        protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
301                            while (true) {
302                                if (++count > directMethodCount) {
303                                    virtualMethodsOffset = reader.getOffset();
304                                    return null;
305                                }
306
307                                DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
308                                        previousIndex, methodAnnotationIterator, parameterAnnotationIterator);
309                                MethodReference currentMethod = previousMethod;
310                                MethodReference nextMethod = ImmutableMethodReference.of(item);
311
312                                previousMethod = nextMethod;
313                                previousIndex = item.methodIndex;
314
315                                if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) {
316                                    continue;
317
318                                }
319                                return item;
320                            }
321                        }
322                    };
323                }
324            };
325        } else {
326            if (directMethodsOffset > 0) {
327                virtualMethodsOffset = directMethodsOffset;
328            }
329            return ImmutableSet.of();
330        }
331    }
332
333    @Nonnull
334    public Iterable<? extends DexBackedMethod> getVirtualMethods(final boolean skipDuplicates) {
335        if (virtualMethodCount > 0) {
336            DexReader reader = dexFile.readerAt(getVirtualMethodsOffset());
337
338            final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
339            final int methodsStartOffset = reader.getOffset();
340
341            return new Iterable<DexBackedMethod>() {
342                final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
343                        annotationsDirectory.getMethodAnnotationIterator();
344                final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
345                        annotationsDirectory.getParameterAnnotationIterator();
346
347                @Nonnull
348                @Override
349                public Iterator<DexBackedMethod> iterator() {
350                    return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) {
351                        private int count;
352                        @Nullable private MethodReference previousMethod;
353                        private int previousIndex;
354
355                        @Nullable
356                        @Override
357                        protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
358                            while (true) {
359                                if (++count > virtualMethodCount) {
360                                    return null;
361                                }
362
363                                DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
364                                        previousIndex, methodAnnotationIterator, parameterAnnotationIterator);
365                                MethodReference currentMethod = previousMethod;
366                                MethodReference nextMethod = ImmutableMethodReference.of(item);
367
368                                previousMethod = nextMethod;
369                                previousIndex = item.methodIndex;
370
371                                if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) {
372                                    continue;
373                                }
374                                return item;
375                            }
376                        }
377                    };
378                }
379            };
380        } else {
381            return ImmutableSet.of();
382        }
383    }
384
385    @Nonnull
386    @Override
387    public Iterable<? extends DexBackedMethod> getVirtualMethods() {
388        return getVirtualMethods(true);
389    }
390
391    @Nonnull
392    @Override
393    public Iterable<? extends DexBackedMethod> getMethods() {
394        return Iterables.concat(getDirectMethods(), getVirtualMethods());
395    }
396
397    private AnnotationsDirectory getAnnotationsDirectory() {
398        if (annotationsDirectory == null) {
399            int annotationsDirectoryOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.ANNOTATIONS_OFFSET);
400            annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexFile, annotationsDirectoryOffset);
401        }
402        return annotationsDirectory;
403    }
404
405    private int getInstanceFieldsOffset() {
406        if (instanceFieldsOffset > 0) {
407            return instanceFieldsOffset;
408        }
409        DexReader reader = new DexReader(dexFile, staticFieldsOffset);
410        DexBackedField.skipFields(reader, staticFieldCount);
411        instanceFieldsOffset = reader.getOffset();
412        return instanceFieldsOffset;
413    }
414
415    private int getDirectMethodsOffset() {
416        if (directMethodsOffset > 0) {
417            return directMethodsOffset;
418        }
419        DexReader reader = dexFile.readerAt(getInstanceFieldsOffset());
420        DexBackedField.skipFields(reader, instanceFieldCount);
421        directMethodsOffset = reader.getOffset();
422        return directMethodsOffset;
423    }
424
425    private int getVirtualMethodsOffset() {
426        if (virtualMethodsOffset > 0) {
427            return virtualMethodsOffset;
428        }
429        DexReader reader = dexFile.readerAt(getDirectMethodsOffset());
430        DexBackedMethod.skipMethods(reader, directMethodCount);
431        virtualMethodsOffset = reader.getOffset();
432        return virtualMethodsOffset;
433    }
434}
435