Update unsupported invoke-special test.
Bug: 157969878
Bug: 110175213
Change-Id: I008a6cb98685ba9b9121e5f325aaeda6a41cc0bf
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index b47570c..8889e81 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -286,7 +286,7 @@
// default interface methods, it is expected they are targeted with invoke-direct.
return this.itf && desugaringEnabled ? Type.DIRECT : Type.SUPER;
}
- if (!encodedMethod.isNonPrivateVirtualMethod()) {
+ if (encodedMethod.isPrivateMethod() || !encodedMethod.isVirtualMethod()) {
return Type.DIRECT;
}
if (encodedMethod.accessFlags.isFinal()) {
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialTest.java
deleted file mode 100644
index 642c263..0000000
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright (c) 2019, 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.
-
-package com.android.tools.r8.graph.invokespecial;
-
-import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.AsmTestBase;
-import com.android.tools.r8.ByteDataView;
-import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.utils.StringUtils;
-import java.nio.file.Path;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-public class InvokeSpecialTest extends AsmTestBase {
-
- @ClassRule
- public static TemporaryFolder tempFolder = ToolHelper.getTemporaryFolderForTest();
-
- private static Path inputJar;
-
- @BeforeClass
- public static void setup() throws Exception {
- inputJar = tempFolder.getRoot().toPath().resolve("input.jar");
- ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
- consumer.accept(
- ByteDataView.of(ToolHelper.getClassAsBytes(Main.class)),
- javaTypeToDescriptor(Main.class.getTypeName()),
- null);
- consumer.accept(
- ByteDataView.of(TestClassDump.dump()),
- javaTypeToDescriptor(TestClass.class.getTypeName()),
- null);
- consumer.finished(null);
- }
-
- @Test
- public void testExpectedBehavior() throws Exception {
- testForJvm()
- .addProgramClasses(Main.class, TestClass.class)
- .run(Main.class)
- .assertSuccessWithOutput(StringUtils.lines("true", "false"));
- }
-
- @Test(expected = CompilationFailedException.class)
- public void testD8Behavior() throws Exception {
- // TODO(b/110175213): Should succeed with output "true\nfalse\n".
- testForD8()
- .addProgramFiles(inputJar)
- .compileWithExpectedDiagnostics(
- testDiagnosticMessages ->
- testDiagnosticMessages.assertErrorMessageThatMatches(
- containsString("Failed to compile unsupported use of invokespecial")));
- }
-
- @Test
- public void testDXBehavior() throws Exception {
- assumeTrue(ToolHelper.artSupported());
- testForDX()
- .addProgramFiles(inputJar)
- .run(Main.class)
- .assertFailureWithErrorThatMatches(containsString(getExpectedOutput()));
- }
-
- private static String getExpectedOutput() {
- if (ToolHelper.getDexVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
- return "VFY: unable to resolve direct method";
- }
- return "was expected to be of type direct but instead was found to be of type virtual";
- }
-}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java
new file mode 100644
index 0000000..730ae81
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialToVirtualMethodTest.java
@@ -0,0 +1,138 @@
+// Copyright (c) 2020, 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.
+package com.android.tools.r8.graph.invokespecial;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromJavaType;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+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.DexVm.Version;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class InvokeSpecialToVirtualMethodTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("Bar::foo", "Foo::foo", "Foo::foo", "Foo::foo");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public InvokeSpecialToVirtualMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(Base.class, Bar.class, TestClass.class)
+ .addProgramClassFileData(getFooTransform())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClasses(Base.class, Bar.class, TestClass.class)
+ .addProgramClassFileData(getFooTransform())
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertOnlyErrors()
+ .assertErrorsMatch(
+ diagnosticMessage(containsString("unsupported use of invokespecial"))));
+ }
+
+ @Test
+ public void testDX() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForDX()
+ .addProgramClasses(Base.class, Bar.class, TestClass.class)
+ .addProgramClassFileData(getFooTransform())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatMatches(containsString(getExpectedOutput()));
+ }
+
+ private String getExpectedOutput() {
+ if (parameters.getRuntime().asDex().getVm().getVersion().isOlderThanOrEqual(Version.V4_4_4)) {
+ return "VFY: unable to resolve direct method";
+ }
+ return "was expected to be of type direct but instead was found to be of type virtual";
+ }
+
+ private byte[] getFooTransform() throws IOException {
+ return transformer(Foo.class)
+ .transformMethodInsnInMethod(
+ "foo",
+ (opcode, owner, name, descriptor, isInterface, visitor) -> {
+ if (Opcodes.INVOKESPECIAL == opcode) {
+ assertEquals(getBinaryNameFromJavaType(Base.class.getName()), owner);
+ assertEquals("foo", name);
+ visitor.visitMethodInsn(
+ opcode,
+ getBinaryNameFromJavaType(Foo.class.getName()),
+ name,
+ descriptor,
+ isInterface);
+ } else {
+ visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ })
+ .transform();
+ }
+
+ static class Base {
+ // Base method is never hit.
+ public void foo(int i) {
+ System.out.println("Base::foo");
+ }
+ }
+
+ static class Foo extends Base {
+
+ public void foo(int i) {
+ if (i > 0) {
+ System.out.println("Foo::foo");
+ // Will be converted to invoke-special Foo.foo.
+ super.foo(i - 1);
+ }
+ }
+ }
+
+ static class Bar extends Foo {
+
+ // Subclass override is only hit initially.
+ @Override
+ public void foo(int i) {
+ System.out.println("Bar::foo");
+ super.foo(3);
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new Bar().foo(0);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/Main.java b/src/test/java/com/android/tools/r8/graph/invokespecial/Main.java
deleted file mode 100644
index deb316d..0000000
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/Main.java
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (c) 2018, 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.
-
-package com.android.tools.r8.graph.invokespecial;
-
-public class Main {
-
- public static void main(String[] args) {
- TestClass x = new TestClass();
- x.m(true);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/TestClass.java b/src/test/java/com/android/tools/r8/graph/invokespecial/TestClass.java
deleted file mode 100644
index c08091e..0000000
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/TestClass.java
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) 2018, 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.
-
-package com.android.tools.r8.graph.invokespecial;
-
-public class TestClass {
-
- public void m(boolean recurse) {
- System.out.println(recurse);
- if (recurse) {
- m(false);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/TestClassDump.java b/src/test/java/com/android/tools/r8/graph/invokespecial/TestClassDump.java
deleted file mode 100644
index ae336834..0000000
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/TestClassDump.java
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2018, 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.
-
-package com.android.tools.r8.graph.invokespecial;
-
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-
-// Generated by running ./tools/asmifier.py build/classes/test/com/android/tools/r8/graph/-
-// invokespecial/TestClass.class, and changing the invoke-virtual TestClass.m() instruction to
-// an invoke-special instruction.
-public class TestClassDump implements Opcodes {
-
- public static byte[] dump() throws Exception {
-
- ClassWriter cw = new ClassWriter(0);
- FieldVisitor fv;
- MethodVisitor mv;
- AnnotationVisitor av0;
-
- cw.visit(
- V1_8,
- ACC_PUBLIC + ACC_SUPER,
- "com/android/tools/r8/graph/invokespecial/TestClass",
- null,
- "java/lang/Object",
- null);
-
- {
- mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
- mv.visitCode();
- mv.visitVarInsn(ALOAD, 0);
- mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
- mv.visitInsn(RETURN);
- mv.visitMaxs(1, 1);
- mv.visitEnd();
- }
- {
- mv = cw.visitMethod(ACC_PUBLIC, "m", "(Z)V", null, null);
- mv.visitCode();
- mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
- mv.visitVarInsn(ILOAD, 1);
- mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Z)V", false);
- mv.visitVarInsn(ILOAD, 1);
- Label l0 = new Label();
- mv.visitJumpInsn(IFEQ, l0);
- mv.visitVarInsn(ALOAD, 0);
- mv.visitInsn(ICONST_0);
- // Note: Changed from INVOKEVIRTUAL to INVOKESPECIAL.
- mv.visitMethodInsn(
- INVOKESPECIAL, "com/android/tools/r8/graph/invokespecial/TestClass", "m", "(Z)V", false);
- mv.visitLabel(l0);
- mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
- mv.visitInsn(RETURN);
- mv.visitMaxs(2, 2);
- mv.visitEnd();
- }
- cw.visitEnd();
-
- return cw.toByteArray();
- }
-}