Separate direct/virtual methods and static/instance fields in the ClassDef interface

This is unfortunately required to support not-quite-well-formed dex files
containing duplicate static/instance fields, or duplicate direct/virtual methods,
which dalvik inadvertently allows.

In cases when there are duplicate fields/methods in the same category, we
unambiguously remove/hide the latter duplicate fields/methods.
This commit is contained in:
Ben Gruver 2013-04-13 14:32:08 -07:00
parent 5b99529feb
commit 0a18ea7f8b
19 changed files with 799 additions and 327 deletions

View File

@ -67,7 +67,7 @@ public class ClassDefinition {
private HashSet<String> findFieldsSetInStaticConstructor() {
HashSet<String> fieldsSetInStaticConstructor = new HashSet<String>();
for (Method method: classDef.getMethods()) {
for (Method method: classDef.getDirectMethods()) {
if (method.getName().equals("<clinit>")) {
MethodImplementation impl = method.getImplementation();
if (impl != null) {
@ -165,93 +165,77 @@ public class ClassDefinition {
private void writeStaticFields(IndentingWriter writer) throws IOException {
boolean wroteHeader = false;
for (Field field: classDef.getFields()) {
if (AccessFlags.STATIC.isSet(field.getAccessFlags())) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# static fields");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate fields.
boolean setInStaticConstructor =
fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field));
FieldDefinition.writeTo(writer, field, setInStaticConstructor);
for (Field field: classDef.getStaticFields()) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# static fields");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate fields.
boolean setInStaticConstructor =
fieldsSetInStaticConstructor.contains(ReferenceUtil.getShortFieldDescriptor(field));
FieldDefinition.writeTo(writer, field, setInStaticConstructor);
}
}
private void writeInstanceFields(IndentingWriter writer) throws IOException {
boolean wroteHeader = false;
for (Field field: classDef.getFields()) {
if (!AccessFlags.STATIC.isSet(field.getAccessFlags())) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# instance fields");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate fields.
FieldDefinition.writeTo(writer, field, false);
for (Field field: classDef.getInstanceFields()) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# instance fields");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate fields.
FieldDefinition.writeTo(writer, field, false);
}
}
private void writeDirectMethods(IndentingWriter writer) throws IOException {
boolean wroteHeader = false;
for (Method method: classDef.getMethods()) {
int accessFlags = method.getAccessFlags();
for (Method method: classDef.getDirectMethods()) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# direct methods");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate methods.
// TODO: check for method validation errors
if (AccessFlags.STATIC.isSet(accessFlags) ||
AccessFlags.PRIVATE.isSet(accessFlags) ||
AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# direct methods");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate methods.
// TODO: check for method validation errors
MethodImplementation methodImpl = method.getImplementation();
if (methodImpl == null) {
MethodDefinition.writeEmptyMethodTo(writer, method);
} else {
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
methodDefinition.writeTo(writer);
}
MethodImplementation methodImpl = method.getImplementation();
if (methodImpl == null) {
MethodDefinition.writeEmptyMethodTo(writer, method);
} else {
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
methodDefinition.writeTo(writer);
}
}
}
private void writeVirtualMethods(IndentingWriter writer) throws IOException {
boolean wroteHeader = false;
for (Method method: classDef.getMethods()) {
int accessFlags = method.getAccessFlags();
for (Method method: classDef.getVirtualMethods()) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# virtual methods");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate methods.
// TODO: check for method validation errors
if (!AccessFlags.STATIC.isSet(accessFlags) &&
!AccessFlags.PRIVATE.isSet(accessFlags) &&
!AccessFlags.CONSTRUCTOR.isSet(accessFlags)) {
if (!wroteHeader) {
writer.write("\n\n");
writer.write("# virtual methods");
wroteHeader = true;
}
writer.write('\n');
// TODO: detect duplicate methods.
// TODO: check for method validation errors
MethodImplementation methodImpl = method.getImplementation();
if (methodImpl == null) {
MethodDefinition.writeEmptyMethodTo(writer, method);
} else {
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
methodDefinition.writeTo(writer);
}
MethodImplementation methodImpl = method.getImplementation();
if (methodImpl == null) {
MethodDefinition.writeEmptyMethodTo(writer, method);
} else {
MethodDefinition methodDefinition = new MethodDefinition(this, method, methodImpl);
methodDefinition.writeTo(writer);
}
}
}

View File

@ -0,0 +1,22 @@
.class public LDuplicateStaticFields;
.super Ljava/lang/Object;
.method private alah()V
.registers 1
return-void
.end method
.method private blah()V
.registers 1
return-void
.end method
.method private blah()V
.registers 1
return-void
.end method
.method private clah()V
.registers 1
return-void
.end method

View File

@ -0,0 +1,32 @@
.class public LDuplicateStaticFields;
.super Ljava/lang/Object;
.method public alah()V
.registers 1
return-void
.end method
.method private blah()V
.registers 1
return-void
.end method
.method private blah()V
.registers 1
return-void
.end method
.method public blah()V
.registers 1
return-void
.end method
.method public blah()V
.registers 1
return-void
.end method
.method public clah()V
.registers 1
return-void
.end method

View File

@ -0,0 +1,9 @@
.class public LDuplicateInstanceFields;
.super Ljava/lang/Object;
.field public alah:I
.field public blah:I
.field public blah:I
.field public clah:I

View File

@ -0,0 +1,9 @@
.class public LDuplicateStaticFields;
.super Ljava/lang/Object;
.field public static alah:I
.field public static blah:I
.field public static blah:I
.field public static clah:I

View File

@ -0,0 +1,22 @@
.class public LDuplicateStaticFields;
.super Ljava/lang/Object;
.method public alah()V
.registers 1
return-void
.end method
.method public blah()V
.registers 1
return-void
.end method
.method public blah()V
.registers 1
return-void
.end method
.method public clah()V
.registers 1
return-void
.end method

Binary file not shown.

View File

@ -32,6 +32,7 @@
package org.jf.dexlib2.analysis.reflection;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
@ -104,6 +105,50 @@ public class ReflectionClassDef extends BaseTypeReference implements ClassDef {
return ImmutableSet.of();
}
@Nonnull @Override public Iterable<? extends Field> getStaticFields() {
return new Iterable<Field>() {
@Nonnull @Override public Iterator<Field> iterator() {
Iterator<java.lang.reflect.Field> staticFields = Iterators.filter(
Iterators.forArray(cls.getDeclaredFields()),
new Predicate<java.lang.reflect.Field>() {
@Override public boolean apply(@Nullable java.lang.reflect.Field input) {
return input!=null && Modifier.isStatic(input.getModifiers());
}
});
return Iterators.transform(staticFields,
new Function<java.lang.reflect.Field, Field>() {
@Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) {
return new ReflectionField(input);
}
}
);
}
};
}
@Nonnull @Override public Iterable<? extends Field> getInstanceFields() {
return new Iterable<Field>() {
@Nonnull @Override public Iterator<Field> iterator() {
Iterator<java.lang.reflect.Field> staticFields = Iterators.filter(
Iterators.forArray(cls.getDeclaredFields()),
new Predicate<java.lang.reflect.Field>() {
@Override public boolean apply(@Nullable java.lang.reflect.Field input) {
return input!=null && !Modifier.isStatic(input.getModifiers());
}
});
return Iterators.transform(staticFields,
new Function<java.lang.reflect.Field, Field>() {
@Nullable @Override public Field apply(@Nullable java.lang.reflect.Field input) {
return new ReflectionField(input);
}
}
);
}
};
}
@Nonnull @Override public Set<? extends Field> getFields() {
return new AbstractSet<Field>() {
@Nonnull @Override public Iterator<Field> iterator() {
@ -121,6 +166,58 @@ public class ReflectionClassDef extends BaseTypeReference implements ClassDef {
};
}
private static final int DIRECT_MODIFIERS = Modifier.PRIVATE | Modifier.STATIC;
@Nonnull @Override public Iterable<? extends Method> getDirectMethods() {
return new Iterable<Method>() {
@Nonnull @Override public Iterator<Method> iterator() {
Iterator<Method> constructorIterator =
Iterators.transform(Iterators.forArray(cls.getDeclaredConstructors()),
new Function<Constructor, Method>() {
@Nullable @Override public Method apply(@Nullable Constructor input) {
return new ReflectionConstructor(input);
}
});
Iterator<java.lang.reflect.Method> directMethods = Iterators.filter(
Iterators.forArray(cls.getDeclaredMethods()),
new Predicate<java.lang.reflect.Method>() {
@Override public boolean apply(@Nullable java.lang.reflect.Method input) {
return input != null && (input.getModifiers() & DIRECT_MODIFIERS) != 0;
}
});
Iterator<Method> methodIterator = Iterators.transform(directMethods,
new Function<java.lang.reflect.Method, Method>() {
@Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) {
return new ReflectionMethod(input);
}
});
return Iterators.concat(constructorIterator, methodIterator);
}
};
}
@Nonnull @Override public Iterable<? extends Method> getVirtualMethods() {
return new Iterable<Method>() {
@Nonnull @Override public Iterator<Method> iterator() {
Iterator<java.lang.reflect.Method> directMethods = Iterators.filter(
Iterators.forArray(cls.getDeclaredMethods()),
new Predicate<java.lang.reflect.Method>() {
@Override public boolean apply(@Nullable java.lang.reflect.Method input) {
return input != null && (input.getModifiers() & DIRECT_MODIFIERS) == 0;
}
});
return Iterators.transform(directMethods,
new Function<java.lang.reflect.Method, Method>() {
@Nullable @Override public Method apply(@Nullable java.lang.reflect.Method input) {
return new ReflectionMethod(input);
}
});
}
};
}
@Nonnull @Override public Set<? extends Method> getMethods() {
return new AbstractSet<Method>() {
@Nonnull @Override public Iterator<Method> iterator() {

View File

@ -32,17 +32,21 @@
package org.jf.dexlib2.dexbacked;
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.util.AnnotationsDirectory;
import org.jf.dexlib2.dexbacked.util.FixedSizeSet;
import org.jf.dexlib2.dexbacked.util.StaticInitialValueIterator;
import org.jf.dexlib2.dexbacked.util.VariableSizeIterator;
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.AbstractSet;
import java.util.Iterator;
import java.util.Set;
@ -50,7 +54,16 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
@Nonnull public final DexBackedDexFile dexFile;
private final int classDefOffset;
private int classDataOffset = -1;
private final int classDataOffset;
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;
@ -58,6 +71,23 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
int classDefOffset) {
this.dexFile = dexFile;
this.classDefOffset = classDefOffset;
this.classDataOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
if (classDataOffset == 0) {
staticFieldsOffset = -1;
staticFieldCount = 0;
instanceFieldCount = 0;
directMethodCount = 0;
virtualMethodCount = 0;
} else {
DexReader reader = dexFile.readerAt(classDataOffset);
staticFieldCount = reader.readSmallUleb128();
instanceFieldCount = reader.readSmallUleb128();
directMethodCount = reader.readSmallUleb128();
virtualMethodCount = reader.readSmallUleb128();
staticFieldsOffset = reader.getOffset();
}
}
@Nonnull
@ -110,115 +140,249 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
@Nonnull
@Override
public Set<? extends DexBackedField> getFields() {
int classDataOffset = getClassDataOffset();
if (getClassDataOffset() != 0) {
DexReader reader = dexFile.readerAt(classDataOffset);
final int staticFieldCount = reader.readSmallUleb128();
int instanceFieldCount = reader.readSmallUleb128();
final int fieldCount = staticFieldCount + instanceFieldCount;
if (fieldCount > 0) {
reader.skipUleb128(); //direct_methods_size
reader.skipUleb128(); //virtual_methods_size
public Iterable<? extends DexBackedField> getStaticFields() {
if (staticFieldCount > 0) {
DexReader reader = dexFile.readerAt(staticFieldsOffset);
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int staticInitialValuesOffset =
dexFile.readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET);
final int fieldsStartOffset = reader.getOffset();
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int staticInitialValuesOffset =
dexFile.readSmallUint(classDefOffset + ClassDefItem.STATIC_VALUES_OFFSET);
final int fieldsStartOffset = reader.getOffset();
final AnnotationsDirectory.AnnotationIterator annotationIterator =
annotationsDirectory.getFieldAnnotationIterator();
final StaticInitialValueIterator staticInitialValueIterator =
StaticInitialValueIterator.newOrEmpty(dexFile, staticInitialValuesOffset);
return new AbstractSet<DexBackedField>() {
@Nonnull
@Override
public Iterator<DexBackedField> iterator() {
return new VariableSizeIterator<DexBackedField>(dexFile, fieldsStartOffset, fieldCount) {
private int previousFieldIndex = 0;
@Nonnull private final AnnotationsDirectory.AnnotationIterator annotationIterator =
annotationsDirectory.getFieldAnnotationIterator();
@Nonnull private final StaticInitialValueIterator staticInitialValueIterator =
StaticInitialValueIterator.newOrEmpty(dexFile, staticInitialValuesOffset);
@Nonnull
@Override
protected DexBackedField readNextItem(@Nonnull DexReader reader, int index) {
if (index == staticFieldCount) {
// We reached the end of the static field, restart the numbering for
// instance fields
previousFieldIndex = 0;
annotationIterator.reset();
return new Iterable<DexBackedField>() {
@Nonnull
@Override
public Iterator<DexBackedField> iterator() {
return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) {
private int count;
// TODO: consider implementing and using two MutableFieldReference classes.
// this would prevent creating a lot of throw-away ImmutableFieldReference objects while
// iterating
@Nullable private FieldReference previousField;
private int previousIndex;
@Nullable
@Override
protected DexBackedField readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > staticFieldCount) {
instanceFieldsOffset = reader.getOffset();
return null;
}
DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this,
previousFieldIndex, staticInitialValueIterator, annotationIterator);
previousFieldIndex = item.fieldIndex;
previousIndex, staticInitialValueIterator, annotationIterator);
FieldReference currentField = previousField;
FieldReference nextField = ImmutableFieldReference.of(item);
previousField = nextField;
previousIndex = item.fieldIndex;
if (currentField != null && currentField.equals(nextField)) {
continue;
}
return item;
}
};
}
@Override public int size() { return fieldCount; }
};
}
}
};
}
};
} else {
instanceFieldsOffset = staticFieldsOffset;
return ImmutableSet.of();
}
return ImmutableSet.of();
}
@Nonnull
@Override
public Set<? extends DexBackedMethod> getMethods() {
int classDataOffset = getClassDataOffset();
if (classDataOffset > 0) {
DexReader reader = dexFile.readerAt(classDataOffset);
int staticFieldCount = reader.readSmallUleb128();
int instanceFieldCount = reader.readSmallUleb128();
final int directMethodCount = reader.readSmallUleb128();
int virtualMethodCount = reader.readSmallUleb128();
final int methodCount = directMethodCount + virtualMethodCount;
if (methodCount > 0) {
DexBackedField.skipAllFields(reader, staticFieldCount + instanceFieldCount);
public Iterable<? extends DexBackedField> getInstanceFields() {
if (instanceFieldCount > 0) {
DexReader reader = dexFile.readerAt(getInstanceFieldsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final int methodsStartOffset = reader.getOffset();
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final AnnotationsDirectory.AnnotationIterator annotationIterator =
annotationsDirectory.getFieldAnnotationIterator();
final int fieldsStartOffset = reader.getOffset();
return new AbstractSet<DexBackedMethod>() {
@Nonnull
@Override
public Iterator<DexBackedMethod> iterator() {
return new VariableSizeIterator<DexBackedMethod>(dexFile, methodsStartOffset, methodCount) {
private int previousMethodIndex = 0;
@Nonnull private final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
@Nonnull private final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
return new Iterable<DexBackedField>() {
@Nonnull
@Override
public Iterator<DexBackedField> iterator() {
return new VariableSizeLookaheadIterator<DexBackedField>(dexFile, fieldsStartOffset) {
private int count;
// TODO: consider implementing and using two MutableFieldReference classes.
// this would prevent creating a lot of throw-away ImmutableFieldReference objects while
// iterating
@Nullable private FieldReference previousField;
private int previousIndex;
@Nonnull
@Override
protected DexBackedMethod readNextItem(@Nonnull DexReader reader, int index) {
if (index == directMethodCount) {
// We reached the end of the direct methods, restart the numbering for
// virtual methods
previousMethodIndex = 0;
methodAnnotationIterator.reset();
parameterAnnotationIterator.reset();
@Nullable
@Override
protected DexBackedField readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > instanceFieldCount) {
directMethodsOffset = reader.getOffset();
return null;
}
DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
previousMethodIndex, methodAnnotationIterator, parameterAnnotationIterator);
previousMethodIndex = item.methodIndex;
DexBackedField item = new DexBackedField(reader, DexBackedClassDef.this,
previousIndex, annotationIterator);
FieldReference currentField = previousField;
FieldReference nextField = ImmutableFieldReference.of(item);
previousField = nextField;
previousIndex = item.fieldIndex;
if (currentField != null && currentField.equals(nextField)) {
continue;
}
return item;
}
};
}
@Override public int size() { return methodCount; }
};
}
};
}
};
} else {
if (instanceFieldsOffset > 0) {
directMethodsOffset = instanceFieldsOffset;
}
return ImmutableSet.of();
}
return ImmutableSet.of();
}
private int getClassDataOffset() {
if (classDataOffset == -1) {
classDataOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
@Nonnull
@Override
public Iterable<? extends DexBackedField> getFields() {
return Iterables.concat(getStaticFields(), getInstanceFields());
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getDirectMethods() {
if (directMethodCount > 0) {
DexReader reader = dexFile.readerAt(getDirectMethodsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
final int methodsStartOffset = reader.getOffset();
return new Iterable<DexBackedMethod>() {
@Nonnull
@Override
public Iterator<DexBackedMethod> iterator() {
return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) {
private int count;
// TODO: consider implementing and using two MutableMethodReference classes.
// this would prevent creating a lot of throw-away ImmutableMethodReference objects while
// iterating
@Nullable private MethodReference previousMethod;
private int previousIndex;
@Nullable
@Override
protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > directMethodCount) {
virtualMethodsOffset = reader.getOffset();
return null;
}
DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
previousIndex, methodAnnotationIterator, parameterAnnotationIterator);
MethodReference currentMethod = previousMethod;
MethodReference nextMethod = ImmutableMethodReference.of(item);
previousMethod = nextMethod;
previousIndex = item.methodIndex;
if (currentMethod != null && currentMethod.equals(nextMethod)) {
continue;
}
return item;
}
}
};
}
};
} else {
if (directMethodsOffset > 0) {
virtualMethodsOffset = directMethodsOffset;
}
return ImmutableSet.of();
}
return classDataOffset;
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getVirtualMethods() {
if (virtualMethodCount > 0) {
DexReader reader = dexFile.readerAt(getVirtualMethodsOffset());
final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
final int methodsStartOffset = reader.getOffset();
return new Iterable<DexBackedMethod>() {
@Nonnull
@Override
public Iterator<DexBackedMethod> iterator() {
return new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) {
private int count;
// TODO: consider implementing and using two MutableMethodReference classes.
// this would prevent creating a lot of throw-away ImmutableMethodReference objects while
// iterating
@Nullable private MethodReference previousMethod;
private int previousIndex;
@Nullable
@Override
protected DexBackedMethod readNextItem(@Nonnull DexReader reader) {
while (true) {
if (++count > virtualMethodCount) {
return null;
}
DexBackedMethod item = new DexBackedMethod(reader, DexBackedClassDef.this,
previousIndex, methodAnnotationIterator, parameterAnnotationIterator);
MethodReference currentMethod = previousMethod;
MethodReference nextMethod = ImmutableMethodReference.of(item);
previousMethod = nextMethod;
previousIndex = item.methodIndex;
if (currentMethod != null && currentMethod.equals(nextMethod)) {
continue;
}
return item;
}
}
};
}
};
} else {
return ImmutableSet.of();
}
}
@Nonnull
@Override
public Iterable<? extends DexBackedMethod> getMethods() {
return Iterables.concat(getDirectMethods(), getVirtualMethods());
}
private AnnotationsDirectory getAnnotationsDirectory() {
@ -228,4 +392,34 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
}
return annotationsDirectory;
}
private int getInstanceFieldsOffset() {
if (instanceFieldsOffset > 0) {
return instanceFieldsOffset;
}
DexReader reader = new DexReader(dexFile, staticFieldsOffset);
DexBackedField.skipAllFields(reader, staticFieldCount);
instanceFieldsOffset = reader.getOffset();
return instanceFieldsOffset;
}
private int getDirectMethodsOffset() {
if (directMethodsOffset > 0) {
return directMethodsOffset;
}
DexReader reader = dexFile.readerAt(getInstanceFieldsOffset());
DexBackedField.skipAllFields(reader, instanceFieldCount);
directMethodsOffset = reader.getOffset();
return directMethodsOffset;
}
private int getVirtualMethodsOffset() {
if (virtualMethodsOffset > 0) {
return virtualMethodsOffset;
}
DexReader reader = dexFile.readerAt(getDirectMethodsOffset());
DexBackedField.skipAllFields(reader, instanceFieldCount);
virtualMethodsOffset = reader.getOffset();
return virtualMethodsOffset;
}
}

View File

@ -71,6 +71,21 @@ public class DexBackedField extends BaseFieldReference implements Field {
this.initialValue = staticInitialValueIterator.getNextOrNull();
}
public DexBackedField(@Nonnull DexReader reader,
@Nonnull DexBackedClassDef classDef,
int previousFieldIndex,
@Nonnull AnnotationsDirectory.AnnotationIterator annotationIterator) {
this.dexFile = reader.dexBuf;
this.classDef = classDef;
int fieldIndexDiff = reader.readSmallUleb128();
this.fieldIndex = fieldIndexDiff + previousFieldIndex;
this.accessFlags = reader.readSmallUleb128();
this.annotationSetOffset = annotationIterator.seekTo(fieldIndex);
this.initialValue = null;
}
@Nonnull
@Override
public String getName() {

View File

@ -98,20 +98,68 @@ public interface ClassDef extends TypeReference {
@Nonnull Set<? extends Annotation> getAnnotations();
/**
* Gets a set of the fields that are defined by this class.
* Gets the static fields that are defined by this class.
*
* TODO: uniqueness?
* The static fields that are returned must have no duplicates.
*
* @return The static fields that are defined by this class
*/
@Nonnull Iterable<? extends Field> getStaticFields();
/**
* Gets the instance fields that are defined by this class.
*
* The instance fields that are returned must have no duplicates.
*
* @return The instance fields that are defined by this class
*/
@Nonnull Iterable<? extends Field> getInstanceFields();
/**
* Gets all the fields that are defined by this class.
*
* This is a convenience method that combines getStaticFields() and getInstanceFields()
*
* The returned fields may be in any order. I.e. It's not safe to assume that all instance fields will come after
* all static fields.
*
* Note that there typically should not be any duplicate fields between the two, but some versions of
* dalvik inadvertently allow duplicate static/instance fields, and are supported here for completeness
*
* @return A set of the fields that are defined by this class
*/
@Nonnull Set<? extends Field> getFields();
@Nonnull Iterable<? extends Field> getFields();
/**
* Gets a set of the methods that are defined by this class.
* Gets the direct methods that are defined by this class.
*
* TODO: uniqueness?
* The direct methods that are returned must have no duplicates.
*
* @return A set of the methods that are defined by this class.
* @return The direct methods that are defined by this class.
*/
@Nonnull Set<? extends Method> getMethods();
@Nonnull Iterable<? extends Method> getDirectMethods();
/**
* Gets the virtual methods that are defined by this class.
*
* The virtual methods that are returned must have no duplicates.
*
* @return The virtual methods that are defined by this class.
*/
@Nonnull Iterable<? extends Method> getVirtualMethods();
/**
* Gets all the methods that are defined by this class.
*
* This is a convenience method that combines getDirectMethods() and getVirtualMethods().
*
* The returned methods may be in any order. I.e. It's not safe to assume that all virtual methods will come after
* all direct methods.
*
* Note that there typically should not be any duplicate methods between the two, but some versions of
* dalvik inadvertently allow duplicate direct/virtual methods, and are supported here for completeness
*
* @return An iterable of the methods that are defined by this class.
*/
@Nonnull Iterable<? extends Method> getMethods();
}

View File

@ -32,6 +32,8 @@
package org.jf.dexlib2.immutable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterators;
import org.jf.dexlib2.base.reference.BaseTypeReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.ClassDef;
@ -42,7 +44,9 @@ import org.jf.util.ImmutableUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
@Nonnull protected final String type;
@ -51,8 +55,10 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
@Nonnull protected final ImmutableSet<String> interfaces;
@Nullable protected final String sourceFile;
@Nonnull protected final ImmutableSet<? extends ImmutableAnnotation> annotations;
@Nonnull protected final ImmutableSet<? extends ImmutableField> fields;
@Nonnull protected final ImmutableSet<? extends ImmutableMethod> methods;
@Nonnull protected final ImmutableSortedSet<? extends ImmutableField> staticFields;
@Nonnull protected final ImmutableSortedSet<? extends ImmutableField> instanceFields;
@Nonnull protected final ImmutableSortedSet<? extends ImmutableMethod> directMethods;
@Nonnull protected final ImmutableSortedSet<? extends ImmutableMethod> virtualMethods;
public ImmutableClassDef(@Nonnull String type,
int accessFlags,
@ -60,16 +66,20 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
@Nullable Collection<String> interfaces,
@Nullable String sourceFile,
@Nullable Collection<? extends Annotation> annotations,
@Nullable Collection<? extends Field> fields,
@Nullable Collection<? extends Method> methods) {
@Nullable Iterable<? extends Field> staticFields,
@Nullable Iterable<? extends Field> instanceFields,
@Nullable Iterable<? extends Method> directMethods,
@Nullable Iterable<? extends Method> virtualMethods) {
this.type = type;
this.accessFlags = accessFlags;
this.superclass = superclass;
this.interfaces = interfaces==null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(interfaces);
this.sourceFile = sourceFile;
this.annotations = ImmutableAnnotation.immutableSetOf(annotations);
this.fields = ImmutableField.immutableSetOf(fields);
this.methods = ImmutableMethod.immutableSetOf(methods);
this.staticFields = ImmutableField.immutableSetOf(staticFields);
this.instanceFields = ImmutableField.immutableSetOf(instanceFields);
this.directMethods = ImmutableMethod.immutableSetOf(directMethods);
this.virtualMethods = ImmutableMethod.immutableSetOf(virtualMethods);
}
public ImmutableClassDef(@Nonnull String type,
@ -78,16 +88,20 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
@Nullable ImmutableSet<String> interfaces,
@Nullable String sourceFile,
@Nullable ImmutableSet<? extends ImmutableAnnotation> annotations,
@Nullable ImmutableSet<? extends ImmutableField> fields,
@Nullable ImmutableSet<? extends ImmutableMethod> methods) {
@Nullable ImmutableSortedSet<? extends ImmutableField> staticFields,
@Nullable ImmutableSortedSet<? extends ImmutableField> instanceFields,
@Nullable ImmutableSortedSet<? extends ImmutableMethod> directMethods,
@Nullable ImmutableSortedSet<? extends ImmutableMethod> virtualMethods) {
this.type = type;
this.accessFlags = accessFlags;
this.superclass = superclass;
this.interfaces = ImmutableUtils.nullToEmptySet(interfaces);
this.sourceFile = sourceFile;
this.annotations = ImmutableUtils.nullToEmptySet(annotations);
this.fields = ImmutableUtils.nullToEmptySet(fields);
this.methods = ImmutableUtils.nullToEmptySet(methods);
this.staticFields = ImmutableUtils.nullToEmptySortedSet(staticFields);
this.instanceFields = ImmutableUtils.nullToEmptySortedSet(instanceFields);
this.directMethods = ImmutableUtils.nullToEmptySortedSet(directMethods);
this.virtualMethods = ImmutableUtils.nullToEmptySortedSet(virtualMethods);
}
public static ImmutableClassDef of(ClassDef classDef) {
@ -101,8 +115,10 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
classDef.getInterfaces(),
classDef.getSourceFile(),
classDef.getAnnotations(),
classDef.getFields(),
classDef.getMethods());
classDef.getStaticFields(),
classDef.getInstanceFields(),
classDef.getDirectMethods(),
classDef.getVirtualMethods());
}
@Nonnull @Override public String getType() { return type; }
@ -111,8 +127,42 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
@Nonnull @Override public ImmutableSet<String> getInterfaces() { return interfaces; }
@Nullable @Override public String getSourceFile() { return sourceFile; }
@Nonnull @Override public ImmutableSet<? extends ImmutableAnnotation> getAnnotations() { return annotations; }
@Nonnull @Override public ImmutableSet<? extends ImmutableField> getFields() { return fields; }
@Nonnull @Override public ImmutableSet<? extends ImmutableMethod> getMethods() { return methods; }
@Nonnull @Override public ImmutableSet<? extends ImmutableField> getStaticFields() { return staticFields; }
@Nonnull @Override public ImmutableSet<? extends ImmutableField> getInstanceFields() { return instanceFields; }
@Nonnull @Override public ImmutableSet<? extends ImmutableMethod> getDirectMethods() { return directMethods; }
@Nonnull @Override public ImmutableSet<? extends ImmutableMethod> getVirtualMethods() { return virtualMethods; }
@Nonnull
@Override
public Collection<? extends ImmutableField> getFields() {
return new AbstractCollection<ImmutableField>() {
@Nonnull
@Override
public Iterator<ImmutableField> iterator() {
return Iterators.concat(staticFields.iterator(), instanceFields.iterator());
}
@Override public int size() {
return staticFields.size() + instanceFields.size();
}
};
}
@Nonnull
@Override
public Collection<? extends ImmutableMethod> getMethods() {
return new AbstractCollection<ImmutableMethod>() {
@Nonnull
@Override
public Iterator<ImmutableMethod> iterator() {
return Iterators.concat(directMethods.iterator(), virtualMethods.iterator());
}
@Override public int size() {
return directMethods.size() + virtualMethods.size();
}
};
}
@Nonnull
public static ImmutableSet<ImmutableClassDef> immutableSetOf(@Nullable Iterable<? extends ClassDef> iterable) {

View File

@ -32,6 +32,8 @@
package org.jf.dexlib2.immutable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import org.jf.dexlib2.base.reference.BaseFieldReference;
import org.jf.dexlib2.iface.Annotation;
import org.jf.dexlib2.iface.Field;
@ -102,8 +104,8 @@ public class ImmutableField extends BaseFieldReference implements Field {
@Nonnull @Override public ImmutableSet<? extends ImmutableAnnotation> getAnnotations() { return annotations; }
@Nonnull
public static ImmutableSet<ImmutableField> immutableSetOf(@Nullable Iterable<? extends Field> list) {
return CONVERTER.toSet(list);
public static ImmutableSortedSet<ImmutableField> immutableSetOf(@Nullable Iterable<? extends Field> list) {
return CONVERTER.toSortedSet(Ordering.natural(), list);
}
private static final ImmutableConverter<ImmutableField, Field> CONVERTER =

View File

@ -39,6 +39,19 @@ import java.io.IOException;
import java.io.Writer;
public final class ReferenceUtil {
public static String getShortMethodDescriptor(MethodReference methodReference) {
// TODO: try using a thread local StringBuilder
StringBuilder sb = new StringBuilder();
sb.append(methodReference.getName());
sb.append('(');
for (CharSequence paramType: methodReference.getParameterTypes()) {
sb.append(paramType);
}
sb.append(')');
sb.append(methodReference.getReturnType());
return sb.toString();
}
public static String getMethodDescriptor(MethodReference methodReference) {
// TODO: try using a thread local StringBuilder
StringBuilder sb = new StringBuilder();

View File

@ -32,6 +32,7 @@
package org.jf.dexlib2.writer;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.*;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
@ -42,7 +43,9 @@ import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class AnnotationDirectoryPool {
@Nonnull private final Map<Key, Integer> internedAnnotationDirectoryItems = Maps.newHashMap();
@ -72,16 +75,17 @@ public class AnnotationDirectoryPool {
}
dexFile.annotationSetPool.intern(classDef.getAnnotations());
for (Field field: key.getFieldsWithAnnotations()) {
for (Field field: Iterables.filter(classDef.getFields(), FIELD_HAS_ANNOTATION)) {
dexFile.annotationSetPool.intern(field.getAnnotations());
}
for (Method method: key.getMethodsWithAnnotations()) {
dexFile.annotationSetPool.intern(method.getAnnotations());
}
for (Method method: key.getMethodsWithParameterAnnotations()) {
dexFile.annotationSetRefPool.intern(method);
for (Method method: classDef.getMethods()) {
if (METHOD_HAS_ANNOTATION.apply(method)) {
dexFile.annotationSetPool.intern(method.getAnnotations());
}
if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) {
dexFile.annotationSetRefPool.intern(method);
}
}
}
@ -140,39 +144,33 @@ public class AnnotationDirectoryPool {
writer.writeInt(key.methodAnnotationCount);
writer.writeInt(key.parameterAnnotationCount);
Iterable<? extends Field> fieldsWithAnnotations;
if (CollectionUtils.isNaturalSortedSet(key.classDef.getFields())) {
fieldsWithAnnotations = key.getFieldsWithAnnotations();
} else {
fieldsWithAnnotations = Ordering.natural().immutableSortedCopy(key.getFieldsWithAnnotations());
}
for (Field field: fieldsWithAnnotations) {
List<? extends Field> sortedFieldsWithAnnotations = Ordering.natural().immutableSortedCopy(
Iterables.filter(key.classDef.getFields(), FIELD_HAS_ANNOTATION));
for (Field field: sortedFieldsWithAnnotations) {
writer.writeInt(dexFile.fieldPool.getIndex(field));
writer.writeInt(dexFile.annotationSetPool.getOffset(field.getAnnotations()));
}
boolean sortMethods = !CollectionUtils.isNaturalSortedSet(key.classDef.getMethods());
Iterable<? extends Method> methodsWithAnnotations;
if (sortMethods) {
methodsWithAnnotations = Ordering.natural().immutableSortedCopy(key.getMethodsWithAnnotations());
} else {
methodsWithAnnotations = key.getMethodsWithAnnotations();
}
for (Method method: methodsWithAnnotations) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetPool.getOffset(method.getAnnotations()));
List<? extends Method> sortedMethods = Ordering.natural().immutableSortedCopy(
Iterables.filter(
key.classDef.getMethods(),
Predicates.or(METHOD_HAS_ANNOTATION, METHOD_HAS_PARAMETER_ANNOTATION)));
// It's safe to assume that we don't have any duplicate methods here. We would have already caught that and
// thrown an exception
for (Method method: sortedMethods) {
if (METHOD_HAS_ANNOTATION.apply(method)) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetPool.getOffset(method.getAnnotations()));
}
}
Iterable<? extends Method> methodsWithParameterAnnotations;
if (sortMethods) {
methodsWithParameterAnnotations = Ordering.natural().immutableSortedCopy(
key.getMethodsWithParameterAnnotations());
} else {
methodsWithParameterAnnotations = key.getMethodsWithParameterAnnotations();
}
for (Method method: methodsWithParameterAnnotations) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetRefPool.getOffset(method));
for (Method method: sortedMethods) {
if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) {
writer.writeInt(dexFile.methodPool.getIndex(method));
writer.writeInt(dexFile.annotationSetRefPool.getOffset(method));
}
}
}
}
@ -207,28 +205,19 @@ public class AnnotationDirectoryPool {
public Key(@Nonnull ClassDef classDef) {
this.classDef = classDef;
this.fieldAnnotationCount = Iterables.size(getFieldsWithAnnotations());
this.methodAnnotationCount = Iterables.size(getMethodsWithAnnotations());
this.parameterAnnotationCount = Iterables.size(getMethodsWithParameterAnnotations());
}
public int getFieldAnnotationCount() { return fieldAnnotationCount; }
public int getMethodAnnotationCount() { return methodAnnotationCount; }
public int getParameterAnnotationCount() { return parameterAnnotationCount; }
@Nonnull
public Iterable<? extends Field> getFieldsWithAnnotations() {
return FluentIterable.from(classDef.getFields()).filter(FIELD_HAS_ANNOTATION);
}
@Nonnull
public Iterable<? extends Method> getMethodsWithAnnotations() {
return FluentIterable.from(classDef.getMethods()).filter(METHOD_HAS_ANNOTATION);
}
@Nonnull
public Iterable<? extends Method> getMethodsWithParameterAnnotations() {
return FluentIterable.from(classDef.getMethods()).filter(METHOD_HAS_PARAMETER_ANNOTATION);
this.fieldAnnotationCount = Iterables.size(Iterables.filter(classDef.getFields(), FIELD_HAS_ANNOTATION));
int methodAnnotationCount = 0;
int parameterAnnotationCount = 0;
for (Method method: classDef.getMethods()) {
if (METHOD_HAS_ANNOTATION.apply(method)) {
methodAnnotationCount++;
}
if (METHOD_HAS_PARAMETER_ANNOTATION.apply(method)) {
parameterAnnotationCount++;
}
}
this.methodAnnotationCount = methodAnnotationCount;
this.parameterAnnotationCount = parameterAnnotationCount;
}
public boolean hasClassAnnotations() {

View File

@ -31,17 +31,16 @@
package org.jf.dexlib2.writer;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.jf.dexlib2.iface.*;
import org.jf.dexlib2.util.FieldUtil;
import org.jf.dexlib2.util.MethodUtil;
import com.google.common.collect.*;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.util.ReferenceUtil;
import org.jf.util.ExceptionWithContext;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -73,17 +72,32 @@ public class ClassDefPool {
dexFile.typeListPool.intern(ImmutableSortedSet.copyOf(classDef.getInterfaces()));
dexFile.stringPool.internNullable(classDef.getSourceFile());
dexFile.encodedArrayPool.intern(classDef);
dexFile.annotationDirectoryPool.intern(classDef);
boolean hasClassData = false;
HashSet<String> fields = new HashSet<String>();
for (Field field: classDef.getFields()) {
hasClassData = true;
String fieldDescriptor = ReferenceUtil.getShortFieldDescriptor(field);
if (!fields.add(fieldDescriptor)) {
throw new ExceptionWithContext("Multiple definitions for field %s->%s",
classDef.getType(), fieldDescriptor);
}
dexFile.fieldPool.intern(field);
}
HashSet<String> methods = new HashSet<String>();
for (Method method: classDef.getMethods()) {
hasClassData = true;
String methodDescriptor = ReferenceUtil.getShortMethodDescriptor(method);
if (!methods.add(methodDescriptor)) {
throw new ExceptionWithContext("Multiple definitions for method %s->%s",
classDef.getType(), methodDescriptor);
}
dexFile.methodPool.intern(method);
dexFile.codeItemPool.intern(method);
}
dexFile.annotationDirectoryPool.intern(classDef);
if (hasClassData) {
classDataCount++;
}
@ -187,8 +201,6 @@ public class ClassDefPool {
private class ClassDataItem {
ClassDef classDef;
List<Field> fields;
List<Method> methods;
int numStaticFields = 0;
int numInstanceFields = 0;
@ -198,26 +210,10 @@ public class ClassDefPool {
private ClassDataItem(ClassDef classDef) {
this.classDef = classDef;
fields = Lists.newArrayList(classDef.getFields());
Collections.sort(fields);
methods = Lists.newArrayList(classDef.getMethods());
Collections.sort(methods);
for (Field field: fields) {
if (FieldUtil.isStatic(field)) {
numStaticFields++;
} else {
numInstanceFields++;
}
}
for (Method method: methods) {
if (MethodUtil.isDirect(method)) {
numDirectMethods++;
} else {
numVirtualMethods++;
}
}
numStaticFields = Iterables.size(classDef.getStaticFields());
numInstanceFields = Iterables.size(classDef.getInstanceFields());
numDirectMethods = Iterables.size(classDef.getDirectMethods());
numVirtualMethods = Iterables.size(classDef.getVirtualMethods());
}
private boolean hasData() {
@ -230,55 +226,63 @@ public class ClassDefPool {
private void writeStaticFields(DexWriter writer) throws IOException {
int lastIdx = 0;
for (Field field: fields) {
if (FieldUtil.isStatic(field)) {
int idx = dexFile.fieldPool.getIndex(field);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(field.getAccessFlags());
}
Iterable<? extends Field> sortedStaticFields =
Ordering.natural().immutableSortedCopy(classDef.getStaticFields());
for (Field field: sortedStaticFields) {
int idx = dexFile.fieldPool.getIndex(field);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(field.getAccessFlags());
}
}
private void writeInstanceFields(DexWriter writer) throws IOException {
int lastIdx = 0;
for (Field field: fields) {
if (!FieldUtil.isStatic(field)) {
int idx = dexFile.fieldPool.getIndex(field);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(field.getAccessFlags());
}
Iterable<? extends Field> sortedInstanceFields =
Ordering.natural().immutableSortedCopy(classDef.getInstanceFields());
for (Field field: sortedInstanceFields) {
int idx = dexFile.fieldPool.getIndex(field);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(field.getAccessFlags());
}
}
private void writeDirectMethods(DexWriter writer) throws IOException {
int lastIdx = 0;
for (Method method: methods) {
if (MethodUtil.isDirect(method)) {
int idx = dexFile.methodPool.getIndex(method);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(method.getAccessFlags());
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
}
Iterable<? extends Method> sortedDirectMethods =
Ordering.natural().immutableSortedCopy(classDef.getDirectMethods());
for (Method method: sortedDirectMethods) {
int idx = dexFile.methodPool.getIndex(method);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(method.getAccessFlags());
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
}
}
private void writeVirtualMethods(DexWriter writer) throws IOException {
int lastIdx = 0;
for (Method method: methods) {
if (!MethodUtil.isDirect(method)) {
int idx = dexFile.methodPool.getIndex(method);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(method.getAccessFlags());
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
}
Iterable<? extends Method> sortedVirtualMethods =
Ordering.natural().immutableSortedCopy(classDef.getVirtualMethods());
for (Method method: sortedVirtualMethods) {
int idx = dexFile.methodPool.getIndex(method);
writer.writeUleb128(idx - lastIdx);
lastIdx = idx;
writer.writeUleb128(method.getAccessFlags());
writer.writeUleb128(dexFile.codeItemPool.getOffset(method));
}
}

View File

@ -33,17 +33,13 @@ package org.jf.dexlib2.writer;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.*;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.Field;
import org.jf.dexlib2.iface.value.EncodedValue;
import org.jf.dexlib2.immutable.value.ImmutableEncodedValueFactory;
import org.jf.dexlib2.util.EncodedValueUtils;
import org.jf.dexlib2.util.FieldUtil;
import org.jf.util.CollectionUtils;
import org.jf.util.ExceptionWithContext;
@ -57,7 +53,7 @@ public class EncodedArrayPool {
@Nonnull private final DexFile dexFile;
private int sectionOffset = -1;
public EncodedArrayPool(DexFile dexFile) {
public EncodedArrayPool(@Nonnull DexFile dexFile) {
this.dexFile = dexFile;
}
@ -111,16 +107,9 @@ public class EncodedArrayPool {
}
public static class Key implements Comparable<Key> {
private final Set<? extends Field> fields;
private final List<? extends Field> fields;
private final int size;
private static class FieldComparator implements Comparator<Field> {
@Override
public int compare(Field o1, Field o2) {
return o1.compareTo(o2);
}
}
private static final Function<Field, EncodedValue> GET_INITIAL_VALUE =
new Function<Field, EncodedValue>() {
@Override
@ -133,16 +122,15 @@ public class EncodedArrayPool {
}
};
private Key(@Nonnull Set<? extends Field> fields, int size) {
private Key(@Nonnull List<? extends Field> fields, int size) {
this.fields = fields;
this.size = size;
}
@Nullable
public static Key of(@Nonnull ClassDef classDef) {
Set<? extends Field> staticFieldsSorted = FluentIterable.from(classDef.getFields())
.filter(IS_STATIC_FIELD)
.toSortedSet(new FieldComparator());
List<? extends Field> staticFieldsSorted = Ordering.natural().immutableSortedCopy(
classDef.getStaticFields());
int lastIndex = CollectionUtils.lastIndexOf(staticFieldsSorted, HAS_INITIALIZER);
if (lastIndex > -1) {
@ -187,13 +175,6 @@ public class EncodedArrayPool {
}
};
private static final Predicate<Field> IS_STATIC_FIELD = new Predicate<Field>() {
@Override
public boolean apply(Field input) {
return FieldUtil.isStatic(input);
}
};
@Override
public int compareTo(Key o) {
int res = Ints.compare(size, o.size);

View File

@ -64,7 +64,7 @@ public class CustomMethodInlineTableTest {
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
null, null, null, null, null, ImmutableList.of(method));
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
@ -90,7 +90,7 @@ public class CustomMethodInlineTableTest {
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
null, null, null, null, ImmutableList.of(method), null);
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
@ -116,7 +116,7 @@ public class CustomMethodInlineTableTest {
methodImpl);
ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
null, null, null, ImmutableList.of(method));
null, null, null, null, ImmutableList.of(method), null);
DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));

View File

@ -41,11 +41,12 @@ import javax.annotation.Nullable;
public class TestUtils {
public static ClassDef makeClassDef(@Nonnull String classType, @Nullable String superType, String... interfaces) {
return new ImmutableClassDef(classType, 0, superType, ImmutableSet.copyOf(interfaces), null, null, null, null);
return new ImmutableClassDef(classType, 0, superType, ImmutableSet.copyOf(interfaces),
null, null, null, null, null, null);
}
public static ClassDef makeInterfaceDef(@Nonnull String classType, String... interfaces) {
return new ImmutableClassDef(classType, AccessFlags.INTERFACE.getValue(), "Ljava/lang/Object;",
ImmutableSet.copyOf(interfaces), null, null, null, null);
ImmutableSet.copyOf(interfaces), null, null, null, null, null, null);
}
}