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