From d1662b67fecaf835227aff3a136949a2358ccd4e Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Sat, 3 Nov 2012 13:26:00 -0700 Subject: [PATCH] Refactor dexbacked implementations Made construction of a new dexbacked item as light weight as possible, only the very mimimum in computed/stored. Some of the values that were previously calculated/loaded in the constructor are now lazily loaded upon first use. In general, nothing is cached, unless the item can't be fully read by the consumer without causing the value to be computed multiple times. Otherwise, it is up to the consume to decide if/when/what they want to cache. --- .../baksmali/Adaptors/MethodDefinition.java | 10 +-- .../dexbacked/DexBackedAnnotation.java | 6 +- .../dexbacked/DexBackedAnnotationElement.java | 8 +- .../DexBackedCatchAllExceptionHandler.java | 16 ++-- .../dexlib2/dexbacked/DexBackedClassDef.java | 85 ++++++++++++------- .../dexbacked/DexBackedExceptionHandler.java | 24 +++--- .../jf/dexlib2/dexbacked/DexBackedField.java | 29 +++++-- .../jf/dexlib2/dexbacked/DexBackedMethod.java | 79 ++++++++++++----- .../DexBackedMethodImplementation.java | 65 +++++++------- .../dexlib2/dexbacked/DexBackedTryBlock.java | 23 +++-- .../dexbacked/util/VariableSizeIterator.java | 74 ++++++++++++++++ .../util/VariableSizeListWithContext.java | 10 +-- .../dexlib2/iface/MethodImplementation.java | 2 +- .../ImmutableMethodImplementation.java | 2 +- .../instruction/ImmutableInstruction.java | 2 +- .../jf/dexlib2/util/InstructionOffsetMap.java | 8 +- .../util/InstructionOffsetMapTest.java | 2 +- 17 files changed, 295 insertions(+), 150 deletions(-) create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 9d7cab99..12f3ee0a 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -28,6 +28,7 @@ package org.jf.baksmali.Adaptors; +import com.google.common.collect.ImmutableList; import org.jf.baksmali.Adaptors.Debug.DebugMethodItem; import org.jf.baksmali.Adaptors.Format.InstructionMethodItemFactory; import org.jf.dexlib2.AccessFlags; @@ -52,6 +53,7 @@ public class MethodDefinition { @Nonnull public final ClassDefinition classDef; @Nonnull public final Method method; @Nonnull public final MethodImplementation methodImpl; + @Nonnull public final ImmutableList instructions; public RegisterFormatter registerFormatter; @Nonnull private final String methodString; @@ -73,11 +75,11 @@ public class MethodDefinition { try { //TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh. - List instructions = methodImpl.getInstructions(); + instructions = ImmutableList.copyOf(methodImpl.getInstructions()); packedSwitchMap = new SparseIntArray(0); sparseSwitchMap = new SparseIntArray(0); - instructionOffsetMap = new InstructionOffsetMap(methodImpl); + instructionOffsetMap = new InstructionOffsetMap(instructions); for (int i=0; i instructions = methodImpl.getInstructions(); Instruction instruction = instructions.get(targetIndex); if (instruction.getOpcode() != type) { // maybe it's pointing to a NOP padding instruction. Look at the next instruction @@ -276,8 +277,6 @@ public class MethodDefinition { } private void addInstructionMethodItems(List methodItems) { - List instructions = methodImpl.getInstructions(); - int currentCodeAddress = 0; for (int i=0; i instructions = methodImpl.getInstructions(); int lastInstructionAddress = instructionOffsetMap.getInstructionCodeOffset(instructions.size() - 1); int codeSize = lastInstructionAddress + instructions.get(instructions.size() - 1).getCodeUnits(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotation.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotation.java index edd60642..965e48f5 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotation.java @@ -42,7 +42,7 @@ public class DexBackedAnnotation implements Annotation { @Nonnull public final DexBuffer dexBuf; public final int visibility; - @Nonnull public final String type; + public final int typeIndex; private final int elementsOffset; public DexBackedAnnotation(@Nonnull DexBuffer dexBuf, @@ -51,12 +51,12 @@ public class DexBackedAnnotation implements Annotation { DexReader reader = dexBuf.readerAt(annotationOffset); this.visibility = reader.readUbyte(); - this.type = reader.getType(reader.readSmallUleb128()); + this.typeIndex = reader.readSmallUleb128(); this.elementsOffset = reader.getOffset(); } @Override public int getVisibility() { return visibility; } - @Nonnull @Override public String getType() { return type; } + @Nonnull @Override public String getType() { return dexBuf.getType(typeIndex); } @Nonnull @Override diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotationElement.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotationElement.java index ef1f0552..dc184459 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotationElement.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedAnnotationElement.java @@ -38,11 +38,13 @@ import org.jf.dexlib2.iface.value.EncodedValue; import javax.annotation.Nonnull; public class DexBackedAnnotationElement implements AnnotationElement { - @Nonnull public final String name; + @Nonnull private final DexBuffer dexBuffer; + public final int nameIndex; @Nonnull public final EncodedValue value; public DexBackedAnnotationElement(@Nonnull DexReader reader) { - this.name = reader.getString(reader.readSmallUleb128()); + this.dexBuffer = reader.getDexBuffer(); + this.nameIndex = reader.readSmallUleb128(); this.value = DexBackedEncodedValue.readFrom(reader); } @@ -51,6 +53,6 @@ public class DexBackedAnnotationElement implements AnnotationElement { DexBackedEncodedValue.skipFrom(reader); } - @Nonnull @Override public String getName() { return name; } + @Nonnull @Override public String getName() { return dexBuffer.getString(nameIndex); } @Nonnull @Override public EncodedValue getValue() { return value; } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java index 82b6ecd1..1fe5ebef 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedCatchAllExceptionHandler.java @@ -31,16 +31,20 @@ package org.jf.dexlib2.dexbacked; -import org.jf.dexlib2.immutable.ImmutableExceptionHandler; +import org.jf.dexlib2.iface.ExceptionHandler; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class DexBackedCatchAllExceptionHandler implements ExceptionHandler { + private final int handlerCodeOffset; -public class DexBackedCatchAllExceptionHandler extends ImmutableExceptionHandler { public DexBackedCatchAllExceptionHandler(@Nonnull DexReader reader) { - super(null, reader.readSmallUleb128()); + this.handlerCodeOffset = reader.readSmallUleb128(); } - public static void skipFrom(@Nonnull DexReader reader) { - reader.skipUleb128(); - } + @Nullable @Override public String getExceptionType() { return null; } + @Override public int getHandlerCodeOffset() { return handlerCodeOffset; } + + public static void skipFrom(@Nonnull DexReader reader) { reader.skipUleb128(); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java index 8fcd0276..589137ce 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java @@ -41,19 +41,14 @@ import java.util.List; public class DexBackedClassDef implements ClassDef { @Nonnull public final DexBuffer dexBuf; + private final int classDefOffset; - @Nonnull public final String name; - public final int accessFlags; - @Nullable public final String superclass; - @Nullable public final String sourceFile; + private int classDataOffset = -1; - @Nonnull private final AnnotationsDirectory annotationsDirectory; - private final int staticInitialValuesOffset; - - private final int interfacesOffset; - private final int classDataOffset; + @Nullable private AnnotationsDirectory annotationsDirectory; //class_def_item offsets + private static final int CLASS_NAME_OFFSET = 0; private static final int ACCESS_FLAGS_OFFSET = 4; private static final int SUPERCLASS_OFFSET = 8; private static final int INTERFACES_OFFSET = 12; @@ -65,29 +60,36 @@ public class DexBackedClassDef implements ClassDef { public DexBackedClassDef(@Nonnull DexBuffer dexBuf, int classDefOffset) { this.dexBuf = dexBuf; - - this.name = dexBuf.getType(dexBuf.readSmallUint(classDefOffset)); - this.accessFlags = dexBuf.readSmallUint(classDefOffset + ACCESS_FLAGS_OFFSET); - this.superclass = dexBuf.getOptionalType(dexBuf.readOptionalUint(classDefOffset + SUPERCLASS_OFFSET)); - this.interfacesOffset = dexBuf.readSmallUint(classDefOffset + INTERFACES_OFFSET); - this.sourceFile = dexBuf.getOptionalString(dexBuf.readOptionalUint(classDefOffset + SOURCE_FILE_OFFSET)); - - int annotationsDirectoryOffset = dexBuf.readSmallUint(classDefOffset + ANNOTATIONS_OFFSET); - this.annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexBuf, annotationsDirectoryOffset); - - this.classDataOffset = dexBuf.readSmallUint(classDefOffset + CLASS_DATA_OFFSET); - this.staticInitialValuesOffset = dexBuf.readSmallUint(classDefOffset + STATIC_INITIAL_VALUES_OFFSET); + this.classDefOffset = classDefOffset; } + @Nonnull + @Override + public String getName() { + return dexBuf.getType(dexBuf.readSmallUint(classDefOffset + CLASS_NAME_OFFSET)); + } - @Nonnull @Override public String getName() { return name; } - @Override public int getAccessFlags() { return accessFlags; } - @Nullable @Override public String getSuperclass() { return superclass; } - @Nullable @Override public String getSourceFile() { return sourceFile; } + @Nullable + @Override + public String getSuperclass() { + return dexBuf.getOptionalType(dexBuf.readOptionalUint(classDefOffset + SUPERCLASS_OFFSET)); + } + + @Override + public int getAccessFlags() { + return dexBuf.readSmallUint(classDefOffset + ACCESS_FLAGS_OFFSET); + } + + @Nullable + @Override + public String getSourceFile() { + return dexBuf.getOptionalString(dexBuf.readOptionalUint(classDefOffset + SOURCE_FILE_OFFSET)); + } @Nonnull @Override public List getInterfaces() { + final int interfacesOffset = dexBuf.readSmallUint(classDefOffset + INTERFACES_OFFSET); if (interfacesOffset > 0) { final int size = dexBuf.readSmallUint(interfacesOffset); return new FixedSizeList() { @@ -106,13 +108,14 @@ public class DexBackedClassDef implements ClassDef { @Nonnull @Override public List getAnnotations() { - return annotationsDirectory.getClassAnnotations(); + return getAnnotationsDirectory().getClassAnnotations(); } @Nonnull @Override public List getFields() { - if (classDataOffset != 0) { + int classDataOffset = getClassDataOffset(); + if (getClassDataOffset() != 0) { DexReader reader = dexBuf.readerAt(classDataOffset); final int staticFieldCount = reader.readSmallUleb128(); int instanceFieldCount = reader.readSmallUleb128(); @@ -121,13 +124,16 @@ public class DexBackedClassDef implements ClassDef { reader.skipUleb128(); //direct_methods_size reader.skipUleb128(); //virtual_methods_size + final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); + final int staticInitialValuesOffset = + dexBuf.readSmallUint(classDefOffset + STATIC_INITIAL_VALUES_OFFSET); final int fieldsStartOffset = reader.getOffset(); return new VariableSizeListWithContext() { @Nonnull @Override - public Iterator listIterator() { - return new Iterator(dexBuf, fieldsStartOffset) { + public VariableSizeListIterator listIterator() { + return new VariableSizeListIterator(dexBuf, fieldsStartOffset) { private int previousFieldIndex = 0; @Nonnull private final AnnotationsDirectory.AnnotationIterator annotationIterator = annotationsDirectory.getFieldAnnotationIterator(); @@ -173,6 +179,7 @@ public class DexBackedClassDef implements ClassDef { @Nonnull @Override public List getMethods() { + int classDataOffset = getClassDataOffset(); if (classDataOffset > 0) { DexReader reader = dexBuf.readerAt(classDataOffset); int staticFieldCount = reader.readSmallUleb128(); @@ -183,13 +190,14 @@ public class DexBackedClassDef implements ClassDef { if (methodCount > 0) { DexBackedField.skipAllFields(reader, staticFieldCount + instanceFieldCount); + final AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory(); final int methodsStartOffset = reader.getOffset(); return new VariableSizeListWithContext() { @Nonnull @Override - public Iterator listIterator() { - return new Iterator(dexBuf, methodsStartOffset) { + public VariableSizeListIterator listIterator() { + return new VariableSizeListIterator(dexBuf, methodsStartOffset) { private int previousMethodIndex = 0; @Nonnull private final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator = annotationsDirectory.getMethodAnnotationIterator(); @@ -232,4 +240,19 @@ public class DexBackedClassDef implements ClassDef { } return ImmutableList.of(); } + + private int getClassDataOffset() { + if (classDataOffset == -1) { + classDataOffset = dexBuf.readSmallUint(classDefOffset + CLASS_DATA_OFFSET); + } + return classDataOffset; + } + + private AnnotationsDirectory getAnnotationsDirectory() { + if (annotationsDirectory == null) { + int annotationsDirectoryOffset = dexBuf.readSmallUint(classDefOffset + ANNOTATIONS_OFFSET); + annotationsDirectory = AnnotationsDirectory.newOrEmpty(dexBuf, annotationsDirectoryOffset); + } + return annotationsDirectory; + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java index 41d49e1d..65a2797c 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedExceptionHandler.java @@ -31,24 +31,24 @@ package org.jf.dexlib2.dexbacked; -import org.jf.dexlib2.immutable.ImmutableExceptionHandler; +import org.jf.dexlib2.iface.ExceptionHandler; import javax.annotation.Nonnull; -public class DexBackedExceptionHandler extends ImmutableExceptionHandler { - private DexBackedExceptionHandler(String exceptionType, int handlerCodeOffset) { +public class DexBackedExceptionHandler implements ExceptionHandler { + @Nonnull private final DexBuffer dexBuf; + private final int typeId; + private final int handlerCodeOffset; + + public DexBackedExceptionHandler(@Nonnull DexReader reader) { // TODO: verify dalvik doesn't accept an exception handler that points in the middle of an instruction - super(exceptionType, handlerCodeOffset); + this.dexBuf = reader.getDexBuffer(); + this.typeId = reader.readSmallUleb128(); + this.handlerCodeOffset = reader.readSmallUleb128(); } - // static factory method, because we can't read from the reader in the correct order while calling super() in the - // constructor. ugh. - public static DexBackedExceptionHandler createNew(@Nonnull DexReader reader) { - int typeId = reader.readSmallUleb128(); - String exceptionType = reader.getType(typeId); - int handlerCodeOffset = reader.readSmallUleb128(); - return new DexBackedExceptionHandler(exceptionType, handlerCodeOffset); - } + @Nonnull @Override public String getExceptionType() { return dexBuf.getType(typeId); } + @Override public int getHandlerCodeOffset() { return handlerCodeOffset; } public static void skipFrom(@Nonnull DexReader reader) { reader.skipUleb128(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedField.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedField.java index 517d010b..339a01ce 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedField.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedField.java @@ -43,14 +43,14 @@ import java.util.List; public class DexBackedField implements Field { @Nonnull public final DexBuffer dexBuf; - @Nonnull public final String name; - @Nonnull public final String type; public final int accessFlags; @Nullable public final EncodedValue initialValue; public final int annotationSetOffset; public final int fieldIndex; + private int fieldIdItemOffset; + // offsets for field_id_item private static final int TYPE_OFFSET = 2; private static final int NAME_OFFSET = 4; @@ -67,14 +67,20 @@ public class DexBackedField implements Field { this.annotationSetOffset = annotationIterator.seekTo(fieldIndex); this.initialValue = staticInitialValueIterator.getNextOrNull(); - - int fieldIdItemOffset = reader.getFieldIdItemOffset(fieldIndex); - this.type = reader.getType(reader.readUshort(fieldIdItemOffset + TYPE_OFFSET)); - this.name = reader.getString(reader.readSmallUint(fieldIdItemOffset + NAME_OFFSET)); } - @Nonnull @Override public String getName() { return name; } - @Nonnull @Override public String getType() { return type; } + @Nonnull + @Override + public String getName() { + return dexBuf.getString(dexBuf.readSmallUint(getFieldIdItemOffset() + NAME_OFFSET)); + } + + @Nonnull + @Override + public String getType() { + return dexBuf.getType(dexBuf.readUshort(getFieldIdItemOffset() + TYPE_OFFSET)); + } + @Override public int getAccessFlags() { return accessFlags; } @Nullable @Override public EncodedValue getInitialValue() { return initialValue; } @@ -108,4 +114,11 @@ public class DexBackedField implements Field { reader.skipUleb128(); } } + + private int getFieldIdItemOffset() { + if (fieldIdItemOffset == 0) { + fieldIdItemOffset = dexBuf.getFieldIdItemOffset(fieldIndex); + } + return fieldIdItemOffset; + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java index 20703c2d..c22f3cf9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java @@ -46,17 +46,18 @@ public class DexBackedMethod implements Method { @Nonnull public final DexBuffer dexBuf; @Nonnull public final DexBackedClassDef classDef; - @Nonnull public final String name; public final int accessFlags; - @Nonnull public final String returnType; private final int codeOffset; - private final int parametersOffset; + private final int parameterAnnotationSetListOffset; private final int methodAnnotationSetOffset; - private final List> parameterAnnotations; public final int methodIndex; + private int methodIdItemOffset; + private int protoIdItemOffset; + private int parametersOffset = -1; + // method_id_item offsets private static final int PROTO_OFFSET = 2; private static final int NAME_OFFSET = 4; @@ -65,6 +66,21 @@ public class DexBackedMethod implements Method { private static final int RETURN_TYPE_OFFSET = 4; private static final int PARAMETERS_OFFSET = 8; + public DexBackedMethod(@Nonnull DexReader reader, + @Nonnull DexBackedClassDef classDef, + int previousMethodIndex) { + this.dexBuf = reader.getDexBuffer(); + this.classDef = classDef; + + int methodIndexDiff = reader.readSmallUleb128(); + this.methodIndex = methodIndexDiff + previousMethodIndex; + this.accessFlags = reader.readSmallUleb128(); + this.codeOffset = reader.readSmallUleb128(); + + this.methodAnnotationSetOffset = 0; + this.parameterAnnotationSetListOffset = 0; + } + public DexBackedMethod(@Nonnull DexReader reader, @Nonnull DexBackedClassDef classDef, int previousMethodIndex, @@ -79,29 +95,27 @@ public class DexBackedMethod implements Method { this.codeOffset = reader.readSmallUleb128(); this.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex); - int parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex); - this.parameterAnnotations = - AnnotationsDirectory.getParameterAnnotations(dexBuf, parameterAnnotationSetListOffset); - - int methodIdItemOffset = reader.getMethodIdItemOffset(methodIndex); - int protoIndex = reader.readUshort(methodIdItemOffset + PROTO_OFFSET); - int protoIdItemOffset = reader.getProtoIdItemOffset(protoIndex); - - this.name = reader.getString(reader.readSmallUint(methodIdItemOffset + NAME_OFFSET)); - - this.returnType = reader.getType(reader.readSmallUint(protoIdItemOffset + RETURN_TYPE_OFFSET)); - this.parametersOffset = reader.readSmallUint(protoIdItemOffset + PARAMETERS_OFFSET); + this.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex); } - - @Nonnull @Override public String getName() { return name; } @Override public int getAccessFlags() { return accessFlags; } - @Nonnull @Override public String getReturnType() { return returnType; } + + @Nonnull + @Override + public String getName() { + return dexBuf.getString(dexBuf.readSmallUint(getMethodIdItemOffset() + NAME_OFFSET)); + } + + @Nonnull + @Override + public String getReturnType() { + return dexBuf.getType(dexBuf.readSmallUint(getProtoIdItemOffset() + RETURN_TYPE_OFFSET)); + } @Nonnull @Override public List getParameters() { - if (parametersOffset > 0) { + if (getParametersOffset() > 0) { DexBackedMethodImplementation methodImpl = getImplementation(); if (methodImpl != null) { return methodImpl.getParametersWithNames(); @@ -113,8 +127,11 @@ public class DexBackedMethod implements Method { @Nonnull public List getParametersWithoutNames() { + final int parametersOffset = getParametersOffset(); if (parametersOffset > 0) { final int size = dexBuf.readSmallUint(parametersOffset); + final List> parameterAnnotations = + AnnotationsDirectory.getParameterAnnotations(dexBuf, parameterAnnotationSetListOffset); return new FixedSizeList() { @Nonnull @@ -165,6 +182,28 @@ public class DexBackedMethod implements Method { return null; } + private int getMethodIdItemOffset() { + if (methodIdItemOffset == 0) { + methodIdItemOffset = dexBuf.getMethodIdItemOffset(methodIndex); + } + return methodIdItemOffset; + } + + private int getProtoIdItemOffset() { + if (protoIdItemOffset == 0) { + int protoIndex = dexBuf.readUshort(getMethodIdItemOffset() + PROTO_OFFSET); + protoIdItemOffset = dexBuf.getProtoIdItemOffset(protoIndex); + } + return protoIdItemOffset; + } + + private int getParametersOffset() { + if (parametersOffset == -1) { + parametersOffset = dexBuf.readSmallUint(getProtoIdItemOffset() + PARAMETERS_OFFSET); + } + return parametersOffset; + } + /** * Skips the reader over a single encoded_method structure. * @param reader The {@code DexFileReader} to skip diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java index a90b1d4e..c52aebc4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java @@ -35,8 +35,7 @@ import com.google.common.collect.ImmutableList; import org.jf.dexlib2.dexbacked.instruction.DexBackedInstruction; import org.jf.dexlib2.dexbacked.util.DebugInfo; import org.jf.dexlib2.dexbacked.util.FixedSizeList; -import org.jf.dexlib2.dexbacked.util.VariableSizeList; -import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.dexbacked.util.VariableSizeIterator; import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.MethodParameter; import org.jf.dexlib2.iface.TryBlock; @@ -45,8 +44,7 @@ import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.util.AlignmentUtils; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.ArrayList; +import java.util.Iterator; import java.util.List; public class DexBackedMethodImplementation implements MethodImplementation { @@ -54,10 +52,6 @@ public class DexBackedMethodImplementation implements MethodImplementation { @Nonnull public final DexBackedMethod method; private final int codeOffset; - public final int registerCount; - @Nonnull public final ImmutableList instructions; - @Nonnull public final DebugInfo debugInfo; - // code_item offsets private static final int TRIES_SIZE_OFFSET = 6; private static final int DEBUG_OFFSET_OFFSET = 8; @@ -72,13 +66,31 @@ public class DexBackedMethodImplementation implements MethodImplementation { this.dexBuf = dexBuf; this.method = method; this.codeOffset = codeOffset; - this.registerCount = dexBuf.readUshort(codeOffset); - this.instructions = buildInstructionList(); - this.debugInfo = DebugInfo.newOrEmpty(dexBuf, dexBuf.readSmallUint(codeOffset + DEBUG_OFFSET_OFFSET), this); } - @Override public int getRegisterCount() { return registerCount; } - @Nonnull @Override public ImmutableList getInstructions() { return instructions; } + @Override public int getRegisterCount() { return dexBuf.readUshort(codeOffset); } + + @Nonnull @Override public Iterable getInstructions() { + // instructionsSize is the number of 16-bit code units in the instruction list, not the number of instructions + int instructionsSize = dexBuf.readSmallUint(codeOffset + INSTRUCTIONS_SIZE_OFFSET); + + final int instructionsStartOffset = codeOffset + INSTRUCTIONS_START_OFFSET; + final int endOffset = instructionsStartOffset + (instructionsSize*2); + return new Iterable() { + @Override + public Iterator iterator() { + return new VariableSizeIterator(dexBuf, instructionsStartOffset) { + @Override + protected Instruction readItem(@Nonnull DexReader reader, int index) { + if (reader.getOffset() >= endOffset) { + return null; + } + return DexBackedInstruction.readFrom(reader); + } + }; + } + }; + } @Nonnull @Override @@ -109,30 +121,17 @@ public class DexBackedMethodImplementation implements MethodImplementation { } @Nonnull - @Override + private DebugInfo getDebugInfo() { + return DebugInfo.newOrEmpty(dexBuf, dexBuf.readSmallUint(codeOffset + DEBUG_OFFSET_OFFSET), this); + } + + @Nonnull @Override public Iterable getDebugItems() { - return debugInfo; + return getDebugInfo(); } @Nonnull - private ImmutableList buildInstructionList() { - // instructionsSize is the number of 16-bit code units in the instruction list, not the number of instructions - int instructionsSize = dexBuf.readSmallUint(codeOffset + INSTRUCTIONS_SIZE_OFFSET); - - // we can use instructionsSize as an upper bound on the number of instructions there will be - ArrayList instructions = new ArrayList(instructionsSize); - int instructionsStartOffset = codeOffset + INSTRUCTIONS_START_OFFSET; - DexReader reader = dexBuf.readerAt(instructionsStartOffset); - int endOffset = instructionsStartOffset + (instructionsSize*2); - - while (reader.getOffset() < endOffset) { - instructions.add(DexBackedInstruction.readFrom(reader)); - } - - return ImmutableList.copyOf(instructions); - } - public List getParametersWithNames() { - return debugInfo.getParametersWithNames(); + return getDebugInfo().getParametersWithNames(); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java index fd5ffb4c..dac20930 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedTryBlock.java @@ -40,11 +40,8 @@ import java.util.List; public class DexBackedTryBlock implements TryBlock { @Nonnull public final DexBuffer dexBuf; - - public final int startCodeOffset; - public final int codeUnitCount; - - private final int exceptionHandlersOffset; + private final int tryItemOffset; + private final int handlersStartOffset; private static final int START_ADDRESS_OFFSET = 0; private static final int CODE_UNIT_COUNT_OFFSET = 4; @@ -54,18 +51,18 @@ public class DexBackedTryBlock implements TryBlock { int tryItemOffset, int handlersStartOffset) { this.dexBuf = dexBuf; - this.startCodeOffset = dexBuf.readSmallUint(tryItemOffset + START_ADDRESS_OFFSET); - this.codeUnitCount = dexBuf.readUshort(tryItemOffset + CODE_UNIT_COUNT_OFFSET); - this.exceptionHandlersOffset = handlersStartOffset + dexBuf.readUshort(tryItemOffset + HANDLER_OFFSET_OFFSET); + this.tryItemOffset = tryItemOffset; + this.handlersStartOffset = handlersStartOffset; } - @Override public int getStartCodeOffset() { return startCodeOffset; } - @Override public int getCodeUnitCount() { return codeUnitCount; } + @Override public int getStartCodeOffset() { return dexBuf.readSmallUint(tryItemOffset + START_ADDRESS_OFFSET); } + @Override public int getCodeUnitCount() { return dexBuf.readUshort(tryItemOffset + CODE_UNIT_COUNT_OFFSET); } @Nonnull @Override public List getExceptionHandlers() { - DexReader reader = dexBuf.readerAt(exceptionHandlersOffset); + DexReader reader = + dexBuf.readerAt(handlersStartOffset + dexBuf.readUshort(tryItemOffset + HANDLER_OFFSET_OFFSET)); final int encodedSize = reader.readSleb128(); if (encodedSize > 0) { @@ -74,7 +71,7 @@ public class DexBackedTryBlock implements TryBlock { @Nonnull @Override protected ExceptionHandler readItem(@Nonnull DexReader reader, int index) { - return DexBackedExceptionHandler.createNew(reader); + return new DexBackedExceptionHandler(reader); } @Override @@ -94,7 +91,7 @@ public class DexBackedTryBlock implements TryBlock { if (index == sizeWithCatchAll-1) { return new DexBackedCatchAllExceptionHandler(dexReader); } else { - return DexBackedExceptionHandler.createNew(dexReader); + return new DexBackedExceptionHandler(dexReader); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java new file mode 100644 index 00000000..55887ecd --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.dexbacked.util; + +import org.jf.dexlib2.dexbacked.DexBuffer; +import org.jf.dexlib2.dexbacked.DexReader; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Iterator; + +public abstract class VariableSizeIterator implements Iterator { + private final DexReader reader; + + private int index = 0; + private T cachedItem = null; + + protected VariableSizeIterator(DexBuffer dexBuf, int offset) { + this.reader = dexBuf.readerAt(offset); + cachedItem = readItem(reader, index++); + } + + /** + * Reads the next item from reader. If the end of the list has been reached, it should return null. + * + * @param reader The {@code DexReader} to read from + * @param index The index of the item that is being read + * @return The item that was read, or null if the end of the list has been reached. + */ + @Nullable protected abstract T readItem(@Nonnull DexReader reader, int index); + + @Override + public boolean hasNext() { + return cachedItem != null; + } + + @Override + public T next() { + T ret = cachedItem; + cachedItem = readItem(reader, index++); + return ret; + } + + @Override public void remove() { throw new UnsupportedOperationException(); } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeListWithContext.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeListWithContext.java index 5a83b06e..5b5364a7 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeListWithContext.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeListWithContext.java @@ -51,8 +51,8 @@ import java.util.NoSuchElementException; public abstract class VariableSizeListWithContext extends AbstractSequentialList { @Nonnull @Override - public Iterator listIterator(int startIndex) { - Iterator iterator = listIterator(); + public VariableSizeListIterator listIterator(int startIndex) { + VariableSizeListIterator iterator = listIterator(); if (startIndex < 0 || startIndex >= size()) { throw new IndexOutOfBoundsException(); } @@ -62,13 +62,13 @@ public abstract class VariableSizeListWithContext extends AbstractSequentialL return iterator; } - @Nonnull @Override public abstract Iterator listIterator(); + @Nonnull @Override public abstract VariableSizeListIterator listIterator(); - public abstract class Iterator extends AbstractListIterator { + public abstract class VariableSizeListIterator extends AbstractListIterator { private int index = 0; @Nonnull private final DexReader reader; - public Iterator(@Nonnull DexBuffer dexBuf, int offset) { + public VariableSizeListIterator(@Nonnull DexBuffer dexBuf, int offset) { this.reader = dexBuf.readerAt(offset); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java index 1a6a2749..2953cfe9 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/MethodImplementation.java @@ -39,7 +39,7 @@ import java.util.List; public interface MethodImplementation { int getRegisterCount(); - @Nonnull List getInstructions(); + @Nonnull Iterable getInstructions(); @Nonnull List getTryBlocks(); @Nonnull Iterable getDebugItems(); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java index e03ba3e9..db22f867 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableMethodImplementation.java @@ -51,7 +51,7 @@ public class ImmutableMethodImplementation implements MethodImplementation { @Nonnull public final ImmutableList debugItems; public ImmutableMethodImplementation(int registerCount, - @Nullable List instructions, + @Nullable Iterable instructions, @Nullable List tryBlocks, @Nullable Iterable debugItems) { this.registerCount = registerCount; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction.java index 1eb85fbd..7add448a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/instruction/ImmutableInstruction.java @@ -131,7 +131,7 @@ public abstract class ImmutableInstruction implements Instruction { } @Nonnull - public static ImmutableList immutableListOf(List list) { + public static ImmutableList immutableListOf(Iterable list) { return CONVERTER.convert(list); } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/InstructionOffsetMap.java b/dexlib2/src/main/java/org/jf/dexlib2/util/InstructionOffsetMap.java index 3330d403..3675a5d4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/InstructionOffsetMap.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/InstructionOffsetMap.java @@ -31,7 +31,6 @@ package org.jf.dexlib2.util; -import org.jf.dexlib2.iface.MethodImplementation; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.util.ExceptionWithContext; @@ -42,17 +41,14 @@ import java.util.List; public class InstructionOffsetMap { @Nonnull private final int[] instructionCodeOffsets; - public InstructionOffsetMap(@Nonnull MethodImplementation methodImpl) { - List instructions = methodImpl.getInstructions(); - int[] instructionCodeOffsets = new int[instructions.size()]; + public InstructionOffsetMap(@Nonnull List instructions) { + this.instructionCodeOffsets = new int[instructions.size()]; int codeOffset = 0; for (int i=0; i