From 6417e812e167c87c53655a6c27af35aea34faca5 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Sat, 20 Feb 2016 11:56:22 -0800 Subject: Refactor ClassPath to take ClassProviders This allows for a more general mechanism for providing defined classes --- .../java/org/jf/dexlib2/analysis/ClassPath.java | 107 +++++++++------------ .../org/jf/dexlib2/analysis/ClassProvider.java | 40 ++++++++ .../org/jf/dexlib2/analysis/DexClassProvider.java | 56 +++++++++++ .../jf/dexlib2/analysis/CommonSuperclassTest.java | 4 +- .../dexlib2/analysis/util/SuperclassChainTest.java | 6 +- 5 files changed, 145 insertions(+), 68 deletions(-) create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java (limited to 'dexlib2/src') 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 cf75dffe..9f9e396b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java @@ -36,7 +36,9 @@ import com.google.common.base.Suppliers; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import com.google.common.collect.*; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import org.jf.dexlib2.DexFileFactory; import org.jf.dexlib2.DexFileFactory.DexFileNotFound; import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException; @@ -52,63 +54,41 @@ import javax.annotation.Nonnull; import java.io.File; import java.io.IOException; import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ClassPath { @Nonnull private final TypeProto unknownClass; - @Nonnull private HashMap availableClasses = Maps.newHashMap(); + @Nonnull private List classProviders; private final boolean checkPackagePrivateAccess; public final int oatVersion; public static final int NOT_ART = -1; /** - * Creates a new ClassPath instance that can load classes from the given dex files + * Creates a new ClassPath instance that can load classes from the given providers * - * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order + * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in + * order */ - public ClassPath(DexFile... classPath) throws IOException { - this(Lists.newArrayList(classPath), 15); + public ClassPath(ClassProvider... classProviders) throws IOException { + this(Arrays.asList(classProviders), false, NOT_ART); } /** - * Creates a new ClassPath instance that can load classes from the given dex files + * Creates a new ClassPath instance that can load classes from the given providers * - * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order - * @param api API level - */ - public ClassPath(@Nonnull Iterable classPath, int api) { - this(Lists.newArrayList(classPath), api == 17); - } - - /** - * Creates a new ClassPath instance that can load classes from the given dex files - * - * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order - * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by - * default - */ - public ClassPath(@Nonnull Iterable classPath, boolean checkPackagePrivateAccess) { - this(classPath, checkPackagePrivateAccess, NOT_ART); - } - - /** - * Creates a new ClassPath instance that can load classes from the given dex files - * - * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order + * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in + * order * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by * default * @param oatVersion The applicable oat version, or NOT_ART */ - public ClassPath(@Nonnull Iterable classPath, boolean checkPackagePrivateAccess, + public ClassPath(@Nonnull Iterable classProviders, boolean checkPackagePrivateAccess, int oatVersion) { // add fallbacks for certain special classes that must be present - Iterable dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses())); - unknownClass = new UnknownClassProto(this); loadedClasses.put(unknownClass.getType(), unknownClass); this.checkPackagePrivateAccess = checkPackagePrivateAccess; @@ -124,30 +104,23 @@ public class ClassPath { loadPrimitiveType("D"); loadPrimitiveType("L"); - for (DexFile dexFile: dexFiles) { - for (ClassDef classDef: dexFile.getClasses()) { - ClassDef prev = availableClasses.get(classDef.getType()); - if (prev == null) { - availableClasses.put(classDef.getType(), classDef); - } - } - } + this.classProviders = Lists.newArrayList(classProviders); + this.classProviders.add(getBasicClasses()); } private void loadPrimitiveType(String type) { loadedClasses.put(type, new PrimitiveProto(this, type)); } - private static DexFile getBasicClasses() { + private static ClassProvider getBasicClasses() { // fallbacks for some special classes that we assume are present - return new ImmutableDexFile(Opcodes.forApi(19), - ImmutableSet.of( - new ReflectionClassDef(Class.class), - new ReflectionClassDef(Cloneable.class), - new ReflectionClassDef(Object.class), - new ReflectionClassDef(Serializable.class), - new ReflectionClassDef(String.class), - new ReflectionClassDef(Throwable.class))); + return new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of( + new ReflectionClassDef(Class.class), + new ReflectionClassDef(Cloneable.class), + new ReflectionClassDef(Object.class), + new ReflectionClassDef(Serializable.class), + new ReflectionClassDef(String.class), + new ReflectionClassDef(Throwable.class)))); } public boolean isArt() { @@ -173,11 +146,13 @@ public class ClassPath { @Nonnull public ClassDef getClassDef(String type) { - ClassDef ret = availableClasses.get(type); - if (ret == null) { - throw new UnresolvedClassException("Could not resolve class %s", type); + for (ClassProvider provider: classProviders) { + ClassDef classDef = provider.getClassDef(type); + if (classDef != null) { + return classDef; + } } - return ret; + throw new UnresolvedClassException("Could not resolve class %s", type); } @Nonnull @@ -198,7 +173,7 @@ public class ClassPath { @Nonnull public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, int api, boolean checkPackagePrivateAccess, boolean experimental) { - ArrayList dexFiles = Lists.newArrayList(); + List providers = Lists.newArrayList(); int oatVersion = NOT_ART; @@ -213,23 +188,29 @@ public class ClassPath { } } } - dexFiles.addAll(classPathDexFiles); + for (DexFile classPathDexFile: classPathDexFiles) { + providers.add(new DexClassProvider(classPathDexFile)); + } } - dexFiles.add(dexFile); - return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion); + providers.add(new DexClassProvider(dexFile)); + return new ClassPath(providers, checkPackagePrivateAccess, oatVersion); } @Nonnull public static ClassPath fromClassPath(Iterable classPathDirs, Iterable classPath, DexFile dexFile, int api, boolean checkPackagePrivateAccess, boolean experimental, int oatVersion) { - ArrayList dexFiles = Lists.newArrayList(); + List providers = Lists.newArrayList(); for (String classPathEntry: classPath) { - dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental)); + List classPathDexFiles = + loadClassPathEntry(classPathDirs, classPathEntry, api, experimental); + for (DexFile classPathDexFile: classPathDexFiles) { + providers.add(new DexClassProvider(classPathDexFile)); + } } - dexFiles.add(dexFile); - return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion); + providers.add(new DexClassProvider(dexFile)); + return new ClassPath(providers, checkPackagePrivateAccess, oatVersion); } private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$"); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java new file mode 100644 index 00000000..7c823ff0 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016, 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.iface.ClassDef; + +import javax.annotation.Nullable; + +public interface ClassProvider { + @Nullable ClassDef getClassDef(String type); +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java new file mode 100644 index 00000000..c460cc3f --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016, 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 com.google.common.collect.Maps; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; + +import javax.annotation.Nullable; +import java.util.Map; + +public class DexClassProvider implements ClassProvider { + private final DexFile dexFile; + private Map classMap = Maps.newHashMap(); + + public DexClassProvider(DexFile dexFile) { + this.dexFile = dexFile; + + for (ClassDef classDef: dexFile.getClasses()) { + classMap.put(classDef.getType(), classDef); + } + } + + @Nullable @Override public ClassDef getClassDef(String type) { + return classMap.get(type); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java index 521290f0..3f1ee56d 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java @@ -54,7 +54,7 @@ public class CommonSuperclassTest { private final ClassPath classPath; public CommonSuperclassTest() throws IOException { - classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), + classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of( TestUtils.makeClassDef("Ljava/lang/Object;", null), TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"), @@ -89,7 +89,7 @@ public class CommonSuperclassTest { TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"), TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;", "Liface/sub2;", "Liface/sub3;", "Liface/sub4;") - ))); + )))); } public void superclassTest(String commonSuperclass, diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java index c9a9af95..84cd284b 100644 --- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java @@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableSet; import junit.framework.Assert; import org.jf.dexlib2.Opcodes; import org.jf.dexlib2.analysis.ClassPath; +import org.jf.dexlib2.analysis.DexClassProvider; import org.jf.dexlib2.analysis.TestUtils; import org.jf.dexlib2.analysis.TypeProto; import org.jf.dexlib2.iface.ClassDef; @@ -46,7 +47,6 @@ import java.io.IOException; public class SuperclassChainTest { - @Test public void testGetSuperclassChain() throws IOException { ClassDef objectClassDef = TestUtils.makeClassDef("Ljava/lang/Object;", null); @@ -57,7 +57,7 @@ public class SuperclassChainTest { ImmutableSet classes = ImmutableSet.of( objectClassDef, oneClassDef, twoClassDef, threeClassDef); - ClassPath classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), classes)); + ClassPath classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), classes))); TypeProto objectClassProto = classPath.getClass("Ljava/lang/Object;"); TypeProto oneClassProto = classPath.getClass("Ltest/one;"); @@ -88,7 +88,7 @@ public class SuperclassChainTest { ClassDef twoClassDef = TestUtils.makeClassDef("Ltest/two;", "Ltest/one;"); ClassDef threeClassDef = TestUtils.makeClassDef("Ltest/three;", "Ltest/two;"); ImmutableSet classes = ImmutableSet.of(twoClassDef, threeClassDef); - ClassPath classPath = new ClassPath(new ImmutableDexFile(Opcodes.forApi(19), classes)); + ClassPath classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), classes))); TypeProto unknownClassProto = classPath.getUnknownClass(); TypeProto oneClassProto = classPath.getClass("Ltest/one;"); -- cgit v1.2.3