Rewriting of assertions to invoke assertion handler for Kotlin assertions
Bug: 209445989
Change-Id: Ia476aa6362744b04b11d1e557a611f03b4ff8d00
diff --git a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
index 12e7b48..2f119ca 100644
--- a/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
+++ b/src/main/java/com/android/tools/r8/AssertionsConfiguration.java
@@ -170,8 +170,11 @@
/**
* Rewrite the throwing of <code>java.lang.AssertionError</code> to call the supplied method
* <code>assertionHandler</code>. The method must be a reference to a static method taking one
- * argument of type <code>java.lang.AssertionError</code>. After the assertion handler as been
- * called, the code continues as if assertions where disabled.
+ * argument. The type of the argument should be <code>java.lang.Throwable</code> as kotlinc will
+ * generate code where the assertion error is thrown as <code>java.lang.Throwable</code>. If all
+ * code is generated by javac then the type of the argument can be <code>
+ * java.lang.AssertionError</code>. After the assertion handler as been called, the code
+ * continues as if assertions where disabled.
*/
public AssertionsConfiguration.Builder setAssertionHandler(MethodReference assertionHandler) {
this.transformation = null;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7896e62..05ffa2b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -1005,9 +1005,11 @@
enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView));
}
if (AssertionsRewriter.isEnabled(appView.options())) {
- enqueuer.registerAnalysis(
+ ClassInitializerAssertionEnablingAnalysis analysis =
new ClassInitializerAssertionEnablingAnalysis(
- appView, OptimizationFeedbackSimple.getInstance()));
+ appView, OptimizationFeedbackSimple.getInstance());
+ enqueuer.registerAnalysis(analysis);
+ enqueuer.registerFieldAccessAnalysis(analysis);
}
if (options.isClassMergingExtensionRequired(enqueuer.getMode())) {
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
index 2585329..783a837 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFieldInstruction.java
@@ -88,6 +88,10 @@
return opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC;
}
+ public boolean isStaticFieldGet() {
+ return opcode == Opcodes.GETSTATIC;
+ }
+
@Override
public CfFieldInstruction asFieldInstruction() {
return this;
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 ac77ce0..d110049 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
@@ -16,11 +16,12 @@
import com.android.tools.r8.cf.code.CfStaticFieldWrite;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedMethod;
+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;
+import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -31,7 +32,8 @@
import java.util.stream.Collectors;
import org.objectweb.asm.Opcodes;
-public class ClassInitializerAssertionEnablingAnalysis extends EnqueuerAnalysis {
+public class ClassInitializerAssertionEnablingAnalysis extends EnqueuerAnalysis
+ implements EnqueuerFieldAccessAnalysis {
private final DexItemFactory dexItemFactory;
private final OptimizationFeedback feedback;
private final DexString kotlinAssertionsEnabled;
@@ -50,24 +52,40 @@
.collect(Collectors.toList());
}
+ private boolean isUsingJavaAssertionsDisabledField(DexField field) {
+ // This does not check the holder, as for inner classes the field is read from the outer class
+ // and not the class itself.
+ return field.getName() == dexItemFactory.assertionsDisabled
+ && field.getType() == dexItemFactory.booleanType;
+ }
+
+ private boolean isUsingKotlinAssertionsEnabledField(DexField field) {
+ return field == dexItemFactory.kotlin.assertions.enabledField;
+ }
+
+ @Override
+ public void traceStaticFieldRead(
+ DexField field,
+ FieldResolutionResult resolutionResult,
+ ProgramMethod context,
+ EnqueuerWorklist worklist) {
+ if (isUsingJavaAssertionsDisabledField(field) || isUsingKotlinAssertionsEnabledField(field)) {
+ assertionHandlers.forEach(
+ assertionHandler -> worklist.enqueueTraceInvokeStaticAction(assertionHandler, context));
+ }
+ }
+
@Override
public void processNewlyLiveMethod(
ProgramMethod method, ProgramDefinition context, EnqueuerWorklist worklist) {
DexEncodedMethod definition = method.getDefinition();
- if (definition.isClassInitializer()) {
- Code code = definition.getCode();
- if (code.isCfCode()) {
- if (hasJavacClinitAssertionCode(code.asCfCode()) || hasKotlincClinitAssertionCode(method)) {
- feedback.setInitializerEnablingJavaVmAssertions(definition);
- // In R8 this might be rewritten to calling an assertion handler if any are required.
- // Conservatively mark them all as invoked.
- if (worklist != null) {
- assertionHandlers.forEach(
- assertionHandler ->
- worklist.enqueueTraceInvokeStaticAction(assertionHandler, method));
- }
- }
- }
+ if (!definition.hasCode() || !definition.getCode().isCfCode()) {
+ return;
+ }
+ CfCode code = definition.getCode().asCfCode();
+ if (definition.isClassInitializer()
+ && (hasJavacClinitAssertionCode(code) || hasKotlincClinitAssertionCode(method))) {
+ feedback.setInitializerEnablingJavaVmAssertions(definition);
}
}
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 69bcf83..0da7886 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
@@ -34,12 +34,10 @@
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.io.UTFDataFormatException;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.stream.Collectors;
public class AssertionsRewriter {
@@ -365,25 +363,32 @@
clinit = clazz.getClassInitializer();
}
// For the transformation to rewrite the throw with a callback collect information on the
- // blocks covered by the if (!$assertionsDisabled) condition.
- Set<If> assertionEntryIfs = Sets.newIdentityHashSet();
+ // blocks covered by the if (!$assertionsDisabled or ENABLED) condition together with weather
+ // the assertion handling is on the true or false branch.
+ Map<If, Boolean> assertionEntryIfs = new IdentityHashMap<>();
Map<Throw, BasicBlock> throwSuccessorAfterHandler = new IdentityHashMap<>();
- if (configuration.isAssertionHandler() && method != clinit) {
+ if (configuration.isAssertionHandler()) {
LazyBox<DominatorTree> dominatorTree = new LazyBox<>(() -> new DominatorTree(code));
code.getBlocks()
.forEach(
basicBlock -> {
If theIf = isCheckAssertionsEnabledBlock(basicBlock);
if (theIf != null) {
- // All blocks dominated by the if is the assertion code (check is negation of
- // field read).
- BasicBlock assertionBlockEntry = theIf.targetFromFalse();
+ // All blocks dominated by the if is the assertion code. For Java it is on the
+ // false branch and for Kotlin on the true branch (for Java it is negated
+ // $assertionsDisabled field and for Kotlin it is the ENABLED field).
+ boolean conditionForAssertionBlock =
+ !isUsingJavaAssertionsDisabledField(
+ theIf.lhs().getDefinition().asStaticGet());
+ BasicBlock assertionBlockEntry =
+ theIf.targetFromBoolean(conditionForAssertionBlock);
List<BasicBlock> blocks =
dominatorTree.computeIfAbsent().dominatedBlocks(assertionBlockEntry);
Throw throwInstruction = isAlwaysThrowingEntry(assertionBlockEntry, blocks);
if (throwInstruction != null) {
- assertionEntryIfs.add(theIf);
- throwSuccessorAfterHandler.put(throwInstruction, theIf.targetFromTrue());
+ assertionEntryIfs.put(theIf, conditionForAssertionBlock);
+ throwSuccessorAfterHandler.put(
+ throwInstruction, theIf.targetFromBoolean(!conditionForAssertionBlock));
}
}
});
@@ -409,13 +414,15 @@
}
} else if (current.isStaticPut()) {
StaticPut staticPut = current.asStaticPut();
- if (isInitializerEnablingJavaVmAssertions && isUsingAssertionsDisabledField(staticPut)) {
+ if (isInitializerEnablingJavaVmAssertions
+ && isUsingJavaAssertionsDisabledField(staticPut)) {
iterator.remove();
}
} else if (current.isStaticGet()) {
StaticGet staticGet = current.asStaticGet();
// Rewrite $assertionsDisabled getter (only if the initializer enabled assertions).
- if (isInitializerEnablingJavaVmAssertions && isUsingAssertionsDisabledField(staticGet)) {
+ if (isInitializerEnablingJavaVmAssertions
+ && isUsingJavaAssertionsDisabledField(staticGet)) {
// For assertion handler rewrite just leave the static get, as it will become dead code.
if (!configuration.isAssertionHandler()) {
iterator.replaceCurrentInstruction(
@@ -425,9 +432,12 @@
}
// Rewrite kotlin._Assertions.ENABLED getter.
if (staticGet.getField() == dexItemFactory.kotlin.assertions.enabledField) {
- iterator.replaceCurrentInstruction(
- code.createIntConstant(
- kotlinTransformation.isCompileTimeDisabled() ? 0 : 1, current.getLocalInfo()));
+ // For assertion handler rewrite just leave the static get, as it will become dead code.
+ if (!configuration.isAssertionHandler()) {
+ iterator.replaceCurrentInstruction(
+ code.createIntConstant(
+ kotlinTransformation.isCompileTimeDisabled() ? 0 : 1, current.getLocalInfo()));
+ }
}
}
@@ -435,8 +445,10 @@
if (configuration.isAssertionHandler()) {
if (current.isIf()) {
If ifInstruction = current.asIf();
- if (assertionEntryIfs.contains(ifInstruction)) {
- ifInstruction.targetFromTrue().unlinkSinglePredecessorSiblingsAllowed();
+ if (assertionEntryIfs.containsKey(ifInstruction)) {
+ ifInstruction
+ .targetFromBoolean(!assertionEntryIfs.get(ifInstruction))
+ .unlinkSinglePredecessorSiblingsAllowed();
ifInstruction.lhs().removeUser(ifInstruction);
iterator.replaceCurrentInstruction(new Goto());
}
@@ -496,13 +508,22 @@
}
}
- private boolean isUsingAssertionsDisabledField(FieldInstruction instruction) {
+ private boolean isUsingAssertionsControlField(FieldInstruction instruction) {
+ return isUsingJavaAssertionsDisabledField(instruction)
+ || isUsingKotlinAssertionsEnabledField(instruction);
+ }
+
+ private boolean isUsingJavaAssertionsDisabledField(FieldInstruction instruction) {
// This does not check the holder, as for inner classe the field is read from the outer class
// and not the class itself.
return instruction.getField().getName() == dexItemFactory.assertionsDisabled
&& instruction.getField().getType() == dexItemFactory.booleanType;
}
+ private boolean isUsingKotlinAssertionsEnabledField(FieldInstruction instruction) {
+ return instruction.getField() == dexItemFactory.kotlin.assertions.enabledField;
+ }
+
private If isCheckAssertionsEnabledBlock(BasicBlock basicBlock) {
if (!basicBlock.exit().isIf()) {
return null;
@@ -513,7 +534,7 @@
return null;
}
StaticGet staticGet = theIf.lhs().getDefinition().asStaticGet();
- return isUsingAssertionsDisabledField(staticGet)
+ return isUsingAssertionsControlField(staticGet)
&& staticGet.value().hasSingleUniqueUser()
&& !staticGet.value().hasPhiUsers()
? theIf
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
new file mode 100644
index 0000000..314d180
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions;
+
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsInClinit;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest
+ extends AssertionConfigurationAssertionHandlerTestBase {
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("assertionHandler: <clinit>");
+
+ @Override
+ String getExpectedOutput() {
+ return EXPECTED_OUTPUT;
+ }
+
+ @Override
+ MethodReference getAssertionHandler() throws Exception {
+ return Reference.methodFromMethod(
+ AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+ }
+
+ @Override
+ List<Class<?>> getTestClasses() {
+ return ImmutableList.of(AssertionsInClinit.class);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
new file mode 100644
index 0000000..84bd1ef
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerKotlinTestBase.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public abstract class AssertionConfigurationAssertionHandlerKotlinTestBase extends KotlinTestBase {
+
+ @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getKotlinTestParameters().withAllCompilersAndTargetVersions().build(),
+ BooleanUtils.values(),
+ BooleanUtils.values());
+ }
+
+ protected final TestParameters parameters;
+ protected final boolean kotlinStdlibAsLibrary;
+ protected final boolean useJvmAssertions;
+ protected final KotlinCompileMemoizer compiledForAssertions;
+
+ public AssertionConfigurationAssertionHandlerKotlinTestBase(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean kotlinStdlibAsClasspath,
+ boolean useJvmAssertions)
+ throws IOException {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath;
+ this.useJvmAssertions = useJvmAssertions;
+ this.compiledForAssertions =
+ useJvmAssertions ? kotlinWithJvmAssertions() : kotlinWithoutJvmAssertions();
+ }
+
+ private KotlinCompileMemoizer kotlinWithJvmAssertions() throws IOException {
+ return getCompileMemoizer(getKotlinFiles())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(true));
+ }
+
+ private KotlinCompileMemoizer kotlinWithoutJvmAssertions() throws IOException {
+ return getCompileMemoizer(getKotlinFiles())
+ .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(false));
+ }
+
+ protected abstract String getExpectedOutput();
+
+ protected abstract MethodReference getAssertionHandler() throws Exception;
+
+ protected abstract List<Path> getKotlinFiles() throws IOException;
+
+ protected abstract String getTestClassName();
+
+ protected void configureR8(R8FullTestBuilder builder) {}
+
+ private Path kotlinStdlibLibraryForRuntime() throws Exception {
+ Path kotlinStdlibCf = kotlinc.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 MethodReference getAssertionHandlerIgnoreException() {
+ try {
+ return getAssertionHandler();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private void configureKotlinStdlib(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+ if (kotlinStdlibAsLibrary) {
+ builder
+ .addClasspathFiles(kotlinc.getKotlinStdlibJar())
+ .addRunClasspathFiles(kotlinStdlibLibraryForRuntime());
+ } else {
+ builder.addProgramFiles(kotlinc.getKotlinStdlibJar());
+ }
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .apply(this::configureKotlinStdlib)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(AssertionHandlers.class)
+ .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+ .addAssertionsConfiguration(
+ builder ->
+ builder
+ .setAssertionHandler(getAssertionHandlerIgnoreException())
+ .setScopeAll()
+ .build())
+ .run(parameters.getRuntime(), getTestClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::configureKotlinStdlib)
+ .setMinApi(parameters.getApiLevel())
+ .addProgramClasses(AssertionHandlers.class)
+ .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion))
+ .addAssertionsConfiguration(
+ builder ->
+ builder
+ .setAssertionHandler(getAssertionHandlerIgnoreException())
+ .setScopeAll()
+ .build())
+ .addKeepMainRule(getTestClassName())
+ .apply(this::configureR8)
+ .allowDiagnosticWarningMessages(!kotlinStdlibAsLibrary)
+ .compile()
+ .applyIf(
+ !kotlinStdlibAsLibrary,
+ result ->
+ result.assertAllWarningMessagesMatch(
+ equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")))
+ .run(parameters.getRuntime(), getTestClassName())
+ .assertSuccessWithOutput(getExpectedOutput());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java
index 5924002..c1172d8 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerRethrowingTest.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsWithExceptionHandlers;
-import com.android.tools.r8.rewrite.assertions.assertionhandler.Shared;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -38,7 +38,7 @@
@Override
MethodReference getAssertionHandler() throws Exception {
return Reference.methodFromMethod(
- Shared.class.getMethod("assertionHandlerRethrowing", AssertionError.class));
+ AssertionHandlers.class.getMethod("assertionHandlerRethrowing", Throwable.class));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java
index 1a4059b..43e0a7e 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerSimpleTest.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsSimple;
-import com.android.tools.r8.rewrite.assertions.assertionhandler.Shared;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -32,7 +32,7 @@
@Override
MethodReference getAssertionHandler() throws Exception {
return Reference.methodFromMethod(
- Shared.class.getMethod("assertionHandler", AssertionError.class));
+ AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
index a79ac24..fa4de84 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.rewrite.assertions.assertionhandler.Shared;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,7 +46,7 @@
public void testD8() throws Exception {
assumeTrue(parameters.isDexRuntime());
testForD8(parameters.getBackend())
- .addProgramClasses(Shared.class)
+ .addProgramClasses(AssertionHandlers.class)
.addProgramClasses(getTestClasses())
.setMinApi(parameters.getApiLevel())
.addOptionsModification(o -> o.testing.forceIRForCfToCfDesugar = true)
@@ -63,7 +63,7 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramClasses(Shared.class)
+ .addProgramClasses(AssertionHandlers.class)
.addProgramClasses(getTestClasses())
.addKeepMainRule(getTestClasses().get(0))
.addKeepAnnotation()
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java
index 7f93dac..94f47bc 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerWithExceptionHandlersTest.java
@@ -6,8 +6,8 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionsWithExceptionHandlers;
-import com.android.tools.r8.rewrite.assertions.assertionhandler.Shared;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -32,7 +32,7 @@
@Override
MethodReference getAssertionHandler() throws Exception {
return Reference.methodFromMethod(
- Shared.class.getMethod("assertionHandler", AssertionError.class));
+ AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/Shared.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java
similarity index 75%
rename from src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/Shared.java
rename to src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java
index 401e09c..bbf856d 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/Shared.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionHandlers.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.rewrite.assertions.assertionhandler;
-public class Shared {
- static String methodWithAssertionError(AssertionError assertion) {
+public class AssertionHandlers {
+ static String methodWithAssertionError(Throwable assertion) {
return assertion.getStackTrace()[0].getMethodName();
}
- public static void assertionHandler(AssertionError assertion) {
+ public static void assertionHandler(Throwable assertion) {
System.out.println(
"assertionHandler: "
+ (assertion.getMessage() != null
@@ -17,7 +17,7 @@
: methodWithAssertionError(assertion)));
}
- public static void assertionHandlerRethrowing(AssertionError assertion) {
+ public static void assertionHandlerRethrowing(Throwable assertion) throws Throwable {
System.out.println(
"assertionHandlerRethrowing: "
+ (assertion.getMessage() != null
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java
new file mode 100644
index 0000000..d077af1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsInClinit.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.assertionhandler;
+
+public class AssertionsInClinit {
+
+ static {
+ assert false;
+ }
+
+ public static void main(String[] args) {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java
index 6ce661d..e5bdad7 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/assertionhandler/AssertionsWithExceptionHandlers.java
@@ -42,11 +42,11 @@
try {
assert false : "Fourth assertion";
} catch (AssertionError e1) {
- System.out.println("Caught from: " + Shared.methodWithAssertionError(e1));
+ System.out.println("Caught from: " + AssertionHandlers.methodWithAssertionError(e1));
try {
simpleAssertion();
} catch (AssertionError e2) {
- System.out.println("Caught from: " + Shared.methodWithAssertionError(e2));
+ System.out.println("Caught from: " + AssertionHandlers.methodWithAssertionError(e2));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
new file mode 100644
index 0000000..4d900ad
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionConfigurationAssertionHandlerKotlinSimpleTest.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlersimple;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerKotlinSimpleTest
+ extends AssertionConfigurationAssertionHandlerKotlinTestBase {
+
+ public AssertionConfigurationAssertionHandlerKotlinSimpleTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean kotlinStdlibAsClasspath,
+ boolean useJvmAssertions)
+ throws IOException {
+ super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions);
+ }
+
+ @Override
+ protected String getExpectedOutput() {
+ return StringUtils.lines(
+ "assertionHandler: simpleAssertion",
+ "assertionHandler: multipleAssertions 1",
+ "assertionHandler: multipleAssertions 2");
+ }
+
+ @Override
+ protected MethodReference getAssertionHandler() throws Exception {
+ return Reference.methodFromMethod(
+ AssertionHandlers.class.getMethod("assertionHandler", Throwable.class));
+ }
+
+ @Override
+ protected List<Path> getKotlinFiles() throws IOException {
+ return getKotlinFilesInTestPackage(getClass().getPackage());
+ }
+
+ @Override
+ protected String getTestClassName() {
+ return getClass().getPackage().getName() + ".AssertionSimpleKt";
+ }
+
+ @Override
+ protected void configureR8(R8FullTestBuilder builder) {
+ builder.applyIf(
+ !kotlinStdlibAsLibrary && !useJvmAssertions,
+ b -> b.addDontWarn("org.jetbrains.annotations.NotNull"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt
new file mode 100644
index 0000000..7f6066b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlersimple/AssertionSimple.kt
@@ -0,0 +1,15 @@
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlersimple
+
+fun simpleAssertion() {
+ assert(false) { "simpleAssertion" }
+}
+
+fun multipleAssertions() {
+ assert(false) { "multipleAssertions 1" }
+ assert(false) { "multipleAssertions 2" }
+}
+
+fun main() {
+ simpleAssertion();
+ multipleAssertions();
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
new file mode 100644
index 0000000..93b3b57
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionConfigurationAssertionHandlerKotlinRethrowingTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlerwithexceptions;
+
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.AssertionConfigurationAssertionHandlerKotlinTestBase;
+import com.android.tools.r8.rewrite.assertions.assertionhandler.AssertionHandlers;
+import com.android.tools.r8.utils.StringUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class AssertionConfigurationAssertionHandlerKotlinRethrowingTest
+ extends AssertionConfigurationAssertionHandlerKotlinTestBase {
+
+ public AssertionConfigurationAssertionHandlerKotlinRethrowingTest(
+ TestParameters parameters,
+ KotlinTestParameters kotlinParameters,
+ boolean kotlinStdlibAsClasspath,
+ boolean useJvmAssertions)
+ throws IOException {
+ super(parameters, kotlinParameters, kotlinStdlibAsClasspath, useJvmAssertions);
+ }
+
+ @Override
+ protected String getExpectedOutput() {
+ return StringUtils.lines(
+ "assertionHandlerRethrowing: First assertion",
+ "assertionHandlerRethrowing: Second assertion",
+ "Caught: Second assertion",
+ "assertionHandlerRethrowing: Third assertion",
+ "Caught: Third assertion",
+ "assertionHandlerRethrowing: Fourth assertion",
+ "Caught from: assertionsWithCatch3",
+ "assertionHandlerRethrowing: Fifth assertion",
+ "Caught from: simpleAssertion");
+ }
+
+ @Override
+ protected MethodReference getAssertionHandler() throws Exception {
+ return Reference.methodFromMethod(
+ AssertionHandlers.class.getMethod("assertionHandlerRethrowing", Throwable.class));
+ }
+
+ @Override
+ protected List<Path> getKotlinFiles() throws IOException {
+ return getKotlinFilesInTestPackage(getClass().getPackage());
+ }
+
+ @Override
+ protected String getTestClassName() {
+ return getClass().getPackage().getName() + ".AssertionsWithExceptionHandlersKt";
+ }
+
+ @Override
+ protected void configureR8(R8FullTestBuilder builder) {
+ builder
+ .addKeepRules("-keep class **.*Kt { assertionsWith*(); simpleAssertion(); }")
+ .addDontWarn("org.jetbrains.annotations.NotNull")
+ .applyIf(!kotlinStdlibAsLibrary, b -> b.addDontWarn("org.jetbrains.annotations.Nullable"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt
new file mode 100644
index 0000000..dd70bc1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/kotlinassertionhandlerwithexceptions/AssertionsWithExceptionHandlers.kt
@@ -0,0 +1,54 @@
+package com.android.tools.r8.rewrite.assertions.kotlinassertionhandlerwithexceptions
+
+fun methodWithAssertionError(assertion : Throwable) : String {
+ return assertion.getStackTrace()[0].getMethodName();
+}
+
+fun assertionsWithCatch1() {
+ try {
+ assert(false) { "First assertion" }
+ } catch (e : NoSuchMethodError) {
+ } catch (e: NoSuchFieldError) {
+ } catch (e : NoClassDefFoundError) {
+ }
+}
+
+fun assertionsWithCatch2() {
+ try {
+ assert(false) { "Second assertion" }
+ } catch (e : AssertionError) {
+ println("Caught: " + e.message)
+ try {
+ assert(false) { "Third assertion" }
+ } catch (e : AssertionError) {
+ println("Caught: " + e.message)
+ }
+ }
+}
+
+fun simpleAssertion() {
+ assert(false) { "Fifth assertion" }
+}
+
+fun assertionsWithCatch3() {
+ try {
+ assert(false) { "Fourth assertion" }
+ } catch (e1 : AssertionError) {
+ println("Caught from: " + methodWithAssertionError(e1));
+ try {
+ simpleAssertion();
+ } catch (e2 : AssertionError) {
+ println("Caught from: " + methodWithAssertionError(e2));
+ }
+ }
+}
+
+fun main() {
+ try {
+ assertionsWithCatch1();
+ } catch (e : AssertionError) {
+ // Ignore.
+ }
+ assertionsWithCatch2();
+ assertionsWithCatch3();
+}