[await] Update async iterators to return a rejected promise on error

This implements the behavior discussed and specified here:
https://github.com/tc39/ecma262/issues/1461
https://github.com/tc39/ecma262/pull/1470

As part of making this change, I realized that we didn't actually
toggle the behavior between the optimized and unoptimized version
based on the --harmony-await-optimization flag at all and just the
unoptimized version by default.

This patch removes the unoptimized version and uses the optimized
version as the default.

The other builtins that use this flag are not touched as part of this
CL, they will be updated separately.

Bug: v8:8998
Change-Id: I315e1b39dda91d0127b5e567986485d713eaa78d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1525872
Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60310}
This commit is contained in:
Sathya Gunasekaran 2019-03-18 10:32:39 -07:00 committed by Commit Bot
parent efa249a898
commit 1cb05f1ff4
2 changed files with 35 additions and 89 deletions

View File

@ -36,13 +36,6 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr);
void Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr);
void Generate_AsyncFromSyncIteratorMethod(
Node* const context, Node* const iterator, Node* const sent_value,
@ -57,19 +50,6 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
context, iterator, sent_value, get_method, if_method_undefined,
operation_name, reject_label_type, initial_exception_value);
}
void Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
Node* const initial_exception_value = nullptr) {
auto get_method = [=](Node* const sync_iterator) {
return GetProperty(context, sync_iterator, name);
};
return Generate_AsyncFromSyncIteratorMethodOptimized(
context, iterator, sent_value, get_method, if_method_undefined,
operation_name, reject_label_type, initial_exception_value);
}
// Load "value" and "done" from an iterator result object. If an exception
// is thrown at any point, jumps to te `if_exception` label with exception
@ -148,71 +128,6 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
method, sync_iterator, sent_value);
GotoIfException(iter_result, &reject_promise, &var_exception);
Node* value;
Node* done;
std::tie(value, done) = LoadIteratorResult(
context, native_context, iter_result, &reject_promise, &var_exception);
Node* const wrapper = AllocateAndInitJSPromise(context);
// Perform ! Call(valueWrapperCapability.[[Resolve]], undefined, «
// throwValue »).
CallBuiltin(Builtins::kResolvePromise, context, wrapper, value);
// Let onFulfilled be a new built-in function object as defined in
// Async Iterator Value Unwrap Functions.
// Set onFulfilled.[[Done]] to throwDone.
Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
// Perform ! PerformPromiseThen(valueWrapperCapability.[[Promise]],
// onFulfilled, undefined, promiseCapability).
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, wrapper,
on_fulfilled, UndefinedConstant(), promise));
BIND(&reject_promise);
{
Node* const exception = var_exception.value();
CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
TrueConstant());
Return(promise);
}
}
void AsyncFromSyncBuiltinsAssembler::
Generate_AsyncFromSyncIteratorMethodOptimized(
Node* const context, Node* const iterator, Node* const sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name, Label::Type reject_label_type,
Node* const initial_exception_value) {
Node* const native_context = LoadNativeContext(context);
Node* const promise = AllocateAndInitJSPromise(context);
VARIABLE(var_exception, MachineRepresentation::kTagged,
initial_exception_value == nullptr ? UndefinedConstant()
: initial_exception_value);
Label reject_promise(this, reject_label_type);
ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
&var_exception, operation_name);
Node* const sync_iterator =
LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
Node* const method = get_method(sync_iterator);
if (if_method_undefined) {
Label if_isnotundefined(this);
GotoIfNot(IsUndefined(method), &if_isnotundefined);
if_method_undefined(native_context, promise, &reject_promise);
BIND(&if_isnotundefined);
}
Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
method, sync_iterator, sent_value);
GotoIfException(iter_result, &reject_promise, &var_exception);
Node* value;
Node* done;
std::tie(value, done) = LoadIteratorResult(
@ -222,9 +137,11 @@ void AsyncFromSyncBuiltinsAssembler::
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
CSA_ASSERT(this, IsConstructor(promise_fun));
// Let valueWrapper be ? PromiseResolve(« value »).
Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve,
native_context, promise_fun, value);
// Let valueWrapper be PromiseResolve(%Promise%, « value »).
Node* const value_wrapper = CallBuiltin(Builtins::kPromiseResolve,
native_context, promise_fun, value);
// IfAbruptRejectPromise(valueWrapper, promiseCapability).
GotoIfException(value_wrapper, &reject_promise, &var_exception);
// Let onFulfilled be a new built-in function object as defined in
// Async Iterator Value Unwrap Functions.
@ -233,7 +150,7 @@ void AsyncFromSyncBuiltinsAssembler::
// Perform ! PerformPromiseThen(valueWrapper,
// onFulfilled, undefined, promiseCapability).
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper,
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, value_wrapper,
on_fulfilled, UndefinedConstant(), promise));
BIND(&reject_promise);

View File

@ -0,0 +1,29 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --allow-natives-syntax
let actual = [];
async function f() {
var p = Promise.resolve(0);
Object.defineProperty(p, "constructor", {
get() {
throw new Error();
}
});
actual.push("start");
for await (var x of [p]);
actual.push("never reached");
}
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
f().catch(() => actual.push("catch"));
%PerformMicrotaskCheckpoint();
assertSame(["start", "tick 1", "tick 2", "catch"].join(), actual.join());