From 0acc897cddb531d60bc61f5a5cbc872e40b4df58 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Mon, 31 Dec 2012 20:17:30 -0800 Subject: [PATCH] Refactor how method parameters are read/handled --- .../jf/dexlib2/dexbacked/DexBackedMethod.java | 77 ++++++--------- .../DexBackedMethodImplementation.java | 6 +- .../jf/dexlib2/dexbacked/util/DebugInfo.java | 79 ++++++++-------- .../dexbacked/util/ParameterIterator.java | 94 +++++++++++++++++++ .../util/VariableSizeCollection.java | 4 +- .../dexbacked/util/VariableSizeIterator.java | 9 +- .../dexbacked/util/VariableSizeSet.java | 3 +- 7 files changed, 172 insertions(+), 100 deletions(-) create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/ParameterIterator.java 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 b442c459..352c95e4 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethod.java @@ -32,17 +32,19 @@ package org.jf.dexlib2.dexbacked; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import org.jf.dexlib2.base.BaseMethodParameter; +import com.google.common.collect.Iterators; import org.jf.dexlib2.base.reference.BaseMethodReference; import org.jf.dexlib2.dexbacked.util.AnnotationsDirectory; import org.jf.dexlib2.dexbacked.util.FixedSizeList; +import org.jf.dexlib2.dexbacked.util.ParameterIterator; import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.Method; import org.jf.dexlib2.iface.MethodParameter; +import org.jf.util.AbstractForwardSequentialList; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -102,6 +104,7 @@ public class DexBackedMethod extends BaseMethodReference implements Method { this.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex); } + public int getMethodIndex() { return methodIndex; } @Nonnull @Override public String getDefiningClass() { return classDef.getType(); } @Override public int getAccessFlags() { return accessFlags; } @@ -120,63 +123,43 @@ public class DexBackedMethod extends BaseMethodReference implements Method { @Nonnull @Override public List getParameters() { - if (getParametersOffset() > 0) { - DexBackedMethodImplementation methodImpl = getImplementation(); - if (methodImpl != null) { - return methodImpl.getParametersWithNames(); - } - return getParametersWithoutNames(); + int parametersOffset = getParametersOffset(); + if (parametersOffset > 0) { + final List parameterTypes = getParameterTypes(); + + return new AbstractForwardSequentialList() { + @Nonnull @Override public Iterator iterator() { + return new ParameterIterator(parameterTypes, + getParameterAnnotations(), + getParameterNames()); + } + + @Override public int size() { + return parameterTypes.size(); + } + }; } return ImmutableList.of(); } @Nonnull - public List getParametersWithoutNames() { - final int parametersOffset = getParametersOffset(); - if (parametersOffset > 0) { - final int size = dexBuf.readSmallUint(parametersOffset); - final List> parameterAnnotations = - AnnotationsDirectory.getParameterAnnotations(dexBuf, parameterAnnotationSetListOffset); + public List> getParameterAnnotations() { + return AnnotationsDirectory.getParameterAnnotations(dexBuf, parameterAnnotationSetListOffset); + } - return new FixedSizeList() { - @Nonnull - @Override - public MethodParameter readItem(final int index) { - return new BaseMethodParameter() { - @Nonnull - @Override - public String getType() { - int typeIndex = dexBuf.readUshort(parametersOffset + 4 + (index * 2)); - return dexBuf.getType(typeIndex); - } - - @Nonnull - @Override - public Set getAnnotations() { - if (index < parameterAnnotations.size()) { - return parameterAnnotations.get(index); - } - return ImmutableSet.of(); - } - - @Nullable @Override public String getName() { return null; } - //TODO: iterate over the annotations to get the signature - @Nullable @Override public String getSignature() { return null; } - }; - } - - @Override public int size() { return size; } - }; + @Nonnull + public Iterator getParameterNames() { + DexBackedMethodImplementation methodImpl = getImplementation(); + if (methodImpl != null) { + return methodImpl.getParameterNames(null); } - - return ImmutableList.of(); + return Iterators.emptyIterator(); } @Nonnull @Override public List getParameterTypes() { - int protoIdItemOffset = getProtoIdItemOffset(); - final int parametersOffset = dexBuf.readSmallUint(protoIdItemOffset + DexBuffer.PROTO_PARAM_LIST_OFF_OFFSET); + final int parametersOffset = getParametersOffset(); if (parametersOffset > 0) { final int parameterCount = dexBuf.readSmallUint(parametersOffset + DexBuffer.TYPE_LIST_SIZE_OFFSET); final int paramListStart = parametersOffset + DexBuffer.TYPE_LIST_LIST_OFFSET; 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 d909e4e4..c485b2e7 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedMethodImplementation.java @@ -37,13 +37,13 @@ import org.jf.dexlib2.dexbacked.util.DebugInfo; import org.jf.dexlib2.dexbacked.util.FixedSizeList; import org.jf.dexlib2.dexbacked.util.VariableSizeLookaheadIterator; import org.jf.dexlib2.iface.MethodImplementation; -import org.jf.dexlib2.iface.MethodParameter; import org.jf.dexlib2.iface.TryBlock; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.util.AlignmentUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.Iterator; import java.util.List; @@ -132,7 +132,7 @@ public class DexBackedMethodImplementation implements MethodImplementation { } @Nonnull - public List getParametersWithNames() { - return getDebugInfo().getParametersWithNames(); + public Iterator getParameterNames(@Nullable DexReader dexReader) { + return getDebugInfo().getParameterNames(dexReader); } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java index 24e8b584..2d3919c7 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/DebugInfo.java @@ -34,12 +34,10 @@ package org.jf.dexlib2.dexbacked.util; import com.google.common.collect.Iterators; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.DebugItemType; -import org.jf.dexlib2.base.BaseMethodParameter; import org.jf.dexlib2.dexbacked.DexBackedMethod; import org.jf.dexlib2.dexbacked.DexBackedMethodImplementation; import org.jf.dexlib2.dexbacked.DexBuffer; import org.jf.dexlib2.dexbacked.DexReader; -import org.jf.dexlib2.iface.Annotation; import org.jf.dexlib2.iface.MethodParameter; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.debug.EndLocal; @@ -48,25 +46,33 @@ import org.jf.dexlib2.immutable.debug.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.Iterator; public abstract class DebugInfo implements Iterable { - @Nonnull public abstract List getParametersWithNames(); + /** + * Gets an iterator that yields the parameter names from the debug_info_item + * + * @param reader Optional. If provided, the reader must be positioned at the debug_info_item.parameters_size + * field, and will + * @return An iterator that yields the parameter names as strings + */ + @Nonnull public abstract Iterator getParameterNames(@Nullable DexReader reader); public static DebugInfo newOrEmpty(@Nonnull DexBuffer dexBuf, int debugInfoOffset, @Nonnull DexBackedMethodImplementation methodImpl) { if (debugInfoOffset == 0) { - return new EmptyDebugInfo(methodImpl.method); + return EmptyDebugInfo.INSTANCE; } return new DebugInfoImpl(dexBuf, debugInfoOffset, methodImpl); } private static class EmptyDebugInfo extends DebugInfo { - @Nonnull private final DexBackedMethod method; - public EmptyDebugInfo(@Nonnull DexBackedMethod method) { this.method = method; } + public static final EmptyDebugInfo INSTANCE = new EmptyDebugInfo(); + private EmptyDebugInfo() {} @Nonnull @Override public Iterator iterator() { return Iterators.emptyIterator(); } - @Nonnull @Override public List getParametersWithNames() { - return method.getParametersWithoutNames(); + @Nonnull @Override public Iterator getParameterNames(@Nullable DexReader reader) { + return Iterators.emptyIterator(); } } @@ -92,17 +98,24 @@ public abstract class DebugInfo implements Iterable { @Nonnull @Override public Iterator iterator() { - DexReader initialReader = dexBuf.readerAt(debugInfoOffset); + DexReader reader = dexBuf.readerAt(debugInfoOffset); // TODO: this unsigned value could legitimally be > MAX_INT - final int lineNumberStart = initialReader.readSmallUleb128(); + final int lineNumberStart = reader.readSmallUleb128(); int registerCount = methodImpl.getRegisterCount(); //TODO: does dalvik allow references to invalid registers? final LocalInfo[] locals = new LocalInfo[registerCount]; Arrays.fill(locals, EMPTY_LOCAL_INFO); - final VariableSizeListIterator parameterIterator = - getParametersWithNames().listIterator(); + DexBackedMethod method = methodImpl.method; + + // Create a MethodParameter iterator that uses our DexReader instance to read the parameter names. + // After we have finished iterating over the parameters, reader will "point to" the beginning of the + // debug instructions + final Iterator parameterIterator = + new ParameterIterator(method.getParameterTypes(), + method.getParameterAnnotations(), + getParameterNames(reader)); // first, we grab all the parameters and temporarily store them at the beginning of locals, // disregarding any wide types @@ -111,7 +124,7 @@ public abstract class DebugInfo implements Iterable { // add the local info for the "this" parameter locals[parameterIndex++] = new LocalInfo() { @Override public String getName() { return "this"; } - @Override public String getType() { return methodImpl.method.classDef.getType(); } + @Override public String getType() { return methodImpl.method.getDefiningClass(); } @Override public String getSignature() { return null; } }; } @@ -138,7 +151,7 @@ public abstract class DebugInfo implements Iterable { } } - return new VariableSizeLookaheadIterator(dexBuf, parameterIterator.getReaderOffset()) { + return new VariableSizeLookaheadIterator(dexBuf, reader.getOffset()) { private int codeAddress = 0; private int lineNumber = lineNumberStart; @@ -231,34 +244,16 @@ public abstract class DebugInfo implements Iterable { @Nonnull @Override - public VariableSizeList getParametersWithNames() { - DexReader reader = dexBuf.readerAt(debugInfoOffset); - reader.skipUleb128(); - final int parameterNameCount = reader.readSmallUleb128(); - final List methodParametersWithoutNames = - methodImpl.method.getParametersWithoutNames(); + public VariableSizeIterator getParameterNames(@Nullable DexReader reader) { + if (reader == null) { + reader = dexBuf.readerAt(debugInfoOffset); + reader.skipUleb128(); + } //TODO: make sure dalvik doesn't allow more parameter names than we have parameters - - return new VariableSizeList(dexBuf, reader.getOffset(), - methodParametersWithoutNames.size()) { - @Nonnull - @Override - protected MethodParameter readNextItem(@Nonnull DexReader reader, int index) { - final MethodParameter methodParameter = methodParametersWithoutNames.get(index); - String _name = null; - if (index < parameterNameCount) { - _name = dexBuf.getOptionalString(reader.readSmallUleb128() - 1); - } - final String name = _name; - - return new BaseMethodParameter() { - @Nonnull @Override public String getType() { return methodParameter.getType(); } - @Nullable @Override public String getName() { return name; } - @Nullable @Override public String getSignature() { return methodParameter.getSignature();} - @Nonnull @Override public Set getAnnotations() { - return methodParameter.getAnnotations(); - } - }; + final int parameterNameCount = reader.readSmallUleb128(); + return new VariableSizeIterator(reader, parameterNameCount) { + @Override protected String readNextItem(@Nonnull DexReader reader, int index) { + return dexBuf.getOptionalString(reader.readSmallUleb128() - 1); } }; } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/ParameterIterator.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/ParameterIterator.java new file mode 100644 index 00000000..8342a0b4 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/ParameterIterator.java @@ -0,0 +1,94 @@ +/* + * 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 com.google.common.collect.ImmutableSet; +import org.jf.dexlib2.base.BaseMethodParameter; +import org.jf.dexlib2.iface.Annotation; +import org.jf.dexlib2.iface.MethodParameter; +import org.jf.util.ExceptionWithContext; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class ParameterIterator implements Iterator { + private final Iterator parameterTypes; + private final Iterator> parameterAnnotations; + private final Iterator parameterNames; + + public ParameterIterator(@Nonnull List parameterTypes, + @Nonnull List> parameterAnnotations, + @Nonnull Iterator parameterNames) { + // TODO: verify if dalvik allows this + if (parameterAnnotations.size() > parameterTypes.size()) { + throw new ExceptionWithContext("Too many parameter annotations"); + } + this.parameterTypes = parameterTypes.iterator(); + this.parameterAnnotations = parameterAnnotations.iterator(); + this.parameterNames = parameterNames; + } + + @Override public boolean hasNext() { + return parameterTypes.hasNext(); + } + + @Override public MethodParameter next() { + @Nonnull final String type = parameterTypes.next().toString(); + @Nonnull final Set annotations; + @Nullable final String name; + + if (parameterAnnotations.hasNext()) { + annotations = parameterAnnotations.next(); + } else { + annotations = ImmutableSet.of(); + } + + if (parameterNames.hasNext()) { + name = parameterNames.next(); + } else { + name = null; + } + + return new BaseMethodParameter() { + @Nonnull @Override public Set getAnnotations() { return annotations; } + @Nullable @Override public String getName() { return name; } + @Nonnull @Override public String getType() { return type; } + }; + } + + @Override public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeCollection.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeCollection.java index c1adb406..7d8c8ed8 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeCollection.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeCollection.java @@ -36,7 +36,6 @@ import org.jf.dexlib2.dexbacked.DexReader; import javax.annotation.Nonnull; import java.util.AbstractCollection; -import java.util.Iterator; public abstract class VariableSizeCollection extends AbstractCollection { @Nonnull private final DexBuffer dexBuf; @@ -49,12 +48,11 @@ public abstract class VariableSizeCollection extends AbstractCollection { this.size = size; } - @Nonnull protected abstract T readNextItem(@Nonnull DexReader reader, int index); + protected abstract T readNextItem(@Nonnull DexReader reader, int index); @Override public VariableSizeIterator iterator() { return new VariableSizeIterator(dexBuf, offset, size) { - @Nonnull @Override protected T readNextItem(@Nonnull DexReader reader, int index) { return VariableSizeCollection.this.readNextItem(reader, index); 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 index bbffc72c..960d1e86 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeIterator.java @@ -35,7 +35,6 @@ import org.jf.dexlib2.dexbacked.DexBuffer; import org.jf.dexlib2.dexbacked.DexReader; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.Iterator; import java.util.NoSuchElementException; @@ -50,6 +49,11 @@ public abstract class VariableSizeIterator implements Iterator { this.size = size; } + protected VariableSizeIterator(@Nonnull DexReader reader, int size) { + this.reader = reader; + this.size = size; + } + /** * Reads the next item from reader. * @@ -57,7 +61,7 @@ public abstract class VariableSizeIterator implements Iterator { * @param index The index of the item being read. This is guaranteed to be less than {@code size} * @return The item that was read */ - @Nonnull protected abstract T readNextItem(@Nonnull DexReader reader, int index); + protected abstract T readNextItem(@Nonnull DexReader reader, int index); public int getReaderOffset() { return reader.getOffset(); @@ -69,7 +73,6 @@ public abstract class VariableSizeIterator implements Iterator { } @Override - @Nonnull public T next() { if (index >= size) { throw new NoSuchElementException(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeSet.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeSet.java index b7e02852..50397908 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeSet.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/util/VariableSizeSet.java @@ -48,12 +48,11 @@ public abstract class VariableSizeSet extends AbstractSet { this.size = size; } - @Nonnull protected abstract T readNextItem(@Nonnull DexReader reader, int index); + protected abstract T readNextItem(@Nonnull DexReader reader, int index); @Override public VariableSizeIterator iterator() { return new VariableSizeIterator(dexBuf, offset, size) { - @Nonnull @Override protected T readNextItem(@Nonnull DexReader reader, int index) { return VariableSizeSet.this.readNextItem(reader, index);