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();
+}