aboutsummaryrefslogtreecommitdiff
path: root/dexmaker
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2016-07-22 12:16:26 +0100
committerPaul Duffin <paulduffin@google.com>2016-08-01 15:16:05 +0100
commita13e8e98b64e3f5271759dac44967f1c24c76995 (patch)
treec94534535613b25f7cf6d6332be39b31bd661d61 /dexmaker
parenta4d0f615c2921f01ce6f4486158272a8f760fed8 (diff)
downloaddexmaker-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.xml76
-rw-r--r--dexmaker/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java72
-rw-r--r--dexmaker/src/test/java/com/google/dexmaker/stock/ProxyBuilderTest.java71
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)