This CL updates the parser so that it allows annotations of the form:
`@` <constructorDesignation> <argumentPart>
Where <argumentPart> can contain type arguments. Previously this was
prohibited at parse time.
Generic annotations are still prohibited in the current language
version, but the error is now generated by the parser listener
(BodyBuilder in the case of CFE, AstBuilder in the case of analyzer);
this should open the door for future CLs to add support for generic
annotations when the `generic-metadata` experimental flag is supplied.
Bug: https://github.com/dart-lang/sdk/issues/44838
Change-Id: I90604f38bcb378fc798c5737e486fb26589d4d1e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182668
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Bug: https://github.com/dart-lang/sdk/issues/44797
This check was previously introduced without a language version,
and therefore lead to a breaking change on SDK update. It's now
versioned for releases >= 2.13, allowing users to migrate easier.
It also adds a hint for the error, providing alternatives for
users whose code triggers this check.
Change-Id: I4f61066a917ddb18071508291cde00710802fc5d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182920
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Adds static errors in web backends requiring external members to be
annotated with @JS(). Members can be annotated directly, or on the
enclosing class or library.
Also removes duplicate checks in dart2js for non-native external
members that are now covered by the checks on external @JS members.
Change-Id: I2b17456f8f546f80f1ea6a817360c7f4c40f9640
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/180442
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Commit-Queue: Riley Porter <rileyporter@google.com>
This CL implements the core flow analysis infrastructure for tracking
reasons why an expression was not promoted. It supports the following
reasons:
- Expression was a property access
- Expression has been written to since it was promoted
I expect to add support for other non-promotion reasons in the future,
for example:
- `this` cannot be promoted
- Expression has been write captured
- Expression was a reference to a static field or top level variable
These non-promotion reasons are plumbed through to the CFE and
analyzer for the purpose of making errors easier for the user to
understand. For example, given the following code:
class C {
int? i;
f() {
if (i == null) return;
print(i.isEven);
}
}
The front end now prints:
../../tmp/test.dart:5:13: Error: Property 'isEven' cannot be accessed on 'int?' because it is potentially null.
Try accessing using ?. instead.
print(i.isEven);
^^^^^^
Context: 'i' refers to a property so it could not be promoted.
Much work still needs to be done to round out this feature, for example:
- Currently the analyzer only shows the new "why not promoted"
messages when the "--verbose" flag is specified; this means the
feature is unlikely to be noticed by users.
- Currently the analyzer doesn't show a "why not promoted" message
when the non-promotion reason is that the expression is a property
access.
- We need one or more web pages explaining non-promotion reasons in
more detail so that the error messages can contain pointers to them.
- The analyzer and front end currently only show non-promotion reasons
for expressions of the form `x.y` where `x` fails to be promoted to
non-nullable. There are many other scenarios that should be
handled.
Change-Id: I0a12df74d0fc6274dfb3cb555abea81a75884231
Bug: https://github.com/dart-lang/sdk/issues/38773
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/181741
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This CL is a warm-up to a follow-up change that will make running the
CFE trybot faster.
This CL:
* Rename tests that require git (because when working with filesets
and shards in the test system, the "checkout" is not a git checkout
and git commands thus doesn't work properly.
* Filters the front-end unit test setup on the front-end bot the way
it was probably intended, i.e. run tests in pkg/kernel, pkg/front_end
and pkg/fasta (actually, the last one doesn't exist, but still)
instead of just in folders inside the "suite" 'pkg' that somewhere in
the path has something called 'kernel', 'front_end' or 'fasta'.
* Split unit_test_suites.dart into a "forwarding shell" and a impl.
In a follow-up CL the impl will be converted to nnbd to allow for
using 'required' on named parameters, but if the entry point was
nnbd it would run in sound nnbd mode and nothing would compile
because all imports are not nnbd.
Overall this CL should change very little, mostly just run a few less
tests, i.e. for instance skip tests that live inside a folder called
'fasta' somewhere inside the analyzer directory path.
Change-Id: I3226c7261cff8b68cc287cff07dc1715dfd85159
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/181381
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
We now track the following additional information:
- All local variable reads and read captures (previously we only
tracked writes)
- For any conditional expression (`?:`) or if-test, the variables
written in the "then" branch
- For any logical and expression (`&&`), the variables written in the
right hand side of the expression
Tracking this information will allow us to implement legacy type
promotion as part of _fe_analyzer_shared, using the same API as flow
anaylsis, which will in turn allow a lot of front end and analyzer
code to be removed and simplified.
Change-Id: I01aaf64cefb989a4450c8e6d3a8373c1ca2b6dec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179980
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
The new implementation uses the same API as flow analysis. This
should allow us to significantly simplify the CFE and analyzer, by
dropping their implementations of legacy type promotion in favor of
the shared implementation.
This CL just introduces the new implementation and unit tests for it;
it does not integrate it with the analyzer or CFE. I will follow up
with a CL that does the integration.
Change-Id: Ie07b3b39604d6a022ad42f3ae6b648a317c8af28
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179560
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
The only references that are promotable are references to variables,
however in the near future I'll be adding logic to flor analysis to
keep track of attempts to promote fields; this will require tracking
references to property gets as well as references to `this`, so we
need to start building up data structures to track those references.
Bug: https://github.com/dart-lang/sdk/issues/38773
Change-Id: I0e3fd44f580bb85db3e8f405675b66aa4069e455
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179280
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This adds a new messages kind 'info' to the CFE for showing general
information during compilation. A 'configuration' options is added
to `CompilerOptions` for telling the CFE how it is run.
The configuration 'compile' is added for when the CFE is invoked to
produces an "executable" as when running `dart compile`. When
configuration is set, the CFE emits an info message about the
null safety compilation mode.
Support for `dart compile exe` and `dart compile js` is added in this
CL. Support for `dart compile kernel|app-jit|aot` is not included.
In response to https://github.com/dart-lang/sdk/issues/44234
TEST=pkg/dartdev/test/commands/compile_test.dart
Change-Id: I08f51e2a3f5ad4841c4d703bcd266b7afb63c7c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178982
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Jens Johansen <jensj@google.com>
Bug: https://github.com/flutter/flutter/issues/73176
This reverts commit 825f56c6aa.
Both ddc and dart2js keep a set of native class names to make sure
@JS() classes don't conflict. Instead of processing native classes
for every component, this CL caches the set of native classes. This
should address the recompile time issues related to the original
revert.
Change-Id: If8028f0842440e8b9354bec9d7f72d0d42fcd8bd
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178926
Commit-Queue: Srujan Gaddam <srujzs@google.com>
Reviewed-by: Sigmund Cherem <sigmund@google.com>
Previously, it wrapped _FlowAnalysisImpl. I'm planning a series of
changes to allow legacy type promotion to be expressed using the same
API as flow analysis; in order to make this possible to debug using
FlowAnalysisDebug, it needs to be able to wrap any implementation of
FlowAnalysis, not just _FlowAnalysisImpl.
Change-Id: Ic1ae87c22de6d4ff1e0d5d86090726272f741c34
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/179380
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
These test harness methods now take strings rather than Type objects,
which should make the tests easier to read (and less work to write).
Change-Id: I160bb7a7a61116e18736d414d5a549971d851c02
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/178883
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This CL avoids the infinite loop created by allowing the parser
to parse it as a complex type (despite no actual name after it) and
adds a few asserts in an attempt to catch rewriting on eof (setting a
token after eof).
Fixes#42229Fixes#44477
Change-Id: Ie2da0957894067a3d4748cb6384bc9d6cf32c215
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150521
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This CL adds support for nested structs in FFI calls, callbacks, and
memory loads and stores through the Struct classes itself.
Nesting empty structs and nesting a structs in themselves (directly or
indirectly) is reported as error.
This feature is almost fully implemented in the CFE transformation.
Because structs depend on the sizes of their nested structs, the structs
now need to be processed in topological order.
Field access to nested structs branches at runtime on making a derived
Pointer if the backing memory of the outer struct was a Pointer or
making a TypedDataView if the backing memory of the outer struct was
a TypedData.
Assigning to a nested struct is a byte for byte copy from the source.
The only changes in the VM are contained in the native calling
convention calculation which now recursively needs to reason about
fundamental types instead of just 1 struct deep.
Because of the amount of corner cases in the calling conventions that
need to be covered, the tests are generated, rather than hand-written.
ABIs tested on CQ: x64 (Linux, MacOS, Windows), ia32 (Linux, Windows),
arm (Android softFP, Linux hardFP), arm64 Android.
ABIs tested locally through Flutter: arm64 iOS.
ABIs not tested: ia32 Android (emulator), x64 iOS (simulator), arm iOS.
TEST=runtime/bin/ffi_test/ffi_test_functions_generated.cc
TEST=runtime/bin/ffi_test/ffi_test_functions.cc
TEST=tests/{ffi,ffi_2}/function_structs_by_value_generated_test.dart
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_generated_tes
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_test.dart
TEST=tests/{ffi,ffi_2}/vmspecific_static_checks_test.dart
Closes https://github.com/dart-lang/sdk/issues/37271.
Contains a temporary workaround for
https://github.com/dart-lang/sdk/issues/44454.
Change-Id: I5e5d10e09e5c3fc209f5f7e997efe17bd362214d
Cq-Include-Trybots: luci.dart.try:dart-sdk-linux-try,dart-sdk-mac-try,dart-sdk-win-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-mac-release-x64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,analyzer-analysis-server-linux-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169221
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
When analyzing code like this with the
`allowLocalBooleanVarsToPromote` flag enabled, don't treat a variable
containing `null` as equivalent to a literal `null`. For example:
f(int? x) {
var y = null;
if (x != y) {
print(x + 1); // ERROR: x must be null checked.
}
}
The rationale is that flow analysis doesn't promote variables that are
known to be `null` to the `Null` type, so it seems like it would be
inconsistent if we used the fact that they're `null` to promote other
variables.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: Icfa14104936a26732353921e3a29a840c53d4d52
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176560
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This CL adds functionality to FlowAnalysis.variableRead so that if the
truth value of the variable is known to be correlated with other flow
analysis state, that state can be restored. This allows promotion
based on boolean variables in addition to boolean expressions, e.g.:
int? x = ...;
var xIsNotNull = x != null;
if (xIsNotNull) {
print(x + 1); // Ok; x is known to be non-null.
}
This functionality is not enabled yet; it is hidden behind the flag
`allowLocalBooleanVarsToPromoteByDefault`.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: I2a0183b69d285193db7acb36cc6af5c34e165c2c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176500
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This CL modifies flow analysis API so that when a variable is written
or initialized, if the written expression has non-trivial flow
analysis information, it is captured in the SsaNode associated with
the variable.
The stored information is not yet used; in a follow-up CL, I will add
the ability to retrieve it on a read and use it for promotions.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: I1e2590205d4a0c59f4400a119f3d6b380a11414c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176460
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
When https://github.com/dart-lang/language/issues/1274 (Infer
non-nullability from local boolean variables) is fixed, this algorithm
will replace the `restrict` algorithm is currently used at the bottom
of a try/finally statement to combine the flow models from the `try`
and `finally` blocks. The new algorithm has very similar behavior to
the old one, however since it is based on SSA nodes it (a) is able to
make slightly more promotions than the old one, and (b) will be able
to properly update SSA nodes so that local boolean variables assigned
inside a `try` block will be able to used for promotion after the
`finally` block.
The new functionality is hidden behind a flag for now, so there's no
customer-visible change yet.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: I1d37f981688f58da1e5c6c7eee48f2319c997cef
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/174960
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This is the first step in a sequence of CLs to support type promotion
based on local boolean variables
(https://github.com/dart-lang/language/issues/1274). To do this, we
will need a way to reliably tell whether a variable's value has
changed from one point in program execution to another, and to
associate additional information with a particular value of a
variable. We'll accomplish both of these tasks by associating each
variable with a pointer to an "SSA node". This pointer is updated to
point to a fresh node whenever the variable is written to, or two
different possible values come together at a control flow join.
Note that when a variable is write captured, it becomes impossible to
track when/if its value might change Previously we tracked this using
a `writeCaptured` boolean; now we track it by setting the SSA node
pointer to `null`.
This CL just lays the groundwork infrastructure and unit tests it;
there is no user-visible change.
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: Id729390655c9371cba264816b418f6c0463e1758
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176180
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This CL adds passing structs by value in FFI trampolines.
Nested structs and inline arrays are future work.
C defines passing empty structs as undefined behavior, so that is not
supported in this CL.
Suggested review order:
1) commit message
2) ffi/marshaller (decisions for what is done in IL and what in MC)
3) frontend/kernel_to_il (IL construction)
4) backend/il (MC generation from IL)
5) rest in VM
Overall architecture is that structs are split up into word-size chunks
in IL when this is possible: 1 definition in IL per chunk, 1 Location in
IL per chunk, and 1 NativeLocation for the backend per chunk.
In some cases it is not possible or less convenient to split into
chunks. In these cases TypedDataBase objects are stored into and loaded
from directly in machine code.
The various cases:
- FFI call arguments which are not passed as pointers: pass individual
chunks to FFI call which already have the right location.
- FFI call arguments which are passed as pointers: Pass in TypedDataBase
to FFI call, allocate space on the stack, and make a copy on the stack
and pass the copies' address to the callee.
- FFI call return value: pass in TypedData to FFI call, and copy result
in machine code.
- FFI callback arguments which are not passed as pointers: IL definition
for each chunk, and populate a new TypedData with those chunks.
- FFI callback arguments which are passed as pointer: IL definition for
the pointer, and copying of contents in IL.
- FFI return value when location is pointer: Copy data to callee result
location in IL.
- FFI return value when location is not a pointer: Copy data in machine
code to the right registers.
Some other notes about the implementation:
- Due to Store/LoadIndexed loading doubles from float arrays, we use
a int32 instead and use the BitCastInstr.
- Linux ia32 uses `ret 4` when returning structs by value. This requires
special casing in the FFI callback trampolines to either use `ret` or
`ret 4` when returning.
- The 1 IL definition, 1 Location, and 1 NativeLocation approach does
not remove the need for special casing PairLocations in the machine
code generation because they are 1 Location belonging to 1 definition.
Because of the amount of corner cases in the calling conventions that
need to be covered, the tests are generated, rather than hand-written.
ABIs tested on CQ: x64 (Linux, MacOS, Windows), ia32 (Linux, Windows),
arm (Android softFP, Linux hardFP), arm64 Android.
ABIs tested locally through Flutter: ia32 Android (emulator), x64 iOS
(simulator), arm64 iOS.
ABIs not tested: arm iOS.
TEST=runtime/bin/ffi_test/ffi_test_functions_generated.cc
TEST=runtime/bin/ffi_test/ffi_test_functions.cc
TEST=tests/{ffi,ffi_2}/function_structs_by_value_generated_test.dart
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_generated_tes
TEST=tests/{ffi,ffi_2}/function_callbacks_structs_by_value_test.dart
TEST=tests/{ffi,ffi_2}/vmspecific_static_checks_test.dart
Closes https://github.com/dart-lang/sdk/issues/36730.
Change-Id: I474d3a4ee1faadbe767ddadd1b696e24d8dc364c
Cq-Include-Trybots: luci.dart.try:dart-sdk-linux-try,dart-sdk-mac-try,dart-sdk-win-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-mac-debug-x64-try,vm-kernel-linux-debug-ia32-try,vm-kernel-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-x64-try,vm-kernel-nnbd-linux-debug-ia32-try,vm-kernel-nnbd-mac-release-x64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-nnbd-linux-debug-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,analyzer-analysis-server-linux-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/140290
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Some notes.
1. `Token.next` is `Token?` because there are null(s) for comment tokens.
But I think that it is never `null` in code, because there is at
least EOF token. So, lots of `!` null checks.
2. `Link.tail` is never null when `isNotEmpty`, but the type system
cannot express it. So, some number of `!` null checks.
3. I keep assert(s) for now, and use comment
`// ignore: unnecessary_null_comparison`.
4. I disabled `can_get_rid_of_nnbd_issue_error`, I think it fails
because of changes in `_fe_analyzer_shared` language version.
Smoke test in google3 looks green.
https://test.corp.google.com/ui#id=OCL:346825148:BASE:346841611:1607632317129:6e87bf7
I have not tried yet full TAP, will do in the evening.
I have not tried yet Golem, or looked at benchmarks.
Change-Id: I651301e5d3a851dd77d73af960dac779cb0fc991
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175620
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
This is a reland of fd2a6c6815
Original change's description:
> Flow analysis: Track expression variables separately from promotion info.
>
> Previously, we used a single class hierarchy, ExpressionInfo, to store
> all the information that flow analysis needs to know about a variable,
> including:
>
> 1. What is known about the program state if the expression evaluates
> to true/false
>
> 2. Whether the expression is a `null` literal
>
> 3. Whether the expression is a reference to a variable.
>
> However, in order to address
> https://github.com/dart-lang/language/issues/1274 (Infer
> non-nullability from local boolean variables), we'll need #3 to be
> tracked orthogonally from #1, so that when a local boolean is referred
> to later, we can track information of type #1 and #3 simultaneously.
>
> However, it makes sense to keep #1 and #2 in the same data structure,
> because future work is planned to represent them in a more uniform
> way, as part of addressing
> https://github.com/dart-lang/language/issues/1224 (Using `if (foo?.bar
> == somethingNotNull)` should promote `foo`).
>
> Change-Id: I432f6e2e80543bb1d565b49403180c520eef66a5
> Bug: https://github.com/dart-lang/language/issues/1274
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175008
> Reviewed-by: Johnni Winther <johnniwinther@google.com>
> Commit-Queue: Paul Berry <paulberry@google.com>
Bug: https://github.com/dart-lang/language/issues/1274
Change-Id: I002adbde782887def50dc80ab6673411b321c341
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/175362
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>