aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2017-03-16 13:54:35 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-03-16 13:54:36 +0000
commit4dbea06afc2932cb8bd62e72181edb77aac9dbe2 (patch)
tree76fea7b9473b7cbd2ec8ed20f61bf86ff9d3d47a
parent7d2fa18cfd9d254e73e31ca4674ee315d369f1a4 (diff)
parent485c4374832af732e073c9656e5422ceb7d1635d (diff)
downloaddexmaker-4dbea06afc2932cb8bd62e72181edb77aac9dbe2.tar.gz
Merge "Hacks to allow mocking of package private classes"
-rw-r--r--dexmaker/src/main/java/com/google/dexmaker/DexMaker.java29
-rw-r--r--dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java31
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) {