Forward the absence of the argument on AsyncFromSyncIterator prototype methods

https://github.com/tc39/ecma262/pull/1776 is a normative change that
reached consensus in the November 2019 TC39. It changes
%AsyncFromSyncIteratorPrototype% methods to forward the absence of
arguments to the underlying sync iterator. This is observable via
`arguments.length` inside the underlying sync iterator.

For example, .next is changed to, roughly:

```
%AsyncFromSyncIteratorPrototype%.next = function(value) {
  let res;
  if (arguments.length < 1) {
     res = [[SyncIteratorRecord]].[[Iterator]].next();
  } else {
     res = [[SyncIteratorRecord]].[[Iterator]].next(value);
  }
  // ...
};
```

Bug: v8:10395
Change-Id: Ib8127d08cd78b8d502e6510241f3f13fbbaba5c7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2247041
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68398}
This commit is contained in:
Shu-yu Guo 2020-06-15 21:24:55 -07:00 committed by Commit Bot
parent 401513217a
commit 44a655c8af
4 changed files with 70 additions and 37 deletions

View File

@ -16,6 +16,10 @@ namespace internal {
namespace {
class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
public:
// The 'next' and 'return' take an optional value parameter, and the 'throw'
// method take an optional reason parameter.
static const int kValueOrReasonArg = 0;
explicit AsyncFromSyncBuiltinsAssembler(compiler::CodeAssemblerState* state)
: AsyncBuiltinsAssembler(state) {}
@ -31,8 +35,8 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
using SyncIteratorNodeGenerator =
std::function<TNode<Object>(TNode<JSReceiver>)>;
void Generate_AsyncFromSyncIteratorMethod(
const TNode<Context> context, const TNode<Object> iterator,
const TNode<Object> sent_value,
CodeStubArguments* args, const TNode<Context> context,
const TNode<Object> iterator, const TNode<Object> sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
@ -40,9 +44,9 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
base::Optional<TNode<Object>> initial_exception_value = base::nullopt);
void Generate_AsyncFromSyncIteratorMethod(
const TNode<Context> context, const TNode<Object> iterator,
const TNode<Object> sent_value, Handle<String> name,
const UndefinedMethodHandler& if_method_undefined,
CodeStubArguments* args, const TNode<Context> context,
const TNode<Object> iterator, const TNode<Object> sent_value,
Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
const char* operation_name,
Label::Type reject_label_type = Label::kDeferred,
base::Optional<TNode<Object>> initial_exception_value = base::nullopt) {
@ -50,7 +54,7 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
return GetProperty(context, sync_iterator, name);
};
return Generate_AsyncFromSyncIteratorMethod(
context, iterator, sent_value, get_method, if_method_undefined,
args, context, iterator, sent_value, get_method, if_method_undefined,
operation_name, reject_label_type, initial_exception_value);
}
@ -97,8 +101,9 @@ void AsyncFromSyncBuiltinsAssembler::ThrowIfNotAsyncFromSyncIterator(
}
void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
const TNode<Context> context, const TNode<Object> iterator,
const TNode<Object> sent_value, const SyncIteratorNodeGenerator& get_method,
CodeStubArguments* args, const TNode<Context> context,
const TNode<Object> iterator, const TNode<Object> sent_value,
const SyncIteratorNodeGenerator& get_method,
const UndefinedMethodHandler& if_method_undefined,
const char* operation_name, Label::Type reject_label_type,
base::Optional<TNode<Object>> initial_exception_value) {
@ -128,16 +133,31 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
BIND(&if_isnotundefined);
}
TNode<Object> iter_result;
TVARIABLE(Object, iter_result);
{
Label has_sent_value(this), no_sent_value(this), merge(this);
ScopedExceptionHandler handler(this, &reject_promise, &var_exception);
iter_result = Call(context, method, sync_iterator, sent_value);
Branch(
IntPtrGreaterThan(args->GetLength(), IntPtrConstant(kValueOrReasonArg)),
&has_sent_value, &no_sent_value);
BIND(&has_sent_value);
{
iter_result = Call(context, method, sync_iterator, sent_value);
Goto(&merge);
}
BIND(&no_sent_value);
{
iter_result = Call(context, method, sync_iterator);
Goto(&merge);
}
BIND(&merge);
}
TNode<Object> value;
TNode<Oddball> done;
std::tie(value, done) = LoadIteratorResult(
context, native_context, iter_result, &reject_promise, &var_exception);
std::tie(value, done) =
LoadIteratorResult(context, native_context, iter_result.value(),
&reject_promise, &var_exception);
const TNode<JSFunction> promise_fun =
CAST(LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX));
@ -160,15 +180,16 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
// Perform ! PerformPromiseThen(valueWrapper,
// onFulfilled, undefined, promiseCapability).
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, value_wrapper,
on_fulfilled, UndefinedConstant(), promise));
args->PopAndReturn(CallBuiltin(Builtins::kPerformPromiseThen, context,
value_wrapper, on_fulfilled,
UndefinedConstant(), promise));
BIND(&reject_promise);
{
const TNode<Object> exception = var_exception.value();
CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
TrueConstant());
Return(promise);
args->PopAndReturn(promise);
}
}
@ -252,8 +273,12 @@ AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
// https://tc39.github.io/proposal-async-iteration/
// Section #sec-%asyncfromsynciteratorprototype%.next
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
const TNode<Object> iterator = CAST(Parameter(Descriptor::kReceiver));
const TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
CodeStubArguments args(this, argc);
const TNode<Object> iterator = args.GetReceiver();
const TNode<Object> value = args.GetOptionalArgumentValue(kValueOrReasonArg);
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
auto get_method = [=](const TNode<JSReceiver> unused) {
@ -261,7 +286,7 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
JSAsyncFromSyncIterator::kNextOffset);
};
Generate_AsyncFromSyncIteratorMethod(
context, iterator, value, get_method, UndefinedMethodHandler(),
&args, context, iterator, value, get_method, UndefinedMethodHandler(),
"[Async-from-Sync Iterator].prototype.next");
}
@ -269,11 +294,16 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
// Section #sec-%asyncfromsynciteratorprototype%.return
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
AsyncFromSyncBuiltinsAssembler) {
const TNode<Object> iterator = CAST(Parameter(Descriptor::kReceiver));
const TNode<Object> value = CAST(Parameter(Descriptor::kValue));
TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
CodeStubArguments args(this, argc);
const TNode<Object> iterator = args.GetReceiver();
const TNode<Object> value = args.GetOptionalArgumentValue(kValueOrReasonArg);
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
auto if_return_undefined = [=](const TNode<NativeContext> native_context,
auto if_return_undefined = [=, &args](
const TNode<NativeContext> native_context,
const TNode<JSPromise> promise,
Label* if_exception) {
// If return is undefined, then
@ -285,20 +315,24 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
// IfAbruptRejectPromise(nextDone, promiseCapability).
// Return promiseCapability.[[Promise]].
CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
Return(promise);
args.PopAndReturn(promise);
};
Generate_AsyncFromSyncIteratorMethod(
context, iterator, value, factory()->return_string(), if_return_undefined,
"[Async-from-Sync Iterator].prototype.return");
&args, context, iterator, value, factory()->return_string(),
if_return_undefined, "[Async-from-Sync Iterator].prototype.return");
}
// https://tc39.github.io/proposal-async-iteration/
// Section #sec-%asyncfromsynciteratorprototype%.throw
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
AsyncFromSyncBuiltinsAssembler) {
const TNode<Object> iterator = CAST(Parameter(Descriptor::kReceiver));
const TNode<Object> reason = CAST(Parameter(Descriptor::kReason));
TNode<IntPtrT> argc = ChangeInt32ToIntPtr(
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount)));
CodeStubArguments args(this, argc);
const TNode<Object> iterator = args.GetReceiver();
const TNode<Object> reason = args.GetOptionalArgumentValue(kValueOrReasonArg);
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
auto if_throw_undefined = [=](const TNode<NativeContext> native_context,
@ -306,9 +340,9 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
Label* if_exception) { Goto(if_exception); };
Generate_AsyncFromSyncIteratorMethod(
context, iterator, reason, factory()->throw_string(), if_throw_undefined,
"[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
reason);
&args, context, iterator, reason, factory()->throw_string(),
if_throw_undefined, "[Async-from-Sync Iterator].prototype.throw",
Label::kNonDeferred, reason);
}
} // namespace internal

