File: java_asserts.md

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (80 lines) | stat: -rw-r--r-- 2,879 bytes parent folder | download | duplicates (11)
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
# 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.