Use resolution to determine library target in interface desugaring
Bug: b/234711664
Change-Id: I8ba2040463caa3a13f9d215d530503be03300b13
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index cbf88a6..01236b0 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -591,6 +591,19 @@
// 1.7 or below, this will make a VerificationError on the input a VerificationError
// on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
// will remain the same.
+ upgradeCfVersionToSupportInterfaceMethodInvoke(method);
+ }
+
+ private void leavingSuperInvokeToInterface(ProgramMethod method) {
+ // When leaving interface method invokes possibly upgrade the class file
+ // version, but don't go above the initial class file version. If the input was
+ // 1.7 or below, this will make a VerificationError on the input a VerificationError
+ // on the output. If the input was 1.8 or above the runtime behaviour (potential ICCE)
+ // will remain the same.
+ upgradeCfVersionToSupportInterfaceMethodInvoke(method);
+ }
+
+ private void upgradeCfVersionToSupportInterfaceMethodInvoke(ProgramMethod method) {
if (method.getHolder().hasClassFileVersion()) {
method
.getDefinition()
@@ -644,7 +657,7 @@
return computeInvokeAsThrowRewrite(invoke, resolutionResult, context);
}
- if (clazz.isInterface() && !clazz.isLibraryClass()) {
+ if (clazz.isInterface() && !resolutionResult.getResolutionPair().getHolder().isLibraryClass()) {
// NOTE: we intentionally don't desugar super calls into interface methods
// coming from android.jar since it is only possible in case v24+ version
// of android.jar is provided.
@@ -697,7 +710,7 @@
// resolution was implemented in full.
DexMethod amendedMethod =
amendDefaultMethod(context12.getHolder(), invokedMethod);
- // assert method.getReference() == amendedMethod;
+ assert method.getReference() == amendedMethod;
DexClassAndMethod companionMethod =
helper.ensureDefaultAsMethodOfCompanionClassStub(method);
acceptCompanionMethod(method, companionMethod, eventConsumer);
@@ -709,20 +722,27 @@
DesugarDescription emulatedInterfaceDesugaring =
computeEmulatedInterfaceInvokeSpecial(clazz, invokedMethod, context);
- if (!emulatedInterfaceDesugaring.needsDesugaring() && context.isDefaultMethod()) {
- return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowNSMERewrite(
- appView,
- invoke,
- () ->
- appView
- .reporter()
- .warning(
- new StringDiagnostic(
- "Interface method desugaring has inserted NoSuchMethodError replacing a"
- + " super call in "
- + context.toSourceString(),
- context.getOrigin())));
+ if (!emulatedInterfaceDesugaring.needsDesugaring()) {
+ if (context.isDefaultMethod()) {
+ return AlwaysThrowingInstructionDesugaring.computeInvokeAsThrowNSMERewrite(
+ appView,
+ invoke,
+ () ->
+ appView
+ .reporter()
+ .warning(
+ new StringDiagnostic(
+ "Interface method desugaring has inserted NoSuchMethodError replacing a"
+ + " super call in "
+ + context.toSourceString(),
+ context.getOrigin())));
+ } else {
+ return DesugarDescription.builder()
+ .addScanEffect(() -> leavingSuperInvokeToInterface(context))
+ .build();
+ }
}
+
return emulatedInterfaceDesugaring;
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
index 0319cf1..6e19e01 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultMethodResolvingToLibraryTest.java
@@ -5,11 +5,14 @@
package com.android.tools.r8.desugaring.interfacemethods;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -34,28 +37,14 @@
private static final String EXPECTED_OUTPUT = StringUtils.lines("8");
private void inspect(CodeInspector inspector) {
- if (parameters
- .getApiLevel()
- .isLessThan(TestBase.apiLevelWithDefaultInterfaceMethodsSupport())) {
- assertTrue(
- inspector
- .clazz(B.class)
- .uniqueMethodWithName("compose")
- .streamInstructions()
- .filter(InstructionSubject::isInvoke)
- .map(invoke -> invoke.getMethod().getHolderType().toString())
- // TODO(b/234711664): This should not happen.
- .anyMatch(name -> name.equals("java.util.function.Function$-CC")));
- } else {
- assertTrue(
- inspector
- .clazz(B.class)
- .uniqueMethodWithName("compose")
- .streamInstructions()
- .filter(InstructionSubject::isInvoke)
- .map(invoke -> invoke.getMethod().getHolderType().toString())
- .noneMatch(name -> name.endsWith("$-CC")));
- }
+ assertTrue(
+ inspector
+ .clazz(B.class)
+ .uniqueMethodWithName("compose")
+ .streamInstructions()
+ .filter(InstructionSubject::isInvoke)
+ .map(invoke -> invoke.getMethod().getHolderType().toString())
+ .noneMatch(name -> name.endsWith("$-CC")));
}
@Test
@@ -65,7 +54,87 @@
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
.setMinApi(parameters.getApiLevel())
.compile()
- .inspect(this::inspect);
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .asDex()
+ .maxSupportedApiLevel()
+ .isLessThan(AndroidApiLevel.N),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testDesugaringWithApiLevelCheck() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .addAndroidBuildVersion(parameters.getRuntime().asDex().maxSupportedApiLevel())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .asDex()
+ .maxSupportedApiLevel()
+ .isLessThan(AndroidApiLevel.N),
+ r -> r.assertSuccessWithOutputLines("No call"),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ try {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .compile()
+ // .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .asDex()
+ .maxSupportedApiLevel()
+ .isLessThan(AndroidApiLevel.N),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ } catch (CompilationFailedException e) {
+ // TODO(b/235184674): Fix this.
+ assertTrue(parameters.isCfRuntime());
+ }
+ }
+
+ // TODO(b/235184674): Fix this.
+ @Test(expected = CompilationFailedException.class)
+ public void testR8WithApiLevelCheck() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.T))
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClassWithApiLevelCheck.class)
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClassWithApiLevelCheck.class)
+ .applyIf(
+ parameters.isDexRuntime()
+ && parameters
+ .getRuntime()
+ .asDex()
+ .maxSupportedApiLevel()
+ .isLessThan(AndroidApiLevel.N),
+ r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
}
static class TestClass {
@@ -79,6 +148,21 @@
}
}
+ static class TestClassWithApiLevelCheck {
+
+ private static void m(C c) {
+ System.out.println(c.compose(c).apply(2));
+ }
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 24) {
+ m(new C());
+ } else {
+ System.out.println("No call");
+ }
+ }
+ }
+
interface MyFunction<V, R> extends Function<V, R> {}
abstract static class B<V, R> implements MyFunction<V, R> {