• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 package org.jf.dexlib2.dexbacked.util;
33 
34 import com.google.common.collect.ImmutableList;
35 import com.google.common.collect.ImmutableSet;
36 import org.jf.dexlib2.dexbacked.DexBackedAnnotation;
37 import org.jf.dexlib2.dexbacked.DexBackedDexFile;
38 
39 import javax.annotation.Nonnull;
40 import java.util.List;
41 import java.util.Set;
42 
43 public 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 
getFieldAnnotationCount()52     public abstract int getFieldAnnotationCount();
getClassAnnotations()53     @Nonnull public abstract Set<? extends DexBackedAnnotation> getClassAnnotations();
getFieldAnnotationIterator()54     @Nonnull public abstract AnnotationIterator getFieldAnnotationIterator();
getMethodAnnotationIterator()55     @Nonnull public abstract AnnotationIterator getMethodAnnotationIterator();
getParameterAnnotationIterator()56     @Nonnull public abstract AnnotationIterator getParameterAnnotationIterator();
57 
58     @Nonnull
newOrEmpty(@onnull DexBackedDexFile dexFile, int directoryAnnotationsOffset)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      * This provides a forward-only, skipable iteration over the field_annotation, method_annotation or
69      * parameter_annotation lists in an annotations_directory_item.
70      *
71      * These lists associate a key, either a field or method index, with an offset to where the annotation data for
72      * that field/method/parameter is stored.
73      */
74     public interface AnnotationIterator {
75         public static final AnnotationIterator EMPTY = new AnnotationIterator() {
76             @Override public int seekTo(int key) { return 0; }
77             @Override public void reset() {}
78         };
79 
80         /**
81          * Seeks the iterator forward, to the first item whose key is >= the requested key. If the requested key value
82          * is less than that of the item that the iterator currently points to, it will not be moved forward.
83          *
84          * If an item with the requested key is found, the associated annotation offset is returned. Otherwise, 0 is
85          * returned.
86          *
87          * @param key The method/field index to search for
88          * @return The annotation offset associated with the requested key, or 0 if not found.
89          */
seekTo(int key)90         public int seekTo(int key);
91 
92         /**
93          * Resets the iterator to the beginning of its list.
94          */
reset()95         public void reset();
96     }
97 
98     @Nonnull
getAnnotations(@onnull final DexBackedDexFile dexFile, final int annotationSetOffset)99     public static Set<? extends DexBackedAnnotation> getAnnotations(@Nonnull final DexBackedDexFile dexFile,
100                                                                      final int annotationSetOffset) {
101         if (annotationSetOffset != 0) {
102             final int size = dexFile.readSmallUint(annotationSetOffset);
103             return new FixedSizeSet<DexBackedAnnotation>() {
104                 @Nonnull
105                 @Override
106                 public DexBackedAnnotation readItem(int index) {
107                     int annotationOffset = dexFile.readSmallUint(annotationSetOffset + 4 + (4*index));
108                     return new DexBackedAnnotation(dexFile, annotationOffset);
109                 }
110 
111                 @Override public int size() { return size; }
112             };
113         }
114 
115         return ImmutableSet.of();
116     }
117 
118     @Nonnull
119     public static List<Set<? extends DexBackedAnnotation>> getParameterAnnotations(
120             @Nonnull final DexBackedDexFile dexFile, final int annotationSetListOffset) {
121         if (annotationSetListOffset > 0) {
122             final int size = dexFile.readSmallUint(annotationSetListOffset);
123 
124             return new FixedSizeList<Set<? extends DexBackedAnnotation>>() {
125                 @Nonnull
126                 @Override
127                 public Set<? extends DexBackedAnnotation> readItem(int index) {
128                     int annotationSetOffset = dexFile.readSmallUint(annotationSetListOffset + 4 + index * 4);
129                     return getAnnotations(dexFile, annotationSetOffset);
130                 }
131 
132                 @Override public int size() { return size; }
133             };
134         }
135         return ImmutableList.of();
136     }
137 
138     private static class AnnotationsDirectoryImpl extends AnnotationsDirectory {
139         @Nonnull public final DexBackedDexFile dexFile;
140         private final int directoryOffset;
141 
142         private static final int FIELD_COUNT_OFFSET = 4;
143         private static final int METHOD_COUNT_OFFSET = 8;
144         private static final int PARAMETER_COUNT_OFFSET = 12;
145         private static final int ANNOTATIONS_START_OFFSET = 16;
146 
147         /** The size of a field_annotation structure */
148         private static final int FIELD_ANNOTATION_SIZE = 8;
149         /** The size of a method_annotation structure */
150         private static final int METHOD_ANNOTATION_SIZE = 8;
151 
152         public AnnotationsDirectoryImpl(@Nonnull DexBackedDexFile dexFile,
153                                         int directoryOffset) {
154             this.dexFile = dexFile;
155             this.directoryOffset = directoryOffset;
156         }
157 
158         public int getFieldAnnotationCount() {
159             return dexFile.readSmallUint(directoryOffset + FIELD_COUNT_OFFSET);
160         }
161 
162         public int getMethodAnnotationCount() {
163             return dexFile.readSmallUint(directoryOffset + METHOD_COUNT_OFFSET);
164         }
165 
166         public int getParameterAnnotationCount() {
167             return dexFile.readSmallUint(directoryOffset + PARAMETER_COUNT_OFFSET);
168         }
169 
170         @Nonnull
171         public Set<? extends DexBackedAnnotation> getClassAnnotations() {
172             return getAnnotations(dexFile, dexFile.readSmallUint(directoryOffset));
173         }
174 
175         @Nonnull
176         public AnnotationIterator getFieldAnnotationIterator() {
177             int fieldAnnotationCount = getFieldAnnotationCount();
178             if (fieldAnnotationCount == 0) {
179                 return AnnotationIterator.EMPTY;
180             }
181             return new AnnotationIteratorImpl(directoryOffset + ANNOTATIONS_START_OFFSET, fieldAnnotationCount);
182         }
183 
184         @Nonnull
185         public AnnotationIterator getMethodAnnotationIterator() {
186             int methodCount = getMethodAnnotationCount();
187             if (methodCount == 0) {
188                 return AnnotationIterator.EMPTY;
189             }
190             int fieldCount = getFieldAnnotationCount();
191             int methodAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
192                     fieldCount * FIELD_ANNOTATION_SIZE;
193             return new AnnotationIteratorImpl(methodAnnotationsOffset, methodCount);
194         }
195 
196         @Nonnull
197         public AnnotationIterator getParameterAnnotationIterator() {
198             int parameterAnnotationCount = getParameterAnnotationCount();
199             if (parameterAnnotationCount == 0) {
200                 return AnnotationIterator.EMPTY;
201             }
202             int fieldCount = getFieldAnnotationCount();
203             int methodCount = getMethodAnnotationCount();
204             int parameterAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
205                     fieldCount * FIELD_ANNOTATION_SIZE +
206                     methodCount * METHOD_ANNOTATION_SIZE;
207             return new AnnotationIteratorImpl(parameterAnnotationsOffset, parameterAnnotationCount);
208         }
209 
210         private class AnnotationIteratorImpl implements AnnotationIterator {
211             private final int startOffset;
212             private final int size;
213             private int currentIndex;
214             private int currentItemIndex;
215 
216             public AnnotationIteratorImpl(int startOffset, int size) {
217                 this.startOffset = startOffset;
218                 this.size = size;
219                 this.currentItemIndex = dexFile.readSmallUint(startOffset);
220                 this.currentIndex = 0;
221             }
222 
223             public int seekTo(int itemIndex) {
224                 while (currentItemIndex < itemIndex && (currentIndex+1) < size) {
225                     currentIndex++;
226                     currentItemIndex = dexFile.readSmallUint(startOffset + (currentIndex*8));
227                 }
228 
229                 if (currentItemIndex == itemIndex) {
230                     return dexFile.readSmallUint(startOffset + (currentIndex*8)+4);
231                 }
232                 return 0;
233             }
234 
235             public void reset() {
236                 this.currentItemIndex = dexFile.readSmallUint(startOffset);
237                 this.currentIndex = 0;
238             }
239         }
240     }
241 }
242