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