1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80

build / android / docs / java_asserts.md [blame]

# 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 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](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.