Add more Kotlin accessor tests
Adds tests for accessor methods to regular properties and
user-defined properties using lambdas.
Bug: 70158739
Bug: 74103342
Change-Id: Id39ef5c7d2cdbd546a000066d13b727095da5d1a
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 85f2eb5..1419b95 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -119,8 +119,8 @@
protected static FieldSubject checkFieldIsPresent(ClassSubject classSubject, String fieldType,
String fieldName) {
FieldSubject fieldSubject = classSubject.field(fieldType, fieldName);
- assertNotNull(fieldSubject);
- assertTrue(fieldSubject.isPresent());
+ assertTrue("No field " + fieldName + " in " + classSubject.getOriginalName(),
+ fieldSubject.isPresent());
return fieldSubject;
}
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 fbb14f1..254899d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.kotlin;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.kotlin.TestKotlinClass.AccessorKind;
import com.android.tools.r8.kotlin.TestKotlinClass.Visibility;
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.utils.AndroidApp;
@@ -19,6 +22,7 @@
import java.nio.file.Path;
import java.util.Collections;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
public class R8KotlinAccessorTest extends AbstractR8KotlinTestBase {
@@ -39,24 +43,38 @@
.addProperty("publicProp", JAVA_LANG_STRING, Visibility.PUBLIC)
.addProperty("primitiveProp", "int", Visibility.PUBLIC);
+ private static final TestKotlinCompanionClass COMPANION_LATE_INIT_PROPERTY_CLASS =
+ new TestKotlinCompanionClass("properties.CompanionLateInitProperties")
+ .addProperty("privateLateInitProp", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("internalLateInitProp", JAVA_LANG_STRING, Visibility.INTERNAL)
+ .addProperty("publicLateInitProp", JAVA_LANG_STRING, Visibility.PUBLIC);
+
+ private static final TestKotlinClass PROPERTY_ACCESS_FOR_INNER_CLASS =
+ new TestKotlinClass("accessors.PropertyAccessorForInnerClass")
+ .addProperty("privateProp", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("privateLateInitProp", JAVA_LANG_STRING, Visibility.PRIVATE);
+
+ private static final TestKotlinClass PROPERTY_ACCESS_FOR_LAMBDA_CLASS =
+ new TestKotlinClass("accessors.PropertyAccessorForLambda")
+ .addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE)
+ .addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE);
+
@Test
public void testCompanionProperty_primitivePropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePrimitiveProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "primitiveProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, "int", propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPrimitiveProp$cp", "int",
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPrimitiveProp$cp", "void",
- Collections.singletonList("int"));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -72,22 +90,20 @@
@Test
public void testCompanionProperty_privatePropertyIsAlwaysInlined() throws Exception {
- String mainClass = addMainToClasspath(
- "properties.CompanionPropertiesKt", "companionProperties_usePrivateProp");
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
+ "companionProperties_usePrivateProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "privateProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPrivateProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPrivateProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -104,22 +120,20 @@
@Test
public void testCompanionProperty_internalPropertyIsAlwaysInlined() throws Exception {
- String mainClass = addMainToClasspath(
- "properties.CompanionPropertiesKt", "companionProperties_useInternalProp");
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
+ "companionProperties_useInternalProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "internalProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getInternalProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setInternalProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -135,22 +149,20 @@
@Test
public void testCompanionProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("properties.CompanionPropertiesKt",
"companionProperties_usePublicProp");
runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- ClassSubject outerClass = checkClassExists(dexInspector,
- COMPANION_PROPERTY_CLASS.getOuterClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
String propertyName = "publicProp";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
- MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getPublicProp$cp", JAVA_LANG_STRING,
- Collections.emptyList());
- MemberNaming.MethodSignature setterAccessor =
- new MemberNaming.MethodSignature("access$setPublicProp$cp", "void",
- Collections.singletonList(JAVA_LANG_STRING));
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
@@ -165,28 +177,98 @@
}
@Test
+ public void testCompanionLateInitProperty_privatePropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_usePrivateLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(outerClass, getterAccessor);
+ checkMethodIsPresent(outerClass, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ public void testCompanionLateInitProperty_internalPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_useInternalLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "internalLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ });
+ }
+
+ @Test
+ public void testCompanionLateInitProperty_publicPropertyIsAlwaysInlined() throws Exception {
+ final TestKotlinCompanionClass testedClass = COMPANION_LATE_INIT_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("properties.CompanionLateInitPropertiesKt",
+ "companionLateInitProperties_usePublicLateInitProp");
+ runTest(PROPERTIES_PACKAGE_NAME, mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ String propertyName = "publicLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor = testedClass
+ .getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+ MemberNaming.MethodSignature setterAccessor = testedClass
+ .getSetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
+
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(outerClass, getterAccessor);
+ checkMethodIsAbsent(outerClass, setterAccessor);
+ });
+ }
+
+ @Test
public void testAccessor() throws Exception {
+ TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
String mainClass = addMainToClasspath("accessors.AccessorKt",
- "accessor_accessCompanionPrivate");
+ "accessor_accessPropertyFromCompanionClass");
runTest("accessors", mainClass, (app) -> {
DexInspector dexInspector = new DexInspector(app);
- TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
- ClassSubject outerClass = checkClassExists(dexInspector,
- testedClass.getOuterClassName());
- ClassSubject companionClass = checkClassExists(dexInspector,
- testedClass.getClassName());
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
String propertyName = "property";
FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
assertTrue(fieldSubject.getField().accessFlags.isStatic());
// The getter is always inlined since it just calls into the accessor.
- MemberNaming.MethodSignature getter = testedClass
- .getGetterForProperty(propertyName);
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
checkMethodIsAbsent(companionClass, getter);
MemberNaming.MethodSignature getterAccessor =
- new MemberNaming.MethodSignature("access$getProperty$cp", JAVA_LANG_STRING,
- Collections.emptyList());
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_COMPANION);
if (allowAccessModification) {
assertTrue(fieldSubject.getField().accessFlags.isPublic());
checkMethodIsAbsent(outerClass, getterAccessor);
@@ -198,6 +280,169 @@
}
@Test
+ public void testAccessorFromPrivate() throws Exception {
+ TestKotlinCompanionClass testedClass = ACCESSOR_COMPANION_PROPERTY_CLASS;
+ String mainClass = addMainToClasspath("accessors.AccessorKt",
+ "accessor_accessPropertyFromOuterClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject outerClass = checkClassExists(dexInspector, testedClass.getOuterClassName());
+ ClassSubject companionClass = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject = checkFieldIsPresent(outerClass, JAVA_LANG_STRING, propertyName);
+ assertTrue(fieldSubject.getField().accessFlags.isStatic());
+
+ // We cannot inline the getter because we don't know if NPE is preserved.
+ MemberNaming.MethodSignature getter = testedClass.getGetterForProperty(propertyName);
+ checkMethodIsPresent(companionClass, getter);
+
+ // We should always inline the static accessor method.
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ checkMethodIsAbsent(outerClass, getterAccessor);
+
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ }
+ });
+ }
+
+ @Test
+ public void testAccessorForInnerClassIsRemovedWhenNotUsed() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "noUseOfPropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ for (String propertyName : testedClass.properties.keySet()) {
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testPrivatePropertyAccessorForInnerClassCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePrivatePropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ String propertyName = "privateProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+ propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testPrivateLateInitPropertyAccessorForInnerClassCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_INNER_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePrivateLateInitPropertyAccessorFromInnerClass");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+
+ String propertyName = "privateLateInitProp";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING,
+ propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_INNER);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testAccessorForLambdaIsRemovedWhenNotUsed() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "noUseOfPropertyAccessorFromLambda");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ });
+ }
+
+ @Test
+ @Ignore("b/74103342")
+ public void testAccessorForLambdaCanBeInlined() throws Exception {
+ TestKotlinClass testedClass = PROPERTY_ACCESS_FOR_LAMBDA_CLASS;
+ String mainClass = addMainToClasspath(testedClass.className + "Kt",
+ "usePropertyAccessorFromLambda");
+ runTest("accessors", mainClass, (app) -> {
+ DexInspector dexInspector = new DexInspector(app);
+ ClassSubject classSubject = checkClassExists(dexInspector, testedClass.getClassName());
+ String propertyName = "property";
+ FieldSubject fieldSubject = checkFieldIsPresent(classSubject, JAVA_LANG_STRING, propertyName);
+ assertFalse(fieldSubject.getField().accessFlags.isStatic());
+
+ MemberNaming.MethodSignature getterAccessor =
+ testedClass.getGetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ MemberNaming.MethodSignature setterAccessor =
+ testedClass.getSetterAccessorForProperty(propertyName, AccessorKind.FROM_LAMBDA);
+ if (allowAccessModification) {
+ assertTrue(fieldSubject.getField().accessFlags.isPublic());
+ checkMethodIsAbsent(classSubject, getterAccessor);
+ checkMethodIsAbsent(classSubject, setterAccessor);
+ } else {
+ assertTrue(fieldSubject.getField().accessFlags.isPrivate());
+ checkMethodIsPresent(classSubject, getterAccessor);
+ checkMethodIsPresent(classSubject, setterAccessor);
+ }
+ });
+ }
+
+ @Test
public void testStaticFieldAccessorWithJasmin() throws Exception {
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder classBuilder = jasminBuilder.addClass("Foo");
@@ -228,13 +473,11 @@
if (javaResult.exitCode != 0) {
System.err.println(javaResult.stderr);
Assert.fail();
- } else {
- System.out.println(javaResult.stdout);
}
AndroidApp app = compileWithR8(jasminBuilder.build(),
keepMainProguardConfiguration("Foo") + "\ndontobfuscate");
String artOutput = runOnArt(app, "Foo");
- System.out.println(artOutput);
+ assertEquals(javaResult.stdout, artOutput);
}
}
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 d364bb8..5de3c43 100644
--- a/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
+++ b/src/test/java/com/android/tools/r8/kotlin/TestKotlinClass.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.kotlin;
import com.android.tools.r8.naming.MemberNaming;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
/**
@@ -32,6 +34,22 @@
PRIVATE;
}
+ enum AccessorKind {
+ FROM_COMPANION("cp"),
+ FROM_INNER("p"),
+ FROM_LAMBDA("lp");
+
+ private final String accessorSuffix;
+
+ AccessorKind(String accessorSuffix) {
+ this.accessorSuffix = accessorSuffix;
+ }
+
+ public String getAccessorSuffix() {
+ return accessorSuffix;
+ }
+ }
+
protected static class KotlinProperty {
private final String name;
private final String type;
@@ -117,4 +135,52 @@
Collections.singleton(property.getType()));
}
+ // TODO(shertz) refactor to avoid duplicated code with getGetterForProperty.
+ public MemberNaming.MethodSignature getGetterAccessorForProperty(String propertyName,
+ AccessorKind accessorKind) {
+ KotlinProperty property = getProperty(propertyName);
+ String type = property.type;
+ String getterName;
+ if (propertyName.length() > 2 && propertyName.startsWith("is")
+ && (propertyName.charAt(2) == '_' || Character.isUpperCase(propertyName.charAt(2)))) {
+ // Getter for property "isAbc" is "isAbc".
+ getterName = propertyName;
+ } else {
+ // Getter for property "abc" is "getAbc".
+ getterName =
+ "get" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+ }
+ // Unlike normal getter, module name is not appended for accessor method of internal property.
+ getterName = wrapWithAccessorPrefixAndSuffix(accessorKind, getterName);
+ List<String> argumentTypes;
+ if (accessorKind != AccessorKind.FROM_COMPANION) {
+ argumentTypes = ImmutableList.of(getClassName());
+ } else {
+ argumentTypes = ImmutableList.of();
+ }
+ return new MemberNaming.MethodSignature(getterName, type, argumentTypes);
+ }
+
+ // TODO(shertz) refactor to avoid duplicated code with getSetterForProperty.
+ public MemberNaming.MethodSignature getSetterAccessorForProperty(String propertyName,
+ AccessorKind accessorKind) {
+ KotlinProperty property = getProperty(propertyName);
+ String setterName = "set"
+ + Character.toUpperCase(property.name.charAt(0))
+ + property.name.substring(1);
+ // Unlike normal setter, module name is not appended for accessor method of internal property.
+ setterName = wrapWithAccessorPrefixAndSuffix(accessorKind, setterName);
+ List<String> argumentTypes;
+ if (accessorKind != AccessorKind.FROM_COMPANION) {
+ argumentTypes = ImmutableList.of(getClassName(), property.getType());
+ } else {
+ argumentTypes = ImmutableList.of(property.getType());
+ }
+ return new MemberNaming.MethodSignature(setterName, "void", argumentTypes);
+ }
+
+ private String wrapWithAccessorPrefixAndSuffix(AccessorKind accessorKind, String methodName) {
+ return "access$" + methodName + "$" + accessorKind.getAccessorSuffix();
+ }
+
}
diff --git a/src/test/kotlinR8TestResources/accessors/Accessor.kt b/src/test/kotlinR8TestResources/accessors/Accessor.kt
index e88a055..ffd0395 100644
--- a/src/test/kotlinR8TestResources/accessors/Accessor.kt
+++ b/src/test/kotlinR8TestResources/accessors/Accessor.kt
@@ -8,12 +8,20 @@
companion object {
private val property = "foo"
- fun printProperty() {
+ fun accessPropertyFromCompanionClass() {
println(property)
}
}
+
+ fun accessPropertyFromOuterClass() {
+ println(property)
+ }
}
-fun accessor_accessCompanionPrivate() {
- Accessor.printProperty()
+fun accessor_accessPropertyFromCompanionClass() {
+ Accessor.accessPropertyFromCompanionClass()
+}
+
+fun accessor_accessPropertyFromOuterClass() {
+ Accessor().accessPropertyFromOuterClass()
}
\ No newline at end of file
diff --git a/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt
new file mode 100644
index 0000000..a2dd823
--- /dev/null
+++ b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForInnerClass.kt
@@ -0,0 +1,44 @@
+// 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 accessors
+
+class PropertyAccessorForInnerClass {
+ private var privateProp = "private"
+
+ private lateinit var privateLateInitProp: String
+
+ // Causes a class initializer to be added to the class.
+ companion object {
+ public var companionProperty = "static"
+ }
+
+ inner class Inner {
+ fun accessPrivateProperty() {
+ privateProp = "bar"
+ println(privateProp)
+ }
+
+ fun accessPrivateLateInitPropertyStatus() {
+ println(::privateLateInitProp.isInitialized)
+ }
+ }
+}
+
+fun noUseOfPropertyAccessorFromInnerClass() {
+ // Create instance of class to keep them after tree shaking.
+ PropertyAccessorForInnerClass().Inner()
+}
+
+fun usePrivatePropertyAccessorFromInnerClass() {
+ // Creates a non-trivial class initializer
+ println(PropertyAccessorForInnerClass.companionProperty)
+ PropertyAccessorForInnerClass().Inner().accessPrivateProperty()
+}
+
+fun usePrivateLateInitPropertyAccessorFromInnerClass() {
+ // Creates a non-trivial class initializer
+ println(PropertyAccessorForInnerClass.companionProperty)
+ PropertyAccessorForInnerClass().Inner().accessPrivateLateInitPropertyStatus()
+}
\ No newline at end of file
diff --git a/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt
new file mode 100644
index 0000000..28bed0a
--- /dev/null
+++ b/src/test/kotlinR8TestResources/accessors/PropertyAccessorForLambda.kt
@@ -0,0 +1,33 @@
+// 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 accessors
+
+class PropertyAccessorForLambda {
+ private var property: String = "foo"
+ get() = { field }()
+ set(v) = { field = v }()
+
+ // Causes a class initializer to be added to the class.
+ companion object {
+ public var companionProperty = "static"
+ }
+
+ fun accessPropertyOfOuterClass() {
+ // Access to the property requires to go through an accessor method, respectively
+ // named "access$getProperty$lp" for getter and "access$setProperty$lp" for setter).
+ property = "bar"
+ println(property)
+ }
+}
+
+fun noUseOfPropertyAccessorFromLambda() {
+ // Create instance of class to keep them after tree shaking.
+ PropertyAccessorForLambda()
+}
+
+fun usePropertyAccessorFromLambda() {
+ PropertyAccessorForLambda.companionProperty = "fake"
+ PropertyAccessorForLambda().accessPropertyOfOuterClass()
+}
\ No newline at end of file