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