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 org.jf.dexlib2.base.reference.BaseFieldReference;
35import org.jf.dexlib2.dexbacked.raw.FieldIdItem;
36import org.jf.dexlib2.dexbacked.reference.DexBackedFieldReference;
37import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
38import org.jf.dexlib2.dexbacked.util.StaticInitialValueIterator;
39import org.jf.dexlib2.dexbacked.value.DexBackedEncodedValue;
40import org.jf.dexlib2.iface.ClassDef;
41import org.jf.dexlib2.iface.Field;
42import org.jf.dexlib2.iface.value.EncodedValue;
43
44import javax.annotation.Nonnull;
45import javax.annotation.Nullable;
46import java.util.Set;
47
48public class DexBackedField extends BaseFieldReference implements Field {
49    @Nonnull public final DexBackedDexFile dexFile;
50    @Nonnull public final ClassDef classDef;
51
52    public final int accessFlags;
53    @Nullable public final EncodedValue initialValue;
54    public final int annotationSetOffset;
55
56    public final int fieldIndex;
57    private final int startOffset;
58    private final int initialValueOffset;
59
60    private int fieldIdItemOffset;
61
62    public DexBackedField(@Nonnull DexReader reader,
63                          @Nonnull DexBackedClassDef classDef,
64                          int previousFieldIndex,
65                          @Nonnull StaticInitialValueIterator staticInitialValueIterator,
66                          @Nonnull AnnotationsDirectory.AnnotationIterator annotationIterator) {
67        this.dexFile = reader.dexBuf;
68        this.classDef = classDef;
69
70        // large values may be used for the index delta, which cause the cumulative index to overflow upon
71        // addition, effectively allowing out of order entries.
72        startOffset = reader.getOffset();
73        int fieldIndexDiff = reader.readLargeUleb128();
74        this.fieldIndex = fieldIndexDiff + previousFieldIndex;
75        this.accessFlags = reader.readSmallUleb128();
76
77        this.annotationSetOffset = annotationIterator.seekTo(fieldIndex);
78        initialValueOffset = staticInitialValueIterator.getReaderOffset();
79        this.initialValue = staticInitialValueIterator.getNextOrNull();
80    }
81
82    public DexBackedField(@Nonnull DexReader reader,
83                          @Nonnull DexBackedClassDef classDef,
84                          int previousFieldIndex,
85                          @Nonnull AnnotationsDirectory.AnnotationIterator annotationIterator) {
86        this.dexFile = reader.dexBuf;
87        this.classDef = classDef;
88
89        // large values may be used for the index delta, which cause the cumulative index to overflow upon
90        // addition, effectively allowing out of order entries.
91        startOffset = reader.getOffset();
92        int fieldIndexDiff = reader.readLargeUleb128();
93        this.fieldIndex = fieldIndexDiff + previousFieldIndex;
94        this.accessFlags = reader.readSmallUleb128();
95
96        this.annotationSetOffset = annotationIterator.seekTo(fieldIndex);
97        initialValueOffset = 0;
98        this.initialValue = null;
99    }
100
101    @Nonnull
102    @Override
103    public String getName() {
104        return dexFile.getString(dexFile.readSmallUint(getFieldIdItemOffset() + FieldIdItem.NAME_OFFSET));
105    }
106
107    @Nonnull
108    @Override
109    public String getType() {
110        return dexFile.getType(dexFile.readUshort(getFieldIdItemOffset() + FieldIdItem.TYPE_OFFSET));
111    }
112
113    @Nonnull @Override public String getDefiningClass() { return classDef.getType(); }
114    @Override public int getAccessFlags() { return accessFlags; }
115    @Nullable @Override public EncodedValue getInitialValue() { return initialValue; }
116
117    @Nonnull
118    @Override
119    public Set<? extends DexBackedAnnotation> getAnnotations() {
120        return AnnotationsDirectory.getAnnotations(dexFile, annotationSetOffset);
121    }
122
123    /**
124     * Skips the reader over the specified number of encoded_field structures
125     *
126     * @param reader The reader to skip
127     * @param count The number of encoded_field structures to skip over
128     */
129    public static void skipFields(@Nonnull DexReader reader, int count) {
130        for (int i=0; i<count; i++) {
131            reader.skipUleb128();
132            reader.skipUleb128();
133        }
134    }
135
136    private int getFieldIdItemOffset() {
137        if (fieldIdItemOffset == 0) {
138            fieldIdItemOffset = dexFile.getFieldIdItemOffset(fieldIndex);
139        }
140        return fieldIdItemOffset;
141    }
142
143    /**
144     * Calculate and return the private size of a field definition.
145     *
146     * Calculated as: field_idx_diff + access_flags + annotations overhead +
147     * initial value size + field reference size
148     *
149     * @return size in bytes
150     */
151    public int getSize() {
152        int size = 0;
153        DexReader reader = dexFile.readerAt(startOffset);
154        reader.readLargeUleb128(); //field_idx_diff
155        reader.readSmallUleb128(); //access_flags
156        size += reader.getOffset() - startOffset;
157
158        Set<? extends DexBackedAnnotation> annotations = getAnnotations();
159        if (!annotations.isEmpty()) {
160            size += 2 * 4; //2 * uint overhead from field_annotation
161        }
162
163        if (initialValueOffset > 0) {
164            reader.setOffset(initialValueOffset);
165            if (initialValue != null) {
166                DexBackedEncodedValue.skipFrom(reader);
167                size += reader.getOffset() - initialValueOffset;
168            }
169        }
170
171        DexBackedFieldReference fieldRef = new DexBackedFieldReference(dexFile, fieldIndex);
172        size += fieldRef.getSize();
173
174        return size;
175    }
176}
177