Reland "Propagate the value of kotlin._Assertions.ENABLED in D8"
Based on the assertions configuration the value of
kotlin._Assertions.ENABLED is propagated in all compilers based on the
Kotlin stdlib code for kotlin._Assertions.
Bug: 139898386
This reverts commit 53c1edda78a23eddbc8fc8b0f77a7ab2c62db041 with a
fix.
Change-Id: I0e12ebc54b2c230ceb8318ca02147498d875d1ec
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index 061cbcf..b41e8fa 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -127,7 +127,7 @@
}
private boolean hasKotlincClinitAssertionCode(DexEncodedMethod method) {
- if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.assertions.type) {
CfCode code = method.getCode().asCfCode();
for (int i = 1; i < code.instructions.size(); i++) {
CfInstruction instruction = code.instructions.get(i - 1);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
index a356e8b..81b43ef 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/AssertionsRewriter.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -77,6 +78,7 @@
private final DexItemFactory dexItemFactory;
private final AssertionTransformation defaultTransformation;
private final List<ConfigurationEntryWithDexString> configuration;
+ private final AssertionsConfiguration.AssertionTransformation kotlinTransformation;
private final boolean enabled;
public AssertionsRewriter(AppView<?> appView) {
@@ -86,6 +88,7 @@
if (!enabled) {
defaultTransformation = null;
configuration = null;
+ kotlinTransformation = null;
return;
}
// Convert the assertion transformation to the representation used for this rewriter.
@@ -94,6 +97,8 @@
appView.options().assertionsConfiguration.assertionsConfigurations.stream()
.map(entry -> new ConfigurationEntryWithDexString(entry, appView.dexItemFactory()))
.collect(Collectors.toList());
+ kotlinTransformation =
+ getTransformationForType(appView.dexItemFactory().kotlin.assertions.type);
}
// Static method used by other analyses to see if additional analysis is required to support
@@ -104,6 +109,10 @@
}
private AssertionTransformation getTransformationForMethod(DexEncodedMethod method) {
+ return getTransformationForType(method.holder());
+ }
+
+ private AssertionTransformation getTransformationForType(DexType type) {
AssertionTransformation transformation = defaultTransformation;
for (ConfigurationEntryWithDexString entry : configuration) {
switch (entry.entry.getScope()) {
@@ -112,18 +121,18 @@
break;
case PACKAGE:
if (entry.value.size == 0) {
- if (!method.holder().descriptor.contains(dexItemFactory.descriptorSeparator)) {
+ if (!type.descriptor.contains(dexItemFactory.descriptorSeparator)) {
transformation = entry.entry.getTransformation();
}
- } else if (method.holder().descriptor.startsWith(entry.value)) {
+ } else if (type.descriptor.startsWith(entry.value)) {
transformation = entry.entry.getTransformation();
}
break;
case CLASS:
- if (method.holder().descriptor.equals(entry.value)) {
+ if (type.descriptor.equals(entry.value)) {
transformation = entry.entry.getTransformation();
}
- if (isDescriptorForClassOrInnerClass(entry.value, method.holder().descriptor)) {
+ if (isDescriptorForClassOrInnerClass(entry.value, type.descriptor)) {
transformation = entry.entry.getTransformation();
}
break;
@@ -317,10 +326,10 @@
}
clinit = clazz.getClassInitializer();
}
- if (clinit == null || !clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions()) {
- return;
- }
-
+ // For javac generated code it is assumed that the code in <clinit> will tell if the code
+ // in other methods of the class can have assertion checks.
+ boolean isInitializerEnablingJavaVmAssertions =
+ clinit != null && clinit.getOptimizationInfo().isInitializerEnablingJavaVmAssertions();
// This code will process the assertion code in all methods including <clinit>.
InstructionListIterator iterator = code.instructionListIterator();
while (iterator.hasNext()) {
@@ -328,7 +337,7 @@
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
- if (method.holder() == dexItemFactory.kotlin.kotlinAssertions) {
+ if (method.holder() == dexItemFactory.kotlin.assertions.type) {
rewriteKotlinAssertionEnable(code, transformation, iterator, invoke);
} else {
iterator.replaceCurrentInstruction(code.createIntConstant(0));
@@ -336,15 +345,24 @@
}
} else if (current.isStaticPut()) {
StaticPut staticPut = current.asStaticPut();
- if (staticPut.getField().name == dexItemFactory.assertionsDisabled) {
+ if (isInitializerEnablingJavaVmAssertions
+ && staticPut.getField().name == dexItemFactory.assertionsDisabled) {
iterator.remove();
}
} else if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
- if (staticGet.getField().name == dexItemFactory.assertionsDisabled) {
+ // Rewrite $assertionsDisabled getter (only if the initializer enabled assertions).
+ if (isInitializerEnablingJavaVmAssertions
+ && staticGet.getField().name == dexItemFactory.assertionsDisabled) {
iterator.replaceCurrentInstruction(
code.createIntConstant(transformation == AssertionTransformation.DISABLE ? 1 : 0));
}
+ // Rewrite kotlin._Assertions.ENABLED getter.
+ if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
+ iterator.replaceCurrentInstruction(
+ code.createIntConstant(
+ kotlinTransformation == AssertionTransformation.DISABLE ? 0 : 1));
+ }
}
}
}
@@ -362,7 +380,7 @@
Instruction nextInstruction = iterator.next();
if (nextInstruction.isStaticPut()
&& nextInstruction.asStaticPut().getField().holder
- == dexItemFactory.kotlin.kotlinAssertions
+ == dexItemFactory.kotlin.assertions.type
&& nextInstruction.asStaticPut().getField().name == dexItemFactory.enabledFieldName
&& invoke.outValue().numberOfUsers() == 1
&& invoke.outValue().numberOfPhiUsers() == 0
diff --git a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
index b4308b9..6fe2c18 100644
--- a/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
+++ b/src/main/java/com/android/tools/r8/kotlin/Kotlin.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
@@ -35,10 +36,10 @@
public final DexItemFactory factory;
- public final DexType kotlinAssertions;
public final Functional functional;
public final Intrinsics intrinsics;
public final Metadata metadata;
+ public final _Assertions assertions;
public static final class ClassClassifiers {
@@ -51,10 +52,10 @@
public Kotlin(DexItemFactory factory) {
this.factory = factory;
- this.kotlinAssertions = factory.createType(addKotlinPrefix("_Assertions;"));
this.functional = new Functional();
this.intrinsics = new Intrinsics();
this.metadata = new Metadata();
+ this.assertions = new _Assertions();
// See {@link org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite}
this.knownTypeConversion =
@@ -172,6 +173,13 @@
public final DexString extraInt = factory.createString("xi");
}
+ public final class _Assertions {
+ public final DexType type = factory.createType(addKotlinPrefix("_Assertions;"));
+ public final DexString enabledFieldName = factory.createString("ENABLED");
+ public final DexField enabledField =
+ factory.createField(type, factory.booleanType, enabledFieldName);
+ }
+
// kotlin.jvm.internal.Intrinsics class
public final class Intrinsics {
public final DexType type = factory.createType(addKotlinPrefix("jvm/internal/Intrinsics;"));
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
index b201ce6..c2fdaa1 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -52,17 +53,23 @@
private static final Map<KotlinTargetVersion, Path> kotlinClasses = new HashMap<>();
private final TestParameters parameters;
+ private final boolean kotlinStdlibAsLibrary;
- @Parameterized.Parameters(name = "{0},{1}")
+ @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}")
public static Collection<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values());
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ KotlinTargetVersion.values(),
+ BooleanUtils.values());
}
public AssertionConfigurationKotlinTest(
- TestParameters parameters, KotlinTargetVersion targetVersion) {
+ TestParameters parameters,
+ KotlinTargetVersion targetVersion,
+ boolean kotlinStdlibAsClasspath) {
super(targetVersion);
this.parameters = parameters;
+ this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
}
@BeforeClass
@@ -76,21 +83,50 @@
}
}
+ private Path kotlinStdlibLibraryForRuntime() throws Exception {
+ Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar();
+ if (parameters.getRuntime().isCf()) {
+ return kotlinStdlibCf;
+ }
+
+ Path kotlinStdlibDex = temp.newFolder().toPath().resolve("kotlin-stdlib-dex.jar");
+ testForD8()
+ .addProgramFiles(kotlinStdlibCf)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip(kotlinStdlibDex);
+ return kotlinStdlibDex;
+ }
+
private void runD8Test(
ThrowableConsumer<D8TestBuilder> builderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> inspector,
List<String> outputLines)
throws Exception {
- testForD8()
- .addProgramFiles(ToolHelper.getKotlinStdlibJar())
- .addProgramFiles(kotlinClasses.get(targetVersion))
- .setMinApi(parameters.getApiLevel())
- .apply(builderConsumer)
- .run(
- parameters.getRuntime(),
- getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
- .inspect(inspector)
- .assertSuccessWithOutputLines(outputLines);
+ if (kotlinStdlibAsLibrary) {
+ testForD8()
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .addRunClasspathFiles(kotlinStdlibLibraryForRuntime())
+ .run(
+ parameters.getRuntime(),
+ getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ } else {
+ testForD8()
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .run(
+ parameters.getRuntime(),
+ getClass().getPackage().getName() + ".kotlintestclasses.TestClassKt")
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ }
}
public void runR8Test(
@@ -108,21 +144,38 @@
boolean enableJvmAssertions)
throws Exception {
- testForR8(parameters.getBackend())
- .addProgramFiles(ToolHelper.getKotlinStdlibJar())
- .addProgramFiles(kotlinClasses.get(targetVersion))
- .addKeepMainRule(testClassKt)
- .addKeepClassAndMembersRules(class1, class2)
- .setMinApi(parameters.getApiLevel())
- .apply(builderConsumer)
- .noMinification()
- .allowDiagnosticWarningMessages()
- .compile()
- .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
- .enableRuntimeAssertions(enableJvmAssertions)
- .run(parameters.getRuntime(), testClassKt)
- .inspect(inspector)
- .assertSuccessWithOutputLines(outputLines);
+ if (kotlinStdlibAsLibrary) {
+ testForR8(parameters.getBackend())
+ .addClasspathFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .addKeepMainRule(testClassKt)
+ .addKeepClassAndMembersRules(class1, class2)
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .noMinification()
+ .addRunClasspathFiles(kotlinStdlibLibraryForRuntime())
+ .compile()
+ .enableRuntimeAssertions(enableJvmAssertions)
+ .run(parameters.getRuntime(), testClassKt)
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ } else {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addProgramFiles(kotlinClasses.get(targetVersion))
+ .addKeepMainRule(testClassKt)
+ .addKeepClassAndMembersRules(class1, class2)
+ .setMinApi(parameters.getApiLevel())
+ .apply(builderConsumer)
+ .noMinification()
+ .allowDiagnosticWarningMessages()
+ .compile()
+ .assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
+ .enableRuntimeAssertions(enableJvmAssertions)
+ .run(parameters.getRuntime(), testClassKt)
+ .inspect(inspector)
+ .assertSuccessWithOutputLines(outputLines);
+ }
}
private List<String> allAssertionsExpectedLines() {
@@ -136,6 +189,7 @@
private void checkAssertionCodeRemoved(ClassSubject subject, boolean isR8) {
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as well,
// as is not used.
assertEquals(
@@ -151,9 +205,9 @@
.streamInstructions()
.anyMatch(InstructionSubject::isConstNumber));
} else {
- // In R8 the false (default) value of kotlin._Assertions.ENABLED is propagated.
- assertEquals(
- !isR8,
+ // The value of kotlin._Assertions.ENABLED is propagated from the assertions configuration
+ // for the class kotlin._Assertions.
+ assertFalse(
subject
.uniqueMethodWithName("m")
.streamInstructions()
@@ -168,6 +222,7 @@
private void checkAssertionCodeEnabled(ClassSubject subject, boolean isR8) {
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used.
assertEquals(
(isR8 ? 1 : 2),
@@ -199,6 +254,7 @@
ClassSubject subject = inspector.clazz(clazz);
assertThat(subject, isPresent());
if (subject.getOriginalName().equals("kotlin._Assertions")) {
+ assert !kotlinStdlibAsLibrary;
// With R8 the static-put of the kotlin._Assertions.INSTANCE field is removed as is not used.
assertEquals(
(isR8 ? 1 : 2),
@@ -224,7 +280,7 @@
private void checkAssertionCodeRemoved(CodeInspector inspector, boolean isR8) {
if (isR8) {
assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent()));
- } else {
+ } else if (!kotlinStdlibAsLibrary) {
checkAssertionCodeRemoved(inspector, "kotlin._Assertions", isR8);
}
checkAssertionCodeRemoved(inspector, class1, isR8);
@@ -234,7 +290,7 @@
private void checkAssertionCodeEnabled(CodeInspector inspector, boolean isR8) {
if (isR8) {
assertThat(inspector.clazz("kotlin._Assertions"), not(isPresent()));
- } else {
+ } else if (!kotlinStdlibAsLibrary) {
checkAssertionCodeEnabled(inspector, "kotlin._Assertions", isR8);
}
checkAssertionCodeEnabled(inspector, class1, isR8);
@@ -242,7 +298,9 @@
}
private void checkAssertionCodeLeft(CodeInspector inspector, boolean isR8) {
- checkAssertionCodeLeft(inspector, "kotlin._Assertions", isR8);
+ if (!kotlinStdlibAsLibrary) {
+ checkAssertionCodeLeft(inspector, "kotlin._Assertions", isR8);
+ }
checkAssertionCodeLeft(inspector, class1, isR8);
checkAssertionCodeLeft(inspector, class2, isR8);
}