Extend modeling of Proxy to Proxy#getProxyClass
Bug: b/445971857
Change-Id: I0684beae36a3c9062617fc6db31f12639a2e62ee
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index b880ca8..ad19a9f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -2942,9 +2942,15 @@
public class ProxyMethods {
+ public final DexMethod getProxyClass;
public final DexMethod newProxyInstance;
private ProxyMethods() {
+ getProxyClass =
+ createMethod(
+ proxyType,
+ createProto(classType, classLoaderType, classArrayType),
+ createString("getProxyClass"));
newProxyInstance =
createMethod(
proxyType,
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
index 0941d2a..5042fc5 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/EnqueuerReflectiveIdentificationEventConsumer.java
@@ -107,7 +107,7 @@
}
@Override
- public void onJavaLangReflectProxyNewProxyInstance(
+ public void onJavaLangReflectProxyGetProxyClassOrNewProxyInstance(
Set<DexProgramClass> classes, ProgramMethod context) {
KeepReason reason = KeepReason.reflectiveUseIn(context);
for (DexProgramClass clazz : classes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
index ea733a4..50c601f 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/KeepAllReflectiveIdentificationEventConsumer.java
@@ -59,7 +59,7 @@
}
@Override
- public void onJavaLangReflectProxyNewProxyInstance(
+ public void onJavaLangReflectProxyGetProxyClassOrNewProxyInstance(
Set<DexProgramClass> classes, ProgramMethod context) {
for (DexProgramClass clazz : classes) {
keep(clazz, context);
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
index 5d49cb7..8aef51a 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentification.java
@@ -97,7 +97,8 @@
}
} else if (holder.isIdenticalTo(factory.proxyType)) {
// java.lang.reflect.Proxy
- if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+ if (invokedMethod.isIdenticalTo(factory.proxyMethods.getProxyClass)
+ || invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
enqueue(method);
}
} else if (holder.isIdenticalTo(factory.serviceLoaderType)) {
@@ -187,8 +188,9 @@
}
} else if (holder.isIdenticalTo(factory.proxyType)) {
// java.lang.reflect.Proxy
- if (invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
- handleJavaLangReflectProxyNewProxyInstance(method, invoke);
+ if (invokedMethod.isIdenticalTo(factory.proxyMethods.getProxyClass)
+ || invokedMethod.isIdenticalTo(factory.proxyMethods.newProxyInstance)) {
+ handleJavaLangReflectProxyGetProxyClassOrNewProxyInstance(method, invoke);
return true;
}
} else if (holder.isIdenticalTo(factory.serviceLoaderType)) {
@@ -423,7 +425,7 @@
* Handles reflective uses of {@link java.lang.reflect.Proxy#newProxyInstance(ClassLoader,
* Class[], InvocationHandler)}.
*/
- private void handleJavaLangReflectProxyNewProxyInstance(
+ private void handleJavaLangReflectProxyGetProxyClassOrNewProxyInstance(
ProgramMethod method, InvokeMethod invoke) {
if (!invoke.isInvokeStatic()) {
assert false;
@@ -466,7 +468,7 @@
}
}
if (!classes.isEmpty()) {
- eventConsumer.onJavaLangReflectProxyNewProxyInstance(classes, method);
+ eventConsumer.onJavaLangReflectProxyGetProxyClassOrNewProxyInstance(classes, method);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
index d6255c3..3c3163b 100644
--- a/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/shaking/reflectiveidentification/ReflectiveIdentificationEventConsumer.java
@@ -24,7 +24,8 @@
void onJavaLangReflectConstructorNewInstance(ProgramMethod initializer, ProgramMethod context);
- void onJavaLangReflectProxyNewProxyInstance(Set<DexProgramClass> classes, ProgramMethod context);
+ void onJavaLangReflectProxyGetProxyClassOrNewProxyInstance(
+ Set<DexProgramClass> classes, ProgramMethod context);
void onJavaUtilConcurrentAtomicAtomicIntegerFieldUpdaterNewUpdater(
ProgramField field, ProgramMethod context);
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithGetProxyClassTest.java
similarity index 64%
copy from src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java
copy to src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithGetProxyClassTest.java
index ecdf0ba..93e146c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithGetProxyClassTest.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -10,31 +10,31 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InterfaceWithProxyTest extends TestBase {
+public class InterfaceWithGetProxyClassTest extends TestBase {
private static final String EXPECTED = StringUtils.lines("Hello world!");
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public InterfaceWithProxyTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void testReference() throws Exception {
testForRuntime(parameters)
- .addInnerClasses(InterfaceWithProxyTest.class)
+ .addInnerClasses(InterfaceWithGetProxyClassTest.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@@ -42,7 +42,7 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(InterfaceWithProxyTest.class)
+ .addInnerClasses(InterfaceWithGetProxyClassTest.class)
.addKeepMainRule(TestClass.class)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -53,17 +53,15 @@
static class TestClass {
- public static void main(String[] args) {
- I obj =
- (I)
- Proxy.newProxyInstance(
- TestClass.class.getClassLoader(),
- new Class<?>[] {I.class},
- (proxy, method, args1) -> {
- System.out.print("Hello");
- return null;
- });
- obj.greet();
+ public static void main(String[] args) throws Exception {
+ Class<?> proxyClass = Proxy.getProxyClass(TestClass.class.getClassLoader(), I.class);
+ InvocationHandler ih =
+ (proxy, method, args1) -> {
+ System.out.print("Hello");
+ return null;
+ };
+ I i = (I) proxyClass.getDeclaredConstructor(InvocationHandler.class).newInstance(ih);
+ i.greet();
new A().greet();
}
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithNewProxyInstanceTest.java
similarity index 84%
rename from src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java
rename to src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithNewProxyInstanceTest.java
index ecdf0ba..a512441 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithProxyTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/InterfaceWithNewProxyInstanceTest.java
@@ -14,27 +14,26 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class InterfaceWithProxyTest extends TestBase {
+public class InterfaceWithNewProxyInstanceTest extends TestBase {
private static final String EXPECTED = StringUtils.lines("Hello world!");
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public InterfaceWithProxyTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void testReference() throws Exception {
testForRuntime(parameters)
- .addInnerClasses(InterfaceWithProxyTest.class)
+ .addInnerClasses(InterfaceWithNewProxyInstanceTest.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@@ -42,7 +41,7 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(InterfaceWithProxyTest.class)
+ .addInnerClasses(InterfaceWithNewProxyInstanceTest.class)
.addKeepMainRule(TestClass.class)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()