Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "vm/AsyncIteration.h"
#include "builtin/Promise.h" // js::PromiseHandler, js::CreatePromiseObjectForAsyncGenerator, js::AsyncFromSyncIteratorMethod, js::ResolvePromiseInternal, js::RejectPromiseInternal, js::InternalAsyncGeneratorAwait
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h"
#include "vm/CompletionKind.h"
#include "vm/FunctionFlags.h" // js::FunctionFlags
#include "vm/GeneratorObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/PlainObject.h" // js::PlainObject
#include "vm/PromiseObject.h" // js::PromiseObject
#include "vm/Realm.h"
#include "vm/SelfHosting.h"
#include "vm/JSObject-inl.h"
#include "vm/List-inl.h"
using namespace js;
// ---------------
// Async generator
// ---------------
const JSClass AsyncGeneratorObject::class_ = {
"AsyncGenerator",
JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots),
&classOps_,
};
const JSClassOps AsyncGeneratorObject::classOps_ = {
nullptr, // addProperty
nullptr, // delProperty
nullptr, // enumerate
nullptr, // newEnumerate
nullptr, // resolve
nullptr, // mayResolve
nullptr, // finalize
nullptr, // call
nullptr, // construct
CallTraceMethod<AbstractGeneratorObject>, // trace
};
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto
// [ , internalSlotsList ] )
//
// specialized for AsyncGeneratorObjects.
static AsyncGeneratorObject* OrdinaryCreateFromConstructorAsynGen(
JSContext* cx, HandleFunction constructor) {
// Step 1: Assert...
// (implicit)
// Step 2. Let proto be
// ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
RootedValue protoVal(cx);
if (!GetProperty(cx, constructor, constructor, cx->names().prototype,
&protoVal)) {
return nullptr;
}
RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
if (!proto) {
proto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, cx->global());
if (!proto) {
return nullptr;
}
}
// Step 3. Return ! OrdinaryObjectCreate(proto, internalSlotsList).
return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorStart ( generator, generatorBody )
//
// Steps 6-7.
/* static */
AsyncGeneratorObject* AsyncGeneratorObject::create(JSContext* cx,
HandleFunction asyncGen) {
MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator());
AsyncGeneratorObject* generator =
OrdinaryCreateFromConstructorAsynGen(cx, asyncGen);
if (!generator) {
return nullptr;
}
// Step 6. Set generator.[[AsyncGeneratorState]] to suspendedStart.
generator->setSuspendedStart();
// Step 7. Set generator.[[AsyncGeneratorQueue]] to a new empty List.
generator->clearSingleQueueRequest();
generator->clearCachedRequest();
return generator;
}
/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::createRequest(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
CompletionKind completionKind, HandleValue completionValue,
Handle<PromiseObject*> promise) {
if (!generator->hasCachedRequest()) {
return AsyncGeneratorRequest::create(cx, completionKind, completionValue,
promise);
}
AsyncGeneratorRequest* request = generator->takeCachedRequest();
request->init(completionKind, completionValue, promise);
return request;
}
/* static */ [[nodiscard]] bool AsyncGeneratorObject::enqueueRequest(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
Handle<AsyncGeneratorRequest*> request) {
if (generator->isSingleQueue()) {
if (generator->isSingleQueueEmpty()) {
generator->setSingleQueueRequest(request);
return true;
}
Rooted<ListObject*> queue(cx, ListObject::create(cx));
if (!queue) {
return false;
}
RootedValue requestVal(cx, ObjectValue(*generator->singleQueueRequest()));
if (!queue->append(cx, requestVal)) {
return false;
}
requestVal = ObjectValue(*request);
if (!queue->append(cx, requestVal)) {
return false;
}
generator->setQueue(queue);
return true;
}
Rooted<ListObject*> queue(cx, generator->queue());
RootedValue requestVal(cx, ObjectValue(*request));
return queue->append(cx, requestVal);
}
/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::dequeueRequest(
JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
if (generator->isSingleQueue()) {
AsyncGeneratorRequest* request = generator->singleQueueRequest();
generator->clearSingleQueueRequest();
return request;
}
Rooted<ListObject*> queue(cx, generator->queue());
return &queue->popFirstAs<AsyncGeneratorRequest>(cx);
}
/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::peekRequest(
Handle<AsyncGeneratorObject*> generator) {
if (generator->isSingleQueue()) {
return generator->singleQueueRequest();
}
return &generator->queue()->getAs<AsyncGeneratorRequest>(0);
}
const JSClass AsyncGeneratorRequest::class_ = {
"AsyncGeneratorRequest",
JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)};
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorRequest Records
/* static */
AsyncGeneratorRequest* AsyncGeneratorRequest::create(
JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
Handle<PromiseObject*> promise) {
AsyncGeneratorRequest* request =
NewObjectWithGivenProto<AsyncGeneratorRequest>(cx, nullptr);
if (!request) {
return nullptr;
}
request->init(completionKind, completionValue, promise);
return request;
}
[[nodiscard]] static bool AsyncGeneratorResume(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
CompletionKind completionKind, HandleValue argument);
[[nodiscard]] static bool AsyncGeneratorDrainQueue(
JSContext* cx, Handle<AsyncGeneratorObject*> generator);
[[nodiscard]] static bool AsyncGeneratorCompleteStepNormal(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value,
bool done);
[[nodiscard]] static bool AsyncGeneratorCompleteStepThrow(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
HandleValue exception);
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorStart ( generator, generatorBody )
//
// Steps 4.e-j. "return" case.
[[nodiscard]] static bool AsyncGeneratorReturned(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
// Step 4.e. Set generator.[[AsyncGeneratorState]] to completed.
generator->setCompleted();
// Step 4.g. If result.[[Type]] is return, set result to
// NormalCompletion(result.[[Value]]).
// (implicit)
// Step 4.h. Perform ! AsyncGeneratorCompleteStep(generator, result, true).
if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) {
return false;
}
// Step 4.i. Perform ! AsyncGeneratorDrainQueue(generator).
// Step 4.j. Return undefined.
return AsyncGeneratorDrainQueue(cx, generator);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorStart ( generator, generatorBody )
//
// Steps 4.e-j. "throw" case.
[[nodiscard]] static bool AsyncGeneratorThrown(
JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
// Step 4.e. Set generator.[[AsyncGeneratorState]] to completed.
generator->setCompleted();
// Not much we can do about uncatchable exceptions, so just bail.
if (!cx->isExceptionPending()) {
return false;
}
// Step 4.h. Perform ! AsyncGeneratorCompleteStep(generator, result, true).
RootedValue value(cx);
if (!GetAndClearException(cx, &value)) {
return false;
}
if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
return false;
}
// Step 4.i. Perform ! AsyncGeneratorDrainQueue(generator).
// Step 4.j. Return undefined.
return AsyncGeneratorDrainQueue(cx, generator);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
//
// Steps 4-5.
[[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedFulfilled(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
MOZ_ASSERT(generator->isAwaitingYieldReturn(),
"YieldReturn-Await fulfilled when not in "
"'AwaitingYieldReturn' state");
// Step 4. Assert: awaited.[[Type]] is normal.
// Step 5. Return Completion { [[Type]]: return, [[Value]]:
// awaited.[[Value]], [[Target]]: empty }.
return AsyncGeneratorResume(cx, generator, CompletionKind::Return, value);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
//
// Step 3.
[[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedRejected(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
HandleValue reason) {
MOZ_ASSERT(
generator->isAwaitingYieldReturn(),
"YieldReturn-Await rejected when not in 'AwaitingYieldReturn' state");
// Step 3. If awaited.[[Type]] is throw, return Completion(awaited).
return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
//
// Steps 1-2.
[[nodiscard]] static bool AsyncGeneratorUnwrapYieldResumptionAndResume(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
CompletionKind completionKind, HandleValue resumptionValue) {
// Step 1. If resumptionValue.[[Type]] is not return, return
// Completion(resumptionValue).
if (completionKind != CompletionKind::Return) {
return AsyncGeneratorResume(cx, generator, completionKind, resumptionValue);
}
// Step 2. Let awaited be Await(resumptionValue.[[Value]]).
//
// Since we don't have the place that handles return from yield
// inside the generator, handle the case here, with extra state
// State_AwaitingYieldReturn.
generator->setAwaitingYieldReturn();
const PromiseHandler onFulfilled =
PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled;
const PromiseHandler onRejected =
PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected;
return InternalAsyncGeneratorAwait(cx, generator, resumptionValue,
onFulfilled, onRejected);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorYield ( value )
//
// Stesp 10-13.
[[nodiscard]] static bool AsyncGeneratorYield(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
// Step 10. Perform
// ! AsyncGeneratorCompleteStep(generator, completion, false,
// previousRealm).
if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, false)) {
return false;
}
// Step 11. Let queue be generator.[[AsyncGeneratorQueue]].
// Step 12. If queue is not empty, then
// Step 13. Else,
// (reordered)
if (generator->isQueueEmpty()) {
// Step 13.a. Set generator.[[AsyncGeneratorState]] to suspendedYield.
generator->setSuspendedYield();
// Steps 13.b-c are done in caller.
// Step 13.d. Return undefined.
return true;
}
// Step 12. If queue is not empty, then
// Step 12.a. NOTE: Execution continues without suspending the generator.
// Step 12.b. Let toYield be the first element of queue.
Rooted<AsyncGeneratorRequest*> toYield(
cx, AsyncGeneratorObject::peekRequest(generator));
if (!toYield) {
return false;
}
// Step 12.c. Let resumptionValue be toYield.[[Completion]].
CompletionKind completionKind = toYield->completionKind();
RootedValue resumptionValue(cx, toYield->completionValue());
// Step 12.d. Return AsyncGeneratorUnwrapYieldResumption(resumptionValue).
return AsyncGeneratorUnwrapYieldResumptionAndResume(
cx, generator, completionKind, resumptionValue);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// Await in async function
//
// Steps 3.c-f.
[[nodiscard]] static bool AsyncGeneratorAwaitedFulfilled(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
MOZ_ASSERT(generator->isExecuting(),
"Await fulfilled when not in 'Executing' state");
// Step 3.c. Push asyncContext onto the execution context stack; asyncContext
// is now the running execution context.
// Step 3.d. Resume the suspended evaluation of asyncContext using
// NormalCompletion(value) as the result of the operation that
// suspended it.
// Step 3.f. Return undefined.
return AsyncGeneratorResume(cx, generator, CompletionKind::Normal, value);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// Await in async function
//
// Steps 5.c-f.
[[nodiscard]] static bool AsyncGeneratorAwaitedRejected(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
HandleValue reason) {
MOZ_ASSERT(generator->isExecuting(),
"Await rejected when not in 'Executing' state");
// Step 5.c. Push asyncContext onto the execution context stack; asyncContext
// is now the running execution context.
// Step 5.d. Resume the suspended evaluation of asyncContext using
// ThrowCompletion(reason) as the result of the operation that
// suspended it.
// Step 5.f. Return undefined.
return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// Await in async function
[[nodiscard]] static bool AsyncGeneratorAwait(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
return InternalAsyncGeneratorAwait(
cx, generator, value, PromiseHandler::AsyncGeneratorAwaitedFulfilled,
PromiseHandler::AsyncGeneratorAwaitedRejected);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )
//
// "normal" case.
[[nodiscard]] static bool AsyncGeneratorCompleteStepNormal(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value,
bool done) {
// Step 1. Let queue be generator.[[AsyncGeneratorQueue]].
// Step 2. Assert: queue is not empty.
MOZ_ASSERT(!generator->isQueueEmpty());
// Step 3. Let next be the first element of queue.
// Step 4. Remove the first element from queue.
AsyncGeneratorRequest* next =
AsyncGeneratorObject::dequeueRequest(cx, generator);
if (!next) {
return false;
}
// Step 5. Let promiseCapability be next.[[Capability]].
Rooted<PromiseObject*> resultPromise(cx, next->promise());
generator->cacheRequest(next);
// Step 6. Let value be completion.[[Value]].
// (passed by caller)
// Step 7. If completion.[[Type]] is throw, then
// Step 8. Else,
// Step 8.a. Assert: completion.[[Type]] is normal.
// Step 8.b. If realm is present, then
// (skipped)
// Step 8.c. Else,
// Step 8.c.i. Let iteratorResult be ! CreateIterResultObject(value, done).
JSObject* resultObj = CreateIterResultObject(cx, value, done);
if (!resultObj) {
return false;
}
// Step 8.d. Perform
// ! Call(promiseCapability.[[Resolve]], undefined,
// « iteratorResult »).
RootedValue resultValue(cx, ObjectValue(*resultObj));
return ResolvePromiseInternal(cx, resultPromise, resultValue);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )
//
// "throw" case.
[[nodiscard]] static bool AsyncGeneratorCompleteStepThrow(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
HandleValue exception) {
// Step 1. Let queue be generator.[[AsyncGeneratorQueue]].
// Step 2. Assert: queue is not empty.
MOZ_ASSERT(!generator->isQueueEmpty());
// Step 3. Let next be the first element of queue.
// Step 4. Remove the first element from queue.
AsyncGeneratorRequest* next =
AsyncGeneratorObject::dequeueRequest(cx, generator);
if (!next) {
return false;
}
// Step 5. Let promiseCapability be next.[[Capability]].
Rooted<PromiseObject*> resultPromise(cx, next->promise());
generator->cacheRequest(next);
// Step 6. Let value be completion.[[Value]].
// (passed by caller)
// Step 7. If completion.[[Type]] is throw, then
// Step 7.a. Perform
// ! Call(promiseCapability.[[Reject]], undefined, « value »).
return RejectPromiseInternal(cx, resultPromise, exception);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorAwaitReturn ( generator )
//
// Steps 7.a-e.
[[nodiscard]] static bool AsyncGeneratorAwaitReturnFulfilled(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
MOZ_ASSERT(generator->isAwaitingReturn(),
"AsyncGeneratorResumeNext-Return fulfilled when not in "
"'AwaitingReturn' state");
// Step 7.a. Set generator.[[AsyncGeneratorState]] to completed.
generator->setCompleted();
// Step 7.b. Let result be NormalCompletion(value).
// Step 7.c. Perform ! AsyncGeneratorCompleteStep(generator, result, true).
if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) {
return false;
}
// Step 7.d. Perform ! AsyncGeneratorDrainQueue(generator).
// Step 7.e. Return undefined.
return AsyncGeneratorDrainQueue(cx, generator);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorAwaitReturn ( generator )
//
// Steps 9.a-e.
[[nodiscard]] static bool AsyncGeneratorAwaitReturnRejected(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
MOZ_ASSERT(generator->isAwaitingReturn(),
"AsyncGeneratorResumeNext-Return rejected when not in "
"'AwaitingReturn' state");
// Step 9.a. Set generator.[[AsyncGeneratorState]] to completed.
generator->setCompleted();
// Step 9.b. Let result be ThrowCompletion(reason).
// Step 9.c. Perform ! AsyncGeneratorCompleteStep(generator, result, true).
if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
return false;
}
// Step 9.d. Perform ! AsyncGeneratorDrainQueue(generator).
// Step 9.e. Return undefined.
return AsyncGeneratorDrainQueue(cx, generator);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorAwaitReturn ( generator )
[[nodiscard]] static bool AsyncGeneratorAwaitReturn(
JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue next) {
// Step 1. Let queue be generator.[[AsyncGeneratorQueue]].
// Step 2. Assert: queue is not empty.
MOZ_ASSERT(!generator->isQueueEmpty());
// Step 3. Let next be the first element of queue.
// (passed by caller)
// Step 4. Let completion be next.[[Completion]].
// Step 5. Assert: completion.[[Type]] is return.
// (implicit)
// Steps 6-11.
return InternalAsyncGeneratorAwait(
cx, generator, next, PromiseHandler::AsyncGeneratorAwaitReturnFulfilled,
PromiseHandler::AsyncGeneratorAwaitReturnRejected);
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorDrainQueue ( generator )
[[nodiscard]] static bool AsyncGeneratorDrainQueue(
JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
// Step 1. Assert: generator.[[AsyncGeneratorState]] is completed.
MOZ_ASSERT(generator->isCompleted());
// Step 2. Let queue be generator.[[AsyncGeneratorQueue]].
// Step 3. If queue is empty, return.
if (generator->isQueueEmpty()) {
return true;
}
// Step 4. Let done be false.
// (implicit)
// Step 5. Repeat, while done is false,
while (true) {
// Step 5.a. Let next be the first element of queue.
Rooted<AsyncGeneratorRequest*> next(
cx, AsyncGeneratorObject::peekRequest(generator));
if (!next) {
return false;
}
// Step 5.b. Let completion be next.[[Completion]].
CompletionKind completionKind = next->completionKind();
// Step 5.c. If completion.[[Type]] is return, then
if (completionKind == CompletionKind::Return) {
RootedValue value(cx, next->completionValue());
// Step 5.c.i. Set generator.[[AsyncGeneratorState]] to awaiting-return.
generator->setAwaitingReturn();
// Step 5.c.ii. Perform ! AsyncGeneratorAwaitReturn(generator).
// Step 5.c.iii. Set done to true.
return AsyncGeneratorAwaitReturn(cx, generator, value);
}
// Step 5.d. Else,
if (completionKind == CompletionKind::Throw) {
RootedValue value(cx, next->completionValue());
// Step 5.d.ii. Perform
// ! AsyncGeneratorCompleteStep(generator, completion, true).
if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
return false;
}
} else {
// Step 5.d.i. If completion.[[Type]] is normal, then
// Step 5.d.i.1. Set completion to NormalCompletion(undefined).
// Step 5.d.ii. Perform
// ! AsyncGeneratorCompleteStep(generator, completion, true).
if (!AsyncGeneratorCompleteStepNormal(cx, generator, UndefinedHandleValue,
true)) {
return false;
}
}
// Step 5.d.iii. If queue is empty, set done to true.
if (generator->isQueueEmpty()) {
return true;
}
}
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorValidate ( generator, generatorBrand )
//
// Testing part.
[[nodiscard]] static bool IsAsyncGeneratorValid(HandleValue asyncGenVal) {
// Step 1. Perform
// ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]).
// Step 2. Perform
// ? RequireInternalSlot(generator, [[AsyncGeneratorState]]).
// Step 3. Perform
// ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]).
// Step 4. If generator.[[GeneratorBrand]] is not the same value as
// generatorBrand, throw a TypeError exception.
return asyncGenVal.isObject() &&
asyncGenVal.toObject().canUnwrapAs<AsyncGeneratorObject>();
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorValidate ( generator, generatorBrand )
//
// Throwing part.
[[nodiscard]] static bool AsyncGeneratorValidateThrow(
JSContext* cx, MutableHandleValue result) {
Rooted<PromiseObject*> resultPromise(
cx, CreatePromiseObjectForAsyncGenerator(cx));
if (!resultPromise) {
return false;
}
RootedValue badGeneratorError(cx);
if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError)) {
return false;
}
if (!RejectPromiseInternal(cx, resultPromise, badGeneratorError)) {
return false;
}
result.setObject(*resultPromise);
return true;
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorEnqueue ( generator, completion, promiseCapability )
[[nodiscard]] static bool AsyncGeneratorEnqueue(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
CompletionKind completionKind, HandleValue completionValue,
Handle<PromiseObject*> resultPromise) {
// Step 1. Let request be
// AsyncGeneratorRequest { [[Completion]]: completion,
// [[Capability]]: promiseCapability }.
Rooted<AsyncGeneratorRequest*> request(
cx, AsyncGeneratorObject::createRequest(cx, generator, completionKind,
completionValue, resultPromise));
if (!request) {
return false;
}
// Step 2. Append request to the end of generator.[[AsyncGeneratorQueue]].
return AsyncGeneratorObject::enqueueRequest(cx, generator, request);
}
class MOZ_STACK_CLASS MaybeEnterAsyncGeneratorRealm {
mozilla::Maybe<AutoRealm> ar_;
public:
MaybeEnterAsyncGeneratorRealm() = default;
~MaybeEnterAsyncGeneratorRealm() = default;
// Enter async generator's realm, and wrap the method's argument value if
// necessary.
[[nodiscard]] bool maybeEnterAndWrap(JSContext* cx,
Handle<AsyncGeneratorObject*> generator,
MutableHandleValue value) {
if (generator->compartment() == cx->compartment()) {
return true;
}
ar_.emplace(cx, generator);
return cx->compartment()->wrap(cx, value);
}
// Leave async generator's realm, and wrap the method's result value if
// necessary.
[[nodiscard]] bool maybeLeaveAndWrap(JSContext* cx,
MutableHandleValue result) {
if (!ar_) {
return true;
}
ar_.reset();
return cx->compartment()->wrap(cx, result);
}
};
[[nodiscard]] static bool AsyncGeneratorMethodSanityCheck(
JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
if (generator->isCompleted() || generator->isSuspendedStart() ||
generator->isSuspendedYield()) {
// The spec assumes the queue is empty when async generator methods are
// called with those state, but our debugger allows calling those methods
// in unexpected state, such as before suspendedStart.
if (MOZ_UNLIKELY(!generator->isQueueEmpty())) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_SUSPENDED_QUEUE_NOT_EMPTY);
return false;
}
}
return true;
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGenerator.prototype.next ( value )
bool js::AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 3. Let result be AsyncGeneratorValidate(generator, empty).
// Step 4. IfAbruptRejectPromise(result, promiseCapability).
// (reordered)
if (!IsAsyncGeneratorValid(args.thisv())) {
return AsyncGeneratorValidateThrow(cx, args.rval());
}
// Step 1. Let generator be the this value.
// (implicit)
Rooted<AsyncGeneratorObject*> generator(
cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());
MaybeEnterAsyncGeneratorRealm maybeEnterRealm;
RootedValue completionValue(cx, args.get(0));
if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
return false;
}
// Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
Rooted<PromiseObject*> resultPromise(
cx, CreatePromiseObjectForAsyncGenerator(cx));
if (!resultPromise) {
return false;
}
if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
return false;
}
// Step 5. Let state be generator.[[AsyncGeneratorState]].
// Step 6. If state is completed, then
if (generator->isCompleted()) {
// Step 6.a. Let iteratorResult be
// ! CreateIterResultObject(undefined, true).
JSObject* resultObj =
CreateIterResultObject(cx, UndefinedHandleValue, true);
if (!resultObj) {
return false;
}
// Step 6.b. Perform
// ! Call(promiseCapability.[[Resolve]], undefined,
// « iteratorResult »).
RootedValue resultValue(cx, ObjectValue(*resultObj));
if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) {
return false;
}
} else {
// Step 7. Let completion be NormalCompletion(value).
// Step 8. Perform
// ! AsyncGeneratorEnqueue(generator, completion,
// promiseCapability).
if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Normal,
completionValue, resultPromise)) {
return false;
}
// Step 9. If state is either suspendedStart or suspendedYield, then
if (generator->isSuspendedStart() || generator->isSuspendedYield()) {
RootedValue resumptionValue(cx, completionValue);
// Step 9.a. Perform ! AsyncGeneratorResume(generator, completion).
if (!AsyncGeneratorResume(cx, generator, CompletionKind::Normal,
resumptionValue)) {
return false;
}
} else {
// Step 10. Else,
// Step 10.a. Assert: state is either executing or awaiting-return.
MOZ_ASSERT(generator->isExecuting() || generator->isAwaitingReturn() ||
generator->isAwaitingYieldReturn());
}
}
// Step 6.c. Return promiseCapability.[[Promise]].
// and
// Step 11. Return promiseCapability.[[Promise]].
args.rval().setObject(*resultPromise);
return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGenerator.prototype.return ( value )
bool js::AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 3. Let result be AsyncGeneratorValidate(generator, empty).
// Step 4. IfAbruptRejectPromise(result, promiseCapability).
// (reordered)
if (!IsAsyncGeneratorValid(args.thisv())) {
return AsyncGeneratorValidateThrow(cx, args.rval());
}
// Step 1. Let generator be the this value.
Rooted<AsyncGeneratorObject*> generator(
cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());
MaybeEnterAsyncGeneratorRealm maybeEnterRealm;
RootedValue completionValue(cx, args.get(0));
if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
return false;
}
// Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
Rooted<PromiseObject*> resultPromise(
cx, CreatePromiseObjectForAsyncGenerator(cx));
if (!resultPromise) {
return false;
}
if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
return false;
}
// Step 5. Let completion be
// Completion { [[Type]]: return, [[Value]]: value,
// [[Target]]: empty }.
// Step 6. Perform
// ! AsyncGeneratorEnqueue(generator, completion, promiseCapability).
if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Return,
completionValue, resultPromise)) {
return false;
}
// Step 7. Let state be generator.[[AsyncGeneratorState]].
// Step 8. If state is either suspendedStart or completed, then
if (generator->isSuspendedStart() || generator->isCompleted()) {
// Step 8.a. Set generator.[[AsyncGeneratorState]] to awaiting-return.
generator->setAwaitingReturn();
// Step 8.b. Perform ! AsyncGeneratorAwaitReturn(generator).
if (!AsyncGeneratorAwaitReturn(cx, generator, completionValue)) {
return false;
}
} else if (generator->isSuspendedYield()) {
// Step 9. Else if state is suspendedYield, then
// Step 9.a. Perform ! AsyncGeneratorResume(generator, completion).
if (!AsyncGeneratorUnwrapYieldResumptionAndResume(
cx, generator, CompletionKind::Return, completionValue)) {
return false;
}
} else {
// Step 10. Else,
// Step 10.a. Assert: state is either executing or awaiting-return.
MOZ_ASSERT(generator->isExecuting() || generator->isAwaitingReturn() ||
generator->isAwaitingYieldReturn());
}
// Step 11. Return promiseCapability.[[Promise]].
args.rval().setObject(*resultPromise);
return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGenerator.prototype.throw ( exception )
bool js::AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 3. Let result be AsyncGeneratorValidate(generator, empty).
// Step 4. IfAbruptRejectPromise(result, promiseCapability).
// (reordered)
if (!IsAsyncGeneratorValid(args.thisv())) {
return AsyncGeneratorValidateThrow(cx, args.rval());
}
// Step 1. Let generator be the this value.
Rooted<AsyncGeneratorObject*> generator(
cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());
MaybeEnterAsyncGeneratorRealm maybeEnterRealm;
RootedValue completionValue(cx, args.get(0));
if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
return false;
}
// Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
Rooted<PromiseObject*> resultPromise(
cx, CreatePromiseObjectForAsyncGenerator(cx));
if (!resultPromise) {
return false;
}
if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
return false;
}
// Step 5. Let state be generator.[[AsyncGeneratorState]].
// Step 6. If state is suspendedStart, then
if (generator->isSuspendedStart()) {
// Step 6.a. Set generator.[[AsyncGeneratorState]] to completed.
// Step 6.b. Set state to completed.
generator->setCompleted();
}
// Step 7. If state is completed, then
if (generator->isCompleted()) {
// Step 7.a. Perform
// ! Call(promiseCapability.[[Reject]], undefined, « exception »).
if (!RejectPromiseInternal(cx, resultPromise, completionValue)) {
return false;
}
} else {
// Step 8. Let completion be ThrowCompletion(exception).
// Step 9. Perform
// ! AsyncGeneratorEnqueue(generator, completion,
// promiseCapability).
if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Throw,
completionValue, resultPromise)) {
return false;
}
// Step 10. If state is suspendedYield, then
if (generator->isSuspendedYield()) {
// Step 10.a. Perform ! AsyncGeneratorResume(generator, completion).
if (!AsyncGeneratorResume(cx, generator, CompletionKind::Throw,
completionValue)) {
return false;
}
} else {
// Step 11. Else,
// Step 11.a. Assert: state is either executing or awaiting-return.
MOZ_ASSERT(generator->isExecuting() || generator->isAwaitingReturn() ||
generator->isAwaitingYieldReturn());
}
}
// Step 7.b. Return promiseCapability.[[Promise]].
// and
// Step 12. Return promiseCapability.[[Promise]].
args.rval().setObject(*resultPromise);
return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGeneratorResume ( generator, completion )
[[nodiscard]] static bool AsyncGeneratorResume(
JSContext* cx, Handle<AsyncGeneratorObject*> generator,
CompletionKind completionKind, HandleValue argument) {
MOZ_ASSERT(!generator->isClosed(),
"closed generator when resuming async generator");
MOZ_ASSERT(generator->isSuspended(),
"non-suspended generator when resuming async generator");
// Step 1. Assert: generator.[[AsyncGeneratorState]] is either
// suspendedStart or suspendedYield.
//
// NOTE: We're using suspend/resume also for await. and the state can be
// anything.
// Steps 2-4 are handled in generator.
// Step 5. Set generator.[[AsyncGeneratorState]] to executing.
generator->setExecuting();
// Step 6. Push genContext onto the execution context stack; genContext is
// now the running execution context.
// Step 7. Resume the suspended evaluation of genContext using completion as
// the result of the operation that suspended it. Let result be the
// completion record returned by the resumed computation.
Handle<PropertyName*> funName = completionKind == CompletionKind::Normal
? cx->names().AsyncGeneratorNext
: completionKind == CompletionKind::Throw
? cx->names().AsyncGeneratorThrow
: cx->names().AsyncGeneratorReturn;
FixedInvokeArgs<1> args(cx);
args[0].set(argument);
RootedValue thisOrRval(cx, ObjectValue(*generator));
if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) {
// 25.5.3.2, steps 5.f, 5.g.
if (!generator->isClosed()) {
generator->setClosed(cx);
}
return AsyncGeneratorThrown(cx, generator);
}
// 6.2.3.1, steps 2-9.
if (generator->isAfterAwait()) {
return AsyncGeneratorAwait(cx, generator, thisOrRval);
}
// 25.5.3.7, steps 5-6, 9.
if (generator->isAfterYield()) {
return AsyncGeneratorYield(cx, generator, thisOrRval);
}
// 25.5.3.2, steps 5.d-g.
return AsyncGeneratorReturned(cx, generator, thisOrRval);
}
static const JSFunctionSpec async_generator_methods[] = {
JS_FN("next", js::AsyncGeneratorNext, 1, 0),
JS_FN("throw", js::AsyncGeneratorThrow, 1, 0),
JS_FN("return", js::AsyncGeneratorReturn, 1, 0), JS_FS_END};
static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) {
RootedObject proto(cx, &cx->global()->getFunctionConstructor());
Handle<PropertyName*> name = cx->names().AsyncGeneratorFunction;
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// The AsyncGeneratorFunction Constructor
return NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1,
FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
gc::AllocKind::FUNCTION, TenuredObject);
}
static JSObject* CreateAsyncGeneratorFunctionPrototype(JSContext* cx,
JSProtoKey key) {
return NewTenuredObjectWithFunctionPrototype(cx, cx->global());
}
static bool AsyncGeneratorFunctionClassFinish(JSContext* cx,
HandleObject asyncGenFunction,
HandleObject asyncGenerator) {
Handle<GlobalObject*> global = cx->global();
// Change the "constructor" property to non-writable before adding any other
// properties, so it's still the last property and can be modified without a
// dictionary-mode transition.
MOZ_ASSERT(asyncGenerator->as<NativeObject>().getLastProperty().key() ==
NameToId(cx->names().constructor));
MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
RootedValue asyncGenFunctionVal(cx, ObjectValue(*asyncGenFunction));
if (!DefineDataProperty(cx, asyncGenerator, cx->names().constructor,
asyncGenFunctionVal, JSPROP_READONLY)) {
return false;
}
MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());
RootedObject asyncIterProto(
cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
if (!asyncIterProto) {
return false;
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// AsyncGenerator Objects
RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting(
cx, &PlainObject::class_, asyncIterProto));
if (!asyncGenProto) {
return false;
}
if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr,
async_generator_methods) ||
!DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) {
return false;
}
// ES2022 draft rev 193211a3d889a61e74ef7da1475dfa356e029f29
//
// Properties of the AsyncGeneratorFunction Prototype Object
if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto,
JSPROP_READONLY, JSPROP_READONLY) ||
!DefineToStringTag(cx, asyncGenerator,
cx->names().AsyncGeneratorFunction)) {
return false;
}
global->setAsyncGeneratorPrototype(asyncGenProto);
return true;
}
static const ClassSpec AsyncGeneratorFunctionClassSpec = {
CreateAsyncGeneratorFunction,
CreateAsyncGeneratorFunctionPrototype,
nullptr,
nullptr,
nullptr,
nullptr,
AsyncGeneratorFunctionClassFinish,
ClassSpec::DontDefineConstructor};
const JSClass js::AsyncGeneratorFunctionClass = {
"AsyncGeneratorFunction", 0, JS_NULL_CLASS_OPS,
&AsyncGeneratorFunctionClassSpec};
[[nodiscard]] bool js::AsyncGeneratorPromiseReactionJob(
JSContext* cx, PromiseHandler handler,
Handle<AsyncGeneratorObject*> generator, HandleValue argument) {
// Await's handlers don't return a value, nor throw any exceptions.
// They fail only on OOM.
switch (handler) {
case PromiseHandler::AsyncGeneratorAwaitedFulfilled:
return AsyncGeneratorAwaitedFulfilled(cx, generator, argument);
case PromiseHandler::AsyncGeneratorAwaitedRejected:
return AsyncGeneratorAwaitedRejected(cx, generator, argument);
case PromiseHandler::AsyncGeneratorAwaitReturnFulfilled:
return AsyncGeneratorAwaitReturnFulfilled(cx, generator, argument);
case PromiseHandler::AsyncGeneratorAwaitReturnRejected:
return AsyncGeneratorAwaitReturnRejected(cx, generator, argument);
case PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled:
return AsyncGeneratorYieldReturnAwaitedFulfilled(cx, generator, argument);
case PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected:
return AsyncGeneratorYieldReturnAwaitedRejected(cx, generator, argument);
default:
MOZ_CRASH("Bad handler in AsyncGeneratorPromiseReactionJob");
}
}
// ---------------------
// AsyncFromSyncIterator
// ---------------------
const JSClass AsyncFromSyncIteratorObject::class_ = {
"AsyncFromSyncIteratorObject",
JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)};
/*
* ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
*
* CreateAsyncFromSyncIterator ( syncIteratorRecord )
*/
JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter,
HandleValue nextMethod) {
// Steps 1-5.
return AsyncFromSyncIteratorObject::create(cx, iter, nextMethod);
}
/*
* ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
*
* CreateAsyncFromSyncIterator ( syncIteratorRecord )
*/
/* static */
JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter,
HandleValue nextMethod) {
// Step 1. Let asyncIterator be
// OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, «
// [[SyncIteratorRecord]] »).
RootedObject proto(cx,
GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(
cx, cx->global()));
if (!proto) {
return nullptr;
}
AsyncFromSyncIteratorObject* asyncIter =
NewObjectWithGivenProto<AsyncFromSyncIteratorObject>(cx, proto);
if (!asyncIter) {
return nullptr;
}
// Step 3. Let nextMethod be ! Get(asyncIterator, "next").
// (done in caller)
// Step 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.
// Step 4. Let iteratorRecord be the Iterator Record { [[Iterator]]:
// asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
asyncIter->init(iter, nextMethod);
// Step 5. Return iteratorRecord.
return asyncIter;
}
/**
* ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
*
* %AsyncFromSyncIteratorPrototype%.next ( [ value ] )
*/
static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
}
/**
* ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
*
* %AsyncFromSyncIteratorPrototype%.return ( [ value ] )
*/
static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
}
/**
* ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
*
* %AsyncFromSyncIteratorPrototype%.throw ( [ value ] )
*/
static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc,
Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
}
static const JSFunctionSpec async_from_sync_iter_methods[] = {
JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0), JS_FS_END};
bool GlobalObject::initAsyncFromSyncIteratorProto(
JSContext* cx, Handle<GlobalObject*> global) {
if (global->hasBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto)) {
return true;
}
RootedObject asyncIterProto(
cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
if (!asyncIterProto) {
return false;
}
// ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
//
// The %AsyncFromSyncIteratorPrototype% Object
RootedObject asyncFromSyncIterProto(
cx, GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
asyncIterProto));
if (!asyncFromSyncIterProto) {
return false;
}
if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
async_from_sync_iter_methods) ||
!DefineToStringTag(cx, asyncFromSyncIterProto,
cx->names().Async_from_Sync_Iterator_)) {
return false;
}
global->initBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto,
asyncFromSyncIterProto);
return true;
}
// -------------
// AsyncIterator
// -------------
static const JSFunctionSpec async_iterator_proto_methods[] = {
JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
JS_FS_END};
static const JSFunctionSpec async_iterator_proto_methods_with_helpers[] = {
JS_SELF_HOSTED_FN("map", "AsyncIteratorMap", 1, 0),
JS_SELF_HOSTED_FN("filter", "AsyncIteratorFilter", 1, 0),
JS_SELF_HOSTED_FN("take", "AsyncIteratorTake", 1, 0),
JS_SELF_HOSTED_FN("drop", "AsyncIteratorDrop", 1, 0),
JS_SELF_HOSTED_FN("asIndexedPairs", "AsyncIteratorAsIndexedPairs", 0, 0),
JS_SELF_HOSTED_FN("flatMap", "AsyncIteratorFlatMap", 1, 0),
JS_SELF_HOSTED_FN("reduce", "AsyncIteratorReduce", 1, 0),
JS_SELF_HOSTED_FN("toArray", "AsyncIteratorToArray", 0, 0),
JS_SELF_HOSTED_FN("forEach", "AsyncIteratorForEach", 1, 0),
JS_SELF_HOSTED_FN("some", "AsyncIteratorSome", 1, 0),
JS_SELF_HOSTED_FN("every", "AsyncIteratorEvery", 1, 0),
JS_SELF_HOSTED_FN("find", "AsyncIteratorFind", 1, 0),
JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
JS_FS_END};
bool GlobalObject::initAsyncIteratorProto(JSContext* cx,
Handle<GlobalObject*> global) {
if (global->hasBuiltinProto(ProtoKind::AsyncIteratorProto)) {
return true;
}
// 25.1.3 The %AsyncIteratorPrototype% Object
RootedObject asyncIterProto(
cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
if (!asyncIterProto) {
return false;
}
if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr,
async_iterator_proto_methods)) {
return false;
}
global->initBuiltinProto(ProtoKind::AsyncIteratorProto, asyncIterProto);
return true;
}
// 8f10db5.
static bool AsyncIteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// Step 1.
if (!ThrowIfNotConstructing(cx, args, "AsyncIterator")) {
return false;
}
// Throw TypeError if NewTarget is the active function object, preventing the
// Iterator constructor from being used directly.
if (args.callee() == args.newTarget().toObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_BOGUS_CONSTRUCTOR, "AsyncIterator");
return false;
}
// Step 2.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AsyncIterator,
&proto)) {
return false;
}
JSObject* obj = NewObjectWithClassProto<AsyncIteratorObject>(cx, proto);
if (!obj) {
return false;
}
args.rval().setObject(*obj);
return true;
}
static const ClassSpec AsyncIteratorObjectClassSpec = {
GenericCreateConstructor<AsyncIteratorConstructor, 0,
gc::AllocKind::FUNCTION>,
GenericCreatePrototype<AsyncIteratorObject>,
nullptr,
nullptr,
async_iterator_proto_methods_with_helpers,
nullptr,
nullptr,
};
const JSClass AsyncIteratorObject::class_ = {
"AsyncIterator",
JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
JS_NULL_CLASS_OPS,
&AsyncIteratorObjectClassSpec,
};
const JSClass AsyncIteratorObject::protoClass_ = {
"AsyncIterator.prototype",
JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
JS_NULL_CLASS_OPS,
&AsyncIteratorObjectClassSpec,
};
// Iterator Helper proposal
static const JSFunctionSpec async_iterator_helper_methods[] = {
JS_SELF_HOSTED_FN("next", "AsyncIteratorHelperNext", 1, 0),
JS_SELF_HOSTED_FN("return", "AsyncIteratorHelperReturn", 1, 0),
JS_SELF_HOSTED_FN("throw", "AsyncIteratorHelperThrow", 1, 0),
JS_FS_END,
};
static const JSClass AsyncIteratorHelperPrototypeClass = {
"Async Iterator Helper", 0};
const JSClass AsyncIteratorHelperObject::class_ = {
"Async Iterator Helper",
JSCLASS_HAS_RESERVED_SLOTS(AsyncIteratorHelperObject::SlotCount),
};
/* static */
NativeObject* GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
JSContext* cx, Handle<GlobalObject*> global) {
return MaybeNativeObject(
getOrCreateBuiltinProto(cx, global, ProtoKind::AsyncIteratorHelperProto,
initAsyncIteratorHelperProto));
}
/* static */
bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx,
Handle<GlobalObject*> global) {
if (global->hasBuiltinProto(ProtoKind::AsyncIteratorHelperProto)) {
return true;
}
RootedObject asyncIterProto(
cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
if (!asyncIterProto) {
return false;
}
RootedObject asyncIteratorHelperProto(
cx, GlobalObject::createBlankPrototypeInheriting(
cx, &AsyncIteratorHelperPrototypeClass, asyncIterProto));
if (!asyncIteratorHelperProto) {
return false;
}
if (!DefinePropertiesAndFunctions(cx, asyncIteratorHelperProto, nullptr,
async_iterator_helper_methods)) {
return false;
}
global->initBuiltinProto(ProtoKind::AsyncIteratorHelperProto,
asyncIteratorHelperProto);
return true;
}
AsyncIteratorHelperObject* js::NewAsyncIteratorHelper(JSContext* cx) {
RootedObject proto(cx, GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
cx, cx->global()));
if (!proto) {
return nullptr;
}
return NewObjectWithGivenProto<AsyncIteratorHelperObject>(cx, proto);
}