diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileReader.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileReader.java index 0809dc28..ae6df975 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/DexFileReader.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/DexFileReader.java @@ -54,6 +54,14 @@ public class DexFileReader { return 0; } + public int getMethodIdItemOffset(int methodIndex) { + return 0; + } + + public int getProtoIdItemOffset(int methodIndex) { + return 0; + } + public String getType(int typeIndex) { return null; } 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 607f7fe9..28018747 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedClassDef.java @@ -163,6 +163,52 @@ public class DexBackedClassDef implements ClassDef { @Nonnull @Override public List getMethods() { - return null; + if (classDataOffset > 0) { + DexFileReader reader = dexFile.readerAt(classDataOffset); + int staticFieldCount = reader.readSmallUleb128(); + int instanceFieldCount = reader.readSmallUleb128(); + int directMethodCount = reader.readSmallUleb128(); + int virtualMethodCount = reader.readSmallUleb128(); + final int methodCount = directMethodCount + virtualMethodCount; + if (methodCount > 0) { + DexBackedField.skipAllFields(reader, staticFieldCount + instanceFieldCount); + + final int methodsStartOffset = reader.getOffset(); + + return new VariableSizeListWithContext() { + @Nonnull + @Override + public Iterator listIterator() { + return new Iterator(dexFile, methodsStartOffset) { + private int previousMethodIndex = 0; + @Nonnull private final AnnotationsDirectory.AnnotationIterator methodAnnotationIterator = + annotationsDirectory.getMethodAnnotationIterator(); + @Nonnull private final AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator = + annotationsDirectory.getParameterAnnotationIterator(); + @Nonnull private final StaticInitialValueIterator staticInitialValueIterator = + StaticInitialValueIterator.newOrEmpty(dexFile, staticInitialValuesOffset); + + @Nonnull + @Override + protected DexBackedMethod readItem(DexFileReader reader, int index) { + DexBackedMethod item = new DexBackedMethod(reader, previousMethodIndex, + methodAnnotationIterator, parameterAnnotationIterator); + previousMethodIndex = item.methodIndex; + return item; + } + + @Override + protected void skipItem(DexFileReader reader, int index) { + previousMethodIndex = DexBackedMethod.skipEncodedMethod(reader, previousMethodIndex); + staticInitialValueIterator.skipNext(); + } + }; + } + + @Override public int size() { return methodCount; } + }; + } + } + return ImmutableList.of(); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java new file mode 100644 index 00000000..712d143b --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java @@ -0,0 +1,163 @@ +/* + * 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; + +import com.google.common.collect.ImmutableList; +import org.jf.dexlib2.DexFile; +import org.jf.dexlib2.DexFileReader; +import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory; +import org.jf.dexlib2.dexbacked.util.FixedSizeList; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.MethodParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.List; + +public class DexBackedMethod implements Method { + @Nonnull public final DexFile dexFile; + + @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 methodAnnotationSetOffset; + private final List> parameterAnnotations; + + public final int methodIndex; + + // method_id_item offsets + private static final int PROTO_OFFSET = 2; + private static final int NAME_OFFSET = 4; + + // proto_id_item offsets + private static final int RETURN_TYPE_OFFSET = 4; + private static final int PARAMETERS_OFFSET = 8; + + public DexBackedMethod(@Nonnull DexFileReader dexFileReader, + int previousMethodIndex, + @Nonnull AnnotationsDirectory.AnnotationIterator methodAnnotationIterator, + @Nonnull AnnotationsDirectory.AnnotationIterator paramaterAnnotationIterator) { + this.dexFile = dexFileReader.getDexFile(); + + int methodIndexDiff = dexFileReader.readSmallUleb128(); + this.methodIndex = methodIndexDiff + previousMethodIndex; + this.accessFlags = dexFileReader.readSmallUleb128(); + this.codeOffset = dexFileReader.readSmallUleb128(); + + this.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex); + int parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex); + this.parameterAnnotations = + AnnotationsDirectory.getParameterAnnotations(dexFile, parameterAnnotationSetListOffset); + + int methodIdItemOffset = dexFileReader.getMethodIdItemOffset(methodIndex); + int protoIndex = dexFileReader.readUshort(methodIdItemOffset + PROTO_OFFSET); + int protoIdItemOffset = dexFileReader.getProtoIdItemOffset(protoIndex); + + this.name = dexFileReader.getString(dexFileReader.readSmallUint(methodIdItemOffset + NAME_OFFSET)); + + this.returnType = dexFileReader.getString(dexFileReader.readSmallUint(protoIdItemOffset + RETURN_TYPE_OFFSET)); + this.parametersOffset = dexFileReader.readSmallUint(protoIdItemOffset + PARAMETERS_OFFSET); + } + + + @Nonnull @Override public String getName() { return name; } + @Override public int getAccessFlags() { return accessFlags; } + @Nonnull @Override public String getReturnType() { return returnType; } + + @Nonnull + @Override + public List getParameters() { + if (parametersOffset > 0) { + final int size = dexFile.readSmallUint(parametersOffset); + + return new FixedSizeList() { + @Override + public MethodParameter readItem(final int index) { + return new MethodParameter() { + @Nonnull + @Override + public String getType() { + int typeIndex = dexFile.readUshort(parametersOffset + 4 + (index * 2)); + return dexFile.getType(typeIndex); + } + + @Nonnull + @Override + public List getAnnotations() { + if (index < parameterAnnotations.size()) { + return parameterAnnotations.get(index); + } + return ImmutableList.of(); + } + }; + } + + @Override public int size() { return size; } + }; + } + + return ImmutableList.of(); + } + + @Nonnull + @Override + public List getAnnotations() { + return AnnotationsDirectory.getAnnotations(dexFile, methodAnnotationSetOffset); + } + + @Nullable + @Override + public MethodImplementation getImplementation() { + if (codeOffset > 0) { + return new DexBackedMethodImplementation(dexFile, codeOffset); + } + return null; + } + + /** + * Skips the reader over a single encoded_method structure. + * @param dexFileReader The {@code DexFileReader} to skip + * @param previousMethodIndex The method index of the previous field, or 0 if this is the first + * @return The method index of the field that was skipped + */ + public static int skipEncodedMethod(@Nonnull DexFileReader dexFileReader, int previousMethodIndex) { + int idxDiff = dexFileReader.readSmallUleb128(); + dexFileReader.skipUleb128(); + dexFileReader.skipUleb128(); + return previousMethodIndex + idxDiff; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java new file mode 100644 index 00000000..084d348e --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java @@ -0,0 +1,67 @@ +/* + * 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; + +import org.jf.dexlib2.DexFile; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.iface.TryBlock; +import org.jf.dexlib2.iface.instruction.Instruction; + +import javax.annotation.Nonnull; +import java.util.List; + +public class DexBackedMethodImplementation implements MethodImplementation { + @Nonnull public final DexFile dexFile; + + public DexBackedMethodImplementation(@Nonnull DexFile dexFile, + int codeOffset) { + this.dexFile = dexFile; + } + + + @Override + public int getRegisterCount() { + return 0; + } + + @Nonnull + @Override + public List getInstructions() { + return null; + } + + @Nonnull + @Override + public List getTryBlocks() { + return null; + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/AnnotationsDirectory.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/AnnotationsDirectory.java index 6a878ecd..5cb9f54b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/AnnotationsDirectory.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/AnnotationsDirectory.java @@ -43,11 +43,15 @@ public abstract class AnnotationsDirectory { @Override public int getFieldAnnotationCount() { return 0; } @Nonnull @Override public List getClassAnnotations() {return ImmutableList.of();} @Nonnull @Override public AnnotationIterator getFieldAnnotationIterator() { return AnnotationIterator.EMPTY; } + @Nonnull @Override public AnnotationIterator getMethodAnnotationIterator() { return AnnotationIterator.EMPTY; } + @Nonnull @Override public AnnotationIterator getParameterAnnotationIterator() {return AnnotationIterator.EMPTY;} }; public abstract int getFieldAnnotationCount(); @Nonnull public abstract List getClassAnnotations(); @Nonnull public abstract AnnotationIterator getFieldAnnotationIterator(); + @Nonnull public abstract AnnotationIterator getMethodAnnotationIterator(); + @Nonnull public abstract AnnotationIterator getParameterAnnotationIterator(); public static AnnotationsDirectory newOrEmpty(@Nonnull DexFile dexFile, int directoryAnnotationsOffset) { @@ -85,14 +89,38 @@ public abstract class AnnotationsDirectory { return ImmutableList.of(); } + public static List> getParameterAnnotations(@Nonnull final DexFile dexFile, + final int annotationSetListOffset) { + if (annotationSetListOffset > 0) { + final int size = dexFile.readSmallUint(annotationSetListOffset); + + return new FixedSizeList>() { + @Override + public List readItem(int index) { + int annotationSetOffset = dexFile.readSmallUint(annotationSetListOffset + 4 + index * 4); + return getAnnotations(dexFile, annotationSetOffset); + } + + @Override public int size() { return size; } + }; + } + return ImmutableList.of(); + } private static class AnnotationsDirectoryImpl extends AnnotationsDirectory { @Nonnull public final DexFile dexFile; private final int directoryOffset; private static final int FIELD_COUNT_OFFSET = 4; + private static final int METHOD_COUNT_OFFSET = 8; + private static final int PARAMETER_COUNT_OFFSET = 12; private static final int ANNOTATIONS_START_OFFSET = 16; + /** The size of a field_annotation structure */ + private static final int FIELD_ANNOTATION_SIZE = 8; + /** The size of a method_annotation structure */ + private static final int METHOD_ANNOTATION_SIZE = 8; + public AnnotationsDirectoryImpl(@Nonnull DexFile dexFile, int directoryOffset) { this.dexFile = dexFile; @@ -103,6 +131,14 @@ public abstract class AnnotationsDirectory { return dexFile.readSmallUint(directoryOffset + FIELD_COUNT_OFFSET); } + public int getMethodAnnotationCount() { + return dexFile.readSmallUint(directoryOffset + METHOD_COUNT_OFFSET); + } + + public int getParameterAnnotationCount() { + return dexFile.readSmallUint(directoryOffset + PARAMETER_COUNT_OFFSET); + } + @Nonnull public List getClassAnnotations() { return getAnnotations(dexFile, dexFile.readSmallUint(directoryOffset)); @@ -113,31 +149,49 @@ public abstract class AnnotationsDirectory { return new AnnotationIteratorImpl(directoryOffset + ANNOTATIONS_START_OFFSET, getFieldAnnotationCount()); } + @Nonnull + public AnnotationIterator getMethodAnnotationIterator() { + int fieldCount = getFieldAnnotationCount(); + int methodAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET + + fieldCount * FIELD_ANNOTATION_SIZE; + return new AnnotationIteratorImpl(methodAnnotationsOffset, getMethodAnnotationCount()); + } + + @Nonnull + public AnnotationIterator getParameterAnnotationIterator() { + int fieldCount = getFieldAnnotationCount(); + int methodCount = getMethodAnnotationCount(); + int parameterAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET + + fieldCount * FIELD_ANNOTATION_SIZE + + methodCount + METHOD_ANNOTATION_SIZE; + return new AnnotationIteratorImpl(parameterAnnotationsOffset, getParameterAnnotationCount()); + } + private class AnnotationIteratorImpl implements AnnotationIterator { private final int startOffset; private final int size; private int currentIndex; - private int currentFieldIndex; + private int currentItemIndex; public AnnotationIteratorImpl(int startOffset, int size) { this.startOffset = startOffset; this.size = size; if (size > 0) { - currentFieldIndex = dexFile.readSmallUint(startOffset); + currentItemIndex = dexFile.readSmallUint(startOffset); this.currentIndex = 0; } else { - currentFieldIndex = -1; + currentItemIndex = -1; this.currentIndex = -1; } } - public int seekTo(int fieldIndex) { - while (currentFieldIndex < fieldIndex && (currentIndex+1) < size) { + public int seekTo(int itemIndex) { + while (currentItemIndex < itemIndex && (currentIndex+1) < size) { currentIndex++; - currentFieldIndex = dexFile.readSmallUint(startOffset + (currentIndex*8)); + currentItemIndex = dexFile.readSmallUint(startOffset + (currentIndex*8)); } - if (currentFieldIndex == fieldIndex) { + if (currentItemIndex == itemIndex) { return dexFile.readSmallUint(startOffset + (currentIndex*8)+4); } return 0;