aboutsummaryrefslogtreecommitdiff
path: root/dexmaker
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2017-03-15 16:15:29 +0000
committerPaul Duffin <paulduffin@google.com>2017-03-16 09:06:40 +0000
commitdb20bbcc82de39f499a804d215851995041f3bcf (patch)
treefe118265d154ecaf60663126b807da8890976bf1 /dexmaker
parentb8a5896885d4da2b798888b688a46df1adbf5b89 (diff)
downloaddexmaker-db20bbcc82de39f499a804d215851995041f3bcf.tar.gz
Reapply - Hacks to allow mocking of package private classes
2 things are required to allow mocking for package private classes. First is creating the mock in the same class as the base, that part is simple and seems to have no effect. The second is to have a matching ClassLoader as the source class. To make the classloader match, instead of generating a new ClassLoader, add the new dex to the existing one and share the base class's ClassLoader. Since this is a persistent change and could affect the process, it will be opt-in through setting a system property. This also appears to have unintended speedup in the tests I was testing, possible through cache fixes or brokenness. (cherry picked from commit 31b974435499d3c9769ed6a746e946187f3be64f) Test: Add System.setProperty("dexmaker.share_classloader", "true) to SysuiTestCase then runtest systemui Change-Id: I35c805ee3797ff151ac19bd0c931ebfd41d3b670
Diffstat (limited to 'dexmaker')
-rw-r--r--dexmaker/src/main/java/com/android/dx/DexMaker.java31
-rw-r--r--dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java31
2 files changed, 47 insertions, 15 deletions
diff --git a/dexmaker/src/main/java/com/android/dx/DexMaker.java b/dexmaker/src/main/java/com/android/dx/DexMaker.java
index 5008f0a..ee8d722 100644
--- a/dexmaker/src/main/java/com/android/dx/DexMaker.java
+++ b/dexmaker/src/main/java/com/android/dx/DexMaker.java
@@ -31,12 +31,14 @@ 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.android.dx.stock.ProxyBuilder;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
+import dalvik.system.DexClassLoader;
+
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -355,11 +357,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 +411,11 @@ 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/android/dx/stock/ProxyBuilder.java b/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
index d82f8c4..1daf8e3 100644
--- a/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
+++ b/dexmaker/src/main/java/com/android/dx/stock/ProxyBuilder.java
@@ -248,10 +248,20 @@ public final class ProxyBuilder<T> {
// we only populate the map with matching types
@SuppressWarnings("unchecked")
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
@@ -263,7 +273,8 @@ public final class ProxyBuilder<T> {
Method[] methodsToProxy = getMethodsToProxyRecursive();
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) {
@@ -653,6 +664,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
@@ -670,13 +683,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
@@ -698,7 +713,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) {