diff options
author | Paul Duffin <paulduffin@google.com> | 2017-03-16 13:54:35 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-03-16 13:54:36 +0000 |
commit | 4dbea06afc2932cb8bd62e72181edb77aac9dbe2 (patch) | |
tree | 76fea7b9473b7cbd2ec8ed20f61bf86ff9d3d47a | |
parent | 7d2fa18cfd9d254e73e31ca4674ee315d369f1a4 (diff) | |
parent | 485c4374832af732e073c9656e5422ceb7d1635d (diff) | |
download | dexmaker-4dbea06afc2932cb8bd62e72181edb77aac9dbe2.tar.gz |
Merge "Hacks to allow mocking of package private classes"
-rw-r--r-- | dexmaker/src/main/java/com/google/dexmaker/DexMaker.java | 29 | ||||
-rw-r--r-- | dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java | 31 |
2 files changed, 46 insertions, 14 deletions
diff --git a/dexmaker/src/main/java/com/google/dexmaker/DexMaker.java b/dexmaker/src/main/java/com/google/dexmaker/DexMaker.java index 4e67433..a6a0999 100644 --- a/dexmaker/src/main/java/com/google/dexmaker/DexMaker.java +++ b/dexmaker/src/main/java/com/google/dexmaker/DexMaker.java @@ -32,6 +32,7 @@ import com.android.dx.rop.code.RopMethod; import com.android.dx.rop.cst.CstString; import com.android.dx.rop.cst.CstType; import com.android.dx.rop.type.StdTypeList; +import com.google.dexmaker.stock.ProxyBuilder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -40,6 +41,8 @@ import java.lang.reflect.Modifier; import static java.lang.reflect.Modifier.PRIVATE; import static java.lang.reflect.Modifier.STATIC; +import dalvik.system.DexClassLoader; + import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; @@ -355,11 +358,21 @@ public final class DexMaker { return "Generated_" + checksum +".jar"; } - private ClassLoader generateClassLoader(File result, File dexCache, ClassLoader parent) { + private ClassLoader generateClassLoader(ClassLoader classLoader, File result, File dexCache, + ClassLoader parent) { try { - return (ClassLoader) Class.forName("dalvik.system.DexClassLoader") - .getConstructor(String.class, String.class, String.class, ClassLoader.class) - .newInstance(result.getPath(), dexCache.getAbsolutePath(), null, parent); + boolean shareClassLoader = Boolean.parseBoolean(System.getProperty( + "dexmaker.share_classloader", "false")); + if (shareClassLoader) { + ClassLoader loader = parent != null ? parent : classLoader; + loader.getClass().getMethod("addDexPath", String.class).invoke(loader, + result.getPath()); + return loader; + } else { + return (ClassLoader) Class.forName("dalvik.system.DexClassLoader") + .getConstructor(String.class, String.class, String.class, ClassLoader.class) + .newInstance(result.getPath(), dexCache.getAbsolutePath(), null, parent); + } } catch (ClassNotFoundException e) { throw new UnsupportedOperationException("load() requires a Dalvik VM", e); } catch (InvocationTargetException e) { @@ -399,6 +412,10 @@ public final class DexMaker { * application's private data dir. */ public ClassLoader generateAndLoad(ClassLoader parent, File dexCache) throws IOException { + return generateAndLoad(parent, parent, dexCache); + } + + public ClassLoader generateAndLoad(ClassLoader classLoader, ClassLoader parent, File dexCache) throws IOException { if (dexCache == null) { String property = System.getProperty("dexmaker.dexcache"); if (property != null) { @@ -416,7 +433,7 @@ public final class DexMaker { // Check that the file exists. If it does, return a DexClassLoader and skip all // the dex bytecode generation. if (result.exists()) { - return generateClassLoader(result, dexCache, parent); + return generateClassLoader(classLoader, result, dexCache, parent); } byte[] dex = generate(); @@ -436,7 +453,7 @@ public final class DexMaker { jarOut.write(dex); jarOut.closeEntry(); jarOut.close(); - return generateClassLoader(result, dexCache, parent); + return generateClassLoader(classLoader, result, dexCache, parent); } private static class TypeDeclaration { diff --git a/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java b/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java index c42d24d..8ab06c1 100644 --- a/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java +++ b/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java @@ -245,10 +245,20 @@ public final class ProxyBuilder<T> { // try the cache to see if we've generated this one before @SuppressWarnings("unchecked") // we only populate the map with matching types Class<? extends T> proxyClass = (Class) generatedProxyClasses.get(baseClass); - if (proxyClass != null - && proxyClass.getClassLoader().getParent() == parentClassLoader - && interfaces.equals(asSet(proxyClass.getInterfaces()))) { - return proxyClass; // cache hit! + if (proxyClass != null) { + boolean shareClassLoader = Boolean.parseBoolean(System.getProperty( + "dexmaker.share_classloader", "false")); + boolean validClassLoader; + if (shareClassLoader) { + ClassLoader parent = parentClassLoader != null ? parentClassLoader + : baseClass.getClassLoader(); + validClassLoader = proxyClass.getClassLoader() == parent; + } else { + validClassLoader = proxyClass.getClassLoader().getParent() == parentClassLoader; + } + if (validClassLoader && interfaces.equals(asSet(proxyClass.getInterfaces()))) { + return proxyClass; // cache hit! + } } // the cache missed; generate the class @@ -261,7 +271,8 @@ public final class ProxyBuilder<T> { generateCodeForAllMethods(dexMaker, generatedType, methodsToProxy, superType); dexMaker.declare(generatedType, generatedName + ".generated", PUBLIC, superType, getInterfacesAsTypeIds()); - ClassLoader classLoader = dexMaker.generateAndLoad(parentClassLoader, dexCache); + ClassLoader classLoader = dexMaker.generateAndLoad(baseClass.getClassLoader(), + parentClassLoader, dexCache); try { proxyClass = loadClass(classLoader, generatedName); } catch (IllegalAccessError e) { @@ -655,6 +666,8 @@ public final class ProxyBuilder<T> { private void getMethodsToProxy(Set<MethodSetEntry> sink, Set<MethodSetEntry> seenFinalMethods, Class<?> c) { + boolean shareClassLoader = Boolean.parseBoolean(System.getProperty( + "dexmaker.share_classloader", "false")); for (Method method : c.getDeclaredMethods()) { if ((method.getModifiers() & Modifier.FINAL) != 0) { // Skip final methods, we can't override them. We @@ -672,13 +685,15 @@ public final class ProxyBuilder<T> { continue; } if (!Modifier.isPublic(method.getModifiers()) - && !Modifier.isProtected(method.getModifiers())) { + && !Modifier.isProtected(method.getModifiers()) + && (!shareClassLoader || Modifier.isPrivate(method.getModifiers()))) { // Skip private methods, since they are invoked through direct // invocation (as opposed to virtual). Therefore, it would not // be possible to intercept any private method defined inside // the proxy class except through reflection. - // Skip package-private methods as well. The proxy class does + // Skip package-private methods as well (for non-shared class + // loaders). The proxy class does // not actually inherit package-private methods from the parent // class because it is not a member of the parent's package. // This is even true if the two classes have the same package @@ -708,7 +723,7 @@ public final class ProxyBuilder<T> { } private static <T> String getMethodNameForProxyOf(Class<T> clazz) { - return clazz.getSimpleName() + "_Proxy"; + return clazz.getName().replace(".", "/") + "_Proxy"; } private static TypeId<?>[] classArrayToTypeArray(Class<?>[] input) { |