Refactor Kotlin tests.
* com.android.tools.r8.KotlinTestBase
- the top-level abstraction that defines where to load .kt/.java jar.
Some recently added tests that exercise high-level behavior,
such as stripping off @Metadata if not used, reprocessing lambda groups,
etc., have been using this.
* com.android.tools.r8.kotlin.AbstractR8KotlinTestBase
- with utils that check if certain items are kept, inlined, removed
as expected.
This existing base class has been used for testing expected behaviors
of Kotlin-specific optimizations, such as class inliner, staticizer,
Intrinsics inlining, etc.
* com.android.tools.r8.naming.AbstractR8KotlinNamingTestBase
- extends AbstractR8KotlinTestBase with renaming-related utils.
------
This CL also adds very basic renaming tests for Kotlin Intrinsics null
checks. While investigating b/126056766, observed some utils in
Intrinsics that receive class/member identifiers to check if known-to-
return-non-null methods indeed returned non-null values or if known-to-
be-non-null fields are indeed non-null. Those are added only for Java
libraries, which we don't rename. Thus, no need to rename such
identifiers. Only tested non-pinned items are all renamed as expected.
Change-Id: I282d03024d336941506703f171ef8695b9ca23eb
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 19c931e..b722683 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -4,15 +4,18 @@
package com.android.tools.r8.kotlin;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.Code;
@@ -24,7 +27,6 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -32,7 +34,6 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -42,63 +43,65 @@
import org.junit.Assume;
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 abstract class AbstractR8KotlinTestBase extends TestBase {
+public abstract class AbstractR8KotlinTestBase extends KotlinTestBase {
// This is the name of the Jasmin-generated class which contains the "main" method which will
// invoke the tested method.
private static final String JASMIN_MAIN_CLASS = "TestMain";
- @Parameter(0) public boolean allowAccessModification;
- @Parameter(1) public KotlinTargetVersion targetVersion;
+ protected final boolean allowAccessModification;
private final List<Path> classpath = new ArrayList<>();
private final List<Path> extraClasspath = new ArrayList<>();
- @Parameters(name = "allowAccessModification: {0} target: {1}")
+ @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}")
public static Collection<Object[]> data() {
- return buildParameters(BooleanUtils.values(), KotlinTargetVersion.values());
+ return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values());
+ }
+
+ protected AbstractR8KotlinTestBase(
+ KotlinTargetVersion kotlinTargetVersion, boolean allowAccessModification) {
+ super(kotlinTargetVersion);
+ this.allowAccessModification = allowAccessModification;
}
protected void addExtraClasspath(Path path) {
extraClasspath.add(path);
}
- protected static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
- MethodSignature... methodSignatures) {
+ protected static void checkMethodIsInvokedAtLeastOnce(
+ DexCode dexCode, MethodSignature... methodSignatures) {
for (MethodSignature methodSignature : methodSignatures) {
checkMethodIsInvokedAtLeastOnce(dexCode, methodSignature);
}
}
- private static void checkMethodIsInvokedAtLeastOnce(DexCode dexCode,
- MethodSignature methodSignature) {
+ private static void checkMethodIsInvokedAtLeastOnce(
+ DexCode dexCode, MethodSignature methodSignature) {
assertTrue("No invoke to '" + methodSignature.toString() + "'",
Arrays.stream(dexCode.instructions)
.filter((instr) -> instr.getMethod() != null)
.anyMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
}
- protected static void checkMethodIsNeverInvoked(DexCode dexCode,
- MethodSignature... methodSignatures) {
+ protected static void checkMethodIsNeverInvoked(
+ DexCode dexCode, MethodSignature... methodSignatures) {
for (MethodSignature methodSignature : methodSignatures) {
checkMethodIsNeverInvoked(dexCode, methodSignature);
}
}
- private static void checkMethodIsNeverInvoked(DexCode dexCode,
- MethodSignature methodSignature) {
+ private static void checkMethodIsNeverInvoked(DexCode dexCode, MethodSignature methodSignature) {
assertTrue("At least one invoke to '" + methodSignature.toString() + "'",
Arrays.stream(dexCode.instructions)
.filter((instr) -> instr.getMethod() != null)
.noneMatch((instr) -> instr.getMethod().name.toString().equals(methodSignature.name)));
}
- protected static void checkMethodsPresence(ClassSubject classSubject,
- Map<MethodSignature, Boolean> presenceMap) {
+ protected static void checkMethodsPresence(
+ ClassSubject classSubject, Map<MethodSignature, Boolean> presenceMap) {
presenceMap.forEach(((methodSignature, isPresent) -> {
MethodSubject methodSubject = classSubject.method(methodSignature);
String methodDesc = methodSignature.toString();
@@ -118,8 +121,8 @@
return classSubject;
}
- protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldType,
- String fieldName) {
+ protected FieldSubject checkFieldIsKept(
+ ClassSubject classSubject, String fieldType, String fieldName) {
// Field must exist in the input.
checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, true);
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -128,8 +131,13 @@
return fieldSubject;
}
- protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType,
- String fieldName) {
+ protected FieldSubject checkFieldIsKept(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+ assertThat(fieldSubject, isPresent());
+ return fieldSubject;
+ }
+
+ protected void checkFieldIsAbsent(ClassSubject classSubject, String fieldType, String fieldName) {
// Field must NOT exist in the input.
checkFieldPresenceInInput(classSubject.getOriginalName(), fieldType, fieldName, false);
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
@@ -137,32 +145,47 @@
assertFalse(fieldSubject.isPresent());
}
- protected void checkMethodIsAbsent(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected FieldSubject checkFieldIsAbsent(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = classSubject.uniqueFieldWithName(fieldName);
+ assertThat(fieldSubject, not(isPresent()));
+ return fieldSubject;
+ }
+
+ protected void checkMethodIsAbsent(ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, false);
checkMethodPresenceInOutput(classSubject, methodSignature, false);
}
- protected MethodSubject checkMethodIsKept(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected MethodSubject checkMethodIsKept(
+ ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
return checkMethodIsKeptOrRemoved(classSubject, methodSignature, true);
}
- protected void checkMethodIsRemoved(ClassSubject classSubject,
- MethodSignature methodSignature) {
+ protected MethodSubject checkMethodIsKept(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, isPresent());
+ return methodSubject;
+ }
+
+ protected void checkMethodIsRemoved(ClassSubject classSubject, MethodSignature methodSignature) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
checkMethodIsKeptOrRemoved(classSubject, methodSignature, false);
}
- protected MethodSubject checkMethodIsKeptOrRemoved(ClassSubject classSubject,
- MethodSignature methodSignature, boolean isPresent) {
+ protected void checkMethodIsRemoved(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = classSubject.uniqueMethodWithName(methodName);
+ assertThat(methodSubject, not(isPresent()));
+ }
+
+ protected MethodSubject checkMethodIsKeptOrRemoved(
+ ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
checkMethodPresenceInInput(classSubject.getOriginalName(), methodSignature, true);
return checkMethodPresenceInOutput(classSubject, methodSignature, isPresent);
}
- private MethodSubject checkMethodPresenceInOutput(ClassSubject classSubject,
- MethodSignature methodSignature, boolean isPresent) {
+ private MethodSubject checkMethodPresenceInOutput(
+ ClassSubject classSubject, MethodSignature methodSignature, boolean isPresent) {
MethodSubject methodSubject = classSubject.method(methodSignature);
assertNotNull(methodSubject);
@@ -272,8 +295,8 @@
}
}
- private void checkMethodPresenceInInput(String className, MethodSignature methodSignature,
- boolean isPresent) {
+ private void checkMethodPresenceInInput(
+ String className, MethodSignature methodSignature, boolean isPresent) {
boolean foundMethod = AsmUtils.doesMethodExist(classpath, className,
methodSignature.name, methodSignature.toDescriptor());
if (isPresent != foundMethod) {
@@ -285,8 +308,8 @@
}
}
- private void checkFieldPresenceInInput(String className, String fieldType, String fieldName,
- boolean isPresent) {
+ private void checkFieldPresenceInInput(
+ String className, String fieldType, String fieldName, boolean isPresent) {
boolean foundField = AsmUtils.doesFieldExist(classpath, className, fieldName, fieldType);
if (isPresent != foundField) {
throw new AssertionError(
@@ -296,18 +319,8 @@
}
}
- private Path getKotlinJarFile(String folder) {
- return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
- targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION);
- }
-
- private Path getJavaJarFile(String folder) {
- return Paths.get(ToolHelper.TESTS_BUILD_DIR, "kotlinR8TestResources",
- targetVersion.getFolderName(), folder + ".java" + FileUtils.JAR_EXTENSION);
- }
-
@FunctionalInterface
- interface AndroidAppInspector {
+ public interface AndroidAppInspector {
void inspectApp(AndroidApp androidApp) throws Exception;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 1a690e2..30d90eb 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.SgetObject;
import com.android.tools.r8.graph.DexClass;
@@ -33,6 +34,11 @@
public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase {
+ public KotlinClassInlinerTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
private static boolean isLambda(DexClass clazz) {
return !clazz.type.getPackageDescriptor().startsWith("kotlin") &&
(isKStyleLambdaOrGroup(clazz) || isJStyleLambdaOrGroup(clazz));
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
index ced08f5..3f02c70 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.util.concurrent.atomic.AtomicInteger;
@@ -16,6 +17,11 @@
public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase {
+ public KotlinClassStaticizerTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testCompanionAndRegularObjects() throws Exception {
assumeTrue("Only work with -allowaccessmodification", allowAccessModification);
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
index 7e9269d..0b79b3b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
@@ -39,6 +40,11 @@
opts.forceProguardCompatibility = true;
};
+ public KotlinLambdaMergingTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
abstract static class LambdaOrGroup {
abstract boolean match(DexClass clazz);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
index 343dfdf..7b43112 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Consumer;
import org.junit.Test;
@@ -17,6 +18,11 @@
o.enableLambdaMerging = true;
};
+ public KotlinLambdaMergingWithReprocessingTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testMergingKStyleLambdasAndReprocessing() throws Exception {
final String mainClassName = "reprocess_merged_lambdas_kstyle.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
index ba11914..c5ee56d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.utils.InternalOptions;
import java.util.function.Consumer;
import org.junit.Test;
@@ -18,6 +19,11 @@
o.inliningInstructionAllowance = 3;
};
+ public KotlinLambdaMergingWithSmallInliningBudgetTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testJStyleRunnable() throws Exception {
final String mainClassName = "lambdas_jstyle_runnable.MainKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
index 1515689..0048b1b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -64,6 +65,11 @@
private Consumer<InternalOptions> disableClassStaticizer =
opts -> opts.enableClassStaticizer = false;
+ public R8KotlinAccessorTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 9224405..7e16b2f 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -37,6 +38,11 @@
private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false;
+ public R8KotlinDataClassTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void test_dataclass_gettersOnly() throws Exception {
final String mainClassName = "dataclass.MainGettersOnlyKt";
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
index c90df04..c74b790 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.kotlin;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -20,6 +21,11 @@
private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS =
new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics");
+ public R8KotlinIntrinsicsTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testParameterNullCheckIsInlined() throws Exception {
final String extraRules = keepClassMethod("intrinsics.IntrinsicsKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
index bc23757..d310c6e 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.kotlin.TestKotlinClass.KotlinProperty;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
@@ -90,6 +91,11 @@
o.enableClassStaticizer = false;
};
+ public R8KotlinPropertiesTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void testMutableProperty_getterAndSetterAreRemoveIfNotUsed() throws Exception {
String mainClass = addMainToClasspath("properties/MutablePropertyKt",
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 0ebc393..b4b0c06 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -18,6 +19,11 @@
private static final String FOLDER = "non_null";
private static final String STRING = "java.lang.String";
+ public SimplifyIfNotNullKotlinTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification) {
+ super(targetVersion, allowAccessModification);
+ }
+
@Test
public void test_example1() throws Exception {
final TestKotlinClass ex1 = new TestKotlinClass("non_null.Example1Kt");
diff --git a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
index 685df6a..d135bf9 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
@@ -16,7 +16,7 @@
*
* <p>See https://kotlinlang.org/docs/reference/classes.html</p>
*/
-class TestKotlinClass {
+public class TestKotlinClass {
/**
* This is the suffix appended by Kotlin compiler to getter and setter method names of
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
new file mode 100644
index 0000000..bb9af02
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -0,0 +1,106 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+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.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Collection;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public abstract class AbstractR8KotlinNamingTestBase extends AbstractR8KotlinTestBase {
+
+ protected final boolean minification;
+
+ @Parameters(name = "target: {0}, allowAccessModification: {1}, minification: {2}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ KotlinTargetVersion.values(), BooleanUtils.values(), BooleanUtils.values());
+ }
+
+ AbstractR8KotlinNamingTestBase(
+ KotlinTargetVersion kotlinTargetVersion,
+ boolean allowAccessModification,
+ boolean minification) {
+ super(kotlinTargetVersion, allowAccessModification);
+ this.minification = minification;
+ }
+
+ protected ClassSubject checkClassIsRenamed(CodeInspector inspector, String className) {
+ ClassSubject classSubject = inspector.clazz(className);
+ assertThat(classSubject, isRenamed());
+ return classSubject;
+ }
+
+ protected ClassSubject checkClassIsNotRenamed(CodeInspector inspector, String className) {
+ ClassSubject classSubject = inspector.clazz(className);
+ assertThat(classSubject, not(isRenamed()));
+ return classSubject;
+ }
+
+ protected FieldSubject checkFieldIsRenamed(
+ ClassSubject classSubject, String fieldType, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+ assertThat(fieldSubject, isRenamed());
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsRenamed(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+ assertThat(fieldSubject, isRenamed());
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsNotRenamed(
+ ClassSubject classSubject, String fieldType, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldType, fieldName);
+ assertThat(fieldSubject, not(isRenamed()));
+ return fieldSubject;
+ }
+
+ protected FieldSubject checkFieldIsNotRenamed(ClassSubject classSubject, String fieldName) {
+ FieldSubject fieldSubject = checkFieldIsKept(classSubject, fieldName);
+ assertThat(fieldSubject, not(isRenamed()));
+ return fieldSubject;
+ }
+
+ protected MethodSubject checkMethodIsRenamed(
+ ClassSubject classSubject, MethodSignature methodSignature) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+ assertThat(methodSubject, isRenamed());
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsRenamed(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+ assertThat(methodSubject, isRenamed());
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsNotRenamed(
+ ClassSubject classSubject, MethodSignature methodSignature) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodSignature);
+ assertThat(methodSubject, not(isRenamed()));
+ return methodSubject;
+ }
+
+ protected MethodSubject checkMethodIsNotRenamed(ClassSubject classSubject, String methodName) {
+ MethodSubject methodSubject = checkMethodIsKept(classSubject, methodName);
+ assertThat(methodSubject, not(isRenamed()));
+ return methodSubject;
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
new file mode 100644
index 0000000..aa2e69d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -0,0 +1,150 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestCompileResult;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.kotlin.TestKotlinClass;
+import com.android.tools.r8.utils.StringUtils;
+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.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase {
+ private static final String FOLDER = "intrinsics_identifiers";
+
+ public KotlinIntrinsicsIdentifierTest(
+ KotlinTargetVersion targetVersion, boolean allowAccessModification, boolean minification) {
+ super(targetVersion, allowAccessModification, minification);
+ }
+
+ @Test
+ public void test_example1() throws Exception {
+ TestKotlinClass ex1 = new TestKotlinClass("intrinsics_identifiers.Example1Kt");
+ String targetClassName = "ToBeRenamedClass";
+ String targetFieldName = "toBeRenamedField";
+ String targetMethodName = "toBeRenamedMethod";
+ test(ex1, targetClassName, targetFieldName, targetMethodName);
+ }
+
+ @Test
+ public void test_example2() throws Exception {
+ TestKotlinClass ex2 = new TestKotlinClass("intrinsics_identifiers.Example2Kt");
+ String targetClassName = "AnotherClass";
+ String targetFieldName = "anotherField";
+ String targetMethodName = "anotherMethod";
+ test(ex2, targetClassName, targetFieldName, targetMethodName);
+ }
+
+ @Test
+ public void test_example3() throws Exception {
+ TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt");
+ String mainClassName = ex3.getClassName();
+ TestCompileResult result = testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .addKeepMainRule(mainClassName)
+ .minification(minification)
+ .compile();
+ CodeInspector codeInspector = result.inspector();
+ MethodSubject main = codeInspector.clazz(ex3.getClassName()).mainMethod();
+ assertThat(main, isPresent());
+ verifyKotlinIntrinsicsRenamed(codeInspector, main);
+ }
+
+ private void verifyKotlinIntrinsicsRenamed(CodeInspector inspector, MethodSubject main) {
+ Iterator<InstructionSubject> it = main.iterateInstructions(InstructionSubject::isInvokeStatic);
+ assertTrue(it.hasNext());
+ boolean metKotlinIntrinsicsNullChecks = false;
+ while (it.hasNext()) {
+ DexMethod invokedMethod = it.next().getMethod();
+ if (invokedMethod.getHolder().toSourceString().contains("java.net")) {
+ continue;
+ }
+ ClassSubject invokedMethodHolderSubject =
+ inspector.clazz(invokedMethod.getHolder().toSourceString());
+ assertThat(invokedMethodHolderSubject, isPresent());
+ assertEquals(minification, invokedMethodHolderSubject.isRenamed());
+ MethodSubject invokedMethodSubject = invokedMethodHolderSubject.method(
+ invokedMethod.proto.returnType.toSourceString(),
+ invokedMethod.name.toString(),
+ Arrays.stream(invokedMethod.proto.parameters.values)
+ .map(DexType::toSourceString)
+ .collect(Collectors.toList()));
+ assertThat(invokedMethodSubject, isPresent());
+ assertEquals(minification, invokedMethodSubject.isRenamed());
+ if (invokedMethodSubject.getOriginalName().startsWith("check")
+ && invokedMethodSubject.getOriginalName().endsWith("Null")
+ && invokedMethodHolderSubject.getOriginalDescriptor()
+ .contains("kotlin/jvm/internal/Intrinsics")) {
+ metKotlinIntrinsicsNullChecks = true;
+ }
+ }
+ assertTrue(metKotlinIntrinsicsNullChecks);
+ }
+
+ private void test(
+ TestKotlinClass testMain,
+ String targetClassName,
+ String targetFieldName,
+ String targetMethodName) throws Exception {
+ String mainClassName = testMain.getClassName();
+ TestRunResult result = testForR8(Backend.DEX)
+ .addProgramFiles(getKotlinJarFile(FOLDER))
+ .addProgramFiles(getJavaJarFile(FOLDER))
+ .enableProguardTestOptions()
+ .addKeepMainRule(mainClassName)
+ .addKeepRules(StringUtils.lines(
+ "-neverclassinline class **." + targetClassName,
+ "-nevermerge class **." + targetClassName,
+ "-neverinline class **." + targetClassName + " { <methods>; }"
+ ))
+ .minification(minification)
+ .run(mainClassName);
+ CodeInspector codeInspector = result.inspector();
+
+ MethodSubject main = codeInspector.clazz(testMain.getClassName()).mainMethod();
+ assertThat(main, isPresent());
+ verifyKotlinIntrinsicsRenamed(codeInspector, main);
+ // Examine all const-string and verify that identifiers are not introduced.
+ Iterator<InstructionSubject> it =
+ main.iterateInstructions(i -> i.isConstString(JumboStringMode.ALLOW));
+ assertTrue(it.hasNext());
+ while (it.hasNext()) {
+ String identifier = it.next().getConstString();
+ if (identifier.contains("arg")) {
+ continue;
+ }
+ assertEquals(!minification, identifier.equals(targetMethodName));
+ assertEquals(!minification, identifier.equals(targetFieldName));
+ }
+
+ targetClassName = FOLDER + "." + targetClassName;
+ ClassSubject clazz = minification
+ ? checkClassIsRenamed(codeInspector, targetClassName)
+ : checkClassIsNotRenamed(codeInspector, targetClassName);
+ if (minification) {
+ checkFieldIsRenamed(clazz, targetFieldName);
+ checkMethodIsRenamed(clazz, targetMethodName);
+ } else {
+ checkFieldIsNotRenamed(clazz, targetFieldName);
+ checkMethodIsNotRenamed(clazz, targetMethodName);
+ }
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index 94a779b..5f1b405 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -142,6 +142,14 @@
}
@Override
+ public String getConstString() {
+ if (instruction instanceof CfConstString) {
+ return ((CfConstString) instruction).getString().toSourceString();
+ }
+ return null;
+ }
+
+ @Override
public boolean isConstClass() {
return instruction instanceof CfConstClass;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 60ad731..ba23a0a 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -252,6 +252,17 @@
}
@Override
+ public String getConstString() {
+ if (instruction instanceof ConstString) {
+ return ((ConstString) instruction).BBBB.toSourceString();
+ }
+ if (instruction instanceof ConstStringJumbo) {
+ return ((ConstStringJumbo) instruction).BBBBBBBB.toSourceString();
+ }
+ return null;
+ }
+
+ @Override
public boolean isConstClass() {
return instruction instanceof ConstClass;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 7a272c7..f2eb753 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -48,6 +48,8 @@
boolean isConstString(String value, JumboStringMode jumboStringMode);
+ String getConstString();
+
boolean isConstClass();
boolean isConstClass(String type);
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
new file mode 100644
index 0000000..6218d16
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/ToBeRenamedClass.java
@@ -0,0 +1,14 @@
+// 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 intrinsics_identifiers;
+
+class ToBeRenamedClass {
+ String toBeRenamedField = "SUFFIX";
+ String toBeRenamedMethod(String arg) {
+ return arg + toBeRenamedField;
+ }
+ void updateField(String arg) {
+ toBeRenamedField = arg;
+ }
+}
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
new file mode 100644
index 0000000..4409232
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example1.kt
@@ -0,0 +1,17 @@
+// 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 intrinsics_identifiers
+
+fun main(args: Array<String>) {
+ val instance = ToBeRenamedClass()
+ println(instance.toBeRenamedField)
+ println(instance.toBeRenamedMethod("arg1"))
+
+ if (instance.toBeRenamedField.equals("arg2")) {
+ instance.updateField("arg3")
+ println(instance.toBeRenamedField)
+ println(instance.toBeRenamedMethod("arg4"))
+ }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
new file mode 100644
index 0000000..c6c5e09
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example2.kt
@@ -0,0 +1,27 @@
+// 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 intrinsics_identifiers
+
+class AnotherClass {
+ var anotherField : String = "PREFIX"
+ fun anotherMethod(arg: String) : String {
+ return anotherField + arg
+ }
+ fun updateField(arg: String) : Unit {
+ anotherField = arg
+ }
+}
+
+fun main(args: Array<String>) {
+ val instance = AnotherClass()
+ println(instance.anotherField)
+ println(instance.anotherMethod("arg1"))
+
+ if (instance.anotherField.equals("arg2")) {
+ instance.updateField("arg3")
+ println(instance.anotherField)
+ println(instance.anotherMethod("arg4"))
+ }
+}
+
diff --git a/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
new file mode 100644
index 0000000..47b1d00
--- /dev/null
+++ b/src/test/kotlinR8TestResources/intrinsics_identifiers/example3.kt
@@ -0,0 +1,14 @@
+// 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 intrinsics_identifiers
+
+import java.net.URI
+
+fun main(args: Array<String>) {
+ // By specifying non-null type of variables for library uses,
+ // kotlin.jvm.internal.Intrinsics#check*Null(...) is added by kotlinc.
+ val uri : URI = URI.create("google.com")
+ val host : String = uri.host
+ println(host)
+}
\ No newline at end of file