Before this change, an error reaching an FFI callback would attempt to execute the normal invocation stub from the beginning in the FFI callback's frame, which quickly crashes. After this change, the runtime recognizes this marker use of the invocation stub and returns to the FFI callback function instead.
TEST=ffi/unwind
Bug: https://github.com/dart-lang/sdk/issues/39487
Change-Id: I477cfcfc236e6cf518ebfe52860ba49e466ebf8b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409562
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
We also disable the test in hotreload mode, because threads that are
stuck in ffi calls don't participate in hot-reload requests.
This is something we may want to look into. Right now it's conservative in
the sense that if an embedder did a `Dart_LookupClass()` in such a ffi call
and then a reload happened, such a handle would possibly hold pointer to
an old class object and weird things may happen.
This fixes test failures in hot reload / rollback mode of
`vm/dart/isolates/many_isolates_blocked_at_ffi_test`
and also make it more robust.
TEST=tests/ffi/dl_api_exit_enter_isolate_test
Change-Id: I823d07a6ab04e62e051e4d22ec80cbc3649762a3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409100
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Thread interrupts can be disabled after thread interrupter decided to
interrupt a thread but before thread actually receives and handles
a signal.
This may cause a data race accessing a Sample between profiler which
initializes and adds a Sample and the main thread using
Profiler::sample_block_buffer() in the profiler unit tests.
This is a possible fix for the following benign TSAN error:
WARNING: ThreadSanitizer: data race (pid=5824)
Write of size 4 at 0x7f53e8e53a98 by thread T6:
#0 dart::Sample::set_thread_task(dart::Thread::TaskKind) out/ReleaseTSANX64/../../runtime/vm/profiler.h:325:12 (run_vm_tests+0x292cab2)
#1 dart::SetupSample(dart::Thread*, bool, unsigned long) out/ReleaseTSANX64/../../runtime/vm/profiler.cc:1226:11 (run_vm_tests+0x292cab2)
#2 dart::Profiler::SampleThread(dart::Thread*, dart::InterruptedThreadState const&) out/ReleaseTSANX64/../../runtime/vm/profiler.cc:1381:7 (run_vm_tests+0x292cab2)
#3 dart::ThreadInterrupterLinux::ThreadInterruptSignalHandler(int, siginfo_t*, void*) out/ReleaseTSANX64/../../runtime/vm/thread_interrupter_linux.cc:44:5 (run_vm_tests+0x2a064b0)
#4 __tsan::CallUserSignalHandler(__tsan::ThreadState*, bool, bool, int, __sanitizer::__sanitizer_siginfo*, void*) ../staging/llvm_build/tools/clang/stage2-bins/runtimes/runtimes-x86_64-unknown-linux-gnu-bins/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp:2122:5 (run_vm_tests+0x1fc057f)
#5 dart::Mutex::Unlock() out/ReleaseTSANX64/../../runtime/vm/os_thread_linux.cc:367:16 (run_vm_tests+0x2924b89)
Previous read of size 4 at 0x7f53e8e53a98 by main thread:
#0 dart::Sample::head_sample() const out/ReleaseTSANX64/../../runtime/vm/profiler.h:350:59 (run_vm_tests+0x22c70e7)
#1 dart::SampleBuffer::VisitSamples(dart::SampleVisitor*) out/ReleaseTSANX64/../../runtime/vm/profiler.h:589:20 (run_vm_tests+0x22c70e7)
#2 dart::SampleBlockBuffer::VisitSamples(dart::SampleVisitor*) out/ReleaseTSANX64/../../runtime/vm/profiler.h:752:18 (run_vm_tests+0x22c70e7)
#3 dart::Dart_TestHelperProfiler_GetSourceReport(dart::Thread*) out/ReleaseTSANX64/../../runtime/vm/profiler_test.cc:2241:26 (run_vm_tests+0x22c70e7)
#4 dart::Dart_TestProfiler_GetSourceReport() out/ReleaseTSANX64/../../runtime/vm/profiler_test.cc:2206:1 (run_vm_tests+0x22c70e7)
#5 dart::TestCase::Run() out/ReleaseTSANX64/../../runtime/bin/run_vm_tests.cc:53:3 (run_vm_tests+0x203fe6c)
--- Re-run this test:
python3 tools/test.py -n vm-tsan-linux-release-x64 vm/cc/Profiler_GetSourceReport
Also, this change is a possible fix for https://github.com/dart-lang/sdk/issues/44089.
TEST=ci
Change-Id: I8ad87c340580325cbd6e22b5f068e1e33b0a7d46
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409460
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
A method extractor (implementing get:foo for a method foo()) can be
used as an interface target. If original method can be overridden in
a dynamic module, then method extractor can be overridden too.
So IsDynamicallyOverridden flag should be copied when creating
a method extractor.
TEST=dynamic_modules_suite/tearoff_no_concrete_impl
Fixes https://github.com/dart-lang/sdk/issues/60116
Change-Id: I54439426645b3daf308d18da6e0d06602b9ec30f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409360
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
There is no need to manually add a move on entry to the try.
TEST=ci
Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-linux-product-x64-try,vm-aot-linux-release-arm64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-release-x64-try,vm-aot-mac-product-arm64-try,vm-aot-mac-release-arm64-try
Change-Id: I3720b1dd5b089b22d68d87274f1e3a7814bbd0e5
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/409141
Commit-Queue: Slava Egorov <vegorov@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
AOT compiling the isolate stress test spends the majority of its time canonicalizing function types.
In release mode, 2:45 -> 1:43 (-38%)
TEST=ci
Change-Id: I58ef0157612930d582784c7bea7de177c1219a0d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/408941
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
According to the spec, a call in the form
e(a0,...,aN)
where static type of 'e' is 'dynamic' should succeed only if
(1) 'e' evaluates to a function, or
(2) runtime type of 'e' has a 'call' *method*.
If runtime type of 'e' has a 'call' getter this invocation should
fail with NSM.
This behavior is different from 'e.call(a0,...,aN)' which accepts
'call' getters.
---
In order to implement this behavior in the VM, a special
'dyn:implicit:call' selector is added. It behaves similarly to
'dyn:call' except when looking for a getter target.
This selector is used when CFE sets FlagImplicitCall on a
DynamicInvocation node.
TEST=co19/Language/Expressions/Function_Invocation/Function_Expression_Invocation/call_A04_t01
TEST=co19/Language/Expressions/Function_Invocation/Function_Expression_Invocation/call_A04_t02
Fixes https://github.com/dart-lang/sdk/issues/59965
Issue https://github.com/dart-lang/sdk/issues/59952
Issue https://github.com/dart-lang/sdk/issues/51517
Issue https://github.com/dart-lang/language/issues/3482
Change-Id: Ic45f7743ad75571476642dcec9c91e6a77e8e321
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407161
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
StreamingFlowGraphBuilder::BuildMethodInvocation handles both
DynamicInvocation and InstanceInvocation nodes.
BuildMethodInvocation has been reading flags field and testing
kInstanceInvocationFlagInvariant bit without checking if node is
InstanceInvocation, so it has been handling an unrelated
DynamicInvocation flag (FlagImplicitCall) as FlagInvariant.
TEST=ci
Change-Id: I1e2ecbb0023bfdb393fc24c29cc8263e05198277
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407081
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
All callbacks flow via the single runtime entry, so we add a check
there.
TEST=tests/ffi/function_callbacks_leaf_test.dart
(The transition verification in
https://dart-review.googlesource.com/c/sdk/+/407021
doesn't catch the error. The callbacks already transitioned before
reaching the generated code in that CL.)
Closes: https://github.com/dart-lang/sdk/issues/60021
Change-Id: Ia6bf8af186c9c8c5a66cffa537d80421467330d4
Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407023
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Liam Appelbe <liama@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
The transitions in C++ have asserts which run in debug mode to validate
that the transitions are happening in the right order.
The transitions in the generated code had no equivalent. This CL
introduces the equivalent for the generated code.
The transitions to generated code are misnamed currently. They come
from either VM or Native, not only Native. For example the FFI
callbacks and call return sequence can already transition from native
into the VM when entering the safepoint.
These checks don't catch the invalid FFI callbacks.
Bug: https://github.com/dart-lang/sdk/issues/60021
These are caught when looking up the FFI metadata:
https://dart-review.googlesource.com/c/sdk/+/407023
This CL omits to implement the check in ia32, since ia32 is scheduled
for removal.
The check is only enabled in debug mode. The asserts for the C++ are
also only run in debug mode. And enabling this check in release for
AOT regresses the parent CL benchmark by ~30% on Mac Arm64.
TEST=test/ffi
Change-Id: Ieaf1e43533baae294af37b2e171b68bd314fdbe0
Cq-Include-Trybots: dart/try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-appjit-linux-debug-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-linux-debug-ia32-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-reload-linux-debug-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-arm64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407021
Reviewed-by: Liam Appelbe <liama@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
Adds shared libraries for embedding Dart VM and new API (runtime/engine/include/dart_engine.h).
TEST=tests/standalone/embedder_samples_test.dart
Cq-Include-Trybots: luci.dart.try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv32-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-linux-product-x64-try,vm-aot-linux-release-arm64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-release-x64-try,vm-aot-mac-product-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-aot-win-product-x64-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-appjit-linux-product-x64-try,vm-appjit-linux-release-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-android-product-arm-try,vm-ffi-android-product-arm64c-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-gcc-linux-try,vm-linux-debug-ia32-try,vm-linux-debug-simriscv32-try,vm-linux-debug-simriscv64-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-linux-release-arm64-try,vm-linux-release-ia32-try,vm-linux-release-simarm-try,vm-linux-release-x64-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-mac-release-arm64-try,vm-mac-release-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-msvc-windows-try,vm-reload-linux-debug-x64-try,vm-reload-linux-release-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-reload-rollback-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try,vm-win-release-x64-try
Change-Id: Ia4e4d1b871ddef515cfb2f4639bdaa9fe3676936
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402860
Reviewed-by: Slava Egorov <vegorov@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
Change core library '_throwNew' helpers to return Never.
Since https://dart-review.googlesource.com/c/sdk/+/405604 flow graph
builder automatically appends Stop and closes fragment after appending
a call with return type Never. So additional 'throw' after calling
'_throwNew' are no longer needed and can be removed.
Also, cleanup unused FlowGraphBuilder::ThrowTypeError() and
replace 'throw' with shorter Stop at the end of 'case'.
TEST=ci
Change-Id: I0a5109fd693c0463c53e70e5dd261f8080bf1132
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/406080
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
It is safe for multiple isolates to set their values concurrently. Only growth of the field tables needs to be excluded by the write lock.
A storm of such setters are invoked as part of core library initialization during isolate spawning.
TEST=ci, tsan
Change-Id: I4677e33ee80bb06d261ada2ae9d6b71c94fde611
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405400
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
Convert range boundaries with symbolic references to other instructions
into constant boundaries to avoid serializing extra references.
Specially handle serialization of arguments descriptors which are
cached in the VM isolate.
TEST=ci
Change-Id: Ifab3bb4d9b037aaefd81d38dd93d47cd4b42cf1d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405571
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
For entry-point pragma annotations, most of the time they are
used with either no argument or with an argument that evaluates
to either
* false to denote the annotation should not take effect, or
* null or true to denote the annotation should take effect.
However, the user can also specify that only part of the operations
on a member should be accessed from native code by using a string
argument that is either 'call', 'set', or 'get'.
The entry point verification in Invoke/InvokeGetter/InvokeSetter
assumes that for getters and setters, the only valid string argument
is 'get' or 'set', respectively. This is because those methods are
called via `Dart_GetField`[0] and `Dart_SetField`, respectively, as if
they were the getter or setter of a defined field.
However, the precompiler previously assumed that the string
argument 'call' was the only string argument that meant the link
to a function's code object should be saved. Similarly, it assumed the
string argument 'get' for functions meant that their implicit closure
function should be saved, which ends up including getters. Furthermore,
it did not do anything with setters annotated with the string argument
'set'. This means that the code link would not be saved for getters or
setters that were annotated with the string argument expected by the
entry point verifier.
This CL aligns the precompiler to match the expectations of other
parts of the codebase. It also changes TFA to report an error
if a getter or setter is marked with the string argument 'call'.
[0] `Dart_Invoke` can be called with the name of a getter that
returns a closure, but doing so is semantically equivalent to
calling `Dart_GetField` followed by `Dart_InvokeClosure`.
TEST=vm/dart/entrypoint_verification_test
Fixes: https://github.com/dart-lang/sdk/issues/59920
Change-Id: Ia2768bbaf9058bb14a1cdfb331eb85fa082a0e90
Cq-Include-Trybots: luci.dart.try:vm-aot-dwarf-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-product-x64-try,vm-aot-mac-product-arm64-try,vm-aot-obfuscate-linux-release-x64-try,vm-linux-debug-x64-try,vm-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404823
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
We have to place these classes into a platform namespace to avoid
conflicts with vm/lockers.h.
To avoid changes to files in bin we simply change bin/lockers.h
to introduce bin::{Monitor,Mutex}Locker aliases for
platform::{Monitor,Mutex}Locker.
TEST=ci
R=kustermann@google.com
Change-Id: I975f24a8499ea85d0ba887180a81287fcf4101bb
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405460
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
dart2js benchmarks use --print-metrics to track heap usage, with the
shift to running dart2js using aproduct mode AOT snapshot these
metrics are not printed resulting in errors from the benchmarks.
The size increase from turning on these isolate group metrics in product
mode is not very significant (about 2k).
TEST=ci
Change-Id: Ie9bbaecd57a2b45c27a158f6eb4988966a41ce67
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/405220
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
Use VmInteropHandler for invoking sub commands instead of running them
in an isolate. Running sub commands in an isolate causes an increased footprint.
Changing this to use VmInteropHandler avoids the additional memory footprint.
Commands that need to use an AOT runtime for execution now exec the AOT
runtime and run the command.
TEST=ci
Change-Id: Ic96845b19951170effea3dd3619f798e2c72968a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402781
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Siva Annamalai <asiva@google.com>
Consider the following situation: member M1 is overridden by another
member M2; M2 is overridden in a dynamic module.
Members which can be overridden in a dynamic module (such as M2)
should be specified as 'can-be-overridden' in the dynamic interface.
Members which are overridden implicitly/transitively (such as M1)
are not required to be mentioned in the dynamic interface.
However, when determining possible targets for a call with
interface target M1, compiler should treat it as potentially
overridden in a dynamic module.
This change adds such handling to the VM/AOT. Dynamic interface
annotator now marks members such as M1 with
'dyn-module:can-be-overridden-implicitly' pragma, and
VM/AOT takes both can-be-overridden and can-be-overridden-implicitly
into account.
This change also simplifies handling of implicitly extenable classes
in the VM/AOT - now VM handles both extendable and implicitly-extendable
pragmas (from dynamic interface annotator) instead of recalculating
implicitly extendable classes on its own.
TEST=pkg/vm/test/transformations/dynamic_interface_annotator_test.dart
TEST=dynamic_modules_suite/implicitly_extendable
Fixes https://github.com/dart-lang/sdk/issues/59880
Change-Id: Id4570cc86303f8e45a061d696e9bca0d0b2b4b81
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/403951
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Nate Biggs <natebiggs@google.com>