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