aboutsummaryrefslogtreecommitdiff
path: root/dexlib2
diff options
context:
space:
mode:
authorBen Gruver <bgruv@google.com>2015-09-27 20:40:10 -0700
committerBen Gruver <bgruv@google.com>2015-09-30 20:36:12 -0700
commit827e2db34dc3b8b5504c148a09d594b5c0ddbd64 (patch)
tree32275c8843e419ee79e49d182881552e884e5cf8 /dexlib2
parentc8c70ac58e3b1e7225a5f507871ac5d4aa093198 (diff)
downloadsmali-827e2db34dc3b8b5504c148a09d594b5c0ddbd64.tar.gz
Add support for normalizing virtual methods
This is useful, for example, when comparing the result of deodexing with the original dex file, to remove the "false" differences caused by the different potential ways to reference a given virtual method.
Diffstat (limited to 'dexlib2')
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java70
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java2
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java58
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java112
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java4
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java12
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java13
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java21
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java6
12 files changed, 253 insertions, 66 deletions
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java
new file mode 100644
index 00000000..775a819d
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015, 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.analysis;
+
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.analysis.util.TypeProtoUtils;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.Method;
+import org.jf.dexlib2.util.MethodUtil;
+import org.jf.dexlib2.util.TypeUtils;
+
+import javax.annotation.Nonnull;
+
+public class AnalyzedMethodUtil {
+ public static boolean canAccess(@Nonnull TypeProto type, @Nonnull Method virtualMethod, boolean checkPackagePrivate,
+ boolean checkProtected, boolean checkClass) {
+ if (checkPackagePrivate && MethodUtil.isPackagePrivate(virtualMethod)) {
+ String otherPackage = TypeUtils.getPackage(virtualMethod.getDefiningClass());
+ String thisPackage = TypeUtils.getPackage(type.getType());
+ if (!otherPackage.equals(thisPackage)) {
+ return false;
+ }
+ }
+
+ if (checkProtected && (virtualMethod.getAccessFlags() & AccessFlags.PROTECTED.getValue()) != 0) {
+ if (!TypeProtoUtils.extendsFrom(type, virtualMethod.getDefiningClass())) {
+ return false;
+ }
+ }
+
+ if (checkClass) {
+ ClassPath classPath = type.getClassPath();
+ ClassDef methodClassDef = classPath.getClassDef(virtualMethod.getDefiningClass());
+ if (!TypeUtils.canAccessClass(type.getType(), methodClassDef)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
index 8fcfc8c5..4aa9a5e4 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
@@ -32,6 +32,7 @@
package org.jf.dexlib2.analysis;
import com.google.common.base.Strings;
+import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
@@ -160,7 +161,11 @@ public class ArrayProto implements TypeProto {
@Override
@Nullable
- public MethodReference getMethodByVtableIndex(int vtableIndex) {
+ public Method getMethodByVtableIndex(int vtableIndex) {
return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
}
+
+ @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+ return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
+ }
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
index 83104b2f..4b8920f4 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
@@ -146,7 +146,7 @@ public class ClassPath {
}
@Nonnull
- public TypeProto getClass(CharSequence type) {
+ public TypeProto getClass(@Nonnull CharSequence type) {
return loadedClasses.getUnchecked(type.toString());
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
index d011c1e7..d66e8ebd 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
@@ -34,7 +34,10 @@ package org.jf.dexlib2.analysis;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
-import com.google.common.collect.*;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
@@ -44,6 +47,7 @@ import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.dexlib2.immutable.ImmutableMethod;
+import org.jf.dexlib2.util.MethodUtil;
import org.jf.util.AlignmentUtils;
import org.jf.util.ExceptionWithContext;
import org.jf.util.SparseArray;
@@ -346,7 +350,7 @@ public class ClassProto implements TypeProto {
@Override
@Nullable
- public MethodReference getMethodByVtableIndex(int vtableIndex) {
+ public Method getMethodByVtableIndex(int vtableIndex) {
List<Method> vtable = getVtable();
if (vtableIndex < 0 || vtableIndex >= vtable.size()) {
return null;
@@ -355,6 +359,20 @@ public class ClassProto implements TypeProto {
return vtable.get(vtableIndex);
}
+ public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+ List<Method> vtable = getVtable();
+ for (int i=0; i<vtable.size(); i++) {
+ Method candidate = vtable.get(i);
+ if (MethodUtil.methodSignaturesMatch(candidate, method)) {
+ if (!classPath.shouldCheckPackagePrivateAccess() ||
+ AnalyzedMethodUtil.canAccess(this, candidate, true, false, false)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
@Nonnull SparseArray<FieldReference> getInstanceFields() {
if (classPath.isArt) {
return artInstanceFieldsSupplier.get();
@@ -790,8 +808,9 @@ public class ClassProto implements TypeProto {
outer: for (Method virtualMethod: methods) {
for (int i=0; i<vtable.size(); i++) {
Method superMethod = vtable.get(i);
- if (methodSignaturesMatch(superMethod, virtualMethod)) {
- if (!classPath.shouldCheckPackagePrivateAccess() || canAccess(superMethod)) {
+ if (MethodUtil.methodSignaturesMatch(superMethod, virtualMethod)) {
+ if (!classPath.shouldCheckPackagePrivateAccess() ||
+ AnalyzedMethodUtil.canAccess(ClassProto.this, superMethod, true, false, false)) {
if (replaceExisting) {
vtable.set(i, virtualMethod);
}
@@ -803,37 +822,6 @@ public class ClassProto implements TypeProto {
vtable.add(virtualMethod);
}
}
-
- private boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) {
- return (a.getName().equals(b.getName()) &&
- a.getReturnType().equals(b.getReturnType()) &&
- a.getParameters().equals(b.getParameters()));
- }
-
- private boolean canAccess(@Nonnull Method virtualMethod) {
- if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) {
- return true;
- }
-
- String otherPackage = getPackage(virtualMethod.getDefiningClass());
- String ourPackage = getPackage(getClassDef().getType());
- return otherPackage.equals(ourPackage);
- }
-
- @Nonnull
- private String getPackage(@Nonnull String classType) {
- int lastSlash = classType.lastIndexOf('/');
- if (lastSlash < 0) {
- return "";
- }
- return classType.substring(1, lastSlash);
- }
-
- private boolean methodIsPackagePrivate(int accessFlags) {
- return (accessFlags & (AccessFlags.PRIVATE.getValue() |
- AccessFlags.PROTECTED.getValue() |
- AccessFlags.PUBLIC.getValue())) == 0;
- }
});
private static byte getFieldType(@Nonnull FieldReference field) {
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
index 401c0ec0..3d0318c4 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
@@ -72,6 +72,8 @@ public class MethodAnalyzer {
@Nonnull private final Method method;
@Nonnull private final MethodImplementation methodImpl;
+ private final boolean normalizeVirtualMethods;
+
private final int paramRegisterCount;
@Nonnull private final ClassPath classPath;
@@ -93,9 +95,10 @@ public class MethodAnalyzer {
private final AnalyzedInstruction startOfMethod;
public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method,
- @Nullable InlineMethodResolver inlineResolver) {
+ @Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods) {
this.classPath = classPath;
this.inlineResolver = inlineResolver;
+ this.normalizeVirtualMethods = normalizeVirtualMethods;
this.method = method;
@@ -735,21 +738,32 @@ public class MethodAnalyzer {
case SPUT_OBJECT:
return true;
case INVOKE_VIRTUAL:
+ analyzeInvokeVirtual(analyzedInstruction, false);
+ return true;
case INVOKE_SUPER:
+ analyzeInvokeVirtual(analyzedInstruction, false);
return true;
case INVOKE_DIRECT:
analyzeInvokeDirect(analyzedInstruction);
return true;
case INVOKE_STATIC:
+ return true;
case INVOKE_INTERFACE:
+ // TODO: normalize interfaces
+ return true;
case INVOKE_VIRTUAL_RANGE:
+ analyzeInvokeVirtual(analyzedInstruction, true);
+ return true;
case INVOKE_SUPER_RANGE:
+ analyzeInvokeVirtual(analyzedInstruction, true);
return true;
case INVOKE_DIRECT_RANGE:
analyzeInvokeDirectRange(analyzedInstruction);
return true;
case INVOKE_STATIC_RANGE:
+ return true;
case INVOKE_INTERFACE_RANGE:
+ // TODO: normalize interfaces
return true;
case NEG_INT:
case NOT_INT:
@@ -1545,12 +1559,12 @@ public class MethodAnalyzer {
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
- if (!canAccessClass(thisClass, classPath.getClassDef(resolvedField.getDefiningClass()))) {
+ if (!TypeUtils.canAccessClass(thisClass.getType(), classPath.getClassDef(resolvedField.getDefiningClass()))) {
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
// than resolvedField.getDefiningClass()), and walk up the class hierarchy.
ClassDef fieldClass = classPath.getClassDef(objectRegisterTypeProto.getType());
- while (!canAccessClass(thisClass, fieldClass)) {
+ while (!TypeUtils.canAccessClass(thisClass.getType(), fieldClass)) {
String superclass = fieldClass.getSuperclass();
if (superclass == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
@@ -1585,6 +1599,75 @@ public class MethodAnalyzer {
return true;
}
+ private boolean analyzeInvokeVirtual(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isRange) {
+ MethodReference targetMethod;
+
+ if (!normalizeVirtualMethods) {
+ return true;
+ }
+
+ if (isRange) {
+ Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+ targetMethod = (MethodReference)instruction.getReference();
+ } else {
+ Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
+ targetMethod = (MethodReference)instruction.getReference();
+ }
+
+ TypeProto typeProto = classPath.getClass(targetMethod.getDefiningClass());
+ int methodIndex;
+ try {
+ methodIndex = typeProto.findMethodIndexInVtable(targetMethod);
+ } catch (UnresolvedClassException ex) {
+ return true;
+ }
+
+ if (methodIndex < 0) {
+ return true;
+ }
+
+ Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
+ assert replacementMethod != null;
+ while (true) {
+ String superType = typeProto.getSuperclass();
+ if (superType == null) {
+ break;
+ }
+ typeProto = classPath.getClass(superType);
+ Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
+ if (resolvedMethod == null) {
+ break;
+ }
+
+ if (!resolvedMethod.equals(replacementMethod)) {
+ if (!AnalyzedMethodUtil.canAccess(typeProto, replacementMethod, true, true, true)) {
+ continue;
+ }
+
+ replacementMethod = resolvedMethod;
+ }
+ }
+
+ if (replacementMethod.equals(method)) {
+ return true;
+ }
+
+ Instruction deodexedInstruction;
+ if (isRange) {
+ Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+ deodexedInstruction = new ImmutableInstruction3rc(instruction.getOpcode(), instruction.getStartRegister(),
+ instruction.getRegisterCount(), replacementMethod);
+ } else {
+ Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
+ deodexedInstruction = new ImmutableInstruction35c(instruction.getOpcode(), instruction.getRegisterCount(),
+ instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
+ instruction.getRegisterF(), instruction.getRegisterG(), replacementMethod);
+ }
+
+ analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+ return true;
+ }
+
private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper,
boolean isRange) {
int methodIndex;
@@ -1637,12 +1720,13 @@ public class MethodAnalyzer {
// no need to check class access for invoke-super. A class can obviously access its superclass.
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
- if (!isSuper && !canAccessClass(thisClass, classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
+ if (!isSuper && !TypeUtils.canAccessClass(
+ thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
// than resolvedMethod.getDefiningClass()), and walk up the class hierarchy.
ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType());
- while (!canAccessClass(thisClass, methodClass)) {
+ while (!TypeUtils.canAccessClass(thisClass.getType(), methodClass)) {
String superclass = methodClass.getSuperclass();
if (superclass == null) {
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
@@ -1698,24 +1782,6 @@ public class MethodAnalyzer {
return true;
}
- private boolean canAccessClass(@Nonnull ClassDef accessorClassDef, @Nonnull ClassDef accesseeClassDef) {
- if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
- return true;
- }
-
- // Classes can only be public or package private. Any private or protected inner classes are actually
- // package private.
- return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorClassDef.getType()));
- }
-
- private static String getPackage(String className) {
- int lastSlash = className.lastIndexOf('/');
- if (lastSlash < 0) {
- return "";
- }
- return className.substring(1, lastSlash);
- }
-
private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction) {
return analyzePutGetVolatile(analyzedInstruction, true);
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
index 06ab8e17..2c283932 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
@@ -31,6 +31,7 @@
package org.jf.dexlib2.analysis;
+import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
import org.jf.util.ExceptionWithContext;
@@ -65,7 +66,11 @@ public class PrimitiveProto implements TypeProto {
@Override
@Nullable
- public MethodReference getMethodByVtableIndex(int vtableIndex) {
+ public Method getMethodByVtableIndex(int vtableIndex) {
return null;
}
+
+ @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+ return -1;
+ }
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
index f6db2399..776363b8 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
@@ -31,6 +31,7 @@
package org.jf.dexlib2.analysis;
+import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
@@ -45,5 +46,6 @@ public interface TypeProto {
@Nullable String getSuperclass();
@Nonnull TypeProto getCommonSuperclass(@Nonnull TypeProto other);
@Nullable FieldReference getFieldByOffset(int fieldOffset);
- @Nullable MethodReference getMethodByVtableIndex(int vtableIndex);
+ @Nullable Method getMethodByVtableIndex(int vtableIndex);
+ int findMethodIndexInVtable(@Nonnull MethodReference method);
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
index 38256fbe..32873456 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
@@ -31,6 +31,7 @@
package org.jf.dexlib2.analysis;
+import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.FieldReference;
import org.jf.dexlib2.iface.reference.MethodReference;
@@ -75,7 +76,11 @@ public class UnknownClassProto implements TypeProto {
@Override
@Nullable
- public MethodReference getMethodByVtableIndex(int vtableIndex) {
+ public Method getMethodByVtableIndex(int vtableIndex) {
return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
}
+
+ @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+ return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
+ }
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
index 0313c7c3..e2adf1c4 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
@@ -94,4 +94,16 @@ public class TypeProtoUtils {
return type.getClassPath().getUnknownClass();
}
}
+
+ public static boolean extendsFrom(@Nonnull TypeProto candidate, @Nonnull String possibleSuper) {
+ if (candidate.getType().equals(possibleSuper)) {
+ return true;
+ }
+ for (TypeProto superProto: getSuperclassChain(candidate)) {
+ if (superProto.getType().equals(possibleSuper)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
index 631b8928..dc978daf 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
@@ -35,6 +35,7 @@ import com.google.common.base.Predicate;
import org.jf.dexlib2.AccessFlags;
import org.jf.dexlib2.iface.Method;
import org.jf.dexlib2.iface.reference.MethodReference;
+import org.jf.util.CharSequenceUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -68,6 +69,12 @@ public final class MethodUtil {
return methodReference.getName().equals("<init>");
}
+ public static boolean isPackagePrivate(@Nonnull Method method) {
+ return (method.getAccessFlags() & (AccessFlags.PRIVATE.getValue() |
+ AccessFlags.PROTECTED.getValue() |
+ AccessFlags.PUBLIC.getValue())) == 0;
+ }
+
public static int getParameterRegisterCount(@Nonnull Method method) {
return getParameterRegisterCount(method, MethodUtil.isStatic(method));
}
@@ -109,5 +116,11 @@ public final class MethodUtil {
return sb.toString();
}
+ public static boolean methodSignaturesMatch(@Nonnull MethodReference a, @Nonnull MethodReference b) {
+ return (a.getName().equals(b.getName()) &&
+ a.getReturnType().equals(b.getReturnType()) &&
+ CharSequenceUtils.listEquals(a.getParameterTypes(), b.getParameterTypes()));
+ }
+
private MethodUtil() {}
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java b/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
index 02890b87..6be21af0 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
@@ -31,6 +31,8 @@
package org.jf.dexlib2.util;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.iface.ClassDef;
import org.jf.dexlib2.iface.reference.TypeReference;
import javax.annotation.Nonnull;
@@ -49,5 +51,24 @@ public final class TypeUtils {
return type.length() == 1;
}
+ @Nonnull
+ public static String getPackage(@Nonnull String type) {
+ int lastSlash = type.lastIndexOf('/');
+ if (lastSlash < 0) {
+ return "";
+ }
+ return type.substring(1, lastSlash);
+ }
+
+ public static boolean canAccessClass(@Nonnull String accessorType, @Nonnull ClassDef accesseeClassDef) {
+ if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
+ return true;
+ }
+
+ // Classes can only be public or package private. Any private or protected inner classes are actually
+ // package private.
+ return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorType));
+ }
+
private TypeUtils() {}
}
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
index 65a82f05..25f7778d 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
@@ -71,7 +71,7 @@ public class CustomMethodInlineTableTest {
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
- MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+ MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_VIRTUAL, deodexedInstruction.getOpcode());
@@ -98,7 +98,7 @@ public class CustomMethodInlineTableTest {
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
- MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+ MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_STATIC, deodexedInstruction.getOpcode());
@@ -125,7 +125,7 @@ public class CustomMethodInlineTableTest {
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
15, false);
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
- MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+ MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
Assert.assertEquals(Opcode.INVOKE_DIRECT, deodexedInstruction.getOpcode());