From a13e8e98b64e3f5271759dac44967f1c24c76995 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Fri, 22 Jul 2016 12:16:26 +0100 Subject: Upgrade to v1.3 Removed bug-10862083.patch as that patch is no longer needed. Bug: 30299479 Test: Build and run mockito based tests. Change-Id: Idb07bee4a85f04a2b0408a8a25b665706a656e57 --- dexmaker/pom.xml | 76 ++++++++++++++++++++++ .../com/google/dexmaker/stock/ProxyBuilder.java | 72 ++++++++++++++------ .../google/dexmaker/stock/ProxyBuilderTest.java | 71 +++++++++++++++++++- 3 files changed, 196 insertions(+), 23 deletions(-) create mode 100644 dexmaker/pom.xml (limited to 'dexmaker') diff --git a/dexmaker/pom.xml b/dexmaker/pom.xml new file mode 100644 index 0000000..9e6b526 --- /dev/null +++ b/dexmaker/pom.xml @@ -0,0 +1,76 @@ + + + + 4.0.0 + + + com.crittercism.dexmaker + dexmaker-parent + 1.3 + ../pom.xml + + + dexmaker + dexmaker + + + + com.crittercism.dexmaker + dexmaker-dx + ${project.version} + provided + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.sonatype.plugins + jarjar-maven-plugin + 1.8 + + + package + + jarjar + + + + com.google.dexmaker:dexmaker-dx + + + + com.android.dx.** + com.google.dexmaker.dx.@1 + + + + + + + + + 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 aa59886..9b2545a 100644 --- a/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java +++ b/dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java @@ -229,12 +229,18 @@ public final class ProxyBuilder { // Thrown when the base class constructor throws an exception. throw launderCause(e); } - setHandlerInstanceField(result, handler); + setInvocationHandler(result, handler); return result; } // TODO: test coverage for this - // TODO: documentation for this + + /** + * Generate a proxy class. Note that new instances of this class will not automatically have an + * an invocation handler, even if {@link #handler(InvocationHandler)} was called. The handler + * must be set on each instance after it is created, using + * {@link #setInvocationHandler(Object, InvocationHandler)}. + */ public Class buildProxyClass() throws IOException { // try the cache to see if we've generated this one before @SuppressWarnings("unchecked") // we only populate the map with matching types @@ -292,20 +298,6 @@ public final class ProxyBuilder { throw new UndeclaredThrowableException(cause); } - private static void setHandlerInstanceField(Object instance, InvocationHandler handler) { - try { - Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); - handlerField.setAccessible(true); - handlerField.set(instance, handler); - } catch (NoSuchFieldException e) { - // Should not be thrown, generated proxy class has been generated with this field. - throw new AssertionError(e); - } catch (IllegalAccessException e) { - // Should not be thrown, we just set the field to accessible. - throw new AssertionError(e); - } - } - private static void setMethodsStaticField(Class proxyClass, Method[] methodsToProxy) { try { Field methodArrayField = proxyClass.getDeclaredField(FIELD_NAME_METHODS); @@ -338,6 +330,31 @@ public final class ProxyBuilder { } } + /** + * Sets the proxy's {@link InvocationHandler}. + *

+ * If you create a proxy with {@link #build()}, the proxy will already have a handler set, + * provided that you configured one with {@link #handler(InvocationHandler)}. + *

+ * If you generate a proxy class with {@link #buildProxyClass()}, instances of the proxy class + * will not automatically have a handler set, and it is necessary to use this method with each + * instance. + * + * @throws IllegalArgumentException if the object supplied is not a proxy created by this class. + */ + public static void setInvocationHandler(Object instance, InvocationHandler handler) { + try { + Field handlerField = instance.getClass().getDeclaredField(FIELD_NAME_HANDLER); + handlerField.setAccessible(true); + handlerField.set(instance, handler); + } catch (NoSuchFieldException e) { + throw new IllegalArgumentException("Not a valid proxy instance", e); + } catch (IllegalAccessException e) { + // Should not be thrown, we just set the field to accessible. + throw new AssertionError(e); + } + } + // TODO: test coverage for isProxyClass /** @@ -605,6 +622,11 @@ public final class ProxyBuilder { for (Class c = baseClass; c != null; c = c.getSuperclass()) { getMethodsToProxy(methodsToProxy, seenFinalMethods, c); } + for (Class c = baseClass; c != null; c = c.getSuperclass()) { + for (Class i : c.getInterfaces()) { + getMethodsToProxy(methodsToProxy, seenFinalMethods, i); + } + } for (Class c : interfaces) { getMethodsToProxy(methodsToProxy, seenFinalMethods, c); } @@ -645,6 +667,20 @@ public final class ProxyBuilder { // Skip static methods, overriding them has no effect. continue; } + if (!Modifier.isPublic(method.getModifiers()) + && !Modifier.isProtected(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 + // 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 + // name, as they use different class loaders. + continue; + } if (method.getName().equals("finalize") && method.getParameterTypes().length == 0) { // Skip finalize method, it's likely important that it execute as normal. continue; @@ -657,10 +693,6 @@ public final class ProxyBuilder { } sink.add(entry); } - - for (Class i : c.getInterfaces()) { - getMethodsToProxy(sink, seenFinalMethods, i); - } } private static String getMethodNameForProxyOf(Class clazz) { diff --git a/dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java b/dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java index 763d740..5c499c8 100644 --- a/dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java +++ b/dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java @@ -161,7 +161,15 @@ public class ProxyBuilderTest extends TestCase { } public void testProxyingPrivateMethods_NotIntercepted() throws Throwable { - assertEquals("expected", proxyFor(HasPrivateMethod.class).build().result()); + HasPrivateMethod proxy = proxyFor(HasPrivateMethod.class).build(); + try { + proxy.getClass().getDeclaredMethod("result"); + fail(); + } catch (NoSuchMethodException expected) { + + } + + assertEquals("expected", proxy.result()); } public static class HasPackagePrivateMethod { @@ -170,8 +178,23 @@ public class ProxyBuilderTest extends TestCase { } } - public void testProxyingPackagePrivateMethods_AreIntercepted() throws Throwable { - assertEquals("fake result", proxyFor(HasPackagePrivateMethod.class).build().result()); + public void testProxyingPackagePrivateMethods_NotIntercepted() + throws Throwable { + HasPackagePrivateMethod proxy = proxyFor(HasPackagePrivateMethod.class) + .build(); + try { + proxy.getClass().getDeclaredMethod("result"); + fail(); + } catch (NoSuchMethodException expected) { + + } + + try { + proxy.result(); + fail(); + } catch (AssertionFailedError expected) { + + } } public static class HasProtectedMethod { @@ -184,6 +207,32 @@ public class ProxyBuilderTest extends TestCase { assertEquals("fake result", proxyFor(HasProtectedMethod.class).build().result()); } + public static class MyParentClass { + String someMethod() { + return "package"; + } + } + + public static class MyChildClassWithProtectedMethod extends MyParentClass { + @Override + protected String someMethod() { + return "protected"; + } + } + + public static class MyChildClassWithPublicMethod extends MyParentClass { + @Override + public String someMethod() { + return "public"; + } + } + + public void testProxying_ClassHierarchy() throws Throwable { + assertEquals("package", proxyFor(MyParentClass.class).build().someMethod()); + assertEquals("fake result", proxyFor(MyChildClassWithProtectedMethod.class).build().someMethod()); + assertEquals("fake result", proxyFor(MyChildClassWithPublicMethod.class).build().someMethod()); + } + public static class HasVoidMethod { public void dangerousMethod() { fail(); @@ -833,6 +882,22 @@ public class ProxyBuilderTest extends TestCase { assertEquals("no proxy", proxyFor(ExtenstionOfFinalInterfaceImpl.class).build().foo()); } + // https://code.google.com/p/dexmaker/issues/detail?id=9 + public interface DeclaresMethodLate { + void thisIsTheMethod(); + } + + public static class MakesMethodFinalEarly { + public final void thisIsTheMethod() {} + } + + public static class YouDoNotChooseYourFamily + extends MakesMethodFinalEarly implements DeclaresMethodLate {} + + public void testInterfaceMethodMadeFinalBeforeActualInheritance() throws Exception { + proxyFor(YouDoNotChooseYourFamily.class).build(); + } + /** Simple helper to add the most common args for this test to the proxy builder. */ private ProxyBuilder proxyFor(Class clazz) throws Exception { return ProxyBuilder.forClass(clazz) -- cgit v1.2.3