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();
-  }
-}