Return null for invalid targets of invoke-super.
Bug: 147848950
Change-Id: If881afa3b5e5d56f51085bc6eff9c6c3b3b613df
diff --git a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
index fbecfef..d164bf1 100644
--- a/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/ResolutionResult.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -205,14 +204,11 @@
@Override
public DexEncodedMethod lookupInvokeSuperTarget(
DexProgramClass context, AppInfoWithClassHierarchy appInfo) {
- // TODO(b/147848950): Investigate and remove the Compilation error. It could compile to
- // throw IAE.
if (resolvedMethod.isInstanceInitializer()
- || (appInfo.hasSubtyping()
- && initialResolutionHolder != context
- && !isSuperclass(initialResolutionHolder, context, appInfo.withSubtyping()))) {
- throw new CompilationError(
- "Illegal invoke-super to " + resolvedMethod.toSourceString(), context.getOrigin());
+ || (initialResolutionHolder != context
+ && !isSuperclass(initialResolutionHolder, context, appInfo))) {
+ // If the target is <init> or not on a super class then the call is invalid.
+ return null;
}
if (isAccessibleFrom(context, appInfo)) {
return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true);
@@ -341,7 +337,7 @@
private static boolean isSuperclass(
DexClass sup, DexClass sub, AppInfoWithClassHierarchy appInfo) {
- return sup != sub && appInfo.isSubtype(sub.type, sup.type);
+ return appInfo.isStrictSubtypeOf(sub.type, sup.type);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
index a5c4e81..5ed1539 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethodWithReceiver.java
@@ -83,6 +83,7 @@
DexType refinedReceiverType =
TypeAnalysis.getRefinedReceiverType(appViewWithLiveness, this);
assert receiverLowerBoundType.getClassType() == refinedReceiverType
+ || appView.options().testing.allowTypeErrors
|| receiverLowerBoundType.isBasedOnMissingClass(appViewWithLiveness)
|| upperBoundAssumedByCallSiteOptimizationAndNoLongerInstantiated(
appViewWithLiveness, refinedReceiverType, receiverLowerBoundType.getClassType())
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 74cff92..8ffe6af 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -981,7 +981,6 @@
LEGACY_RUNTIME))
// Dex input contains an illegal InvokeSuper in Z.foo() to Y.foo()
// that R8 will fail to compile.
- .put("594-invoke-super", TestCondition.match(TestCondition.R8DEX_COMPILER))
.put("974-verify-interface-super", TestCondition.match(TestCondition.R8DEX_COMPILER))
// R8 generates too large code in Goto.bigGoto(). b/74327727
.put("003-omnibus-opcodes", TestCondition.match(TestCondition.D8_AFTER_R8CF_COMPILER))
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
index f71538c..1abfea6f 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
@@ -3,12 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -114,48 +111,42 @@
SubClassOfInvokerClass.class)
.addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
.run(parameters.getRuntime(), MainClassFailing.class)
- .apply(this::checkNonVerifyingResult);
+ .apply(r -> checkNonVerifyingResult(r, false));
}
- private void checkNonVerifyingResult(TestRunResult<?> result) {
+ private void checkNonVerifyingResult(TestRunResult<?> result, boolean isR8) {
// The input is invalid and any JVM will fail at verification time.
if (parameters.isCfRuntime()) {
- result.assertFailureWithErrorThatMatches(containsString(VerifyError.class.getName()));
+ result.assertFailureWithErrorThatThrows(VerifyError.class);
return;
}
- // D8 cannot verify its inputs and the behavior of the compiled output differs.
- // The failure is due to lambda desugaring on pre-7 and fails at runtime on 7+.
+ // Dex results vary wildly...
Version version = parameters.getRuntime().asDex().getVm().getVersion();
- if (version.isOlderThanOrEqual(Version.V6_0_1)) {
- result.assertFailureWithErrorThatMatches(
- allOf(containsString("java.lang.NoClassDefFoundError"), containsString("-$$Lambda$")));
- return;
+ if (!isR8 && version.isOlderThanOrEqual(Version.V4_4_4)) {
+ result.assertFailureWithErrorThatThrows(VerifyError.class);
+ } else if (version == Version.V5_1_1 || version == Version.V6_0_1) {
+ result.assertFailure();
+ } else {
+ result.assertSuccessWithOutputThatMatches(containsString(NoSuchMethodError.class.getName()));
}
- result.assertSuccessWithOutputLines(NoSuchMethodError.class.getName());
}
@Test
public void testR8NonVerifying() throws Exception {
- try {
- testForR8(parameters.getBackend())
- .addProgramClasses(
- MainClassFailing.class,
- Consumer.class,
- Super.class,
- SubLevel1.class,
- SubLevel2.class,
- SubClassOfInvokerClass.class)
- .addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(MainClassFailing.class)
- .compileWithExpectedDiagnostics(
- diagnostics -> {
- diagnostics.assertErrorMessageThatMatches(containsString("Illegal invoke-super"));
- });
- fail("Expected compilation to fail");
- } catch (CompilationFailedException e) {
- // Expected compilation failure.
- }
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ MainClassFailing.class,
+ Consumer.class,
+ Super.class,
+ SubLevel1.class,
+ SubLevel2.class,
+ SubClassOfInvokerClass.class)
+ .addProgramClassFileData(InvokerClassDump.dumpNonVerifying())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MainClassFailing.class)
+ .addOptionsModification(o -> o.testing.allowTypeErrors = true)
+ .run(parameters.getRuntime(), MainClassFailing.class)
+ .apply(r -> checkNonVerifyingResult(r, true));
}
/** Copy of {@ref java.util.function.Consumer} to allow tests to run on early versions of art. */
@@ -253,7 +244,7 @@
static class MainClassFailing {
- private static void tryInvoke(java.util.function.Consumer<InvokerClass> function) {
+ private static void tryInvoke(Consumer<InvokerClass> function) {
InvokerClass invoker = new InvokerClass();
try {
function.accept(invoker);
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 2438ab2..8524677 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -261,7 +261,7 @@
assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI1, c1).method);
assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI2, c1).method);
- assertEquals(mOnI0, appInfo.lookupSuperTarget(mOnC1, c2).method);
+ assertNull(appInfo.lookupSuperTarget(mOnC1, c2)); // C2 is not a subclass of C1.
assertEquals(mOnI1, appInfo.lookupSuperTarget(mOnI3, c2).method);
assertEquals(mOnI2, appInfo.lookupSuperTarget(mOnI4, c2).method);
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index b4dc614..fd893ea 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -5,7 +5,6 @@
import static java.util.Collections.emptyList;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ThrowingBiFunction;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -17,7 +16,6 @@
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.utils.ThrowingSupplier;
import java.util.function.BiConsumer;
-import java.util.function.Predicate;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -293,8 +291,7 @@
" invokespecial SubClass/<init>()V",
" invokespecial SubClass/aMethod()V",
" return");
- ensureExceptionOrCompilerError(builder, IllegalAccessError.class,
- compiler -> compiler.equals(CompilerUnderTest.R8));
+ ensureException(builder, IllegalAccessError.class);
}
@Test
@@ -613,18 +610,14 @@
(a, m) -> runOnArtR8Raw(a, m, keepMainProguardConfiguration(MAIN_CLASS), null));
}
- private void ensureExceptionOrCompilerError(JasminBuilder app,
- Class<? extends Throwable> exception,
- Predicate<CompilerUnderTest> predicate) throws Exception {
+ private void ensureException(JasminBuilder app, Class<? extends Throwable> exception)
+ throws Exception {
String name = exception.getSimpleName();
BiConsumer<ThrowingSupplier<ProcessResult, Exception>, CompilerUnderTest> runtest =
(process, compiler) -> {
try {
ProcessResult result = process.get();
- Assert.assertFalse(compiler != null && predicate.test(compiler));
Assert.assertTrue(result.stderr.contains(name));
- } catch (CompilationFailedException e) {
- Assert.assertTrue(compiler == null || predicate.test(compiler));
} catch (Exception e) {
Assert.fail();
}