Rewrite class merging test from smali to jasmin

Change-Id: Ie882b41dd88a690881d44b2b48e69ea65e11cc6a
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 55f0919..e0d73e8 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -23,6 +23,8 @@
 import com.android.tools.r8.code.MoveException;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.utils.AndroidApiLevel;
@@ -43,7 +45,6 @@
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -336,7 +337,6 @@
   //   }
   @Test
   @IgnoreForRangeOfVmVersions(from = Version.V5_1_1, to = Version.V6_0_1)
-  @Ignore // TODO(christofferqa): Need to use jasmin for the classes that are merge candidates.
   public void testSuperCallToMergedClassIsRewritten() throws Exception {
     String main = "classmerging.SuperCallToMergedClassIsRewrittenTest";
     Set<String> preservedClassNames =
@@ -347,47 +347,57 @@
             "classmerging.D",
             "classmerging.F");
 
-    SmaliBuilder smaliBuilder = new SmaliBuilder();
+    JasminBuilder jasminBuilder = new JasminBuilder();
 
-    smaliBuilder.addClass(main);
-    smaliBuilder.addMainMethod(
-        2,
+    ClassBuilder classBuilder = jasminBuilder.addClass(main);
+    classBuilder.addMainMethod(
+        ".limit locals 1",
+        ".limit stack 2",
         // Instantiate B so that it is not merged into C.
-        "new-instance v1, Lclassmerging/B;",
-        "invoke-direct {v1}, Lclassmerging/B;-><init>()V",
-        "invoke-virtual {v1}, Lclassmerging/B;->m()V",
+        "new classmerging/B",
+        "dup",
+        "invokespecial classmerging/B/<init>()V",
+        "invokevirtual classmerging/B/m()V",
         // Instantiate D so that it is not merged into E.
-        "new-instance v1, Lclassmerging/D;",
-        "invoke-direct {v1}, Lclassmerging/D;-><init>()V",
-        "invoke-virtual {v1}, Lclassmerging/D;->m()V",
+        "new classmerging/D",
+        "dup",
+        "invokespecial classmerging/D/<init>()V",
+        "invokevirtual classmerging/D/m()V",
         // Start the actual testing.
-        "new-instance v1, Lclassmerging/F;",
-        "invoke-direct {v1}, Lclassmerging/F;-><init>()V",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnB()V",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnC()V",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnD()V",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnE()V",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnF()V",
+        "new classmerging/F",
+        "dup",
+        "invokespecial classmerging/F/<init>()V",
+        "dup",
+        "invokevirtual classmerging/F/invokeMethodOnB()V",
+        "dup",
+        "invokevirtual classmerging/F/invokeMethodOnC()V",
+        "dup",
+        "invokevirtual classmerging/F/invokeMethodOnD()V",
+        "dup",
+        "invokevirtual classmerging/F/invokeMethodOnE()V",
+        "dup",
+        "invokevirtual classmerging/F/invokeMethodOnF()V",
+        "dup",
         // The method invokeMethodOnA() should yield a NoSuchMethodError.
-        ":try_start",
-        "invoke-virtual {v1}, Lclassmerging/F;->invokeMethodOnA()V",
-        ":try_end",
-        "return-void",
-        ".catch Ljava/lang/NoSuchMethodError; {:try_start .. :try_end} :catch",
-        ":catch",
-        smaliCodeForPrinting("NoSuchMethodError", 0, 1),
-        "return-void");
+        "try_start:",
+        "invokevirtual classmerging/F/invokeMethodOnA()V",
+        "try_end:",
+        "return",
+        "catch:",
+        jasminCodeForPrinting("NoSuchMethodError"),
+        "return",
+        ".catch java/lang/NoSuchMethodError from try_start to try_end using catch");
 
     // Class A deliberately has no method m. We need to make sure that the "invoke-super A.m()"
     // instruction in class F is not rewritten into something that does not throw.
-    smaliBuilder.addClass("classmerging.A");
-    smaliBuilder.addDefaultConstructor();
+    classBuilder = jasminBuilder.addClass("classmerging.A");
+    classBuilder.addDefaultConstructor();
 
     // Class B declares a virtual method m() that prints "In B.m()".
