diff options
Diffstat (limited to 'js/src/vm/AsyncIteration.cpp')
-rw-r--r-- | js/src/vm/AsyncIteration.cpp | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp new file mode 100644 index 0000000000..64708c2dbf --- /dev/null +++ b/js/src/vm/AsyncIteration.cpp @@ -0,0 +1,700 @@ +/* -*- 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/Array.h" + +#include "builtin/Promise.h" // js::AsyncFromSyncIteratorMethod, js::AsyncGeneratorEnqueue +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* +#include "js/PropertySpec.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/JSContext-inl.h" +#include "vm/JSObject-inl.h" +#include "vm/List-inl.h" + +using namespace js; + +// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c +// 6.2.3.1.1 Await Fulfilled Functions +MOZ_MUST_USE bool js::AsyncGeneratorAwaitedFulfilled( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) { + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); +} + +// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c +// 6.2.3.1.2 Await Rejected Functions +MOZ_MUST_USE bool js::AsyncGeneratorAwaitedRejected( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason) { + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c +// 25.5.3.7 AsyncGeneratorYield, step 8.e. +MOZ_MUST_USE bool js::AsyncGeneratorYieldReturnAwaitedFulfilled( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) { + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value); +} + +// ES2019 draft rev 49b781ec80117b60f73327ef3054703a3111e40c +// 25.5.3.7 AsyncGeneratorYield, step 8.c. +MOZ_MUST_USE bool js::AsyncGeneratorYieldReturnAwaitedRejected( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason) { + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +const JSClass AsyncFromSyncIteratorObject::class_ = { + "AsyncFromSyncIteratorObject", + JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)}; + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.1.4.1 CreateAsyncFromSyncIterator +JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter, + HandleValue nextMethod) { + // Steps 1-3. + return AsyncFromSyncIteratorObject::create(cx, iter, nextMethod); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.1.4.1 CreateAsyncFromSyncIterator +/* static */ +JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter, + HandleValue nextMethod) { + // Step 1. + RootedObject proto(cx, + GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype( + cx, cx->global())); + if (!proto) { + return nullptr; + } + + AsyncFromSyncIteratorObject* asyncIter = + NewObjectWithGivenProto<AsyncFromSyncIteratorObject>(cx, proto); + if (!asyncIter) { + return nullptr; + } + + // Step 2. + asyncIter->init(iter, nextMethod); + + // Step 3 (Call to 7.4.1 GetIterator). + // 7.4.1 GetIterator, steps 1-5 are a no-op (*). + // 7.4.1 GetIterator, steps 6-8 are implemented in bytecode. + // + // (*) With <https://github.com/tc39/ecma262/issues/1172> fixed. + return asyncIter; +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next +static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.1.4.2.2 %AsyncFromSyncIteratorPrototype%.return +static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.1.4.2.3 %AsyncFromSyncIteratorPrototype%.throw +static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, + Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.1.2 AsyncGenerator.prototype.next +bool js::AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, + args.get(0), args.rval()); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.1.3 AsyncGenerator.prototype.return +bool js::AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, + args.get(0), args.rval()); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.1.4 AsyncGenerator.prototype.throw +bool js::AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Steps 1-3. + return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, + args.get(0), args.rval()); +} + +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, // hasInstance + nullptr, // construct + CallTraceMethod<AbstractGeneratorObject>, // trace +}; + +// ES 2017 draft 9.1.13. +// OrdinaryCreateFromConstructor specialized for AsyncGeneratorObjects. +static AsyncGeneratorObject* OrdinaryCreateFromConstructorAsynGen( + JSContext* cx, HandleFunction fun) { + // Step 1 (skipped). + + // Step 2. + RootedValue protoVal(cx); + if (!GetProperty(cx, fun, fun, 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 NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto); +} + +/* static */ +AsyncGeneratorObject* AsyncGeneratorObject::create(JSContext* cx, + HandleFunction asyncGen) { + MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator()); + + AsyncGeneratorObject* asyncGenObj = + OrdinaryCreateFromConstructorAsynGen(cx, asyncGen); + if (!asyncGenObj) { + return nullptr; + } + + // ES2019 draft rev c2aad21fee7f5ddc89fdf7d3d305618ca3a13242 + // 25.5.3.2 AsyncGeneratorStart. + + // Step 7. + asyncGenObj->setSuspendedStart(); + + // Step 8. + asyncGenObj->clearSingleQueueRequest(); + + asyncGenObj->clearCachedRequest(); + + return asyncGenObj; +} + +/* static */ +AsyncGeneratorRequest* AsyncGeneratorObject::createRequest( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue completionValue, + Handle<PromiseObject*> promise) { + if (!asyncGenObj->hasCachedRequest()) { + return AsyncGeneratorRequest::create(cx, completionKind, completionValue, + promise); + } + + AsyncGeneratorRequest* request = asyncGenObj->takeCachedRequest(); + request->init(completionKind, completionValue, promise); + return request; +} + +/* static */ MOZ_MUST_USE bool AsyncGeneratorObject::enqueueRequest( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + Handle<AsyncGeneratorRequest*> request) { + if (asyncGenObj->isSingleQueue()) { + if (asyncGenObj->isSingleQueueEmpty()) { + asyncGenObj->setSingleQueueRequest(request); + return true; + } + + Rooted<ListObject*> queue(cx, ListObject::create(cx)); + if (!queue) { + return false; + } + + RootedValue requestVal(cx, ObjectValue(*asyncGenObj->singleQueueRequest())); + if (!queue->append(cx, requestVal)) { + return false; + } + requestVal = ObjectValue(*request); + if (!queue->append(cx, requestVal)) { + return false; + } + + asyncGenObj->setQueue(queue); + return true; + } + + Rooted<ListObject*> queue(cx, asyncGenObj->queue()); + RootedValue requestVal(cx, ObjectValue(*request)); + return queue->append(cx, requestVal); +} + +/* static */ +AsyncGeneratorRequest* AsyncGeneratorObject::dequeueRequest( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) { + if (asyncGenObj->isSingleQueue()) { + AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest(); + asyncGenObj->clearSingleQueueRequest(); + return request; + } + + Rooted<ListObject*> queue(cx, asyncGenObj->queue()); + return &queue->popFirstAs<AsyncGeneratorRequest>(cx); +} + +/* static */ +AsyncGeneratorRequest* AsyncGeneratorObject::peekRequest( + Handle<AsyncGeneratorObject*> asyncGenObj) { + if (asyncGenObj->isSingleQueue()) { + return asyncGenObj->singleQueueRequest(); + } + + return &asyncGenObj->queue()->getAs<AsyncGeneratorRequest>(0); +} + +const JSClass AsyncGeneratorRequest::class_ = { + "AsyncGeneratorRequest", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)}; + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.3.1 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; +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.3.2 AsyncGeneratorStart +static MOZ_MUST_USE bool AsyncGeneratorReturned( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) { + // Step 5.d. + asyncGenObj->setCompleted(); + + // Step 5.e (done in bytecode). + // Step 5.f.i (implicit). + + // Step 5.g. + return AsyncGeneratorResolve(cx, asyncGenObj, value, true); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.3.2 AsyncGeneratorStart +static MOZ_MUST_USE bool AsyncGeneratorThrown( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) { + // Step 5.d. + asyncGenObj->setCompleted(); + + // Not much we can do about uncatchable exceptions, so just bail. + if (!cx->isExceptionPending()) { + return false; + } + + // Step 5.f.i. + RootedValue value(cx); + if (!GetAndClearException(cx, &value)) { + return false; + } + + // Step 5.f.ii. + return AsyncGeneratorReject(cx, asyncGenObj, value); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 25.5.3.7 AsyncGeneratorYield (partially) +// Most steps are done in generator. +static MOZ_MUST_USE bool AsyncGeneratorYield( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) { + // Step 5 is done in bytecode. + + // Step 6. + asyncGenObj->setSuspendedYield(); + + // Step 9. + return AsyncGeneratorResolve(cx, asyncGenObj, value, false); +} + +// ES2019 draft rev c012f9c70847559a1d9dc0d35d35b27fec42911e +// 6.2.3.1 Await, steps 2-9. +// 14.4.13 RS: Evaluation, yield*, steps 7.a.vi, 7.b.ii.7, 7.c.ix. +// 25.5.3.2 AsyncGeneratorStart, steps 5.d-g. +// 25.5.3.5 AsyncGeneratorResumeNext, steps 12-20. +// 25.5.3.7 AsyncGeneratorYield, steps 5-6, 9. +// +// Note: Execution context switching is handled in generator. +MOZ_MUST_USE bool js::AsyncGeneratorResume( + JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue argument) { + MOZ_ASSERT(!asyncGenObj->isClosed(), + "closed generator when resuming async generator"); + MOZ_ASSERT(asyncGenObj->isSuspended(), + "non-suspended generator when resuming async generator"); + MOZ_ASSERT(asyncGenObj->isExecuting(), + "async generator not set into 'executing' state"); + + // 25.5.3.5, steps 12-14, 16-20. + HandlePropertyName 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(*asyncGenObj)); + if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) { + // 25.5.3.2, steps 5.f, 5.g. + if (!asyncGenObj->isClosed()) { + asyncGenObj->setClosed(); + } + return AsyncGeneratorThrown(cx, asyncGenObj); + } + + // 6.2.3.1, steps 2-9. + if (asyncGenObj->isAfterAwait()) { + return AsyncGeneratorAwait(cx, asyncGenObj, thisOrRval); + } + + // 25.5.3.7, steps 5-6, 9. + if (asyncGenObj->isAfterYield()) { + return AsyncGeneratorYield(cx, asyncGenObj, thisOrRval); + } + + // 25.5.3.2, steps 5.d-g. + return AsyncGeneratorReturned(cx, asyncGenObj, thisOrRval); +} + +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}; + +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}; + +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}; + +bool GlobalObject::initAsyncIteratorProto(JSContext* cx, + Handle<GlobalObject*> global) { + if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) { + 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->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto)); + return true; +} + +bool GlobalObject::initAsyncFromSyncIteratorProto( + JSContext* cx, Handle<GlobalObject*> global) { + if (global->getReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO).isObject()) { + return true; + } + + RootedObject asyncIterProto( + cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global)); + if (!asyncIterProto) { + return false; + } + + // 25.1.4.2 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().AsyncFromSyncIterator)) { + return false; + } + + global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, + ObjectValue(*asyncFromSyncIterProto)); + return true; +} + +static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) { + RootedObject proto( + cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global())); + if (!proto) { + return nullptr; + } + HandlePropertyName name = cx->names().AsyncGeneratorFunction; + + // 25.3.1 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(StringEqualsAscii( + JSID_TO_LINEAR_STRING( + asyncGenerator->as<NativeObject>().lastProperty()->propid()), + "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; + } + + // 25.5 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; + } + + // 25.3.3 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}; + +// https://tc39.es/proposal-iterator-helpers/#sec-asynciterator as of revision +// 8f10db5. +static bool AsyncIteratorConstructor(JSContext* cx, unsigned argc, Value* vp) { + CallArgs args = CallArgsFromVp(argc, vp); + + // Step 1. + if (!ThrowIfNotConstructing(cx, args, js_AsyncIterator_str)) { + 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, js_AsyncIterator_str); + 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_ = { + js_AsyncIterator_str, + 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(getOrCreateObject( + cx, global, ASYNC_ITERATOR_HELPER_PROTO, initAsyncIteratorHelperProto)); +} + +/* static */ +bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx, + Handle<GlobalObject*> global) { + if (global->getReservedSlot(ASYNC_ITERATOR_HELPER_PROTO).isObject()) { + 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->setReservedSlot(ASYNC_ITERATOR_HELPER_PROTO, + ObjectValue(*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); +} |