Add tests of nested arrays construction

Bug: b/315753861
Change-Id: Id1b97269154f2fc1bbcd59668f8063f62d764933
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfArraysTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfArraysTest.java
new file mode 100644
index 0000000..131d1bd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfArraysTest.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2024, 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArrayOfArraysTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameters(name = "{0}, mode = {1}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters()
+            .withDefaultCfRuntime()
+            .withDexRuntimesAndAllApiLevels()
+            .withAllApiLevelsAlsoForCf()
+            .build(),
+        CompilationMode.values());
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "[class [B, class [S, class [I, class [J, class [C, class [F, class [D, class"
+              + " [Ljava.lang.String;, [class [B, class [S, class [I, class [J, class [C, class [F,"
+              + " class [D, class [Ljava.lang.String;]]",
+          "[class [B, class [S, class [I, class [J, class [C, class [F, class [D, class"
+              + " [Ljava.lang.String;, [class [B, class [S, class [I, class [J, class [C, class [F,"
+              + " class [D, class [Ljava.lang.String;]]");
+
+  private void inspect(MethodSubject method, int dexFilledNewArray, int cfMaxLocals) {
+    if (parameters.isDexRuntime()) {
+      assertEquals(
+          dexFilledNewArray,
+          method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+
+    } else {
+      assertEquals(
+          0, method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+      assertEquals(cfMaxLocals, method.getMethod().getCode().asCfCode().getMaxLocals());
+    }
+  }
+
+  private void inspect(CodeInspector inspector) {
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"),
+        2
+            + (canUseFilledNewArrayOfStringObjects(parameters) ? 2 : 0)
+            + (canUseFilledNewArrayOfNonStringObjects(parameters) ? 2 : 0),
+        // TODO(b/315753861): Don't use 17 locals.
+        17);
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"), 2, 2);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      Object[] array = {
+        new byte[] {(byte) 1},
+        new short[] {(short) 1},
+        new int[] {1},
+        new long[] {1L},
+        new char[] {(char) 1},
+        new float[] {1.0f},
+        new double[] {1.0d},
+        new String[] {"one"},
+        new Object[] {
+          new byte[] {(byte) 2},
+          new short[] {(short) 2},
+          new int[] {2},
+          new long[] {2L},
+          new char[] {(char) 2},
+          new float[] {2.0f},
+          new double[] {2.0d},
+          new String[] {"two"},
+        }
+      };
+      printArray(array);
+      System.out.println();
+    }
+
+    @NeverInline
+    public static void m2() {
+      try {
+        Object[] array = {
+          new byte[] {(byte) 1},
+          new short[] {(short) 1},
+          new int[] {1},
+          new long[] {1L},
+          new char[] {(char) 1},
+          new float[] {1.0f},
+          new double[] {1.0d},
+          new String[] {"one"},
+          new Object[] {
+            new byte[] {(byte) 2},
+            new short[] {(short) 2},
+            new int[] {2},
+            new long[] {2L},
+            new char[] {(char) 2},
+            new float[] {2.0f},
+            new double[] {2.0d},
+            new String[] {"two"},
+          }
+        };
+        printArray(array);
+        System.out.println();
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void printArray(Object[] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        if (array[i].getClass().isArray()) {
+          if (array[i] instanceof Object[] && !(array[i] instanceof String[])) {
+            printArray((Object[]) array[i]);
+          } else {
+            System.out.print(array[i].getClass());
+          }
+        } else {
+          System.out.print("Unexpected " + array[i].getClass());
+        }
+      }
+      System.out.print("]");
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfConstClassArraysTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfConstClassArraysTest.java
new file mode 100644
index 0000000..c72cd78
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfConstClassArraysTest.java
@@ -0,0 +1,180 @@
+// Copyright (c) 2024, 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.rewrite.arrays.ConstClassArrayWithUniqueValuesTest.TestClass;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArrayOfConstClassArraysTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameters(name = "{0}, mode = {1}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters()
+            .withDefaultCfRuntime()
+            .withDexRuntimesAndAllApiLevels()
+            .withAllApiLevelsAlsoForCf()
+            .build(),
+        CompilationMode.values());
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "[[A, B], [C, D], [E, F, G], [H, I, J]]", "[[J, I, H], [G, F, E], [D, C], [B, A]]");
+
+  private void inspect(MethodSubject method, int dexFilledNewArray, int cfMaxLocals) {
+    if (canUseFilledNewArrayOfNonStringObjects(parameters)) {
+      assertEquals(
+          dexFilledNewArray,
+          method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+
+    } else {
+      assertEquals(
+          0, method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+      if (parameters.isCfRuntime()) {
+        assertEquals(cfMaxLocals, method.getMethod().getCode().asCfCode().getMaxLocals());
+      }
+    }
+  }
+
+  private void inspect(CodeInspector inspector) {
+    // TODO(b/315753861): Don't use 4 locals.
+    inspect(inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"), 5, 4);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        0,
+        compilationMode.isDebug() ? 1 : 2);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      Class<?>[][] array = {
+        new Class<?>[] {A.class, B.class},
+        new Class<?>[] {C.class, D.class},
+        new Class<?>[] {E.class, F.class, G.class},
+        new Class<?>[] {H.class, I.class, J.class}
+      };
+      printArray(array);
+    }
+
+    @NeverInline
+    public static void m2() {
+      try {
+        Class<?>[][] array = {
+          new Class<?>[] {J.class, I.class, H.class},
+          new Class<?>[] {G.class, F.class, E.class},
+          new Class<?>[] {D.class, C.class},
+          new Class<?>[] {B.class, A.class}
+        };
+        printArray(array);
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void printArray(Class<?>[][] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        printArray(array[i]);
+      }
+      System.out.println("]");
+    }
+
+    @NeverInline
+    public static void printArray(Class<?>[] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        String simpleName = array[i].getName();
+        if (simpleName.lastIndexOf("$") > 0) {
+          simpleName = simpleName.substring(simpleName.lastIndexOf("$") + 1);
+        }
+        System.out.print(simpleName);
+      }
+      System.out.print("]");
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+    }
+  }
+
+  class A {}
+
+  class B {}
+
+  class C {}
+
+  class D {}
+
+  class E {}
+
+  class F {}
+
+  class G {}
+
+  class H {}
+
+  class I {}
+
+  class J {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfIntArraysTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfIntArraysTest.java
new file mode 100644
index 0000000..a88dedd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfIntArraysTest.java
@@ -0,0 +1,152 @@
+// Copyright (c) 2024, 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArrayOfIntArraysTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameters(name = "{0}, mode = {1}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters()
+            .withDefaultCfRuntime()
+            .withDexRuntimesAndAllApiLevels()
+            .withAllApiLevelsAlsoForCf()
+            .build(),
+        CompilationMode.values());
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "[[1, 2], [3, 4], [5, 6, 7], [8, 9, 10]]", "[[10, 9, 8], [7, 6, 5], [4, 3], [2, 1]]");
+
+  private void inspect(MethodSubject method, int dexFilledNewArray, int cfMaxLocals) {
+    if (parameters.isDexRuntime()) {
+      assertEquals(
+          dexFilledNewArray,
+          method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+
+    } else {
+      assertEquals(
+          0, method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+      assertEquals(cfMaxLocals, method.getMethod().getCode().asCfCode().getMaxLocals());
+    }
+  }
+
+  private void inspect(CodeInspector inspector) {
+    // This test use smaller int arrays, where filled-new-array is preferred over filled-array-data.
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"),
+        4 + (canUseFilledNewArrayOfNonStringObjects(parameters) ? 1 : 0),
+        // TODO(b/315753861): Don't use 4 locals.
+        4);
+    // With catch handler the int[][] creation is not converted to filled-new-array.
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        4,
+        compilationMode.isDebug() ? 1 : 2);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      int[][] array = {
+        new int[] {1, 2}, new int[] {3, 4}, new int[] {5, 6, 7}, new int[] {8, 9, 10}
+      };
+      printArray(array);
+    }
+
+    @NeverInline
+    public static void m2() {
+      try {
+        int[][] array = {
+          new int[] {10, 9, 8}, new int[] {7, 6, 5}, new int[] {4, 3}, new int[] {2, 1}
+        };
+        printArray(array);
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void printArray(int[][] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        printArray(array[i]);
+      }
+      System.out.println("]");
+    }
+
+    @NeverInline
+    public static void printArray(int[] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        System.out.print(array[i]);
+      }
+      System.out.print("]");
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfStringArraysTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfStringArraysTest.java
new file mode 100644
index 0000000..5ef841c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayOfStringArraysTest.java
@@ -0,0 +1,236 @@
+// Copyright (c) 2024, 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.rewrite.arrays;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.rewrite.arrays.ArrayOfConstClassArraysTest.TestClass;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArrayOfStringArraysTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameter(1)
+  public CompilationMode compilationMode;
+
+  @Parameters(name = "{0}, mode = {1}")
+  public static Iterable<?> data() {
+    return buildParameters(
+        getTestParameters()
+            .withDefaultCfRuntime()
+            .withDexRuntimesAndAllApiLevels()
+            .withAllApiLevelsAlsoForCf()
+            .build(),
+        CompilationMode.values());
+  }
+
+  private static final String EXPECTED_OUTPUT =
+      StringUtils.lines(
+          "[[A, B], [C, D], [E, F, G], [H, I, J]]",
+          "[[J, I, H], [G, F, E], [D, C], [B, A]]",
+          "[[[A, B, C]], [[D, E, F]], [[G, H]], [[I, J]]]",
+          "[[[J, I, H]], [[G, F, E]], [[D, C]], [[B, A]]]",
+          "[[J, I, H], [G, F, E], [D, C], [B, A]]");
+
+  private void inspect(MethodSubject method, int dexFilledNewArray, int cfMaxLocals) {
+    if (parameters.isDexRuntime()) {
+      assertEquals(
+          dexFilledNewArray,
+          method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+
+    } else {
+      assertEquals(
+          0, method.streamInstructions().filter(InstructionSubject::isFilledNewArray).count());
+      assertEquals(cfMaxLocals, method.getMethod().getCode().asCfCode().getMaxLocals());
+    }
+  }
+
+  private void inspect(CodeInspector inspector) {
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m1"),
+        (canUseFilledNewArrayOfStringObjects(parameters) ? 4 : 0)
+            + (canUseFilledNewArrayOfNonStringObjects(parameters) ? 1 : 0),
+        // TODO(b/315753861): Don't use 4 locals.
+        4);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m2"),
+        0,
+        compilationMode.isDebug() ? 2 : 1);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m3"),
+        (canUseFilledNewArrayOfStringObjects(parameters) ? 4 : 0)
+            + (canUseFilledNewArrayOfNonStringObjects(parameters) ? 5 : 0),
+        5);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m4"),
+        0,
+        compilationMode.isDebug() ? 2 : 1);
+    inspect(
+        inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("m5"),
+        (canUseFilledNewArrayOfStringObjects(parameters) ? 4 : 0)
+            + (canUseFilledNewArrayOfNonStringObjects(parameters) ? 1 : 0),
+        compilationMode.isDebug() ? 6 : 4);
+  }
+
+  @Test
+  public void testD8() throws Exception {
+    parameters.assumeDexRuntime();
+    testForD8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  @Test
+  public void testR8() throws Exception {
+    parameters.assumeR8TestParameters();
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .setMode(compilationMode)
+        .enableInliningAnnotations()
+        .addDontObfuscate()
+        .run(parameters.getRuntime(), TestClass.class)
+        .inspect(this::inspect)
+        .assertSuccessWithOutput(EXPECTED_OUTPUT);
+  }
+
+  public static final class TestClass {
+
+    @NeverInline
+    public static void m1() {
+      String[][] array = {
+        new String[] {"A", "B"},
+        new String[] {"C", "D"},
+        new String[] {"E", "F", "G"},
+        new String[] {"H", "I", "J"}
+      };
+      printArray(array);
+      System.out.println();
+    }
+
+    @NeverInline
+    public static void m2() {
+      try {
+        String[][] array = {
+          new String[] {"J", "I", "H"},
+          new String[] {"G", "F", "E"},
+          new String[] {"D", "C"},
+          new String[] {"B", "A"}
+        };
+        printArray(array);
+        System.out.println();
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void m3() {
+      String[][][] array = {
+        new String[][] {new String[] {"A", "B", "C"}},
+        new String[][] {new String[] {"D", "E", "F"}},
+        new String[][] {new String[] {"G", "H"}},
+        new String[][] {new String[] {"I", "J"}}
+      };
+      printArray(array);
+      System.out.println();
+    }
+
+    @NeverInline
+    public static void m4() {
+      try {
+        String[][][] array = {
+          new String[][] {new String[] {"J", "I", "H"}},
+          new String[][] {new String[] {"G", "F", "E"}},
+          new String[][] {new String[] {"D", "C"}},
+          new String[][] {new String[] {"B", "A"}}
+        };
+        printArray(array);
+        System.out.println();
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void m5() {
+      String[] a1 = new String[] {"J", "I", "H"};
+      String[] a2 = new String[] {"G", "F", "E"};
+      String[] a3 = new String[] {"D", "C"};
+      String[] a4 = new String[] {"B", "A"};
+      try {
+        String[][] array = {a1, a2, a3, a4};
+        printArray(array);
+        System.out.println();
+      } catch (Exception e) {
+        throw new RuntimeException();
+      }
+    }
+
+    @NeverInline
+    public static void printArray(String[][][] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        printArray(array[i]);
+      }
+      System.out.print("]");
+    }
+
+    @NeverInline
+    public static void printArray(String[][] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        printArray(array[i]);
+      }
+      System.out.print("]");
+    }
+
+    @NeverInline
+    public static void printArray(String[] array) {
+      System.out.print("[");
+      for (int i = 0; i < array.length; i++) {
+        if (i != 0) {
+          System.out.print(", ");
+        }
+        System.out.print(array[i]);
+      }
+      System.out.print("]");
+    }
+
+    public static void main(String[] args) {
+      m1();
+      m2();
+      m3();
+      m4();
+      m5();
+    }
+  }
+}