diff --git a/build/android/docs/java_asserts.md b/build/android/docs/java_asserts.md new file mode 100644 index 00000000000000..511b311ae9c760 --- /dev/null +++ b/build/android/docs/java_asserts.md @@ -0,0 +1,80 @@ +# 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. + +```java +// 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](https://chromium-review.googlesource.com/c/chromium/src/+/3307087) 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 with an ask to allow us to 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](https://r8.googlesource.com/r8/+/aefe7bc18a7ce19f3e9c6dac0bedf6d182bbe142/src/main/java/com/android/tools/r8/ParseFlagInfoImpl.java#124) +with the `AssertionError`. We then wrote a [silent assertion +reporter](https://chromium-review.googlesource.com/c/chromium/src/+/3746261) +and this reports Java `AssertionErrors` to our crash server without crashing +the browser.