-    smaliBuilder.addClass("classmerging.B", "classmerging.A");
-    smaliBuilder.addDefaultConstructor();
-    smaliBuilder.addInstanceMethod(
-        "void", "m", 2, smaliCodeForPrinting("In B.m()", 0, 1), "return-void");
+    classBuilder = jasminBuilder.addClass("classmerging.B", "classmerging.A");
+    classBuilder.addDefaultConstructor();
+    classBuilder.addVirtualMethod(
+        "m", "V", ".limit locals 1", ".limit stack 2", jasminCodeForPrinting("In B.m()"), "return");
 
     // Class C, D, and E declare a virtual method m() that prints "In C.m()", "In D.m()", and
     // "In E.m()", respectively.
@@ -395,17 +405,24 @@
         new String[][] {new String[] {"C", "B"}, new String[] {"D", "C"}, new String[] {"E", "D"}};
     for (String[] pair : pairs) {
       String name = pair[0], superName = pair[1];
-      smaliBuilder.addClass("classmerging." + name, "classmerging." + superName);
-      smaliBuilder.addDefaultConstructor();
-      smaliBuilder.addInstanceMethod(
-          "void",
+      classBuilder = jasminBuilder.addClass("classmerging." + name, "classmerging." + superName);
+      classBuilder.addDefaultConstructor();
+      classBuilder.addVirtualMethod(
           "m",
-          2,
-          smaliCodeForPrinting("In " + name + ".m()", 0, 1),
-          buildCode("invoke-super {p0}, Lclassmerging/" + superName + ";->m()V", "return-void"));
+          "V",
+          ".limit locals 1",
+          ".limit stack 2",
+          jasminCodeForPrinting("In " + name + ".m()"),
+          "aload_0",
+          "invokespecial classmerging/" + superName + "/m()V",
+          "return");
     }
 
     // Class F declares a virtual method m that throws an exception (it is expected to be dead).
+    //
+    // Note that F is generated from smali since it is the only way to generate an instruction on
+    // the form "invoke-super F.m()".
+    SmaliBuilder smaliBuilder = new SmaliBuilder();
     smaliBuilder.addClass("classmerging.F", "classmerging.E");
     smaliBuilder.addDefaultConstructor();
     smaliBuilder.addInstanceMethod(
@@ -415,7 +432,6 @@
         "new-instance v1, Ljava/lang/Exception;",
         "invoke-direct {v1}, Ljava/lang/Exception;-><init>()V",
         "throw v1");
-
     // Add methods to F with an "invoke-super X.m()" instruction for X in {A, B, C, D, E, F}.
     for (String type : ImmutableList.of("A", "B", "C", "D", "E", "F")) {
       String code =
@@ -424,24 +440,23 @@
     }
 
     // Build app.
-    AndroidApp.Builder builder = AndroidApp.builder();
-    builder.addDexProgramData(smaliBuilder.compile(), Origin.unknown());
+    AndroidApp.Builder appBuilder = AndroidApp.builder();
+    appBuilder.addClassProgramData(jasminBuilder.buildClasses());
+    appBuilder.addDexProgramData(smaliBuilder.compile(), Origin.unknown());
 
     // Run test.
     runTestOnInput(
         main,
-        builder.build(),
+        appBuilder.build(),
         preservedClassNames::contains,
         String.format("-keep class %s { public static void main(...); }", main));
   }
 
-  private static String smaliCodeForPrinting(String message, int reg0, int reg1) {
+  private static String jasminCodeForPrinting(String message) {
     return buildCode(
-        String.format("sget-object v%d, Ljava/lang/System;->out:Ljava/io/PrintStream;", reg0),
-        String.format("const-string v1, \"%s\"", message),
-        String.format(
-            "invoke-virtual {v%d, v%d}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V",
-            reg0, reg1));
+        "getstatic java/lang/System/out Ljava/io/PrintStream;",
+        String.format("ldc \"%s\"", message),
+        "invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V");
   }
 
   @Test
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index ba04454..82381dd 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -131,6 +131,10 @@
       return addMethod("public final", name, argumentTypes, returnType, lines);
     }
 
+    public MethodSignature addVirtualMethod(String name, String returnType, String... lines) {
+      return addVirtualMethod(name, ImmutableList.of(), returnType, lines);
+    }
+
     public MethodSignature addVirtualMethod(
         String name,
         List<String> argumentTypes,