AnnotationsDirectory.java revision 84c1762a62d7fc6638432c6c56e0422aa8cc6939
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.util;
33
34import com.google.common.collect.ImmutableList;
35import com.google.common.collect.ImmutableSet;
36import org.jf.dexlib2.dexbacked.DexBackedAnnotation;
37import org.jf.dexlib2.dexbacked.DexBackedDexFile;
38
39import javax.annotation.Nonnull;
40import java.util.List;
41import java.util.Set;
42
43public abstract class AnnotationsDirectory {
44    public static final AnnotationsDirectory EMPTY = new AnnotationsDirectory() {
45        @Override public int getFieldAnnotationCount() { return 0; }
46        @Nonnull @Override public Set<? extends DexBackedAnnotation> getClassAnnotations() { return ImmutableSet.of(); }
47        @Nonnull @Override public AnnotationIterator getFieldAnnotationIterator() { return AnnotationIterator.EMPTY; }
48        @Nonnull @Override public AnnotationIterator getMethodAnnotationIterator() { return AnnotationIterator.EMPTY; }
49        @Nonnull @Override public AnnotationIterator getParameterAnnotationIterator() {return AnnotationIterator.EMPTY;}
50    };
51
52    public abstract int getFieldAnnotationCount();
53    @Nonnull public abstract Set<? extends DexBackedAnnotation> getClassAnnotations();
54    @Nonnull public abstract AnnotationIterator getFieldAnnotationIterator();
55    @Nonnull public abstract AnnotationIterator getMethodAnnotationIterator();
56    @Nonnull public abstract AnnotationIterator getParameterAnnotationIterator();
57
58    @Nonnull
59    public static AnnotationsDirectory newOrEmpty(@Nonnull DexBackedDexFile dexFile,
60                                                  int directoryAnnotationsOffset) {
61        if (directoryAnnotationsOffset == 0) {
62            return EMPTY;
63        }
64        return new AnnotationsDirectoryImpl(dexFile, directoryAnnotationsOffset);
65    }
66
67
68    public interface AnnotationIterator {
69        public static final AnnotationIterator EMPTY = new AnnotationIterator() {
70            @Override public int seekTo(int fieldIndex) { return 0; }
71            public void reset() {}
72        };
73
74        public int seekTo(int fieldIndex);
75        public void reset();
76    }
77
78    @Nonnull
79    public static Set<? extends DexBackedAnnotation> getAnnotations(@Nonnull final DexBackedDexFile dexFile,
80                                                                     final int annotationSetOffset) {
81        if (annotationSetOffset != 0) {
82            final int size = dexFile.readSmallUint(annotationSetOffset);
83            return new FixedSizeSet<DexBackedAnnotation>() {
84                @Nonnull
85                @Override
86                public DexBackedAnnotation readItem(int index) {
87                    int annotationOffset = dexFile.readSmallUint(annotationSetOffset + 4 + (4*index));
88                    return new DexBackedAnnotation(dexFile, annotationOffset);
89                }
90
91                @Override public int size() { return size; }
92            };
93        }
94
95        return ImmutableSet.of();
96    }
97
98    @Nonnull
99    public static List<Set<? extends DexBackedAnnotation>> getParameterAnnotations(
100            @Nonnull final DexBackedDexFile dexFile, final int annotationSetListOffset) {
101        if (annotationSetListOffset > 0) {
102            final int size = dexFile.readSmallUint(annotationSetListOffset);
103
104            return new FixedSizeList<Set<? extends DexBackedAnnotation>>() {
105                @Nonnull
106                @Override
107                public Set<? extends DexBackedAnnotation> readItem(int index) {
108                    int annotationSetOffset = dexFile.readSmallUint(annotationSetListOffset + 4 + index * 4);
109                    return getAnnotations(dexFile, annotationSetOffset);
110                }
111
112                @Override public int size() { return size; }
113            };
114        }
115        return ImmutableList.of();
116    }
117
118    private static class AnnotationsDirectoryImpl extends AnnotationsDirectory {
119        @Nonnull public final DexBackedDexFile dexFile;
120        private final int directoryOffset;
121
122        private static final int FIELD_COUNT_OFFSET = 4;
123        private static final int METHOD_COUNT_OFFSET = 8;
124        private static final int PARAMETER_COUNT_OFFSET = 12;
125        private static final int ANNOTATIONS_START_OFFSET = 16;
126
127        /** The size of a field_annotation structure */
128        private static final int FIELD_ANNOTATION_SIZE = 8;
129        /** The size of a method_annotation structure */
130        private static final int METHOD_ANNOTATION_SIZE = 8;
131
132        public AnnotationsDirectoryImpl(@Nonnull DexBackedDexFile dexFile,
133                                        int directoryOffset) {
134            this.dexFile = dexFile;
135            this.directoryOffset = directoryOffset;
136        }
137
138        public int getFieldAnnotationCount() {
139            return dexFile.readSmallUint(directoryOffset + FIELD_COUNT_OFFSET);
140        }
141
142        public int getMethodAnnotationCount() {
143            return dexFile.readSmallUint(directoryOffset + METHOD_COUNT_OFFSET);
144        }
145
146        public int getParameterAnnotationCount() {
147            return dexFile.readSmallUint(directoryOffset + PARAMETER_COUNT_OFFSET);
148        }
149
150        @Nonnull
151        public Set<? extends DexBackedAnnotation> getClassAnnotations() {
152            return getAnnotations(dexFile, dexFile.readSmallUint(directoryOffset));
153        }
154
155        @Nonnull
156        public AnnotationIterator getFieldAnnotationIterator() {
157            int fieldAnnotationCount = getFieldAnnotationCount();
158            if (fieldAnnotationCount == 0) {
159                return AnnotationIterator.EMPTY;
160            }
161            return new AnnotationIteratorImpl(directoryOffset + ANNOTATIONS_START_OFFSET, fieldAnnotationCount);
162        }
163
164        @Nonnull
165        public AnnotationIterator getMethodAnnotationIterator() {
166            int methodCount = getMethodAnnotationCount();
167            if (methodCount == 0) {
168                return AnnotationIterator.EMPTY;
169            }
170            int fieldCount = getFieldAnnotationCount();
171            int methodAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
172                    fieldCount * FIELD_ANNOTATION_SIZE;
173            return new AnnotationIteratorImpl(methodAnnotationsOffset, methodCount);
174        }
175
176        @Nonnull
177        public AnnotationIterator getParameterAnnotationIterator() {
178            int parameterAnnotationCount = getParameterAnnotationCount();
179            if (parameterAnnotationCount == 0) {
180                return AnnotationIterator.EMPTY;
181            }
182            int fieldCount = getFieldAnnotationCount();
183            int methodCount = getMethodAnnotationCount();
184            int parameterAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
185                    fieldCount * FIELD_ANNOTATION_SIZE +
186                    methodCount * METHOD_ANNOTATION_SIZE;
187            return new AnnotationIteratorImpl(parameterAnnotationsOffset, parameterAnnotationCount);
188        }
189
190        private class AnnotationIteratorImpl implements AnnotationIterator {
191            private final int startOffset;
192            private final int size;
193            private int currentIndex;
194            private int currentItemIndex;
195
196            public AnnotationIteratorImpl(int startOffset, int size) {
197                this.startOffset = startOffset;
198                this.size = size;
199                this.currentItemIndex = dexFile.readSmallUint(startOffset);
200                this.currentIndex = 0;
201            }
202
203            public int seekTo(int itemIndex) {
204                while (currentItemIndex < itemIndex && (currentIndex+1) < size) {
205                    currentIndex++;
206                    currentItemIndex = dexFile.readSmallUint(startOffset + (currentIndex*8));
207                }
208
209                if (currentItemIndex == itemIndex) {
210                    return dexFile.readSmallUint(startOffset + (currentIndex*8)+4);
211                }
212                return 0;
213            }
214
215            public void reset() {
216                this.currentItemIndex = dexFile.readSmallUint(startOffset);
217                this.currentIndex = 0;
218            }
219        }
220    }
221}
222