diff options
author | Paul Duffin <paulduffin@google.com> | 2016-07-22 12:16:26 +0100 |
---|---|---|
committer | Paul Duffin <paulduffin@google.com> | 2016-08-01 15:16:05 +0100 |
commit | a13e8e98b64e3f5271759dac44967f1c24c76995 (patch) | |
tree | c94534535613b25f7cf6d6332be39b31bd661d61 /dexmaker | |
parent | a4d0f615c2921f01ce6f4486158272a8f760fed8 (diff) | |
download | dexmaker-a13e8e98b64e3f5271759dac44967f1c24c76995.tar.gz |
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
Diffstat (limited to 'dexmaker')
-rw-r--r-- | dexmaker/pom.xml | 76 | ||||
-rw-r--r-- | dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java | 72 | ||||
-rw-r--r-- | dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java | 71 |
3 files changed, 196 insertions, 23 deletions
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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (C) 2012 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>com.crittercism.dexmaker</groupId> + <artifactId>dexmaker-parent</artifactId> + <version>1.3</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>dexmaker</artifactId> + <name>dexmaker</name> + + <dependencies> + <dependency> + <groupId>com.crittercism.dexmaker</groupId> + <artifactId>dexmaker-dx</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.sonatype.plugins</groupId> + <artifactId>jarjar-maven-plugin</artifactId> + <version>1.8</version> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>jarjar</goal> + </goals> + <configuration> + <includes> + <include>com.google.dexmaker:dexmaker-dx</include> + </includes> + <rules> + <rule> + <pattern>com.android.dx.**</pattern> + <result>com.google.dexmaker.dx.@1</result> + </rule> + </rules> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> 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<T> { // 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<? extends T> 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<T> { 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<T> { } } + /** + * Sets the proxy's {@link InvocationHandler}. + * <p> + * 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)}. + * <p> + * 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<T> { 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<T> { // 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<T> { } sink.add(entry); } - - for (Class<?> i : c.getInterfaces()) { - getMethodsToProxy(sink, seenFinalMethods, i); - } } private static <T> String getMethodNameForProxyOf(Class<T> 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 <T> ProxyBuilder<T> proxyFor(Class<T> clazz) throws Exception { return ProxyBuilder.forClass(clazz) |