blob: 5a7be26824a6550f7bf4e2e3c34dcf5b585151d3 [file] [log] [blame]
// 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.rewrite.arrays;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
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.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
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.Parameters;
@RunWith(Parameterized.class)
public class ArrayLengthRewriteTest extends TestBase {
@Parameters(name = "{0}, debug = {1}")
public static Iterable<?> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
private final TestParameters parameters;
private final boolean debugMode;
public ArrayLengthRewriteTest(TestParameters parameters, boolean debugMode) {
this.parameters = parameters;
this.debugMode = debugMode;
}
private static final String[] expectedOutput = {
"boolean 1",
"byte 2",
"char 3",
"double 4",
"float 5",
"int 6",
"long 7",
"short 8",
"class java.lang.String 1",
"class java.lang.Object 2",
"class java.lang.String 3",
"NPE",
"class java.lang.Object 2",
"boolean 1",
"class java.lang.String 0",
"class java.lang.String 1",
"class java.lang.String 2",
"class java.lang.String 1",
"class java.lang.String 1",
};
@Test public void d8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForD8()
.setMinApi(parameters.getApiLevel())
.setMode(debugMode ? CompilationMode.DEBUG : CompilationMode.RELEASE)
.addProgramClasses(Main.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(expectedOutput)
.inspect(this::inspect);
}
@Test public void r8() throws Exception {
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.setMode(debugMode ? CompilationMode.DEBUG : CompilationMode.RELEASE)
.addProgramClasses(Main.class)
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(expectedOutput)
.inspect(this::inspect);
}
private void inspect(CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz(Main.class);
assertTrue(mainClass.isPresent());
MethodSubject primitives = mainClass.uniqueMethodWithName("primitives");
assertArrayLengthCallCount(primitives, debugMode ? 8 : 0);
MethodSubject nonNullReferences = mainClass.uniqueMethodWithName("nonNullReferences");
assertArrayLengthCallCount(nonNullReferences, debugMode ? 3 : 0);
// No assertion on nullReference() because it's seen as always throwing an NPE and
// the array-length instruction is removed. The output check validates behavior.
MethodSubject argument = mainClass.uniqueMethodWithName("argument");
assertArrayLengthCallCount(argument, 1);
MethodSubject phi = mainClass.uniqueMethodWithName("phi");
assertArrayLengthCallCount(phi, 1);
// TODO(139489070): these should be rewritten and result in 0 array-length bytecodes
MethodSubject staticConstants = mainClass.uniqueMethodWithName("staticConstants");
assertArrayLengthCallCount(staticConstants, 3);
MethodSubject staticNonConstants = mainClass.uniqueMethodWithName("staticNonConstants");
assertArrayLengthCallCount(staticNonConstants, 2);
}
private static void assertArrayLengthCallCount(MethodSubject subject, int expected) {
assertTrue(subject.isPresent());
long actual = subject.streamInstructions()
.filter(InstructionSubject::isArrayLength)
.count();
assertEquals(expected, actual);
}
public static final class Main {
public static void main(String[] args) {
primitives();
nonNullReferences();
nullReferences();
argument("one", "two");
phi();
staticConstants();
staticNonConstants();
}
@NeverInline
private static void primitives() {
boolean[] booleanArray = new boolean[1];
byte[] byteArray = new byte[2];
char[] charArray = new char[3];
double[] doubleArray = new double[4];
float[] floatArray = new float[5];
int[] intArray = new int[6];
long[] longArray = new long[7];
short[] shortArray = new short[8];
System.out.println(booleanArray.getClass().getComponentType() + " " + booleanArray.length);
System.out.println(byteArray.getClass().getComponentType() + " " + byteArray.length);
System.out.println(charArray.getClass().getComponentType() + " " + charArray.length);
System.out.println(doubleArray.getClass().getComponentType() + " " + doubleArray.length);
System.out.println(floatArray.getClass().getComponentType() + " " + floatArray.length);
System.out.println(intArray.getClass().getComponentType() + " " + intArray.length);
System.out.println(longArray.getClass().getComponentType() + " " + longArray.length);
System.out.println(shortArray.getClass().getComponentType() + " " + shortArray.length);
}
@NeverInline
private static void nonNullReferences() {
String[] stringArray = new String[1];
Object[] objectArray = new Object[2];
Object[] stringAsObjectArray = new String[3];
System.out.println(stringArray.getClass().getComponentType() + " " + stringArray.length);
System.out.println(objectArray.getClass().getComponentType() + " " + objectArray.length);
System.out.println(
stringAsObjectArray.getClass().getComponentType() + " " + stringAsObjectArray.length);
}
@NeverInline
private static void nullReferences() {
String[] nullArray = null;
try {
System.out.println(nullArray.length);
} catch (NullPointerException expected) {
System.out.println("NPE");
}
}
@NeverInline
private static void argument(Object... input) {
System.out.println(input.getClass().getComponentType() + " " + input.length);
}
@NeverInline
private static void phi() {
boolean[] booleanArray;
if (System.nanoTime() > 0) {
booleanArray = new boolean[1];
} else {
booleanArray = new boolean[2];
}
System.out.println(booleanArray.getClass().getComponentType() + " " + booleanArray.length);
}
private static final String[] zero = {};
private static final String[] one = { "one" };
private static final String[] two = { "one", "two" };
@NeverInline
private static void staticConstants() {
System.out.println(zero.getClass().getComponentType() + " " + zero.length);
System.out.println(one.getClass().getComponentType() + " " + one.length);
System.out.println(two.getClass().getComponentType() + " " + two.length);
}
private static String[] mutable = { "one" };
private static final String[] runtimeInit = { System.lineSeparator() };
@NeverInline
private static void staticNonConstants() {
System.out.println(mutable.getClass().getComponentType() + " " + mutable.length);
System.out.println(runtimeInit.getClass().getComponentType() + " " + runtimeInit.length);
}
}
}