diff options
author | Moonchild <moonchild@palemoon.org> | 2019-12-17 21:47:18 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-17 21:47:18 +0000 |
commit | 07d0bcbf112a4e274905837c6ea0b0212b51e4e3 (patch) | |
tree | 3b43cb63b33d82d4965d402aca39028836983bb4 /js/src/vm | |
parent | e2de507e0261c9b138cd3cf5356c21eca3e7a28d (diff) | |
parent | 6c3e42ac6427fabaf83b5acc7877aa3d15117125 (diff) | |
download | UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.gz UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.lz UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.xz UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.zip |
Merge pull request #1327 from g4jc/async_iteration
Implement Async Iteration in SpiderMonkey
This resolves #1287
Diffstat (limited to 'js/src/vm')
-rw-r--r-- | js/src/vm/AsyncFunction.cpp | 31 | ||||
-rw-r--r-- | js/src/vm/AsyncFunction.h | 3 | ||||
-rw-r--r-- | js/src/vm/AsyncIteration.cpp | 644 | ||||
-rw-r--r-- | js/src/vm/AsyncIteration.h | 256 | ||||
-rw-r--r-- | js/src/vm/CommonPropertyNames.h | 4 | ||||
-rw-r--r-- | js/src/vm/Debugger.cpp | 15 | ||||
-rw-r--r-- | js/src/vm/EnvironmentObject.cpp | 21 | ||||
-rw-r--r-- | js/src/vm/GeneratorObject.cpp | 56 | ||||
-rw-r--r-- | js/src/vm/GeneratorObject.h | 60 | ||||
-rw-r--r-- | js/src/vm/GlobalObject.cpp | 11 | ||||
-rw-r--r-- | js/src/vm/GlobalObject.h | 47 | ||||
-rw-r--r-- | js/src/vm/Interpreter.cpp | 32 | ||||
-rw-r--r-- | js/src/vm/Interpreter.h | 3 | ||||
-rw-r--r-- | js/src/vm/ObjectGroup.cpp | 2 | ||||
-rw-r--r-- | js/src/vm/Opcodes.h | 34 | ||||
-rw-r--r-- | js/src/vm/Probes-inl.h | 5 | ||||
-rw-r--r-- | js/src/vm/SelfHosting.cpp | 111 | ||||
-rw-r--r-- | js/src/vm/Stack-inl.h | 2 | ||||
-rw-r--r-- | js/src/vm/Stack.cpp | 4 | ||||
-rw-r--r-- | js/src/vm/Stack.h | 3 |
20 files changed, 1247 insertions, 97 deletions
diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index e14b77424..7132ed984 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global) proto)); if (!asyncFunction) return false; - if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto)) + if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { return false; + } global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction)); global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto)); @@ -109,7 +112,7 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp) JSObject* js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto) { - MOZ_ASSERT(unwrapped->isStarGenerator()); + MOZ_ASSERT(unwrapped->isAsync()); MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default" "%FunctionPrototype% fallback in NewFunctionWithProto()."); @@ -171,22 +174,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV : cx->names().StarGeneratorThrow; FixedInvokeArgs<1> args(cx); args[0].set(valueOrReason); - RootedValue result(cx); - if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) - return AsyncFunctionThrown(cx, resultPromise); - - RootedObject resultObj(cx, &result.toObject()); - RootedValue doneVal(cx); RootedValue value(cx); - if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) - return false; - if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) - return false; + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value)) + return AsyncFunctionThrown(cx, resultPromise); - if (doneVal.toBoolean()) - return AsyncFunctionReturned(cx, resultPromise, value); + if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait()) + return AsyncFunctionAwait(cx, resultPromise, value); - return AsyncFunctionAwait(cx, resultPromise, value); + return AsyncFunctionReturned(cx, resultPromise, value); } // Async Functions proposal 2.2 steps 3-8. @@ -242,9 +237,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun) { return fun->maybeNative() == WrappedAsyncFunction; } - -MOZ_MUST_USE bool -js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v) -{ - return CheckStarGeneratorResumptionValue(cx, v); -} diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h index d7f2c1311..de7c87d13 100644 --- a/js/src/vm/AsyncFunction.h +++ b/js/src/vm/AsyncFunction.h @@ -35,9 +35,6 @@ MOZ_MUST_USE bool AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue generatorVal, HandleValue reason); -MOZ_MUST_USE bool -CheckAsyncResumptionValue(JSContext* cx, HandleValue v); - } // namespace js #endif /* vm_AsyncFunction_h */ diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp new file mode 100644 index 000000000..300179374 --- /dev/null +++ b/js/src/vm/AsyncIteration.cpp @@ -0,0 +1,644 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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 "jsarray.h" +#include "jscompartment.h" + +#include "builtin/Promise.h" +#include "vm/GeneratorObject.h" +#include "vm/GlobalObject.h" +#include "vm/Interpreter.h" +#include "vm/SelfHosting.h" + +#include "jscntxtinlines.h" +#include "jsobjinlines.h" + +#include "vm/NativeObject-inl.h" + +using namespace js; +using namespace js::gc; + +#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 +#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 + +// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody. +static bool +WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + RootedFunction wrapped(cx, &args.callee().as<JSFunction>()); + RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)); + RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>()); + RootedValue thisValue(cx, args.thisv()); + + // Step 1. + RootedValue generatorVal(cx); + InvokeArgs args2(cx); + if (!args2.init(cx, argc)) + return false; + for (size_t i = 0, len = argc; i < len; i++) + args2[i].set(args[i]); + if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal)) + return false; + + // Step 2. + Rooted<AsyncGeneratorObject*> asyncGenObj( + cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal)); + if (!asyncGenObj) + return false; + + // Step 3 (skipped). + // Done in AsyncGeneratorObject::create and generator. + + // Step 4. + args.rval().setObject(*asyncGenObj); + return true; +} + +JSObject* +js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto) +{ + MOZ_ASSERT(unwrapped->isAsync()); + MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default" + "%FunctionPrototype% fallback in NewFunctionWithProto()."); + + // Create a new function with AsyncGeneratorPrototype, reusing the name and + // the length of `unwrapped`. + + RootedAtom funName(cx, unwrapped->explicitName()); + uint16_t length; + if (!JSFunction::getLength(cx, unwrapped, &length)) + return nullptr; + + RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length, + JSFunction::NATIVE_FUN, nullptr, + funName, proto, + AllocKind::FUNCTION_EXTENDED, + TenuredObject)); + if (!wrapped) + return nullptr; + + if (unwrapped->hasCompileTimeName()) + wrapped->setCompileTimeName(unwrapped->compileTimeName()); + + // Link them to each other to make GetWrappedAsyncGenerator and + // GetUnwrappedAsyncGenerator work. + unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped)); + wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped)); + + return wrapped; +} + +JSObject* +js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped) +{ + RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global())); + if (!proto) + return nullptr; + + return WrapAsyncGeneratorWithProto(cx, unwrapped, proto); +} + +bool +js::IsWrappedAsyncGenerator(JSFunction* fun) +{ + return fun->maybeNative() == WrappedAsyncGenerator; +} + +JSFunction* +js::GetWrappedAsyncGenerator(JSFunction* unwrapped) +{ + MOZ_ASSERT(unwrapped->isAsync()); + return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>(); +} + +JSFunction* +js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) +{ + MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped)); + JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT) + .toObject().as<JSFunction>(); + MOZ_ASSERT(unwrapped->isAsync()); + return unwrapped; +} + +// Async Iteration proposal 4.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); +} + +// Async Iteration proposal 4.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); +} + +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value); +} + +// Async Iteration proposal 11.4.3.7 step 8.d-e. +MOZ_MUST_USE bool +js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +const Class AsyncFromSyncIteratorObject::class_ = { + "AsyncFromSyncIteratorObject", + JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) +}; + +// Async Iteration proposal 11.1.3.1. +JSObject* +js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) +{ + // Step 1 (implicit). + // Done in bytecode emitted by emitAsyncIterator. + + // Steps 2-4. + return AsyncFromSyncIteratorObject::create(cx, iter); +} + +// Async Iteration proposal 11.1.3.1 steps 2-4. +/* static */ JSObject* +AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) +{ + // Step 2. + RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx, + cx->global())); + if (!proto) + return nullptr; + + RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto)); + if (!obj) + return nullptr; + + Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>(); + + // Step 3. + asyncIter->setIterator(iter); + + // Step 4. + return asyncIter; +} + +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. +static bool +AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); +} + +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. +static bool +AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); +} + +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. +static bool +AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); +} + +// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next. +static bool +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()); +} + +// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return. +static bool +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()); +} + +// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw. +static bool +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 Class AsyncGeneratorObject::class_ = { + "AsyncGenerator", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots) +}; + +// ES 2017 draft 9.1.13. +template <typename ProtoGetter> +static JSObject* +OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun, + ProtoGetter protoGetter, const Class* clasp) +{ + // 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 = protoGetter(cx, cx->global()); + if (!proto) + return nullptr; + } + + // Step 3. + return NewNativeObjectWithGivenProto(cx, clasp, proto); +} + +/* static */ AsyncGeneratorObject* +AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal) +{ + MOZ_ASSERT(generatorVal.isObject()); + MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>()); + + RootedObject obj( + cx, OrdinaryCreateFromConstructor(cx, asyncGen, + GlobalObject::getOrCreateAsyncGeneratorPrototype, + &class_)); + if (!obj) + return nullptr; + + Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>(); + + // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart. + // Step 6. + asyncGenObj->setGenerator(generatorVal); + + // Step 7. + asyncGenObj->setSuspendedStart(); + + // Step 8. + asyncGenObj->clearSingleQueueRequest(); + + return asyncGenObj; +} + +static MOZ_MUST_USE bool +InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val) +{ + uint32_t length; + if (!GetLengthProperty(cx, queue, &length)) + return false; + + if (length >= MAX_ARRAY_INDEX) { + ReportOutOfMemory(cx); + return false; + } + + if (!DefineElement(cx, queue, length, val)) + return false; + return SetLengthProperty(cx, queue, length + 1); +} + +static MOZ_MUST_USE bool +InternalDequeue(JSContext* cx, HandleArrayObject queue, MutableHandleValue val) +{ + uint32_t length; + if (!GetLengthProperty(cx, queue, &length)) + return false; + + MOZ_ASSERT(length != 0, "Queue should not be empty here"); + + if (!GetElement(cx, queue, queue, 0, val)) + return false; + + uint32_t newlength = length - 1; + RootedValue tmp(cx); + for (uint32_t i = 0; i < newlength; i++) { + if (!GetElement(cx, queue, queue, i + 1, &tmp)) + return false; + if (!DefineElement(cx, queue, i, tmp)) + return false; + } + ObjectOpResult result; + if (!DeleteElement(cx, queue, newlength, result)) + return false; + if (!result) { + RootedId id(cx, INT_TO_JSID(newlength)); + return result.reportError(cx, queue, id); + } + return SetLengthProperty(cx, queue, newlength); +} + +/* 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; + } + + RootedArrayObject queue(cx, NewDenseEmptyArray(cx)); + if (!queue) + return false; + + if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest()))) + return false; + if (!NewbornArrayPush(cx, queue, ObjectValue(*request))) + return false; + + asyncGenObj->setQueue(queue); + return true; + } + + RootedArrayObject queue(cx, asyncGenObj->queue()); + RootedValue requestVal(cx, ObjectValue(*request)); + return InternalEnqueue(cx, queue, requestVal); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + if (asyncGenObj->isSingleQueue()) { + AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest(); + asyncGenObj->clearSingleQueueRequest(); + return request; + } + + RootedArrayObject queue(cx, asyncGenObj->queue()); + RootedValue requestVal(cx); + if (!InternalDequeue(cx, queue, &requestVal)) + return nullptr; + + return &requestVal.toObject().as<AsyncGeneratorRequest>(); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj) +{ + if (asyncGenObj->isSingleQueue()) + return asyncGenObj->singleQueueRequest(); + + RootedArrayObject queue(cx, asyncGenObj->queue()); + + RootedValue requestVal(cx); + if (!GetElement(cx, queue, queue, 0, &requestVal)) + return nullptr; + + return &requestVal.toObject().as<AsyncGeneratorRequest>(); +} + +const Class AsyncGeneratorRequest::class_ = { + "AsyncGeneratorRequest", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) +}; + +// Async Iteration proposal 11.4.3.1. +/* static */ AsyncGeneratorRequest* +AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, + HandleValue completionValue_, HandleObject promise_) +{ + RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr)); + if (!obj) + return nullptr; + + Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>(); + request->setCompletionKind(completionKind_); + request->setCompletionValue(completionValue_); + request->setPromise(promise_); + return request; +} + +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g. +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); +} + +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f. +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); +} + +// Async Iteration proposal 11.4.3.7 (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); +} + +// Async Iteration proposal 4.1 Await steps 2-9. +// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g. +// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext +// steps 12-14, 16-20. +// Execution context switching is handled in generator. +MOZ_MUST_USE bool +js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue argument) +{ + RootedValue generatorVal(cx, asyncGenObj->generatorVal()); + + // 11.4.3.5 steps 12-14, 16-20. + HandlePropertyName funName = completionKind == CompletionKind::Normal + ? cx->names().StarGeneratorNext + : completionKind == CompletionKind::Throw + ? cx->names().StarGeneratorThrow + : cx->names().StarGeneratorReturn; + FixedInvokeArgs<1> args(cx); + args[0].set(argument); + RootedValue result(cx); + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { + // 11.4.3.2 step 5.d, f. + return AsyncGeneratorThrown(cx, asyncGenObj); + } + + // 4.1 steps 2-9. + if (asyncGenObj->generatorObj()->isAfterAwait()) + return AsyncGeneratorAwait(cx, asyncGenObj, result); + + // The following code corresponds to the following 3 cases: + // * yield + // * yield* + // * return + // For yield and return, property access is done on an internal result + // object and it's not observable. + // For yield*, it's done on a possibly user-provided result object, and + // it's observable. + // + // Note that IteratorComplete steps in 8.2.1 are done in bytecode. + + // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. + RootedObject resultObj(cx, &result.toObject()); + RootedValue value(cx); + + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return false; + + if (asyncGenObj->generatorObj()->isAfterYield()) + return AsyncGeneratorYield(cx, asyncGenObj, value); + + // 11.4.3.2 step 5.d-g. + return AsyncGeneratorReturned(cx, asyncGenObj, value); +} + +static const JSFunctionSpec async_iterator_proto_methods[] = { + 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", AsyncGeneratorNext, 1, 0), + JS_FN("throw", AsyncGeneratorThrow, 1, 0), + JS_FN("return", AsyncGeneratorReturn, 1, 0), + JS_FS_END +}; + +/* static */ MOZ_MUST_USE bool +GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global) +{ + if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) + return true; + + // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%. + RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global)); + if (!asyncIterProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) + return false; + + // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%. + RootedObject asyncFromSyncIterProto( + cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &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; + } + + // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%. + RootedObject asyncGenProto( + cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, + asyncIterProto)); + if (!asyncGenProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) || + !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) + { + return false; + } + + // Async Iteration proposal 11.3.3 %AsyncGenerator%. + RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); + if (!asyncGenerator) + return false; + if (!JSObject::setDelegate(cx, asyncGenerator)) + return false; + if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY, + JSPROP_READONLY) || + !DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction)) + { + return false; + } + + RootedValue function(cx, global->getConstructor(JSProto_Function)); + if (!function.toObjectOrNull()) + return false; + RootedObject proto(cx, &function.toObject()); + RootedAtom name(cx, cx->names().AsyncGeneratorFunction); + + // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%. + RootedObject asyncGenFunction( + cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR, + nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject)); + if (!asyncGenFunction) + return false; + if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { + return false; + } + + global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto)); + global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto)); + global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator)); + global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction)); + global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto)); + return true; +} diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h new file mode 100644 index 000000000..58c43131b --- /dev/null +++ b/js/src/vm/AsyncIteration.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +#ifndef vm_AsyncIteration_h +#define vm_AsyncIteration_h + +#include "jscntxt.h" +#include "jsobj.h" + +#include "builtin/Promise.h" +#include "vm/GeneratorObject.h" + +namespace js { + +// Async generator consists of 2 functions, |wrapped| and |unwrapped|. +// |unwrapped| is a generator function compiled from async generator script, +// |await| behaves just like |yield| there. |unwrapped| isn't exposed to user +// script. +// |wrapped| is a native function that is the value of async generator. + +JSObject* +WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto); + +JSObject* +WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped); + +bool +IsWrappedAsyncGenerator(JSFunction* fun); + +JSFunction* +GetWrappedAsyncGenerator(JSFunction* unwrapped); + +JSFunction* +GetUnwrappedAsyncGenerator(JSFunction* wrapped); + +MOZ_MUST_USE bool +AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle<AsyncGeneratorObject*> asyncGenObj, + HandleValue reason); + +class AsyncGeneratorRequest : public NativeObject +{ + private: + enum AsyncGeneratorRequestSlots { + Slot_CompletionKind = 0, + Slot_CompletionValue, + Slot_Promise, + Slots, + }; + + void setCompletionKind(CompletionKind completionKind_) { + setFixedSlot(Slot_CompletionKind, + Int32Value(static_cast<int32_t>(completionKind_))); + } + void setCompletionValue(HandleValue completionValue_) { + setFixedSlot(Slot_CompletionValue, completionValue_); + } + void setPromise(HandleObject promise_) { + setFixedSlot(Slot_Promise, ObjectValue(*promise_)); + } + + public: + static const Class class_; + + static AsyncGeneratorRequest* + create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue, + HandleObject promise); + + CompletionKind completionKind() const { + return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32()); + } + JS::Value completionValue() const { + return getFixedSlot(Slot_CompletionValue); + } + JSObject* promise() const { + return &getFixedSlot(Slot_Promise).toObject(); + } +}; + +class AsyncGeneratorObject : public NativeObject +{ + private: + enum AsyncGeneratorObjectSlots { + Slot_State = 0, + Slot_Generator, + Slot_QueueOrRequest, + Slots + }; + + enum State { + State_SuspendedStart, + State_SuspendedYield, + State_Executing, + // State_AwaitingYieldReturn corresponds to the case that + // AsyncGenerator#return is called while State_Executing, + // just like the case that AsyncGenerator#return is called + // while State_Completed. + State_AwaitingYieldReturn, + State_AwaitingReturn, + State_Completed + }; + + State state() const { + return static_cast<State>(getFixedSlot(Slot_State).toInt32()); + } + void setState(State state_) { + setFixedSlot(Slot_State, Int32Value(state_)); + } + + void setGenerator(const Value& value) { + setFixedSlot(Slot_Generator, value); + } + + // Queue is implemented in 2 ways. If only one request is queued ever, + // request is stored directly to the slot. Once 2 requests are queued, an + // array is created and requests are pushed into it, and the array is + // stored to the slot. + + bool isSingleQueue() const { + return getFixedSlot(Slot_QueueOrRequest).isNull() || + getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>(); + } + bool isSingleQueueEmpty() const { + return getFixedSlot(Slot_QueueOrRequest).isNull(); + } + void setSingleQueueRequest(AsyncGeneratorRequest* request) { + setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request)); + } + void clearSingleQueueRequest() { + setFixedSlot(Slot_QueueOrRequest, NullHandleValue); + } + AsyncGeneratorRequest* singleQueueRequest() const { + return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>(); + } + + ArrayObject* queue() const { + return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>(); + } + void setQueue(JSObject* queue_) { + setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_)); + } + + public: + static const Class class_; + + static AsyncGeneratorObject* + create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal); + + bool isSuspendedStart() const { + return state() == State_SuspendedStart; + } + bool isSuspendedYield() const { + return state() == State_SuspendedYield; + } + bool isExecuting() const { + return state() == State_Executing; + } + bool isAwaitingYieldReturn() const { + return state() == State_AwaitingYieldReturn; + } + bool isAwaitingReturn() const { + return state() == State_AwaitingReturn; + } + bool isCompleted() const { + return state() == State_Completed; + } + + void setSuspendedStart() { + setState(State_SuspendedStart); + } + void setSuspendedYield() { + setState(State_SuspendedYield); + } + void setExecuting() { + setState(State_Executing); + } + void setAwaitingYieldReturn() { + setState(State_AwaitingYieldReturn); + } + void setAwaitingReturn() { + setState(State_AwaitingReturn); + } + void setCompleted() { + setState(State_Completed); + } + + JS::Value generatorVal() const { + return getFixedSlot(Slot_Generator); + } + GeneratorObject* generatorObj() const { + return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>(); + } + + static MOZ_MUST_USE bool + enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + Handle<AsyncGeneratorRequest*> request); + + static AsyncGeneratorRequest* + dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); + + static AsyncGeneratorRequest* + peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj); + + bool isQueueEmpty() const { + if (isSingleQueue()) + return isSingleQueueEmpty(); + return queue()->length() == 0; + } +}; + +JSObject* +CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter); + +class AsyncFromSyncIteratorObject : public NativeObject +{ + private: + enum AsyncFromSyncIteratorObjectSlots { + Slot_Iterator = 0, + Slots + }; + + void setIterator(HandleObject iterator_) { + setFixedSlot(Slot_Iterator, ObjectValue(*iterator_)); + } + + public: + static const Class class_; + + static JSObject* + create(JSContext* cx, HandleObject iter); + + JSObject* iterator() const { + return &getFixedSlot(Slot_Iterator).toObject(); + } +}; + +MOZ_MUST_USE bool +AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, + CompletionKind completionKind, HandleValue argument); + +} // namespace js + +#endif /* vm_AsyncIteration_h */ diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 99cb02e58..6a8afb56b 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -29,7 +29,10 @@ macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \ macro(as, as, "as") \ macro(Async, Async, "Async") \ + macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \ macro(AsyncFunction, AsyncFunction, "AsyncFunction") \ + macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \ + macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \ macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \ macro(async, async, "async") \ macro(await, await, "await") \ @@ -311,6 +314,7 @@ macro(star, star, "*") \ macro(starDefaultStar, starDefaultStar, "*default*") \ macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \ + macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \ macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \ macro(startTimestamp, startTimestamp, "startTimestamp") \ macro(state, state, "state") \ diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d68d1b75e..2680699a4 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -32,7 +32,6 @@ #include "js/Vector.h" #include "proxy/ScriptedProxyHandler.h" #include "vm/ArgumentsObject.h" -#include "vm/AsyncFunction.h" #include "vm/DebuggerMemory.h" #include "vm/GeneratorObject.h" #include "vm/SPSProfiler.h" @@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleVa JSTrapStatus status, MutableHandleValue vp) { if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) { - // Don't let a { return: ... } resumption value make a generator or - // async function violate the iterator protocol. The return value from + // Don't let a { return: ... } resumption value make a generator + // function violate the iterator protocol. The return value from // such a frame must have the form { done: <bool>, value: <anything> }. RootedFunction callee(cx, frame.callee()); - if (callee->isAsync()) { - if (!CheckAsyncResumptionValue(cx, vp)) { - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT); - return false; - } - } else if (callee->isStarGenerator()) { + if (callee->isStarGenerator()) { if (!CheckStarGeneratorResumptionValue(cx, vp)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD); return false; @@ -7356,7 +7350,8 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame, /* static */ bool DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame) { - return DebuggerFrame::getReferent(frame).script()->isGenerator(); + return DebuggerFrame::getReferent(frame).script()->isStarGenerator() || + DebuggerFrame::getReferent(frame).script()->isLegacyGenerator(); } /* static */ bool diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp index c95bb0597..7834940f1 100644 --- a/js/src/vm/EnvironmentObject.cpp +++ b/js/src/vm/EnvironmentObject.cpp @@ -2448,7 +2448,9 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei, MOZ_ASSERT(cx->compartment() == debugEnv->compartment()); // Generators should always have environments. MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(), - !ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator()); + !ei.scope().as<FunctionScope>().canonicalFunction()->isStarGenerator() && + !ei.scope().as<FunctionScope>().canonicalFunction()->isLegacyGenerator() && + !ei.scope().as<FunctionScope>().canonicalFunction()->isAsync()); if (!CanUseDebugEnvironmentMaps(cx)) return true; @@ -2594,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) if (!frame.environmentChain()->is<CallObject>()) return; - if (frame.callee()->isGenerator()) + if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() || + frame.callee()->isAsync()) + { return; + } CallObject& callobj = frame.environmentChain()->as<CallObject>(); envs->liveEnvs.remove(&callobj); @@ -2726,8 +2731,13 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx) if (frame.environmentChain()->compartment() != cx->compartment()) continue; - if (frame.isFunctionFrame() && frame.callee()->isGenerator()) - continue; + if (frame.isFunctionFrame()) { + if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() || + frame.callee()->isAsync()) + { + continue; + } + } if (!frame.isDebuggee()) continue; @@ -2882,7 +2892,8 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei) if (ei.scope().is<FunctionScope>()) { RootedFunction callee(cx, ei.scope().as<FunctionScope>().canonicalFunction()); // Generators should always reify their scopes. - MOZ_ASSERT(!callee->isGenerator()); + MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() && + !callee->isAsync()); JS::ExposeObjectToActiveJS(callee); Rooted<CallObject*> callobj(cx, CallObject::createHollowForDebug(cx, callee)); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index ba28501e6..2f5c63c28 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -19,12 +19,13 @@ using namespace js; JSObject* GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) { - MOZ_ASSERT(frame.script()->isGenerator()); + MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() || + frame.script()->isAsync()); MOZ_ASSERT(frame.script()->nfixed() == 0); Rooted<GlobalObject*> global(cx, cx->global()); RootedNativeObject obj(cx); - if (frame.script()->isStarGenerator()) { + if (frame.script()->isStarGenerator() || frame.script()->isAsync()) { RootedValue pval(cx); RootedObject fun(cx, frame.callee()); // FIXME: This would be faster if we could avoid doing a lookup to get @@ -63,10 +64,14 @@ bool GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc, Value* vp, unsigned nvalues) { - MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT); Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>()); MOZ_ASSERT(!genObj->hasExpressionStack()); + MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync()); + MOZ_ASSERT_IF(*pc == JSOP_YIELD, + genObj->callee().isStarGenerator() || + genObj->callee().isLegacyGenerator()); if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) { RootedValue val(cx, ObjectValue(*frame.callee())); @@ -74,8 +79,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame return false; } - uint32_t yieldIndex = GET_UINT24(pc); - genObj->setYieldIndex(yieldIndex); + uint32_t yieldAndAwaitIndex = GET_UINT24(pc); + genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex); genObj->setEnvironmentChain(*frame.environmentChain()); if (nvalues) { @@ -172,7 +177,7 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation, } JSScript* script = callee->nonLazyScript(); - uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()]; + uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()]; activation.regs().pc = script->offsetToPC(offset); // Always push on a value, even if we are raising an exception. In the @@ -311,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto)) return false; - if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) || + if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY, + JSPROP_READONLY) || !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) { return false; @@ -328,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global) SingletonObject)); if (!genFunction) return false; - if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto)) + if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto, + JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY)) + { return false; + } global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto)); global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction)); @@ -365,3 +374,34 @@ js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v) return true; } + +bool +GeneratorObject::isAfterYield() +{ + return isAfterYieldOrAwait(JSOP_YIELD); +} + +bool +GeneratorObject::isAfterAwait() +{ + return isAfterYieldOrAwait(JSOP_AWAIT); +} + +bool +GeneratorObject::isAfterYieldOrAwait(JSOp op) +{ + if (isClosed() || isClosing() || isRunning()) + return false; + + JSScript* script = callee().nonLazyScript(); + jsbytecode* code = script->code(); + uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()]; + if (code[nextOffset] != JSOP_DEBUGAFTERYIELD) + return false; + + uint32_t offset = nextOffset - JSOP_YIELD_LENGTH; + MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD || + code[offset] == JSOP_AWAIT); + + return code[offset] == op; +} diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index ca1452b34..f19ca2aac 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -21,15 +21,15 @@ class GeneratorObject : public NativeObject public: // Magic values stored in the yield index slot when the generator is // running or closing. See the yield index comment below. - static const int32_t YIELD_INDEX_RUNNING = INT32_MAX; - static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1; + static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX; + static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1; enum { CALLEE_SLOT = 0, ENV_CHAIN_SLOT, ARGS_OBJ_SLOT, EXPRESSION_STACK_SLOT, - YIELD_INDEX_SLOT, + YIELD_AND_AWAIT_INDEX_SLOT, NEWTARGET_SLOT, RESERVED_SLOTS }; @@ -124,47 +124,48 @@ class GeneratorObject : public NativeObject // The yield index slot is abused for a few purposes. It's undefined if // it hasn't been set yet (before the initial yield), and null if the // generator is closed. If the generator is running, the yield index is - // YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing" - // state, the yield index is YIELD_INDEX_CLOSING. + // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre + // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING. // // If the generator is suspended, it's the yield index (stored as - // JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction that - // suspended the generator. The yield index can be mapped to the bytecode - // offset (interpreter) or to the native code offset (JIT). + // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield + // instruction that suspended the generator. The yield index can be mapped + // to the bytecode offset (interpreter) or to the native code offset (JIT). bool isRunning() const { MOZ_ASSERT(!isClosed()); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING; + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING; } bool isClosing() const { - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING; + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING; } bool isSuspended() const { // Note: also update Baseline's IsSuspendedStarGenerator code if this // changes. MOZ_ASSERT(!isClosed()); - static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING, - "test below should return false for YIELD_INDEX_RUNNING"); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING; + static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING, + "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING"); + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING; } void setRunning() { MOZ_ASSERT(isSuspended()); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING)); } void setClosing() { MOZ_ASSERT(isSuspended()); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING)); - } - void setYieldIndex(uint32_t yieldIndex) { - MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined()); - MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing()); - MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING)); - setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING)); + } + void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) { + MOZ_ASSERT_IF(yieldAndAwaitIndex == 0, + getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined()); + MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing()); + MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex)); MOZ_ASSERT(isSuspended()); } - uint32_t yieldIndex() const { + uint32_t yieldAndAwaitIndex() const { MOZ_ASSERT(isSuspended()); - return getFixedSlot(YIELD_INDEX_SLOT).toInt32(); + return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32(); } bool isClosed() const { return getFixedSlot(CALLEE_SLOT).isNull(); @@ -174,10 +175,17 @@ class GeneratorObject : public NativeObject setFixedSlot(ENV_CHAIN_SLOT, NullValue()); setFixedSlot(ARGS_OBJ_SLOT, NullValue()); setFixedSlot(EXPRESSION_STACK_SLOT, NullValue()); - setFixedSlot(YIELD_INDEX_SLOT, NullValue()); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue()); setFixedSlot(NEWTARGET_SLOT, NullValue()); } + bool isAfterYield(); + bool isAfterAwait(); + +private: + bool isAfterYieldOrAwait(JSOp op); + +public: static size_t offsetOfCalleeSlot() { return getFixedSlotOffset(CALLEE_SLOT); } @@ -187,8 +195,8 @@ class GeneratorObject : public NativeObject static size_t offsetOfArgsObjSlot() { return getFixedSlotOffset(ARGS_OBJ_SLOT); } - static size_t offsetOfYieldIndexSlot() { - return getFixedSlotOffset(YIELD_INDEX_SLOT); + static size_t offsetOfYieldAndAwaitIndexSlot() { + return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT); } static size_t offsetOfExpressionStackSlot() { return getFixedSlotOffset(EXPRESSION_STACK_SLOT); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 013208f66..70c33c647 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -586,17 +586,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject* } bool -js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_) +js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_, + unsigned prototypeAttrs, unsigned constructorAttrs) { RootedObject ctor(cx, ctor_), proto(cx, proto_); RootedValue protoVal(cx, ObjectValue(*proto)); RootedValue ctorVal(cx, ObjectValue(*ctor)); - return DefineProperty(cx, ctor, cx->names().prototype, protoVal, - nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) && - DefineProperty(cx, proto, cx->names().constructor, ctorVal, - nullptr, nullptr, 0); + return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr, + prototypeAttrs) && + DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr, + constructorAttrs); } bool diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 9179abbb7..3fd2762f8 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -100,6 +100,11 @@ class GlobalObject : public NativeObject STAR_GENERATOR_FUNCTION, ASYNC_FUNCTION_PROTO, ASYNC_FUNCTION, + ASYNC_ITERATOR_PROTO, + ASYNC_FROM_SYNC_ITERATOR_PROTO, + ASYNC_GENERATOR, + ASYNC_GENERATOR_FUNCTION, + ASYNC_GENERATOR_PROTO, MAP_ITERATOR_PROTO, SET_ITERATOR_PROTO, COLLATOR_PROTO, @@ -624,6 +629,36 @@ class GlobalObject : public NativeObject return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction); } + static NativeObject* + getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) + { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + + static NativeObject* + getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + + static NativeObject* + getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR, + initAsyncGenerators)); + } + + static JSObject* + getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) { + return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators); + } + + static NativeObject* + getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO, + initAsyncGenerators)); + } + static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) { return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto); @@ -782,6 +817,8 @@ class GlobalObject : public NativeObject static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global); + static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global); + // Implemented in builtin/MapObject.cpp. static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global); static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global); @@ -985,12 +1022,14 @@ GlobalObject::createArrayFromBuffer<uint8_clamped>() const } /* - * Define ctor.prototype = proto as non-enumerable, non-configurable, and - * non-writable; define proto.constructor = ctor as non-enumerable but - * configurable and writable. + * Unless otherwise specified, define ctor.prototype = proto as non-enumerable, + * non-configurable, and non-writable; and define proto.constructor = ctor as + * non-enumerable but configurable and writable. */ extern bool -LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto); +LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto, + unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY, + unsigned constructorAttrs = 0); /* * Define properties and/or functions on any object. Either ps or fs, or both, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 030f0f3b6..3cf9b57f6 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -39,6 +39,7 @@ #include "jit/Ion.h" #include "jit/IonAnalysis.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Debugger.h" #include "vm/GeneratorObject.h" #include "vm/Opcodes.h" @@ -1939,9 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode) CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) CASE(JSOP_UNUSED126) -CASE(JSOP_UNUSED192) -CASE(JSOP_UNUSED209) -CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) CASE(JSOP_UNUSED221) @@ -3585,6 +3583,29 @@ CASE(JSOP_TOASYNC) } END_CASE(JSOP_TOASYNC) +CASE(JSOP_TOASYNCGEN) +{ + ReservedRooted<JSFunction*> unwrapped(&rootFunction0, + ®S.sp[-1].toObject().as<JSFunction>()); + JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped); + if (!wrapped) + goto error; + + REGS.sp[-1].setObject(*wrapped); +} +END_CASE(JSOP_TOASYNCGEN) + +CASE(JSOP_TOASYNCITER) +{ + ReservedRooted<JSObject*> iter(&rootObject1, ®S.sp[-1].toObject()); + JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter); + if (!asyncIter) + goto error; + + REGS.sp[-1].setObject(*asyncIter); +} +END_CASE(JSOP_TOASYNCITER) + CASE(JSOP_SETFUNNAME) { MOZ_ASSERT(REGS.stackDepth() >= 2); @@ -3994,6 +4015,7 @@ CASE(JSOP_INITIALYIELD) } CASE(JSOP_YIELD) +CASE(JSOP_AWAIT) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isFunctionFrame()); @@ -5126,6 +5148,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind) case CheckIsObjectKind::GetIterator: JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE); break; + case CheckIsObjectKind::GetAsyncIterator: + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE); + break; default: MOZ_CRASH("Unknown kind"); } diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h index 9fefd75cc..5e7087f7f 100644 --- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -561,7 +561,8 @@ enum class CheckIsObjectKind : uint8_t { IteratorNext, IteratorReturn, IteratorThrow, - GetIterator + GetIterator, + GetAsyncIterator }; bool diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index ec0a7aec1..91070b3f6 100644 --- a/js/src/vm/ObjectGroup.cpp +++ b/js/src/vm/ObjectGroup.cpp @@ -184,7 +184,7 @@ ObjectGroup::useSingletonForNewObject(JSContext* cx, JSScript* script, jsbytecod * Sub2 lets us continue to distinguish the two subclasses and any extra * properties added to those prototype objects. */ - if (script->isGenerator()) + if (script->isStarGenerator() || script->isLegacyGenerator() || script->isAsync()) return false; if (JSOp(*pc) != JSOP_NEW) return false; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 3c4d61a67..e13cd6221 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1952,7 +1952,15 @@ * Stack: this => this */ \ macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1, 1, 1, JOF_BYTE) \ - macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top of stack value as 'unwrapped', converts it to async + * generator 'wrapped', and pushes 'wrapped' back on the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: unwrapped => wrapped + */ \ + macro(JSOP_TOASYNCGEN, 192, "toasyncgen", NULL, 1, 1, 1, JOF_BYTE) \ \ /* * Pops the top two values on the stack as 'propval' and 'obj', pushes @@ -2055,7 +2063,7 @@ * interpretation. * Category: Statements * Type: Generator - * Operands: uint24_t yieldIndex + * Operands: uint24_t yieldAndAwaitIndex * Stack: generator => */ \ macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 4, 1, 1, JOF_UINT24) \ @@ -2064,7 +2072,7 @@ * returns 'rval1'. Pushes sent value from 'send()' onto the stack. * Category: Statements * Type: Generator - * Operands: uint24_t yieldIndex + * Operands: uint24_t yieldAndAwaitIndex * Stack: rval1, gen => rval2 */ \ macro(JSOP_YIELD, 203,"yield", NULL, 4, 2, 1, JOF_UINT24) \ @@ -2119,8 +2127,24 @@ * Stack: => */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the generator and the return value 'promise', stops interpretation + * and returns 'promise'. Pushes resolved value onto the stack. + * Category: Statements + * Type: Generator + * Operands: uint24_t yieldAndAwaitIndex + * Stack: promise, gen => resolved + */ \ + macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ + /* + * Pops the iterator from the top of the stack, and create async iterator + * from it and push the async iterator back onto the stack. + * Category: Statements + * Type: Generator + * Operands: + * Stack: iter => asynciter + */ \ + macro(JSOP_TOASYNCITER, 210, "toasynciter", NULL, 1, 1, 1, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ /* * Initializes generator frame, creates a generator and pushes it on the diff --git a/js/src/vm/Probes-inl.h b/js/src/vm/Probes-inl.h index 347f842b8..822a8ac59 100644 --- a/js/src/vm/Probes-inl.h +++ b/js/src/vm/Probes-inl.h @@ -41,7 +41,10 @@ probes::EnterScript(JSContext* cx, JSScript* script, JSFunction* maybeFun, if (rt->spsProfiler.enabled()) { if (!rt->spsProfiler.enter(cx, script, maybeFun)) return false; - MOZ_ASSERT_IF(!fp->script()->isGenerator(), !fp->hasPushedSPSFrame()); + MOZ_ASSERT_IF(!fp->script()->isStarGenerator() && + !fp->script()->isLegacyGenerator() && + !fp->script()->isAsync(), + !fp->hasPushedSPSFrame()); fp->setPushedSPSFrame(); } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index ffd707b14..2216bf91e 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -27,6 +27,7 @@ #include "builtin/MapObject.h" #include "builtin/ModuleObject.h" #include "builtin/Object.h" +#include "builtin/Promise.h" #include "builtin/Reflect.h" #include "builtin/SelfHostingDefines.h" #include "builtin/SIMD.h" @@ -2135,6 +2136,105 @@ intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp) return true; } +static bool +intrinsic_CreatePendingPromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 0); + RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx)); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_CreatePromiseResolvedWith(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, args[0])); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_CreatePromiseRejectedWith(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + RootedObject promise(cx, PromiseObject::unforgeableReject(cx, args[0])); + if (!promise) + return false; + args.rval().setObject(*promise); + return true; +} + +static bool +intrinsic_ResolvePromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>()); + if (!PromiseObject::resolve(cx, promise, args[1])) + return false; + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_RejectPromise(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 2); + Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>()); + if (!PromiseObject::reject(cx, promise, args[1])) + return false; + args.rval().setUndefined(); + return true; +} + +static bool +intrinsic_CallOriginalPromiseThen(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 2); + + RootedObject promise(cx, &args[0].toObject()); + Value val = args[1]; + RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + val = args.get(2); + RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + + RootedObject resultPromise(cx, JS::CallOriginalPromiseThen(cx, promise, onResolvedObj, + onRejectedObj)); + if (!resultPromise) + return false; + args.rval().setObject(*resultPromise); + return true; +} + +static bool +intrinsic_AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() >= 2); + + RootedObject promise(cx, &args[0].toObject()); + Value val = args[1]; + RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + val = args.get(2); + RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull()); + + bool result = JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj); + if (!result) + return false; + args.rval().setUndefined(); + return true; +} + // The self-hosting global isn't initialized with the normal set of builtins. // Instead, individual C++-implemented functions that're required by // self-hosted code are defined as global functions. Accessing these @@ -2536,6 +2636,14 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0), JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0), + JS_FN("CreatePendingPromise", intrinsic_CreatePendingPromise, 0, 0), + JS_FN("CreatePromiseResolvedWith", intrinsic_CreatePromiseResolvedWith, 1, 0), + JS_FN("CreatePromiseRejectedWith", intrinsic_CreatePromiseRejectedWith, 1, 0), + JS_FN("ResolvePromise", intrinsic_ResolvePromise, 2, 0), + JS_FN("RejectPromise", intrinsic_RejectPromise, 2, 0), + JS_FN("AddPromiseReactions", intrinsic_AddPromiseReactions, 3, 0), + JS_FN("CallOriginalPromiseThen", intrinsic_CallOriginalPromiseThen, 3, 0), + JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0), JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0), JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0), @@ -3005,7 +3113,8 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name, return false; // JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there // aren't any. - MOZ_ASSERT(!sourceFun->isGenerator()); + MOZ_ASSERT(!sourceFun->isStarGenerator() && !sourceFun->isLegacyGenerator() && + !sourceFun->isAsync()); MOZ_ASSERT(targetFun->isExtended()); MOZ_ASSERT(targetFun->isInterpretedLazy()); MOZ_ASSERT(targetFun->isSelfHostedBuiltin()); diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 11a19d175..2f2b591e5 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -335,7 +335,7 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, HandleFunction callee, HandleValue newTarget, HandleObject envChain) { - MOZ_ASSERT(callee->isGenerator()); + MOZ_ASSERT(callee->isStarGenerator() || callee->isLegacyGenerator() || callee->isAsync()); RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee)); InterpreterFrame* prev = regs.fp(); jsbytecode* prevpc = regs.pc; diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index c5f2cf5f3..95940eeaf 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -270,7 +270,9 @@ InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) UnwindAllEnvironmentsInFrame(cx, ei); if (isFunctionFrame()) { - if (!callee().isGenerator() && + if (!callee().isStarGenerator() && + !callee().isLegacyGenerator() && + !callee().isAsync() && isConstructing() && thisArgument().isObject() && returnValue().isPrimitive()) diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 23e621344..fe04a00f2 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -693,7 +693,8 @@ class InterpreterFrame } void resumeGeneratorFrame(JSObject* envChain) { - MOZ_ASSERT(script()->isGenerator()); + MOZ_ASSERT(script()->isStarGenerator() || script()->isLegacyGenerator() || + script()->isAsync()); MOZ_ASSERT(isFunctionFrame()); flags_ |= HAS_INITIAL_ENV; envChain_ = envChain; |