Don't consider non-interface invokes for interface desugaring.
Bug: 199135051
Change-Id: I2a39d1510985feb81689ae88b19dc0a60fc1eadf
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 49ad7d3..5d05028 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1268,6 +1268,7 @@
newFlags.setSynthetic();
newFlags.unsetAbstract();
// Holder is companion class, or retarget method, not an interface.
+ boolean isInterfaceMethodReference = false;
return syntheticBuilder()
.setMethod(newMethod)
.setAccessFlags(newFlags)
@@ -1276,8 +1277,7 @@
.setCode(
ForwardMethodBuilder.builder(factory)
.setNonStaticSource(newMethod)
- // Holder is companion class, or retarget method, not an interface.
- .setStaticTarget(forwardMethod, false)
+ .setStaticTarget(forwardMethod, isInterfaceMethodReference)
.build())
.setApiLevelForDefinition(target.getDefinition().getApiLevelForDefinition())
.setApiLevelForCode(target.getDefinition().getApiLevelForCode())
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index d7b3d0a..f07a802 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -16,20 +16,21 @@
DexMethod target = lambda.descriptor.implHandle.asMethod();
ForwardMethodBuilder forwardMethodBuilder =
ForwardMethodBuilder.builder(lambda.appView.dexItemFactory()).setStaticSource(accessor);
+ boolean isInterface = lambda.descriptor.implHandle.isInterface;
switch (lambda.descriptor.implHandle.type) {
case INVOKE_INSTANCE:
{
- forwardMethodBuilder.setVirtualTarget(target, false);
+ forwardMethodBuilder.setVirtualTarget(target, isInterface);
break;
}
case INVOKE_STATIC:
{
- forwardMethodBuilder.setStaticTarget(target, false);
+ forwardMethodBuilder.setStaticTarget(target, isInterface);
break;
}
case INVOKE_DIRECT:
{
- forwardMethodBuilder.setDirectTarget(target, false);
+ forwardMethodBuilder.setDirectTarget(target, isInterface);
break;
}
case INVOKE_CONSTRUCTOR:
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 c9b9a70..1f737f2 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
@@ -30,6 +30,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ValueType;
@@ -292,33 +293,40 @@
}
private DesugarDescription computeDescription(CfInstruction instruction, ProgramMethod context) {
- // Interface desugaring is only interested in invokes and should not be double processed.
+ // Interface desugaring is only interested in invokes.
CfInvoke invoke = instruction.asInvoke();
- if (invoke == null || isAlreadyDesugared(invoke, context)) {
+ if (invoke == null) {
+ return DesugarDescription.nothing();
+ }
+ // Don't desugar if the invoke is to be desugared by preceeding desugar tasks.
+ if (isAlreadyDesugared(invoke, context)) {
return DesugarDescription.nothing();
}
// There should never be any calls to interface initializers.
if (invoke.isInvokeSpecial() && invoke.isInvokeConstructor(factory)) {
return DesugarDescription.nothing();
}
+ // If the invoke is not an interface invoke, then there should generally not be any desugaring.
+ // However, there are some cases where the insertion of forwarding methods can change behavior
+ // so we need to identify them at the various call sites here.
+ if (!invoke.isInterface()) {
+ return computeNonInterfaceInvoke(invoke, context);
+ }
// If the target holder does not resolve we may want to issue diagnostics.
DexClass holder = appView.definitionForHolder(invoke.getMethod(), context);
if (holder == null) {
// NOTE: leave unchanged those calls to undefined targets. This may lead to runtime
// exception but we can not report it as error since it can also be the intended
// behavior.
- if (invoke.isInterface()) {
- return DesugarDescription.builder()
- .addScanEffect(
- () -> {
- if (invoke.isInvokeStatic()) {
- leavingStaticInvokeToInterface(context);
- }
- warnMissingType(context, invoke.getMethod().getHolderType());
- })
- .build();
- }
- return DesugarDescription.nothing();
+ return DesugarDescription.builder()
+ .addScanEffect(
+ () -> {
+ if (invoke.isInvokeStatic()) {
+ leavingStaticInvokeToInterface(context);
+ }
+ warnMissingType(context, invoke.getMethod().getHolderType());
+ })
+ .build();
}
// Continue with invoke type logic.
if (invoke.isInvokeStatic()) {
@@ -333,6 +341,43 @@
return DesugarDescription.nothing();
}
+ private DesugarDescription computeNonInterfaceInvoke(CfInvoke invoke, ProgramMethod context) {
+ assert !invoke.isInterface();
+ // Emulated interface desugaring will rewrite non-interface invokes.
+ if (invoke.isInvokeSpecial()) {
+ DexClass clazz = appView.definitionForHolder(invoke.getMethod(), context);
+ if (clazz == null) {
+ return DesugarDescription.nothing();
+ }
+ return computeEmulatedInterfaceInvokeSpecial(clazz, invoke.getMethod(), context);
+ }
+ if (!invoke.isInvokeVirtual() && !invoke.isInvokeInterface()) {
+ return DesugarDescription.nothing();
+ }
+ DesugarDescription description = computeEmulatedInterfaceVirtualDispatchOrNull(invoke);
+ if (description != null) {
+ return description;
+ }
+ // It may be the case that a virtual invoke resolves to a static method. In such a case, if
+ // a default method could give rise to a forwarding method in the resolution path, the program
+ // would change behavior from throwing ICCE to dispatching to the companion class method.
+ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+ MethodResolutionResult resolution =
+ appInfo.resolveMethod(invoke.getMethod(), invoke.isInterface());
+ if (!resolution.isSingleResolution()
+ || !resolution.asSingleResolution().getResolvedMethod().isStatic()) {
+ return DesugarDescription.nothing();
+ }
+ DexClass holder = appInfo.definitionFor(invoke.getMethod().getHolderType(), context);
+ DexClassAndMethod target = appInfo.lookupMaximallySpecificMethod(holder, invoke.getMethod());
+ if (target != null && target.isDefaultMethod()) {
+ // Rewrite the invoke to a throw ICCE as the default method forward would otherwise hide the
+ // static / virtual mismatch.
+ return computeInvokeAsThrowRewrite(invoke, resolution.asSingleResolution());
+ }
+ return DesugarDescription.nothing();
+ }
+
private DesugarDescription computeInvokeSpecial(
DexClass holder, CfInvoke invoke, ProgramMethod context) {
if (invoke.isInvokeSuper(context.getHolderType())) {
@@ -344,12 +389,9 @@
private DesugarDescription computeInvokeStatic(
DexClass holder, CfInvoke invoke, ProgramMethod context) {
if (!holder.isInterface()) {
- if (invoke.isInterface()) {
- return DesugarDescription.builder()
- .addScanEffect(() -> leavingStaticInvokeToInterface(context))
- .build();
- }
- return DesugarDescription.nothing();
+ return DesugarDescription.builder()
+ .addScanEffect(() -> leavingStaticInvokeToInterface(context))
+ .build();
}
// TODO(b/183998768): This should not be needed. Targeted synthetics should be in place.
if (appView.getSyntheticItems().isPendingSynthetic(invoke.getMethod().getHolderType())) {
@@ -462,6 +504,11 @@
if (resolution != null && resolution.getResolvedMethod().isStatic()) {
return computeInvokeAsThrowRewrite(invoke, resolution);
}
+ DesugarDescription description = computeEmulatedInterfaceVirtualDispatchOrNull(invoke);
+ return description != null ? description : DesugarDescription.nothing();
+ }
+
+ private DesugarDescription computeEmulatedInterfaceVirtualDispatchOrNull(CfInvoke invoke) {
DexClassAndMethod defaultMethod =
defaultMethodForEmulatedDispatchOrNull(invoke.getMethod(), invoke.isInterface());
if (defaultMethod != null) {
@@ -479,7 +526,7 @@
.getReference()))
.build();
}
- return DesugarDescription.nothing();
+ return null;
}
private DesugarDescription computeInvokeDirect(
@@ -783,6 +830,11 @@
}
}
+ return computeEmulatedInterfaceInvokeSpecial(clazz, invokedMethod, context);
+ }
+
+ private DesugarDescription computeEmulatedInterfaceInvokeSpecial(
+ DexClass clazz, DexMethod invokedMethod, ProgramMethod context) {
DexType emulatedItf = maximallySpecificEmulatedInterfaceOrNull(invokedMethod);
if (emulatedItf == null) {
if (clazz.isInterface() && appView.rewritePrefix.hasRewrittenType(clazz.type, appView)) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java
index be8211b..01c8b8c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/MergeSynthesizingContextIntoSyntheticLambdaTest.java
@@ -26,6 +26,14 @@
}
@Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I", "J");
+ }
+
+ @Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java
index 1269b34..a0838e8 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeClassTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.desugar;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestBuilder;
@@ -78,8 +79,10 @@
InterfaceDesugarMissingTypeDiagnostic desugarWarning = (InterfaceDesugarMissingTypeDiagnostic) diagnostic;
assertEquals(
Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
- assertEquals(Reference.classFromClass(MyClass.class), desugarWarning.getContextType());
- assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
+ // The type is both missing from the interface referenced invoke and from the implements.
+ // The diagnostics should likely include all contexts akin to the R8 missing types.
+ assertEquals(Reference.classFromClass(TestClass.class), desugarWarning.getContextType());
+ assertNotEquals(Position.UNKNOWN, desugarWarning.getPosition());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
index 875ead2..bd50527 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
@@ -3,7 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar;
+import static com.android.tools.r8.references.Reference.classFromClass;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestBuilder;
@@ -16,7 +19,6 @@
import com.android.tools.r8.errors.DesugarDiagnostic;
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.position.Position;
-import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
@@ -75,12 +77,12 @@
assertTrue(diagnostic instanceof DesugarDiagnostic);
assertTrue(diagnostic instanceof InterfaceDesugarMissingTypeDiagnostic);
InterfaceDesugarMissingTypeDiagnostic desugarWarning = (InterfaceDesugarMissingTypeDiagnostic) diagnostic;
- assertEquals(
- Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
- // TODO(b/132671303): The context class should not be the synthesized lambda class.
- assertTrue(SyntheticItemsTestUtils.isInternalLambda(desugarWarning.getContextType()));
- // TODO(b/132671303): The position info should be the method context.
- assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
+ assertEquals(classFromClass(MissingInterface.class), desugarWarning.getMissingType());
+ // TODO(b/132671303): The diagnostics should also contain the lambda context, but it should
+ // not be the synthesized lambda class.
+ assertFalse(SyntheticItemsTestUtils.isInternalLambda(desugarWarning.getContextType()));
+ assertEquals(classFromClass(TestClass.class), desugarWarning.getContextType());
+ assertNotEquals(Position.UNKNOWN, desugarWarning.getPosition());
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
index 9fe36a6..aeb159d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ProgramRewritingTest.java
@@ -155,7 +155,6 @@
.streamInstructions()
.filter(instr -> instr.isInvokeInterface() || instr.isInvokeStatic())
.collect(Collectors.toList());
- assertEquals(22, invokes.size());
assertInvokeStaticMatching(invokes, 0, "Set$-EL;->spliterator");
assertInvokeStaticMatching(invokes, 1, "Collection$-EL;->stream");
assertInvokeInterfaceMatching(invokes, 2, "Set;->iterator");
@@ -173,6 +172,7 @@
assertInvokeStaticMatching(invokes, 19, "Comparator$-CC;->comparingInt");
assertInvokeStaticMatching(invokes, 20, "List$-EL;->sort");
assertInvokeStaticMatching(invokes, 21, "Collection$-EL;->stream");
+ assertEquals(22, invokes.size());
}
private void assertInvokeInterfaceMatching(List<InstructionSubject> invokes, int i, String s) {
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java
index 667116e..825e3ea 100644
--- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticInterfaceNestedTest.java
@@ -5,18 +5,20 @@
package com.android.tools.r8.desugar.staticinterfacemethod;
import static com.android.tools.r8.desugar.staticinterfacemethod.InvokeStaticInterfaceNestedTest.Library.foo;
-import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
import com.android.tools.r8.R8FullTestBuilder;
-import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.util.List;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -26,38 +28,63 @@
public class InvokeStaticInterfaceNestedTest extends TestBase {
private final TestParameters parameters;
- private final boolean cfToCfDesugar;
- private final String EXPECTED = "Hello World!";
+ private final String UNEXPECTED_SUCCESS = "Hello World!";
- @Parameters(name = "{0}, desugar: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
- public InvokeStaticInterfaceNestedTest(TestParameters parameters, boolean cfToCfDesugar) {
+ public InvokeStaticInterfaceNestedTest(TestParameters parameters) {
this.parameters = parameters;
- this.cfToCfDesugar = cfToCfDesugar;
}
- @Test
- public void testRuntime() throws Exception {
- final SingleTestRunResult<?> runResult =
- testForRuntime(parameters)
- .addProgramClassFileData(
- rewriteToUseNonInterfaceMethodReference(Main.class, "main"),
- rewriteToUseNonInterfaceMethodReference(Library.class, "foo"))
- .run(parameters.getRuntime(), Main.class);
- if (parameters.isCfRuntime() && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
- runResult.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError"));
+ private void checkDexResult(TestRunResult<?> runResult, boolean isDesugared) {
+ boolean didDesugarInterfaceMethods =
+ isDesugared && !parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring();
+ if (parameters.isCfRuntime()) {
+ if (parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK9)) {
+ // The correct expected behavior is ICCE.
+ runResult.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ } else if (didDesugarInterfaceMethods) {
+ runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ } else {
+ // Dex VMs and JDK 8 will just dispatch (this is not the intended behavior).
+ runResult.assertSuccessWithOutputLines(UNEXPECTED_SUCCESS);
+ }
+ return;
+ }
+ if (parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) {
+ // Dex VMs and JDK 8 will just dispatch (this is not the intended behavior).
+ runResult.assertSuccessWithOutputLines(UNEXPECTED_SUCCESS);
+ return;
+ }
+ Version version = parameters.getRuntime().asDex().getVm().getVersion();
+ if (version.isOlderThanOrEqual(Version.V4_4_4)) {
+ runResult.assertFailureWithErrorThatThrows(VerifyError.class);
} else {
- // TODO(b/166247515): This should be ICCE.
- runResult.assertSuccessWithOutputLines(EXPECTED);
+ runResult.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
}
}
@Test
+ public void testDesugar() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClassFileData(
+ rewriteToUseNonInterfaceMethodReference(Main.class, "main"),
+ rewriteToUseNonInterfaceMethodReference(Library.class, "foo"))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(
+ result ->
+ result.applyIf(
+ DesugarTestConfiguration::isDesugared,
+ r -> checkDexResult(r, true),
+ r -> checkDexResult(r, false)));
+ }
+
+ @Test
public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel() == AndroidApiLevel.B);
final R8FullTestBuilder testBuilder =
testForR8(parameters.getBackend())
.addProgramClassFileData(
@@ -65,16 +92,12 @@
rewriteToUseNonInterfaceMethodReference(Library.class, "foo"))
.addKeepAllClassesRule()
.setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .addOptionsModification(
- options -> {
- options.cfToCfDesugar = cfToCfDesugar;
- });
- if (parameters.isCfRuntime()) {
+ .addKeepMainRule(Main.class);
+ if (parameters.isDexRuntime()) {
+ checkDexResult(testBuilder.run(parameters.getRuntime(), Main.class), true);
+ } else {
// TODO(b/166213037): Should not throw an error.
assertThrows(CompilationFailedException.class, testBuilder::compile);
- } else {
- testBuilder.run(parameters.getRuntime(), Main.class).assertSuccessWithOutputLines(EXPECTED);
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
index e5b0bb3..03aaab4 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
@@ -11,11 +11,11 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRunResult;
-import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -27,17 +27,14 @@
private static final String EXPECTED = StringUtils.lines("Hello World!");
private final TestParameters parameters;
- private final boolean isInterface;
- @Parameters(name = "{0}, itf:{1}")
- public static List<Object[]> data() {
- return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public InvokeSpecialInterfaceWithBridge3Test(TestParameters parameters, boolean isInterface) {
+ public InvokeSpecialInterfaceWithBridge3Test(TestParameters parameters) {
this.parameters = parameters;
- this.isInterface = isInterface;
}
@Test
@@ -47,14 +44,28 @@
.addProgramClasses(I.class, A.class, Main.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
.run(parameters.getRuntime(), Main.class);
- if (parameters.isDexRuntime()) {
- // TODO(b/166210854): Runs but should have failed.
+ if (parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ // TODO(b/166210854): Runs really should fail, but since DEX does not have interface
+ // method references the VM will just dispatch.
result.assertSuccessWithOutput(EXPECTED);
} else {
- result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ result.assertFailureWithErrorThatThrows(getExpectedException());
}
}
+ private Class<? extends Throwable> getExpectedException() {
+ if (parameters.isDexRuntime()) {
+ Version version = parameters.getRuntime().asDex().getVm().getVersion();
+ if (version.isOlderThanOrEqual(Version.V4_4_4)) {
+ return VerifyError.class;
+ }
+ if (version.isNewerThanOrEqual(Version.V7_0_0)) {
+ return AbstractMethodError.class;
+ }
+ }
+ return IncompatibleClassChangeError.class;
+ }
+
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 801d678..f529432 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -105,9 +105,9 @@
"In C.m3()",
"In A.m4()",
"In A.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "Caught IncompatibleClassChangeError when calling B.m3()",
+ "In A.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
"In C.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "Caught IncompatibleClassChangeError when calling B.m3()",
+ "In C.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3()
"In C.m1()",
"In C.m3()",
"");
@@ -179,9 +179,7 @@
assertThat(classSubject, isPresentAndRenamed());
assertThat(classSubject.method("void", "m1", ImmutableList.of()), isPresent());
assertThat(classSubject.method("void", "m2", ImmutableList.of()), isAbsent());
- assertThat(
- classSubject.method("void", "m3", ImmutableList.of()),
- parameters.isCfRuntime() ? isPresent() : isAbsent());
+ assertThat(classSubject.method("void", "m3", ImmutableList.of()), isPresent());
assertThat(classSubject.method("void", "m4", ImmutableList.of()), isAbsent());
}
}
@@ -265,8 +263,7 @@
System.out.println("In B.m2()");
}
- // Made static in the dump below. Ends up dead as the targeting call is replaced by throw ICCE.
- // Except in non-desugaring CF the method will remain instead of inserting a stub.
+ // Made static in the dump below. This method is targeted and can therefore not be removed.
@Override
public void m3() {
System.out.println("In B.m3()");