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 com.google.common.collect.ImmutableSet; 35import com.google.common.collect.Iterables; 36import org.jf.dexlib2.base.reference.BaseTypeReference; 37import org.jf.dexlib2.dexbacked.raw.ClassDefItem; 38import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory; 39import org.jf.dexlib2.dexbacked.util.FixedSizeSet; 40import org.jf.dexlib2.dexbacked.util.StaticInitialValueIterator; 41import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator; 42import org.jf.dexlib2.iface.ClassDef; 43import org.jf.dexlib2.iface.reference.FieldReference; 44import org.jf.dexlib2.iface.reference.MethodReference; 45import org.jf.dexlib2.immutable.reference.ImmutableFieldReference; 46import org.jf.dexlib2.immutable.reference.ImmutableMethodReference; 47 48import javax.annotation.Nonnull; 49import javax.annotation.Nullable; 50import java.util.Iterator; 51import java.util.Set; 52 53public class DexBackedClassDef extends BaseTypeReference implements ClassDef { 54 @Nonnull public final DexBackedDexFile dexFile; 55 private final int classDefOffset; 56 57 private final int staticFieldsOffset; 58 private int instanceFieldsOffset = 0; 59 private int directMethodsOffset = 0; 60 private int virtualMethodsOffset = 0; 61 62 private final int staticFieldCount; 63 private final int instanceFieldCount; 64 private final int directMethodCount; 65 private final int virtualMethodCount; 66 67 @Nullable private AnnotationsDirectory annotationsDirectory; 68 69 public DexBackedClassDef(@Nonnull DexBackedDexFile dexFile, 70 int classDefOffset) { 71 this.dexFile = dexFile; 72 this.classDefOffset = classDefOffset; 73 74 int classDataOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET); 75 if (classDataOffset == 0) { 76 staticFieldsOffset = -1; 77 staticFieldCount = 0; 78 instanceFieldCount = 0; 79 directMethodCount = 0; 80 virtualMethodCount = 0; 81 } else { 82 DexReader reader = dexFile.readerAt(classDataOffset); 83 staticFieldCount = reader.readSmallUleb128(); 84 instanceFieldCount = reader.readSmallUleb128(); 85 directMethodCount = reader.readSmallUleb128(); 86 virtualMethodCount = reader.readSmallUleb128(); 87 staticFieldsOffset = reader.getOffset(); 88 } 89 90 } 91 92 @Nonnull 93 @Override 94 public String getType() { 95 return dexFile.getType(dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_OFFSET)); 96 } 97 98 @Nullable 99 @Override 100 public String getSuperclass() { 101 return dexFile.getOptionalType(dexFile.readOptionalUint(classDefOffset + ClassDefItem.SUPERCLASS_OFFSET)); 102 } 103 104 @Override 105 public int getAccessFlags() { 106 return dexFile.readSmallUint(classDefOffset + ClassDefItem.ACCESS_FLAGS_OFFSET); 107 } 108 109 @Nullable 110 @Override 111 public String getSourceFile() { 112 return dexFile.getOptionalString(dexFile.readOptionalUint(classDefOffset + ClassDefItem.SOURCE_FILE_OFFSET)); 113 } 114 115 @Nonnull 116 @Override 117 public Set<String> getInterfaces() { 118 final int interfacesOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.INTERFACES_OFFSET); 119 if (interfacesOffset > 0) { 120 final int size = dexFile.readSmallUint(interfacesOffset); 121 return new FixedSizeSet<String>() { 122 @Nonnull 123 @Override 124 public String readItem(int index) { 125 return dexFile.getType(dexFile.readUshort(interfacesOffset + 4 + (2*index))); 126 } 127 128 @Override public int size() { return size; } 129 }; 130 } 131 return ImmutableSet.of(); 132 } 133 134 @Nonnull 135 @Override 136 public Set<? extends DexBackedAnnotation> getAnnotations() { 137 return getAnnotationsDirectory().getClassAnnotations(); 138 } 139 140 @Nonnull 141 @Override 142 public Iterable<? extends DexBackedField> getStaticFields() { 143 return getStaticFields(true); 144 } 145 146 @Nonnull 147 public Iterable<? extends DexBackedField> getStaticFields(final boolean skipDuplicates) { 148 if (staticFieldCount > 0) { 149 DexReader reader = dexFile.readerAt(staticFieldsOffset); 150 151 final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); 152 final int staticInitialValuesOffset = 153 dexFile.readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET); 154 final int fieldsStartOffset = reader.getOffset(); 155 156 return new Iterable<DexBackedField>() { 157 @Nonnull 158 @Override 159 public Iterator<DexBackedField> iterator() { 160 final AnnotationsDirectory.AnnotationIterator annotationIterator = 161 annotationsDirectory.getFieldAnnotationIterator(); 162 final StaticInitialValueIterator staticInitialValueIterator = 163 StaticInitialValueIterator.newOrEmpty(dexFile, staticInitialValuesOffset); 164 165 return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) { 166 private int count; 167 @Nullable private FieldReference previousField; 168 private int previousIndex; 169 170 @Nullable 171 @Override 172 protected DexBackedField readNextItem(@Nonnull DexReader reader) { 173 while (true) { 174 if (++count > staticFieldCount) { 175 instanceFieldsOffset = reader.getOffset(); 176 return null; 177 } 178 179 DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this, 180 previousIndex, staticInitialValueIterator, annotationIterator); 181 FieldReference currentField = previousField; 182 FieldReference nextField = ImmutableFieldReference.of(item); 183 184 previousField = nextField; 185 previousIndex = item.fieldIndex; 186 187 if (skipDuplicates && currentField != null && currentField.equals(nextField)) { 188 continue; 189 } 190 191 return item; 192 } 193 } 194 }; 195 } 196 }; 197 } else { 198 instanceFieldsOffset = staticFieldsOffset; 199 return ImmutableSet.of(); 200 } 201 } 202 203 @Nonnull 204 @Override 205 public Iterable<? extends DexBackedField> getInstanceFields() { 206 return getInstanceFields(true); 207 } 208 209 @Nonnull 210 public Iterable<? extends DexBackedField> getInstanceFields(final boolean skipDuplicates) { 211 if (instanceFieldCount > 0) { 212 DexReader reader = dexFile.readerAt(getInstanceFieldsOffset()); 213 214 final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); 215 final int fieldsStartOffset = reader.getOffset(); 216 217 return new Iterable<DexBackedField>() { 218 @Nonnull 219 @Override 220 public Iterator<DexBackedField> iterator() { 221 final AnnotationsDirectory.AnnotationIterator annotationIterator = 222 annotationsDirectory.getFieldAnnotationIterator(); 223 224 return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) { 225 private int count; 226 @Nullable private FieldReference previousField; 227 private int previousIndex; 228 229 @Nullable 230 @Override 231 protected DexBackedField readNextItem(@Nonnull DexReader reader) { 232 while (true) { 233 if (++count > instanceFieldCount) { 234 directMethodsOffset = reader.getOffset(); 235 return null; 236 } 237 238 DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this, 239 previousIndex, annotationIterator); 240 FieldReference currentField = previousField; 241 FieldReference nextField = ImmutableFieldReference.of(item); 242 243 previousField = nextField; 244 previousIndex = item.fieldIndex; 245 246 if (skipDuplicates && currentField != null && currentField.equals(nextField)) { 247 continue; 248 } 249 250 return item; 251 } 252 } 253 }; 254 } 255 }; 256 } else { 257 if (instanceFieldsOffset > 0) { 258 directMethodsOffset = instanceFieldsOffset; 259 } 260 return ImmutableSet.of(); 261 } 262 } 263 264 @Nonnull 265 @Override 266 public Iterable<? extends DexBackedField> getFields() { 267 return Iterables.concat(getStaticFields(), getInstanceFields()); 268 } 269 270 @Nonnull 271 @Override 272 public Iterable<? extends DexBackedMethod> getDirectMethods() { 273 return getDirectMethods(true); 274 } 275 276 @Nonnull 277 public Iterable<? extends DexBackedMethod> getDirectMethods(final boolean skipDuplicates) { 278 if (directMethodCount > 0) { 279 DexReader reader = dexFile.readerAt(getDirectMethodsOffset()); 280 281 final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); 282 final int methodsStartOffset = reader.getOffset(); 283 284 return new Iterable<DexBackedMethod>() { 285 @Nonnull 286 @Override 287 public Iterator<DexBackedMethod> iterator() { 288 final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator = 289 annotationsDirectory.getMethodAnnotationIterator(); 290 final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator = 291 annotationsDirectory.getParameterAnnotationIterator(); 292 293 return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) { 294 private int count; 295 @Nullable private MethodReference previousMethod; 296 private int previousIndex; 297 298 @Nullable 299 @Override 300 protected DexBackedMethod readNextItem(@Nonnull DexReader reader) { 301 while (true) { 302 if (++count > directMethodCount) { 303 virtualMethodsOffset = reader.getOffset(); 304 return null; 305 } 306 307 DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this, 308 previousIndex, methodAnnotationIterator, parameterAnnotationIterator); 309 MethodReference currentMethod = previousMethod; 310 MethodReference nextMethod = ImmutableMethodReference.of(item); 311 312 previousMethod = nextMethod; 313 previousIndex = item.methodIndex; 314 315 if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) { 316 continue; 317 318 } 319 return item; 320 } 321 } 322 }; 323 } 324 }; 325 } else { 326 if (directMethodsOffset > 0) { 327 virtualMethodsOffset = directMethodsOffset; 328 } 329 return ImmutableSet.of(); 330 } 331 } 332 333 @Nonnull 334 public Iterable<? extends DexBackedMethod> getVirtualMethods(final boolean skipDuplicates) { 335 if (virtualMethodCount > 0) { 336 DexReader reader = dexFile.readerAt(getVirtualMethodsOffset()); 337 338 final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); 339 final int methodsStartOffset = reader.getOffset(); 340 341 return new Iterable<DexBackedMethod>() { 342 final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator = 343 annotationsDirectory.getMethodAnnotationIterator(); 344 final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator = 345 annotationsDirectory.getParameterAnnotationIterator(); 346 347 @Nonnull 348 @Override 349 public Iterator<DexBackedMethod> iterator() { 350 return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) { 351 private int count; 352 @Nullable private MethodReference previousMethod; 353 private int previousIndex; 354 355 @Nullable 356 @Override 357 protected DexBackedMethod readNextItem(@Nonnull DexReader reader) { 358 while (true) { 359 if (++count > virtualMethodCount) { 360 return null; 361 } 362 363 DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this, 364 previousIndex, methodAnnotationIterator, parameterAnnotationIterator); 365 MethodReference currentMethod = previousMethod; 366 MethodReference nextMethod = ImmutableMethodReference.of(item); 367 368 previousMethod = nextMethod; 369 previousIndex = item.methodIndex; 370 371 if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) { 372 continue; 373 } 374 return item; 375 } 376 } 377 }; 378 } 379 }; 380 } else { 381 return ImmutableSet.of(); 382 } 383 } 384 385 @Nonnull 386 @Override 387 public Iterable<? extends DexBackedMethod> getVirtualMethods() { 388 return getVirtualMethods(true); 389 } 390 391 @Nonnull 392 @Override 393 public Iterable<? extends DexBackedMethod> getMethods() { 394 return Iterables.concat(getDirectMethods(), getVirtualMethods()); 395 } 396 397 private AnnotationsDirectory getAnnotationsDirectory() { 398 if (annotationsDirectory == null) { 399 int annotationsDirectoryOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.ANNOTATIONS_OFFSET); 400 annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexFile, annotationsDirectoryOffset); 401 } 402 return annotationsDirectory; 403 } 404 405 private int getInstanceFieldsOffset() { 406 if (instanceFieldsOffset > 0) { 407 return instanceFieldsOffset; 408 } 409 DexReader reader = new DexReader(dexFile, staticFieldsOffset); 410 DexBackedField.skipFields(reader, staticFieldCount); 411 instanceFieldsOffset = reader.getOffset(); 412 return instanceFieldsOffset; 413 } 414 415 private int getDirectMethodsOffset() { 416 if (directMethodsOffset > 0) { 417 return directMethodsOffset; 418 } 419 DexReader reader = dexFile.readerAt(getInstanceFieldsOffset()); 420 DexBackedField.skipFields(reader, instanceFieldCount); 421 directMethodsOffset = reader.getOffset(); 422 return directMethodsOffset; 423 } 424 425 private int getVirtualMethodsOffset() { 426 if (virtualMethodsOffset > 0) { 427 return virtualMethodsOffset; 428 } 429 DexReader reader = dexFile.readerAt(getDirectMethodsOffset()); 430 DexBackedMethod.skipMethods(reader, directMethodCount); 431 virtualMethodsOffset = reader.getOffset(); 432 return virtualMethodsOffset; 433 } 434} 435