Merge "Add Kotlin tests for null-check simplification."
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 3e46ff8..93f4249 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -47,7 +47,7 @@
// invoke the tested method.
private static final String JASMIN_MAIN_CLASS = "TestMain";
- @Parameters(name = "{0}_{1}")
+ @Parameters(name = "allowAccessModification: {0} target: {1}")
public static Collection<Object[]> data() {
ImmutableList.Builder<Object[]> builder = new ImmutableList.Builder<>();
for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) {
@@ -171,9 +171,16 @@
return proguardRules.toString();
}
+ protected String keepAllMembers(String className) {
+ return "-keep class " + className + " {" + System.lineSeparator()
+ + " *;" + System.lineSeparator()
+ + "}";
+ }
+
protected String keepClassMethod(String className, MethodSignature methodSignature) {
- return "-keep class " + className + " {" + System.lineSeparator() +
- methodSignature.toString() + ";" + System.lineSeparator() + "}";
+ return "-keep class " + className + " {" + System.lineSeparator()
+ + methodSignature.toString() + ";" + System.lineSeparator()
+ + "}";
}
protected void runTest(String folder, String mainClass, AndroidAppInspector inspector)
@@ -199,7 +206,7 @@
// Build with R8
AndroidApp.Builder builder = AndroidApp.builder();
builder.addProgramFiles(classpath);
- AndroidApp app = compileWithR8(builder.build(), proguardRules.toString());
+ AndroidApp app = compileWithR8(builder.build(), proguardRules);
// Materialize file for execution.
Path generatedDexFile = temp.getRoot().toPath().resolve("classes.jar");
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
new file mode 100644
index 0000000..e324dee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -0,0 +1,83 @@
+// Copyright (c) 2018, 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.kotlin;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.code.Format21t;
+import com.android.tools.r8.code.Format22t;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class SimplifyIfNotNullKotlinTest extends AbstractR8KotlinTestBase {
+ private static final String FOLDER = "non_null";
+ private static final String STRING = "java.lang.String";
+
+ private static boolean isIf(Instruction instruction) {
+ return instruction instanceof Format21t || instruction instanceof Format22t;
+ }
+
+ @Test
+ public void test_example1() throws Exception {
+ final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
+ final MethodSignature testMethodSignature =
+ new MethodSignature("forMakeAndModel", "java.util.SortedMap",
+ ImmutableList.of("java.util.Collection", STRING, STRING, "java.lang.Integer"));
+
+ final String mainClassName = ex1.getClassName();
+ final String extraRules = keepAllMembers(mainClassName);
+ runTest(FOLDER, mainClassName, extraRules, app -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject clazz = checkClassExists(dexInspector, ex1.getClassName());
+
+ MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+ DexCode dexCode = getDexCode(testMethod);
+ long count = Arrays.stream(dexCode.instructions)
+ .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+ if (allowAccessModification) {
+ // TODO(b/76200247): 6 -> 5
+ // Three null-check's from inlined checkParameterIsNotNull for receiver and two arguments.
+ assertEquals(6, count);
+ } else {
+ // One after Iterator#hasNext, and another in the filter predicate: sinceYear != null.
+ assertEquals(2, count);
+ }
+ });
+ }
+
+ @Test
+ public void test_example2() throws Exception {
+ final TestKotlinClass ex2 = new TestKotlinClass("non_null.Example2Kt");
+ final MethodSignature testMethodSignature =
+ new MethodSignature("aOrDefault", STRING, ImmutableList.of(STRING, STRING));
+
+ final String mainClassName = ex2.getClassName();
+ final String extraRules = keepAllMembers(mainClassName);
+ runTest(FOLDER, mainClassName, extraRules, app -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject clazz = checkClassExists(dexInspector, ex2.getClassName());
+
+ MethodSubject testMethod = checkMethodIsPresent(clazz, testMethodSignature);
+ DexCode dexCode = getDexCode(testMethod);
+ long count = Arrays.stream(dexCode.instructions)
+ .filter(SimplifyIfNotNullKotlinTest::isIf).count();
+ if (allowAccessModification) {
+ // TODO(b/76202537): 3 -> 2,
+ // Yet another null-check from checkParameterIsNotNull should subsume another from ?:
+ assertEquals(3, count);
+ } else {
+ // One null-check from force inlined coalesce and another from ?:
+ assertEquals(2, count);
+ }
+ });
+ }
+
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example1.kt b/src/test/kotlinR8TestResources/non_null/example1.kt
new file mode 100644
index 0000000..02a3fcf
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example1.kt
@@ -0,0 +1,29 @@
+// Copyright (c) 2018, 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 non_null
+
+data class Car(
+ val make: String,
+ val model: String,
+ val year: Int,
+ val plateNumber: String)
+
+fun Collection<Car>.forMakeAndModel(
+ make: String, model: String, sinceYear: Int?
+) = this.asSequence()
+ .filter { it.make == make }
+ .filter { it.model == model }
+ .filter { sinceYear != null && it.year >= sinceYear }
+ .groupBy { it.year }
+ .toSortedMap()
+
+fun main(args: Array<String>) {
+ val leaf = Car("Nissan", "Leaf", 2015, " LEAF ")
+ val ms1 = Car("Tesla", "Model S", 2015, " LGTM1 ")
+ val ms2 = Car("Tesla", "Model S", 2017, " LGTM2 ")
+ val m3 = Car("Tesla", "Model 3", 2018, " LGTM3 ")
+ val cars: List<Car> = mutableListOf(leaf, ms1, ms2, m3)
+ println(cars.forMakeAndModel("Tesla", "Model S", null))
+}
diff --git a/src/test/kotlinR8TestResources/non_null/example2.kt b/src/test/kotlinR8TestResources/non_null/example2.kt
new file mode 100644
index 0000000..e35b2e8
--- /dev/null
+++ b/src/test/kotlinR8TestResources/non_null/example2.kt
@@ -0,0 +1,14 @@
+// Copyright (c) 2018, 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 non_null
+
+inline fun coalesce(a: String?, b: String?): String? = a ?: b
+fun aOrDefault(a: String?, default: String): String =
+ coalesce(a, default) ?: throw AssertionError()
+
+fun main(args: Array<String>) {
+ println(aOrDefault(null, "null"))
+ println(aOrDefault("null", "non-null"))
+}
\ No newline at end of file