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