Java Asserts in Chromium

This doc exists to explain how asserts in Java are enabled and disabled by Chromium's build system.

javac Assertion Bytecode

Whenever javac compiles a Java class, assertions are transformed into the following bytecode:

    Code:
       0: getstatic     #2            // Static field $assertionsDisabled
       3: ifne          20            // Conditional jump past assertion throw
      12: new           #3            // Class java/lang/AssertionError
      19: athrow                      // Throwing AssertionError
      20: return

// NOTE: this static block was made just to check the desiredAssertionStatus.
// There was no static block on the class before javac created one.
  static {};
    Code:
       2: invokevirtual #6            // Method java/lang/Class.desiredAssertionStatus()
       5: ifne          12
       8: iconst_1
       9: goto          13
      12: iconst_0
      13: putstatic     #2            // Static field $assertionsDisabled
      16: return

TL;DR - every single assertion is gated behind a assertionDisabled flag check, which is a static field that can be set by the JRE's setDefaultAssertionStatus, setPackageAssertionStatus, and setClassAssertionStatus methods.

Assertion Enabling/Disabling

Our tools which consume javac output, namely R8 and D8, each have flags which the build system uses to enable or disable asserts. We control this with the enable_java_asserts gn arg. It does this by deleting the gating check on assertionsDisabled when enabling, and by eliminating any reference to the assert when disabling.

// Example equivalents of:
a = foo();
assert a != 0;
return a;

// Traditional, unoptimized javac output.
a = foo();
if (!assertionsDisabled && a == 0) {
  throw new AssertionError();
}
return a;

// Optimized with assertions enabled.
a = foo();
if (a == 0) {
  throw new AssertionError();
}
return a;

// Optimized with assertions disabled.
a = foo();
return a;

Assertion Enabling on Canary

Recently we enabled asserts on Canary. It spiked our crash rate, and it was decided to not do this again, as it's bad user experience to crash the app incessantly for non-fatal issues.

So, we asked the R8 team for a feature which would rewrite the bytecode of these assertions, which they implemented for us. Now, instead of just turning it on and throwing an AssertionError, R8 would call a provided assertion handler with the AssertionError. We then wrote a silent assertion reporter and this reports Java AssertionErrors to our crash server without crashing the browser.