Desugar multianewarray instructions in new CF front-end.

Bug: 109789539
Change-Id: I86efad06e8f765ce4302a786cd2e9558f70e9ee2
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 3866f5f..0248418 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -317,7 +317,7 @@
   public void print(CfConstClass constClass) {
     indent();
     builder.append("ldc ");
-    appendClass(constClass.getType());
+    appendType(constClass.getType());
   }
 
   public void print(CfReturnVoid ret) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
index e2f7336..94f7223 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfMultiANewArray.java
@@ -4,7 +4,6 @@
 package com.android.tools.r8.cf.code;
 
 import com.android.tools.r8.cf.CfPrinter;
-import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.UseRegistry;
 import com.android.tools.r8.ir.conversion.CfSourceCode;
@@ -55,10 +54,7 @@
   @Override
   public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
     InternalOptions options = builder.appView.options();
-    if (options.isGeneratingDex()) {
-      // TODO(b/109789539): Implement this case (see JarSourceCode.buildPrelude()/buildPostlude()).
-      throw new Unimplemented("CfMultiANewArray to DEX backend");
-    }
+    assert !options.isGeneratingDex();
     int[] dimensions = state.popReverse(this.dimensions);
     builder.addMultiNewArray(type, state.push(type).register, dimensions);
   }
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index 2a10b2d..0df8ee4 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -783,7 +783,47 @@
 
     @Override
     public void visitMultiANewArrayInsn(String desc, int dims) {
-      instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
+      if (!application.options.isGeneratingDex()) {
+        instructions.add(new CfMultiANewArray(factory.createType(desc), dims));
+        return;
+      }
+      // When generating DEX code a multianewarray is desugared to a reflective creation.
+      // The stack transformation is:
+      //   ..., count1, ..., countN (where N = dims)
+      //   ->
+      //   ..., arrayref(of type : desc)
+      //
+      // This is unfolded to a call to java.lang.reflect.Array.newInstance to the same effect:
+      // ..., count1, ..., countN
+      visitLdcInsn(dims);
+      // ..., count1, ..., countN, dims
+      visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_INT);
+      // ..., count1, ..., countN, dim-array
+      for (int i = dims - 1; i >= 0; i--) {
+        visitInsn(Opcodes.DUP_X1);
+        // ..., count1, ..., dim-array, countN, dim-array
+        visitInsn(Opcodes.SWAP);
+        // ..., count1, ..., dim-array, dim-array, countN
+        visitLdcInsn(i);
+        // ..., count1, ..., dim-array, dim-array, countN, index
+        visitInsn(Opcodes.SWAP);
+        // ..., count1, ..., dim-array, dim-array, index, countN
+        visitInsn(Opcodes.IASTORE);
+        // ..., count1, ..., dim-array
+      }
+      visitLdcInsn(Type.getType(desc.substring(dims)));
+      // ..., dim-array, dim-member-type
+      visitInsn(Opcodes.SWAP);
+      // ..., dim-member-type, dim-array
+      visitMethodInsn(
+          Opcodes.INVOKESTATIC,
+          "java/lang/reflect/Array",
+          "newInstance",
+          "(Ljava/lang/Class;[I)Ljava/lang/Object;",
+          false);
+      // ..., ref
+      visitTypeInsn(Opcodes.CHECKCAST, desc);
+      // ..., arrayref(of type : desc)
     }
 
     @Override
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index b3f104b..1f02b9e 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -48,7 +48,6 @@
         "loadconst.LoadConst",
         "loop.UdpServer",
         "nestedtrycatches.NestedTryCatches",
-        "newarray.NewArray",
         "regalloc.RegAlloc",
         "returns.Returns",
         "staticfield.StaticField",
@@ -158,11 +157,6 @@
   @Override
   protected Set<String> getFailingCompileCfToDex() {
     return new ImmutableSet.Builder<String>()
-        // TODO(b/109789539): Implement CfMultiANewArray.buildIR() for DEX backend.
-        .add("newarray.NewArray")
-        .add("trycatch.TryCatch")
-        .add("regress_70737019.Test")
-        .add("regress_72361252.Test")
         .build();
   }
 
diff --git a/src/test/examples/newarray/NewArray.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
similarity index 97%
rename from src/test/examples/newarray/NewArray.java
rename to src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
index b652324..b1f2693 100644
--- a/src/test/examples/newarray/NewArray.java
+++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArray.java
@@ -1,7 +1,7 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// 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 newarray;
+package com.android.tools.r8.examples.newarray;
 
 class NewArray {
 
diff --git a/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java
new file mode 100644
index 0000000..3810a92
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/newarray/NewArrayTestRunner.java
@@ -0,0 +1,78 @@
+// 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.examples.newarray;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NewArrayTestRunner extends TestBase {
+
+  static final Class<?> CLASS = NewArray.class;
+
+  private final TestParameters parameters;
+  private final CompilationMode mode;
+  private final boolean newCfFrontend;
+
+  private static String referenceOut;
+
+  @Parameterized.Parameters(name = "{0}, {1}, new-cf:{2}")
+  public static List<Object[]> data() {
+    return buildParameters(
+        getTestParameters().withAllRuntimes().withAllApiLevels().build(),
+        CompilationMode.values(),
+        BooleanUtils.values());
+  }
+
+  public NewArrayTestRunner(
+      TestParameters parameters, CompilationMode mode, boolean newCfFrontend) {
+    this.parameters = parameters;
+    this.mode = mode;
+    this.newCfFrontend = newCfFrontend;
+  }
+
+  @BeforeClass
+  public static void runReference() throws Exception {
+    referenceOut =
+        testForJvm(getStaticTemp())
+            .addProgramClassesAndInnerClasses(CLASS)
+            .run(TestRuntime.getDefaultJavaRuntime(), CLASS)
+            .assertSuccess()
+            .getStdOut();
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    assumeTrue(parameters.isDexRuntime());
+    testForD8()
+        .addProgramClassesAndInnerClasses(CLASS)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.enableCfFrontend = newCfFrontend)
+        .setMode(mode)
+        .run(parameters.getRuntime(), CLASS)
+        .assertSuccessWithOutput(referenceOut);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    testForR8(parameters.getBackend())
+        .addProgramClassesAndInnerClasses(CLASS)
+        .addKeepMainRule(CLASS)
+        .setMinApi(parameters.getApiLevel())
+        .addOptionsModification(options -> options.enableCfFrontend = newCfFrontend)
+        .setMode(mode)
+        .run(parameters.getRuntime(), CLASS)
+        .assertSuccessWithOutput(referenceOut);
+  }
+}