View File

@ -877,11 +877,11 @@ namespace internal {
/* %AsyncFromSyncIteratorPrototype% */ \
/* See tc39.github.io/proposal-async-iteration/ */ \
/* #sec-%asyncfromsynciteratorprototype%-object) */ \
TFJ(AsyncFromSyncIteratorPrototypeNext, 1, kReceiver, kValue) \
TFJ(AsyncFromSyncIteratorPrototypeNext, kDontAdaptArgumentsSentinel) \
/* #sec-%asyncfromsynciteratorprototype%.throw */ \
TFJ(AsyncFromSyncIteratorPrototypeThrow, 1, kReceiver, kReason) \
TFJ(AsyncFromSyncIteratorPrototypeThrow, kDontAdaptArgumentsSentinel) \
/* #sec-%asyncfromsynciteratorprototype%.return */ \
TFJ(AsyncFromSyncIteratorPrototypeReturn, 1, kReceiver, kValue) \
TFJ(AsyncFromSyncIteratorPrototypeReturn, kDontAdaptArgumentsSentinel) \
/* #sec-async-iterator-value-unwrap-functions */ \
TFJ(AsyncIteratorValueUnwrap, 1, kReceiver, kValue) \
\

View File

@ -918,13 +918,14 @@ void Genesis::CreateAsyncIteratorMaps(Handle<JSFunction> empty) {
Handle<JSObject> async_from_sync_iterator_prototype = factory()->NewJSObject(
isolate()->object_function(), AllocationType::kOld);
SimpleInstallFunction(isolate(), async_from_sync_iterator_prototype, "next",
Builtins::kAsyncFromSyncIteratorPrototypeNext, 1, true);
Builtins::kAsyncFromSyncIteratorPrototypeNext, 1,
false);
SimpleInstallFunction(isolate(), async_from_sync_iterator_prototype, "return",
Builtins::kAsyncFromSyncIteratorPrototypeReturn, 1,
true);
false);
SimpleInstallFunction(isolate(), async_from_sync_iterator_prototype, "throw",
Builtins::kAsyncFromSyncIteratorPrototypeThrow, 1,
true);
false);
InstallToStringTag(isolate(), async_from_sync_iterator_prototype,
"Async-from-Sync Iterator");

View File

@ -535,8 +535,6 @@
'built-ins/RegExp/prototype/Symbol.replace/fn-invoke-args-empty-result': [FAIL],
# https://bugs.chromium.org/p/v8/issues/detail?id=10395
'built-ins/AsyncFromSyncIteratorPrototype/next/absent-value-not-passed': [FAIL],
'built-ins/AsyncFromSyncIteratorPrototype/return/absent-value-not-passed': [FAIL],
'built-ins/AsyncFromSyncIteratorPrototype/return/return-null': [FAIL],
'built-ins/AsyncFromSyncIteratorPrototype/throw/throw-null': [FAIL],