Whenever the VM sends a request to evaluate an expression, it sends over
the list of alive dills (in order) and a sequence number (number of
reloads).
This can then either be
a) ignored if there's already a compiler around that has compiled
everything itself (e.g. if launched from source with observatory
enabled).
b) used to find out that we need to create a new compiler especially
for expression compilation. Either because we don't have any old
compiler or because a reload or GC changed the data available.
c) used to find out that the compiler we created previously is still in
a good state.
Tests for expression compilation has been added, and with the "framework"
added it should be easy to add new ones.
This CL leaves a few things to possibly be improved later:
* It doesn't support if one tries to do a mixed mode, where one
starts from source and reloads from dill. The compiler will not be
updated and the expression compilation will work on a old view of the
world.
* If - in any of the reloads - the main entry point changes library,
the initialization might not work correctly.
Fixes#34736.
Change-Id: Icae009c581e69b810ee19d2a5e2d0a803fd9f7c8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/135683
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
On 1/27/20, the nnbd specification changed weak and strong mode instance checks
to make them behave uniformly across legacy and opted-in libraries.
Therefore, it is not necessary anymore to propagate the library mode in
generated code to the runtime.
Change-Id: I42d3ddc6e9a921899aeac21be6374c7893a6d27c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/138111
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Régis Crelier <regis@google.com>
When these were originally added, we thought we would be able to use them in place of ifdefs and rely on the compiler optimizations and linker GC to remove things. This turned out not to reliably remove what we wanted removed, so we ended up with the ifdefs anyway.
Change-Id: I62e74d60d92b18a688b9dffaf77b1440c10a07ff
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134402
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
If generated code refers to a constant object (which are old and canonical), a pointer
to it will be embedded in the instruction stream on ia32.
If we now reload and morph this constant object and allocate the
morph'ed copy in new space, the become operation will make RX writable
and the slot in the instruction stream gets updated to point to a new
space object and the code gets added to the remembered set.
The next scavenge operation will scan the remembered set as roots, see
the poitner to a new object, move the object and updates the pointer in
the instruction stream.
=> We hit a SEGV trying to write the RX memory
We can avoid this problem by ensuring to morph old+canonical instances
always into old space on ia32.
Change-Id: Id63eb6ee1735bbb661dbeceb1e8d0c6ac92225ae
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/133228
Auto-Submit: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This reverts commit 9eb531bde4.
Reason for revert: Bots are red. Some tests are failing.
https://ci.chromium.org/p/dart/builders/ci.sandbox/vm-kernel-precomp-obfuscate-linux-release-x64/6039https://ci.chromium.org/p/dart/builders/ci.sandbox/vm-kernel-precomp-android-release-arm_x64/957
Original change's description:
> [vm] Add support for real unboxed floating point fields in AOT
>
> Non-nullable floating point fields (double, Float32x4, Float64x2)
> are fully unboxed in their classes.
>
> A bitmap for each class was added to the shared class table in order to keep
> track of the pointers of the classes. Since all classes in Flutter Gallery
> have less than 64 fields, the bitmap is represented by a 64 bit integer and
> fields whose offset is more than 64 words are not unboxed.
>
> The instance sizes and field offsets might change between target and host
> in cross-compilation, since the number of words used to store unboxed fields
> may differ.
>
> dart-aot Xeon
>
> SplayLatency -4.62%
> SplayHarderLatency -4.17%
> NavierStokes -2.20%
> Tracer 8.72%
> ParticleSystemPaint 2.90%
> NBodySIMD 8.35%
> NBody 25.59%
>
> With hack TFA to make doubles in Rect/Offset/Size classes in flutter non-nullable:
>
> flutter arm-v8:
>
> gallery total size: -1%
>
> matrix_utils_transform_rect_perspective -16.70% (less is better)
> matrix_utils_transform_rect_affine -31.82% (less is better)
> matrix_utils_transform_point_perspective -24.90% (less is better)
> matrix_utils_transform_point_affine) -27.26% (less is better)
> rrect_contains_bench -4.719% (less is better)
>
> Change-Id: I9ae09c9c3167d99f9efd071a92937aa51093fd1d
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131824
> Commit-Queue: Victor Agnez Lima <victoragnez@google.com>
> Reviewed-by: Martin Kustermann <kustermann@google.com>
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Reviewed-by: Samir Jindel <sjindel@google.com>
TBR=kustermann@google.com,rmacnak@google.com,sjindel@google.com,victoragnez@google.com
Change-Id: Ic73858f6adb7f55c4129d4f46ff4731b378cb634
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134020
Reviewed-by: Zichang Guo <zichangguo@google.com>
Commit-Queue: Zichang Guo <zichangguo@google.com>
Non-nullable floating point fields (double, Float32x4, Float64x2)
are fully unboxed in their classes.
A bitmap for each class was added to the shared class table in order to keep
track of the pointers of the classes. Since all classes in Flutter Gallery
have less than 64 fields, the bitmap is represented by a 64 bit integer and
fields whose offset is more than 64 words are not unboxed.
The instance sizes and field offsets might change between target and host
in cross-compilation, since the number of words used to store unboxed fields
may differ.
dart-aot Xeon
SplayLatency -4.62%
SplayHarderLatency -4.17%
NavierStokes -2.20%
Tracer 8.72%
ParticleSystemPaint 2.90%
NBodySIMD 8.35%
NBody 25.59%
With hack TFA to make doubles in Rect/Offset/Size classes in flutter non-nullable:
flutter arm-v8:
gallery total size: -1%
matrix_utils_transform_rect_perspective -16.70% (less is better)
matrix_utils_transform_rect_affine -31.82% (less is better)
matrix_utils_transform_point_perspective -24.90% (less is better)
matrix_utils_transform_point_affine) -27.26% (less is better)
rrect_contains_bench -4.719% (less is better)
Change-Id: I9ae09c9c3167d99f9efd071a92937aa51093fd1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/131824
Commit-Queue: Victor Agnez Lima <victoragnez@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Samir Jindel <sjindel@google.com>
After cl/128585 landed a app-jit failure started throwing an error:
>testee>err> kernel-service: Error: Unhandled exception:
>testee>err> Bad state: No element
>testee>err> #0 Iterable.first (dart:core/iterable.dart:520:7)
>testee>err> #1 MappedIterable.first (dart:_internal/iterable.dart:374:31)
>testee>err> #2 lookupOrBuildNewIncrementalCompiler (file:///b/s/w/ir/cache/builder/sdk/pkg/vm/bin/kernel_service.dart:400:45)
>testee>err> #3 _processLoadRequest (file:///b/s/w/ir/cache/builder/sdk/pkg/vm/bin/kernel_service.dart:679:22)
>testee>err> #4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)
>testee>err>
This is caused by the change ultimately adding scripts for the used
mixed in files in "LoadedScripts".
In this case "dart:collection/map.dart" (as I recall) was added to it.
When reloading the source, it runs through all libraries that
"isn't dart scheme", and for all of those libraries through all scripts
reported by LoadedScripts for that library (now including something from
the platform), and checking if it has been modified. Checking if it is
modified only works for file:/// uris through, which platform files aren't,
so it just reports true (i.e. the file is modified).
This is then passed to the kernel service which - based on the list of
one file modified - somehow concludes that it has a compiler already
(which it doesn't) and then crashes.
This CL fixes this specific issue by also skipping scripts that are
"dart scheme".
There's still an underlying issue that has nothing to do with cl/128585
though. I'll comment on that in the bug
(https://github.com/dart-lang/sdk/issues/39869).
Bug: 39869
Change-Id: I1a3f2de888ec53c40f4b6b46a369595abae5bb44
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/130374
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
This does not yet include checking of required named parameters.
Also, inlined type testing code, type testing stubs, and type propagation are
not yet updated.
Change-Id: If6d8996635474e349d28b7fd207d724838d08881
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/129922
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
When creating a KernelProgramInfo, we create several logical views into the kernel buffer. These are fresh ExternalTypedDatas, rather than proper TypedDataViews, so they do not automically keep the original ExternalTypedData alive. Create an explicit reference to the orginal ExternalTypedData in the KernelProgramInfo. When creating snapshots, this reference is ignored/null'd and the views are turned into copies, effectively dropping the parts of the original buffer that do not have views.
Fixes a leak with reload and a use-after-free with eval.
Bug: https://github.com/dart-lang/sdk/issues/33973
Bug: https://github.com/dart-lang/sdk/issues/39610
Change-Id: I09d3830133314ccbaa0341d904127c2b6925c4ec
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/126825
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Runtime functions do not yet implement the NNBD semantics.
In a next change, generated code will pass NNBD mode to the runtime.
Declare one new VM flags: --strong-non-nullable-type-checks
Change-Id: I050a468e4a6b665ce46c345bafba04a947cf7cb0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124105
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Similar to the split of ClassTable into ClassTable/SharedClassTable,
this CL splits up the IsolateReloadContext into:
* IsolateGroupReloadContext: Consists of reload-related information across all
isolates. The [Reload()] method is split up in phases that are
performed on all isolates before the next phase is started.
=> This allows each isolate to add reasons for rolling back, if no
reasons are found the reload will be accepted atomically.
* IsolateReloadContext: Constists of reload-related information for a
particular isolate (e.g. mappings of old to new classes)
The assumption is that all isolates have the same source (and therefore
the same libraries). For certain things, e.g. discovering which libraries
changed, it is necessary to examine the object store. We use the first
isolate in a group (but could use any of them) to do so, since the
isolate group does not have this information atm.
This is a preparation CL for supporting hot-reloading multiple isolates
within one isolate group. Though the support in this CL stays at having
only a single isolate in a group.
=> This CL turns off FLAG_enable_isolate_groups in JIT mode.
Issue https://github.com/dart-lang/sdk/issues/36097
Change-Id: I7f4d536d4f5ab4a2a73fb0c7618ba967c9b77234
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123254
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
This function has been unused for a long time.
Also, computing fingerprints for bytecode function causes bytecode to be loaded, which can lead to an old enum class being initialized with enum values for the new enum class, causing a forwarding loop when migrating enum values.
Change-Id: Iad9b2bb11aaf161d66f7752cb99cc4b4fc9fb310
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116442
Reviewed-by: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Since Dart 2, library prefixes are always loaded eagerly on the VM.
Also remove reload check for deferred prefixes. This check was added to avoid behavior of library prefixes giving spurious "not loaded" errors after a reload, but now this error can never occur. Resolves a difference in reload between AST and bytecode modes, since bytecode mode isn't surfacing the deferred property.
Change-Id: Ide5fb6cac2efc90ca1b108a35bc09d342cbd60de
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116051
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Until now it was possible to register classes with a default size (16 bytes on 64-bit)
and later on change the size for the cid.
This CL changes this to ensure the size information in the class table
for a given cid is either 0 or the final instance size (and adds an
ASSERT for it)
Issue https://github.com/dart-lang/sdk/issues/36097
Change-Id: I94c61c6a1566c13dec7b9eb80c9ae0dadf0e6b6a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/115861
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
The original revision is in patchset 1.
Three bugs are fixed:
1. Fix SIMARM_X64 build: no need to generate trampolines for AOT or simulated JIT.
2. Hot-reload: Fix hot-reload: don't invalidate Code for force-optimized functions.
3. Windows: Provide shadow space to runtime routines.
Cq-Include-Trybots: luci.dart.try:vm-kernel-precomp-mac-debug-simarm_x64-try,vm-kernel-precomp-mac-release-simarm_x64-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-kernel-win-debug-x64-try,vm-kernel-win-release-ia32-try,vm-kernel-win-release-x64-try
Change-Id: I326009cfacb51a84e9de4ddf9ff2d6d415460f91
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/113829
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Hot-reload used to be implemented by calling a tag handler (i.e. embedder),
which would load new Dart source code and call recursively into hot reloading
code.
=> This no longer happens, since the hot reload implementation obtains and
loads the kernel diffs itself in isolate_reload.cc.
With the switch to the Dart 2.0 CFE the VM no longer supports any kind of
deferred library loading. The CFE provides the VM with all sources together.
This CL cleans up some unnecessary code and outdated comments.
Issue https://github.com/dart-lang/sdk/issues/36097
Change-Id: I63aaa6e2a1910165185172a9dd975c3974ac5dd5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/110720
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
- Hook up allocation tracing.
- Fix off-by-one when mapping bcpcs to token positions.
- Fix missing class loading for predefined classes in Dart_Invoke type checks.
Change-Id: Id9177001d493725a0545c5948b7f7c6b4693027c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107564
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Instead, we set a bit on force-optimized code so that deoptimization knows to skip it and exception
handling will execute catch-entry moves.
In addition we don't throw away deoptimization info for these codes, so that exception handling
works.
Bug: dartbug.com/37311
Change-Id: If5bcff41a98c0053c581648a9ee22b3302ceea04
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106740
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
dart-bytecode, arm64: +4.742% geomean
dart-bytecode-jit-unopt, arm64: +12.73% geomean
dart2js-compile, x64: +3.635% geomean
In the polymorphic and unlinked cases, call to a stub the does a linear scan against an ICData.
In the monomorphic case, call to a prologue of the expected target function that checks the expected receiver class. There is additional indirection in the JIT version compared to the AOT version to also tick a usage counter so the inliner can make good decisions.
In the megamorphic case, call to a stub that does a hash table lookup against a MegamorphicCache.
Megamorphic call sites face a loss of precision in usage counts. The call site count is not recorded and the usage counter of the target function is used as an approximation.
Monomorphic and megamorphic calls sites are reset to the polymorphic/unlinked state on hot reload.
Monomorphic and megamorphic calls sites do not check the stepping state, so they are reset to the polymorphic/unlinked state when stepping begins and disabled.
Back-edges now increment the usage counter in addition to checking it. This ensures function with loops containing monomorphic calls will eventually cross the optimization threshold.
Fixed backwards use of kMonomorphicEntryOffset and kPolymorphicEntryOffset.
Fixed C stack overflow when bouncing between the KBC interpreter and a simulator.
Bug: https://github.com/dart-lang/sdk/issues/26780
Bug: https://github.com/dart-lang/sdk/issues/36409
Bug: https://github.com/dart-lang/sdk/issues/36731
Change-Id: I78a49cccd962703a459288e71ce246ed845df474
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102820
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
A number of places in the code will free `Dart_KernelCompilationResult.kernel`
if non-nullptr even if `Dart_KernelCompilationResult.error` is non-nullptr.
Normally the two are mutually exclusive (either `error` is set or `kernel` is set) but
some code frees both if they are non-null.
This issue has caused free() of uninitialized memory which in return
caused some of our benchmarks to break.
Change-Id: Ie65a0c51a32a3616361f3852c13c859e560285b5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104563
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This reverts commit ab6aeaa106.
Revert "[vm/compiler] Speed up the compiler part which deals with kernel reading up in DEBUG mode"
This reverts commit b316210d94.
Reason for revert: regression of snapshot sizes (DNO-599).
Original change's description:
> [vm/kernel] Use GC-tracked ExternalTypedData/TypedDataView for kernel buffers
>
> Until now we often leaked kernel buffers (e.g. hot reload buffers) because various
> objects were referencing ExternalTypedData objects pointing into the middle of
> c-allocated memory. This made it impossible for the GC to determine when the last
> reference is gone.
>
> This CL ensures that the actual buffers are *always* made available via
> ExternalTypedData and any inner pointers into it are created via TypedDataViews.
>
> The embedder guarantees to the free kernel buffers it has provided to:
> - Dart_CreateIsolateFromKernel
> - Dart_LoadScriptFromKernel
> - Dart_LoadLibraryFromKernel
> - Dart_SetDartLibrarySourcesKernel
> on isolate shutdown.
>
> All other kernel buffers will get a finalizer attached, which ensures the
> kernel buffers get freed by the GC once they are no longer referenced:
> - Kernel blobs for expression evaluation
> - Kernel blobs for Hot-Reload
> - Kernel blobs for cc tests
>
> Fixes https://github.com/dart-lang/sdk/issues/33973
> Fixes https://github.com/dart-lang/sdk/issues/36857
> Issue https://github.com/dart-lang/sdk/issues/37030
>
> Change-Id: I1cc410c94c0f4b229413e793728a261afcb10aaf
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103130
> Reviewed-by: Ryan Macnak <rmacnak@google.com>
> Commit-Queue: Martin Kustermann <kustermann@google.com>
TBR=kustermann@google.com,rmacnak@google.com
# Not skipping CQ checks because original CL landed > 1 day ago.
Change-Id: I49715d2400f4a5c8806b7d6a2912b7258f671a0a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104343
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Auto-Submit: Alexander Markov <alexmarkov@google.com>
Until now we often leaked kernel buffers (e.g. hot reload buffers) because various
objects were referencing ExternalTypedData objects pointing into the middle of
c-allocated memory. This made it impossible for the GC to determine when the last
reference is gone.
This CL ensures that the actual buffers are *always* made available via
ExternalTypedData and any inner pointers into it are created via TypedDataViews.
The embedder guarantees to the free kernel buffers it has provided to:
- Dart_CreateIsolateFromKernel
- Dart_LoadScriptFromKernel
- Dart_LoadLibraryFromKernel
- Dart_SetDartLibrarySourcesKernel
on isolate shutdown.
All other kernel buffers will get a finalizer attached, which ensures the
kernel buffers get freed by the GC once they are no longer referenced:
- Kernel blobs for expression evaluation
- Kernel blobs for Hot-Reload
- Kernel blobs for cc tests
Fixes https://github.com/dart-lang/sdk/issues/33973
Fixes https://github.com/dart-lang/sdk/issues/36857
Issue https://github.com/dart-lang/sdk/issues/37030
Change-Id: I1cc410c94c0f4b229413e793728a261afcb10aaf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103130
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Previously, IsolateReloadContext::CheckpointClasses() was filling
local_saved_class_table with raw pointers to classes and at the same
time it was adding classes to the set which is based on Array.
After that local_saved_class_table was saved into saved_class_table_
were it becomes visible to GC. However, adding an element to the set
may trigger GC in the middle and pointers for already processed
classes in local_saved_class_table are not visited and are not updated
if GC moves a Class object.
Also, IsolateReloadContext::VisitObjectPointers was not visiting
classes in saved_class_table_ properly as it was interpreting it as
an array of saved_num_cids_ pointers (while it is an array of
ClassAndSize elements).
This change fixes flaky crashes in vm/dart/trigger_gc_in_native_test.
Change-Id: I25b2e50995cf580bc9347f06875d344f75a1a950
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100728
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
When mixin application is transformed, mixin is added to the end of
interfaces list. This may cause assertion failure if the same class
is used as mixin and mentioned among implemented interfaces.
This CL relaxes that assertion for mixins as this duplication is very
rare and seems harmless.
Note that it would not be valid to remove duplicated interface/mixin
from the list of interfaces, as it would break dart:mirrors in this case:
dart:mirrors needs to reflect duplicated interface/mixin both in
ClassMirror.superinterfaces (which is populated from the
list of interfaces without last element) and ClassMirror.mixin (the last
element in the list of interfaces).
Fixes https://github.com/dart-lang/sdk/issues/35854
Change-Id: I9b986f7759a76cb6f963ebd837e9ea26f6242f8e
Reviewed-on: https://dart-review.googlesource.com/c/91961
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Turn off concurrent marking during a reload as new instances could be allocated when loading a kernel file and this results in inconsistencies for the marking thread which is traversing the heap and expects to find instances only from the old set of classes.
(This change is related to Issue https://github.com/dart-lang/sdk/issues/35401)
Change-Id: I294b91d2cc5d86b860a77684457b0f963d2fe557
Reviewed-on: https://dart-review.googlesource.com/c/87707
Commit-Queue: Siva Annamalai <asiva@google.com>
Reviewed-by: Zach Anderson <zra@google.com>