package org.jf.dexlib2.dexbacked;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.dexbacked.raw.ClassDefItem;
import org.jf.dexlib2.dexbacked.raw.TypeIdItem;
import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory;
import org.jf.dexlib2.dexbacked.util.EncodedArrayItemIterator;
import org.jf.dexlib2.dexbacked.util.VariableSizeListIterator;
import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.jf.dexlib2.writer.DexWriter.NO_OFFSET;
public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
static final int NO_HIDDEN_API_RESTRICTIONS = 7;
@Nonnull public final DexBackedDexFile dexFile;
private final int classDefOffset;
@Nullable private final HiddenApiRestrictionsReader hiddenApiRestrictionsReader;
private final int staticFieldsOffset;
private int instanceFieldsOffset = 0;
private int directMethodsOffset = 0;
private int virtualMethodsOffset = 0;
private final int staticFieldCount;
private final int instanceFieldCount;
private final int directMethodCount;
private final int virtualMethodCount;
@Nullable private AnnotationsDirectory annotationsDirectory;
public DexBackedClassDef(@Nonnull DexBackedDexFile dexFile,
int classDefOffset,
int hiddenApiRestrictionsOffset) {
this.dexFile = dexFile;
this.classDefOffset = classDefOffset;
int classDataOffset = dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
if (classDataOffset == 0) {
staticFieldsOffset = -1;
staticFieldCount = 0;
instanceFieldCount = 0;
directMethodCount = 0;
virtualMethodCount = 0;
} else {
DexReader reader = dexFile.getDataBuffer().readerAt(classDataOffset);
staticFieldCount = reader.readSmallUleb128();
instanceFieldCount = reader.readSmallUleb128();
directMethodCount = reader.readSmallUleb128();
virtualMethodCount = reader.readSmallUleb128();
staticFieldsOffset = reader.getOffset();
}
if (hiddenApiRestrictionsOffset != NO_OFFSET) {
hiddenApiRestrictionsReader = new HiddenApiRestrictionsReader(hiddenApiRestrictionsOffset);
} else {
hiddenApiRestrictionsReader = null;
}
}
@Nonnull
@Override
public String getType() {
return dexFile.getTypeSection().get(
dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.CLASS_OFFSET));
}
@Nullable
@Override
public String getSuperclass() {
return dexFile.getTypeSection().getOptional(
dexFile.getBuffer().readOptionalUint(classDefOffset + ClassDefItem.SUPERCLASS_OFFSET));
}
@Override
public int getAccessFlags() {
return dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.ACCESS_FLAGS_OFFSET);
}
@Nullable
@Override
public String getSourceFile() {
return dexFile.getStringSection().getOptional(
dexFile.getBuffer().readOptionalUint(classDefOffset + ClassDefItem.SOURCE_FILE_OFFSET));
}
@Nonnull
@Override
public List<String> getInterfaces() {
final int interfacesOffset =
dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.INTERFACES_OFFSET);
if (interfacesOffset > 0) {
final int size = dexFile.getDataBuffer().readSmallUint(interfacesOffset);
return new AbstractList<String>() {
@Override
@Nonnull
public String get(int index) {
return dexFile.getTypeSection().get(
dexFile.getDataBuffer().readUshort(interfacesOffset + 4 + (2*index)));
}
@Override public int size() { return size; }
};
}
return ImmutableList.of();
}
@Nonnull
@Override
public Set<? extends DexBackedAnnotation> getAnnotations() {
return getAnnotationsDirectory().getClassAnnotations();
}
@Nonnull
@Override
public Iterable<? extends DexBackedField> getStaticFields() {
return getStaticFields(true);
}
@Nonnull
public Iterable<? extends DexBackedField> getStaticFields(final boolean skipDuplicates) {
if (staticFieldCount > 0) {
DexReader<? extends DexBuffer> reader = dexFile.getDataBuffer().readerAt(staticFieldsOffset);
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int staticInitialValuesOffset =
dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET);
final int fieldsStartOffset = reader.getOffset();
Iterator<Integer> hiddenApiRestrictionIterator = hiddenApiRestrictionsReader == null ?
null : hiddenApiRestrictionsReader.getRestrictionsForStaticFields();
return new Iterable<DexBackedField>() {
@Nonnull
@Override
public Iterator<DexBackedField> iterator() {
final AnnotationsDirectory.AnnotationIterator annotationIterator =
annotationsDirectory.getFieldAnnotationIterator();
final EncodedArrayItemIterator staticInitialValueIterator =
EncodedArrayItemIterator.newOrEmpty(dexFile, staticInitialValuesOffset);
return new VariableSizeLookaheadIterator<DexBackedField>(
dexFile.getDataBuffer(), fieldsStartOffset) {
private int count;
@Nullable private FieldReference previousField;
private int previousIndex;
@Nullable
@Override
protected DexBackedField readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > staticFieldCount) {
instanceFieldsOffset = reader.getOffset();
return endOfData();
}
int hiddenApiRestrictions = NO_HIDDEN_API_RESTRICTIONS;
if (hiddenApiRestrictionIterator != null) {
hiddenApiRestrictions = hiddenApiRestrictionIterator.next();
}
DexBackedField item = new DexBackedField(dexFile, reader, DexBackedClassDef.this,
previousIndex, staticInitialValueIterator, annotationIterator,
hiddenApiRestrictions);
FieldReference currentField = previousField;
FieldReference nextField = ImmutableFieldReference.of(item);
previousField = nextField;
previousIndex = item.fieldIndex;
if (skipDuplicates && currentField != null && currentField.equals(nextField)) {
continue;
}
return item;
}
}
};
}
};
} else {
instanceFieldsOffset = staticFieldsOffset;
return ImmutableSet.of();
}
}
@Nonnull
@Override
public Iterable<? extends DexBackedField> getInstanceFields() {
return getInstanceFields(true);
}
@Nonnull
public Iterable<? extends DexBackedField> getInstanceFields(final boolean skipDuplicates) {
if (instanceFieldCount > 0) {
DexReader reader = dexFile.getDataBuffer().readerAt(getInstanceFieldsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int fieldsStartOffset = reader.getOffset();
Iterator<Integer> hiddenApiRestrictionIterator = hiddenApiRestrictionsReader == null ?
null : hiddenApiRestrictionsReader.getRestrictionsForInstanceFields();
return new Iterable<DexBackedField>() {
@Nonnull
@Override
public Iterator<DexBackedField> iterator() {
final AnnotationsDirectory.AnnotationIterator annotationIterator =
annotationsDirectory.getFieldAnnotationIterator();
return new VariableSizeLookaheadIterator<DexBackedField>(
dexFile.getDataBuffer(), fieldsStartOffset) {
private int count;
@Nullable private FieldReference previousField;
private int previousIndex;
@Nullable
@Override
protected DexBackedField readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > instanceFieldCount) {
directMethodsOffset = reader.getOffset();
return endOfData();
}
int hiddenApiRestrictions = NO_HIDDEN_API_RESTRICTIONS;
if (hiddenApiRestrictionIterator != null) {
hiddenApiRestrictions = hiddenApiRestrictionIterator.next();
}
DexBackedField item = new DexBackedField(dexFile, reader, DexBackedClassDef.this,
previousIndex, annotationIterator, hiddenApiRestrictions);
FieldReference currentField = previousField;
FieldReference nextField = ImmutableFieldReference.of(item);
previousField = nextField;
previousIndex = item.fieldIndex;
if (skipDuplicates && currentField != null && currentField.equals(nextField)) {
continue;
}
return item;
}
}
};
}
};
} else {
if (instanceFieldsOffset > 0) {
directMethodsOffset = instanceFieldsOffset;
}
return ImmutableSet.of();
}
}
@Nonnull
@Override
public Iterable<? extends DexBackedField> getFields() {
return Iterables.concat(getStaticFields(), getInstanceFields());
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getDirectMethods() {
return getDirectMethods(true);
}
@Nonnull
public Iterable<? extends DexBackedMethod> getDirectMethods(final boolean skipDuplicates) {
if (directMethodCount > 0) {
DexReader reader = dexFile.getDataBuffer().readerAt(getDirectMethodsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int methodsStartOffset = reader.getOffset();
Iterator<Integer> hiddenApiRestrictionIterator = hiddenApiRestrictionsReader == null ?
null : hiddenApiRestrictionsReader.getRestrictionsForDirectMethods();
return new Iterable<DexBackedMethod>() {
@Nonnull
@Override
public Iterator<DexBackedMethod> iterator() {
final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
return new VariableSizeLookaheadIterator<DexBackedMethod>(
dexFile.getDataBuffer(), methodsStartOffset) {
private int count;
@Nullable private MethodReference previousMethod;
private int previousIndex;
@Nullable
@Override
protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > directMethodCount) {
virtualMethodsOffset = reader.getOffset();
return endOfData();
}
int hiddenApiRestrictions = NO_HIDDEN_API_RESTRICTIONS;
if (hiddenApiRestrictionIterator != null) {
hiddenApiRestrictions = hiddenApiRestrictionIterator.next();
}
DexBackedMethod item = new DexBackedMethod(dexFile, reader, DexBackedClassDef.this,
previousIndex, methodAnnotationIterator, parameterAnnotationIterator,
hiddenApiRestrictions);
MethodReference currentMethod = previousMethod;
MethodReference nextMethod = ImmutableMethodReference.of(item);
previousMethod = nextMethod;
previousIndex = item.methodIndex;
if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) {
continue;
}
return item;
}
}
};
}
};
} else {
if (directMethodsOffset > 0) {
virtualMethodsOffset = directMethodsOffset;
}
return ImmutableSet.of();
}
}
@Nonnull
public Iterable<? extends DexBackedMethod> getVirtualMethods(final boolean skipDuplicates) {
if (virtualMethodCount > 0) {
DexReader reader = dexFile.getDataBuffer().readerAt(getVirtualMethodsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int methodsStartOffset = reader.getOffset();
Iterator<Integer> hiddenApiRestrictionIterator = hiddenApiRestrictionsReader == null ?
null : hiddenApiRestrictionsReader.getRestrictionsForVirtualMethods();
return new Iterable<DexBackedMethod>() {
final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
@Nonnull
@Override
public Iterator<DexBackedMethod> iterator() {
return new VariableSizeLookaheadIterator<DexBackedMethod>(
dexFile.getDataBuffer(), methodsStartOffset) {
private int count;
@Nullable private MethodReference previousMethod;
private int previousIndex;
@Nullable
@Override
protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > virtualMethodCount) {
return endOfData();
}
int hiddenApiRestrictions = NO_HIDDEN_API_RESTRICTIONS;
if (hiddenApiRestrictionIterator != null) {
hiddenApiRestrictions = hiddenApiRestrictionIterator.next();
}
DexBackedMethod item = new DexBackedMethod(dexFile, reader, DexBackedClassDef.this,
previousIndex, methodAnnotationIterator, parameterAnnotationIterator,
hiddenApiRestrictions);
MethodReference currentMethod = previousMethod;
MethodReference nextMethod = ImmutableMethodReference.of(item);
previousMethod = nextMethod;
previousIndex = item.methodIndex;
if (skipDuplicates && currentMethod != null && currentMethod.equals(nextMethod)) {
continue;
}
return item;
}
}
};
}
};
} else {
return ImmutableSet.of();
}
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getVirtualMethods() {
return getVirtualMethods(true);
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getMethods() {
return Iterables.concat(getDirectMethods(), getVirtualMethods());
}
private AnnotationsDirectory getAnnotationsDirectory() {
if (annotationsDirectory == null) {
int annotationsDirectoryOffset =
dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.ANNOTATIONS_OFFSET);
annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexFile, annotationsDirectoryOffset);
}
return annotationsDirectory;
}
private int getInstanceFieldsOffset() {
if (instanceFieldsOffset > 0) {
return instanceFieldsOffset;
}
DexReader reader = dexFile.getDataBuffer().readerAt(staticFieldsOffset);
DexBackedField.skipFields(reader, staticFieldCount);
instanceFieldsOffset = reader.getOffset();
return instanceFieldsOffset;
}
private int getDirectMethodsOffset() {
if (directMethodsOffset > 0) {
return directMethodsOffset;
}
DexReader reader = dexFile.getDataBuffer().readerAt(getInstanceFieldsOffset());
DexBackedField.skipFields(reader, instanceFieldCount);
directMethodsOffset = reader.getOffset();
return directMethodsOffset;
}
private int getVirtualMethodsOffset() {
if (virtualMethodsOffset > 0) {
return virtualMethodsOffset;
}
DexReader reader = dexFile.getDataBuffer().readerAt(getDirectMethodsOffset());
DexBackedMethod.skipMethods(reader, directMethodCount);
virtualMethodsOffset = reader.getOffset();
return virtualMethodsOffset;
}
public int getSize() {
int size = 8 * 4;
size += TypeIdItem.ITEM_SIZE;
int interfacesLength = getInterfaces().size();
if (interfacesLength > 0) {
size += 4;
size += interfacesLength * 2;
}
AnnotationsDirectory directory = getAnnotationsDirectory();
if (!AnnotationsDirectory.EMPTY.equals(directory)) {
size += 4 * 4;
Set<? extends DexBackedAnnotation> classAnnotations = directory.getClassAnnotations();
if (!classAnnotations.isEmpty()) {
size += 4;
size += classAnnotations.size() * 4;
}
}
int staticInitialValuesOffset =
dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET);
if (staticInitialValuesOffset != 0) {
DexReader reader = dexFile.getDataBuffer().readerAt(staticInitialValuesOffset);
size += reader.peekSmallUleb128Size();
}
int classDataOffset = dexFile.getBuffer().readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
if (classDataOffset > 0) {
DexReader reader = dexFile.getDataBuffer().readerAt(classDataOffset);
reader.readSmallUleb128();
reader.readSmallUleb128();
reader.readSmallUleb128();
reader.readSmallUleb128();
size += reader.getOffset() - classDataOffset;
}
for (DexBackedField dexBackedField : getFields()) {
size += dexBackedField.getSize();
}
for (DexBackedMethod dexBackedMethod : getMethods()) {
size += dexBackedMethod.getSize();
}
return size;
}
private class HiddenApiRestrictionsReader {
private final int startOffset;
private int instanceFieldsStartOffset;
private int directMethodsStartOffset;
private int virtualMethodsStartOffset;
public HiddenApiRestrictionsReader(int startOffset) {
this.startOffset = startOffset;
}
private VariableSizeListIterator<Integer> getRestrictionsForStaticFields() {
return new VariableSizeListIterator<Integer>(
dexFile.getDataBuffer(), startOffset, staticFieldCount) {
@Override protected Integer readNextItem(
@Nonnull DexReader<? extends DexBuffer> reader, int index) {
return reader.readSmallUleb128();
}
@Override public Integer next() {
if (nextIndex() == staticFieldCount) {
instanceFieldsStartOffset = getReaderOffset();
}
return super.next();
}
};
}
private int getInstanceFieldsStartOffset() {
if (instanceFieldsStartOffset == NO_OFFSET) {
DexReader<? extends DexBuffer> reader = dexFile.getDataBuffer().readerAt(startOffset);
for (int i = 0; i < staticFieldCount; i++) {
reader.readSmallUleb128();
}
instanceFieldsStartOffset = reader.getOffset();
}
return instanceFieldsStartOffset;
}
private Iterator<Integer> getRestrictionsForInstanceFields() {
return new VariableSizeListIterator<Integer>(
dexFile.getDataBuffer(), getInstanceFieldsStartOffset(), instanceFieldCount) {
@Override protected Integer readNextItem(
@Nonnull DexReader<? extends DexBuffer> reader, int index) {
return reader.readSmallUleb128();
}
@Override public Integer next() {
if (nextIndex() == instanceFieldCount) {
directMethodsStartOffset = getReaderOffset();
}
return super.next();
}
};
}
private int getDirectMethodsStartOffset() {
if (directMethodsStartOffset == NO_OFFSET) {
DexReader<? extends DexBuffer> reader = dexFile.getDataBuffer().readerAt(getInstanceFieldsStartOffset());
for (int i = 0; i < instanceFieldCount; i++) {
reader.readSmallUleb128();
}
directMethodsStartOffset = reader.getOffset();
}
return directMethodsStartOffset;
}
private Iterator<Integer> getRestrictionsForDirectMethods() {
return new VariableSizeListIterator<Integer>(
dexFile.getDataBuffer(), getDirectMethodsStartOffset(), directMethodCount) {
@Override protected Integer readNextItem(
@Nonnull DexReader<? extends DexBuffer> reader, int index) {
return reader.readSmallUleb128();
}
@Override public Integer next() {
if (nextIndex() == directMethodCount) {
virtualMethodsStartOffset = getReaderOffset();
}
return super.next();
}
};
}
private int getVirtualMethodsStartOffset() {
if (virtualMethodsStartOffset == NO_OFFSET) {
DexReader<? extends DexBuffer> reader = dexFile.getDataBuffer().readerAt(getDirectMethodsStartOffset());
for (int i = 0; i < directMethodCount; i++) {
reader.readSmallUleb128();
}
virtualMethodsStartOffset = reader.getOffset();
}
return virtualMethodsStartOffset;
}
private Iterator<Integer> getRestrictionsForVirtualMethods() {
return new VariableSizeListIterator<Integer>(
dexFile.getDataBuffer(), getVirtualMethodsStartOffset(), virtualMethodCount) {
@Override protected Integer readNextItem(
@Nonnull DexReader<? extends DexBuffer> reader, int index) {
return reader.readSmallUleb128();
}
};
}
}
}