From 300f97150c4b90e4952f5d90b7e493b915ee09fd Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 19:52:52 -0500 Subject: [PATCH 01/32] Bug 1317376 - Part 1: Remove unreachable code and remnants from the self-hosted implementation. Tag #1287 --- js/src/builtin/Promise.cpp | 93 +++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 52 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 1d068f8c64..18a20ccab2 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -31,10 +31,12 @@ MillisecondsSinceStartup() return (now - mozilla::TimeStamp::ProcessCreation(ignored)).ToMilliseconds(); } -#define PROMISE_HANDLER_IDENTITY 0 -#define PROMISE_HANDLER_THROWER 1 -#define PROMISE_HANDLER_AWAIT_FULFILLED 2 -#define PROMISE_HANDLER_AWAIT_REJECTED 3 +enum PromiseHandler { + PromiseHandlerIdentity = 0, + PromiseHandlerThrower, + PromiseHandlerAwaitFulfilled, + PromiseHandlerAwaitRejected, +}; enum ResolutionMode { ResolveMode, @@ -47,9 +49,8 @@ enum ResolveFunctionSlots { }; enum RejectFunctionSlots { - RejectFunctionSlot_Promise = ResolveFunctionSlot_Promise, + RejectFunctionSlot_Promise = 0, RejectFunctionSlot_ResolveFunction, - RejectFunctionSlot_PromiseAllData = RejectFunctionSlot_ResolveFunction, }; enum PromiseAllResolveElementFunctionSlots { @@ -86,7 +87,7 @@ class PromiseAllDataHolder : public NativeObject static const Class class_; JSObject* promiseObj() { return &getFixedSlot(PromiseAllDataHolderSlot_Promise).toObject(); } JSObject* resolveObj() { - return getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObjectOrNull(); + return &getFixedSlot(PromiseAllDataHolderSlot_ResolveFunction).toObject(); } Value valuesArray() { return getFixedSlot(PromiseAllDataHolderSlot_ValuesArray); } int32_t remainingCount() { @@ -126,7 +127,7 @@ NewPromiseAllDataHolder(JSContext* cx, HandleObject resultPromise, HandleValue v dataHolder->setFixedSlot(PromiseAllDataHolderSlot_Promise, ObjectValue(*resultPromise)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_RemainingElements, Int32Value(1)); dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ValuesArray, valuesArray); - dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectOrNullValue(resolve)); + dataHolder->setFixedSlot(PromiseAllDataHolderSlot_ResolveFunction, ObjectValue(*resolve)); return dataHolder; } @@ -277,12 +278,6 @@ CreateResolvingFunctions(JSContext* cx, HandleValue promise, if (!reject) return false; - // TODO: remove this once all self-hosted promise code is gone. - // The resolving functions are trusted, so self-hosted code should be able - // to call them using callFunction instead of callContentFunction. - resolve->setFlags(resolve->flags() | JSFunction::SELF_HOSTED); - reject->setFlags(reject->flags() | JSFunction::SELF_HOSTED); - resolve->setExtendedSlot(ResolveFunctionSlot_Promise, promise); resolve->setExtendedSlot(ResolveFunctionSlot_RejectFunction, ObjectValue(*reject)); @@ -402,10 +397,9 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise)); // Steps 3-4. - // We use the reference to the reject function as a signal for whether - // the resolve or reject function was already called, at which point - // the references on each of the functions are cleared. - if (!resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction).isObject()) { + // If the Promise isn't available anymore, it has been rejected and the + // reference to it removed to make it eligible for collection. + if (promiseVal.isUndefined()) { args.rval().setUndefined(); return true; } @@ -843,12 +837,12 @@ AwaitPromiseReactionJob(JSContext* cx, Handle reaction, RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED - || handlerNum == PROMISE_HANDLER_AWAIT_REJECTED); + MOZ_ASSERT(handlerNum == PromiseHandlerAwaitFulfilled + || handlerNum == PromiseHandlerAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED) { + if (handlerNum == PromiseHandlerAwaitFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { @@ -919,11 +913,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) int32_t handlerNum = int32_t(handlerVal.toNumber()); // Step 4. - if (handlerNum == PROMISE_HANDLER_IDENTITY) { + if (handlerNum == PromiseHandlerIdentity) { handlerResult = argument; } else { // Step 5. - MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER); + MOZ_ASSERT(handlerNum == PromiseHandlerThrower); resolutionMode = RejectMode; handlerResult = argument; } @@ -964,8 +958,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) * * Usage of the function's extended slots is as follows: * ThenableJobSlot_Handler: The handler to use as the Promise reaction. - * This can be PROMISE_HANDLER_IDENTITY, - * PROMISE_HANDLER_THROWER, or a callable. In the + * This can be PromiseHandlerIdentity, + * PromiseHandlerThrower, or a callable. In the * latter case, it's guaranteed to be an object * from the same compartment as the * PromiseReactionJob. @@ -1100,11 +1094,17 @@ GetResolveFunctionFromReject(JSFunction* reject) { MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction); Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction); - if (IsNativeFunction(resolveFunVal, ResolvePromiseFunction)) - return &resolveFunVal.toObject().as(); + MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction)); + return &resolveFunVal.toObject().as(); +} - PromiseAllDataHolder* resolveFunObj = &resolveFunVal.toObject().as(); - return &resolveFunObj->resolveObj()->as(); +static JSFunction* +GetRejectFunctionFromResolve(JSFunction* resolve) +{ + MOZ_ASSERT(resolve->maybeNative() == ResolvePromiseFunction); + Value rejectFunVal = resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction); + MOZ_ASSERT(IsNativeFunction(rejectFunVal, RejectPromiseFunction)); + return &rejectFunVal.toObject().as(); } static JSFunction* @@ -1140,8 +1140,7 @@ ClearResolutionFunctionSlots(JSFunction* resolutionFun) JSFunction* reject; if (resolutionFun->maybeNative() == ResolvePromiseFunction) { resolve = resolutionFun; - reject = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction) - .toObject().as(); + reject = GetRejectFunctionFromResolve(resolutionFun); } else { resolve = GetResolveFunctionFromReject(resolutionFun); reject = resolutionFun; @@ -1585,7 +1584,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (promise->state() != JS::PromiseState::Pending) return true; - if (mode == ResolveMode) { if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION)) return true; @@ -1595,7 +1593,6 @@ RunResolutionFunction(JSContext *cx, HandleObject resolutionFun, HandleValue res if (!PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION)) return true; return RejectMaybeWrappedPromise(cx, promiseObj, result); - } // ES2016, 25.4.4.1.1. @@ -1669,7 +1666,7 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Step 6. RootedValue nextValue(cx); RootedId indexId(cx); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -1690,11 +1687,8 @@ PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, // Steps d.iii-iv. if (remainingCount == 0) { - if (resolve) { - return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, - promiseObj); - } - return ResolvePromiseInternal(cx, promiseObj, valuesArrayVal); + return RunResolutionFunction(cx, resolve, valuesArrayVal, ResolveMode, + promiseObj); } // We're all set for now! @@ -1817,13 +1811,8 @@ PromiseAllResolveElementFunction(JSContext* cx, unsigned argc, Value* vp) // Step 6 (Adapted to work with PromiseAllDataHolder's layout). RootedObject resolveAllFun(cx, data->resolveObj()); RootedObject promiseObj(cx, data->promiseObj()); - if (!resolveAllFun) { - if (!FulfillMaybeWrappedPromise(cx, promiseObj, valuesVal)) - return false; - } else { - if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj)) - return false; - } + if (!RunResolutionFunction(cx, resolveAllFun, valuesVal, ResolveMode, promiseObj)) + return false; } // Step 11. @@ -1904,8 +1893,8 @@ PerformPromiseRace(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, RootedValue CVal(cx, ObjectValue(*C)); RootedValue nextValue(cx); - RootedValue resolveFunVal(cx, ObjectOrNullValue(resolve)); - RootedValue rejectFunVal(cx, ObjectOrNullValue(reject)); + RootedValue resolveFunVal(cx, ObjectValue(*resolve)); + RootedValue rejectFunVal(cx, ObjectValue(*reject)); while (true) { // Steps a-c, e-g. @@ -2211,8 +2200,8 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand return false; // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PROMISE_HANDLER_AWAIT_FULFILLED)); - RootedValue onRejected(cx, Int32Value(PROMISE_HANDLER_AWAIT_REJECTED)); + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAwaitFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAwaitRejected)); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) @@ -2291,12 +2280,12 @@ PerformPromiseThen(JSContext* cx, Handle promise, HandleValue on // Step 3. RootedValue onFulfilled(cx, onFulfilled_); if (!IsCallable(onFulfilled)) - onFulfilled = Int32Value(PROMISE_HANDLER_IDENTITY); + onFulfilled = Int32Value(PromiseHandlerIdentity); // Step 4. RootedValue onRejected(cx, onRejected_); if (!IsCallable(onRejected)) - onRejected = Int32Value(PROMISE_HANDLER_THROWER); + onRejected = Int32Value(PromiseHandlerThrower); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) From 70cbae88eaac0209db0f67f0e4c92cbbe39a150d Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 19:57:31 -0500 Subject: [PATCH 02/32] Bug 1317376 - Part 2: Detect Promise self-resolution when resolving through the Promise resolving fast path. Tag #1287 --- js/src/builtin/Promise.cpp | 32 +++++++++------------ js/src/tests/ecma_6/Promise/self-resolve.js | 20 +++++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 18a20ccab2..2e4529140c 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -346,16 +346,28 @@ static MOZ_MUST_USE bool EnqueuePromiseResolveThenableJob(JSContext* cx, HandleValue thenable, HandleValue thenVal); -// ES2016, 25.4.1.3.2, steps 7-13. +// ES2016, 25.4.1.3.2, steps 6-13. static MOZ_MUST_USE bool ResolvePromiseInternal(JSContext* cx, HandleObject promise, HandleValue resolutionVal) { - // Step 7. + // Step 7 (reordered). if (!resolutionVal.isObject()) return FulfillMaybeWrappedPromise(cx, promise, resolutionVal); RootedObject resolution(cx, &resolutionVal.toObject()); + // Step 6. + if (resolution == promise) { + // Step 6.a. + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF); + RootedValue selfResolutionError(cx); + MOZ_ALWAYS_TRUE(GetAndClearException(cx, &selfResolutionError)); + + // Step 6.b. + return RejectMaybeWrappedPromise(cx, promise, selfResolutionError); + } + // Step 8. RootedValue thenVal(cx); bool status = GetProperty(cx, resolution, resolution, cx->names().then, &thenVal); @@ -421,22 +433,6 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) // functions. Actually marking it as fulfilled/rejected happens later. ClearResolutionFunctionSlots(resolve); - // Step 6. - if (resolutionVal == promiseVal) { - // Step 6.a. - JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, - JSMSG_CANNOT_RESOLVE_PROMISE_WITH_ITSELF); - RootedValue selfResolutionError(cx); - bool status = GetAndClearException(cx, &selfResolutionError); - MOZ_ASSERT(status); - - // Step 6.b. - status = RejectMaybeWrappedPromise(cx, promise, selfResolutionError); - if (status) - args.rval().setUndefined(); - return status; - } - bool status = ResolvePromiseInternal(cx, promise, resolutionVal); if (status) args.rval().setUndefined(); diff --git a/js/src/tests/ecma_6/Promise/self-resolve.js b/js/src/tests/ecma_6/Promise/self-resolve.js index e16a2ceb3d..4e7e36c6ca 100644 --- a/js/src/tests/ecma_6/Promise/self-resolve.js +++ b/js/src/tests/ecma_6/Promise/self-resolve.js @@ -5,6 +5,7 @@ if (!this.Promise) { quit(0); } +// Resolve Promise with itself by directly calling the "Promise Resolve Function". let resolve; let promise = new Promise(function(x) { resolve = x; }); resolve(promise) @@ -20,4 +21,23 @@ drainJobQueue() assertEq(results.length, 1); assertEq(results[0], "rejected"); +// Resolve Promise with itself when the "Promise Resolve Function" is called +// from (the fast path in) PromiseReactionJob. +results = []; + +promise = new Promise(x => { resolve = x; }); +let promise2 = promise.then(() => promise2); + +promise2.then(() => assertEq(true, false, "not reached"), res => { + assertEq(res instanceof TypeError, true); + results.push("rejected"); +}); + +resolve(); + +drainJobQueue(); + +assertEq(results.length, 1); +assertEq(results[0], "rejected"); + this.reportCompare && reportCompare(0, 0, "ok"); From f15e675b86622b05ea4708dfdc368e8c19aa0dfa Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:13:07 -0500 Subject: [PATCH 03/32] Bug 336705 - Part 1: Support creating and resolving Promises without resolve/reject functions. Useful for internally-created Promises that'll only ever be resolved/rejected internally. Tag #1287 --- js/src/builtin/Promise.cpp | 86 ++++++++++++++++++++++---------------- js/src/builtin/Promise.h | 2 + 2 files changed, 51 insertions(+), 37 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 2e4529140c..884cd6bac9 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -314,28 +314,27 @@ RejectPromiseFunction(JSContext* cx, unsigned argc, Value* vp) return true; } + // Step 5. + // Here, we only remove the Promise reference from the resolution + // functions. Actually marking it as fulfilled/rejected happens later. + ClearResolutionFunctionSlots(reject); + RootedObject promise(cx, &promiseVal.toObject()); // In some cases the Promise reference on the resolution function won't // have been removed during resolution, so we need to check that here, // too. if (promise->is() && - PromiseHasAnyFlag(promise->as(), PROMISE_FLAG_RESOLVED)) + promise->as().state() != JS::PromiseState::Pending) { - args.rval().setUndefined(); return true; } - // Step 5. - // Here, we only remove the Promise reference from the resolution - // functions. Actually marking it as fulfilled/rejected happens later. - ClearResolutionFunctionSlots(reject); - // Step 6. - bool result = RejectMaybeWrappedPromise(cx, promise, reasonVal); - if (result) - args.rval().setUndefined(); - return result; + if (!RejectMaybeWrappedPromise(cx, promise, reasonVal)) + return false; + args.rval().setUndefined(); + return true; } static MOZ_MUST_USE bool FulfillMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, @@ -405,38 +404,37 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp) RootedFunction resolve(cx, &args.callee().as()); RootedValue resolutionVal(cx, args.get(0)); - // Steps 1-2. - RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise)); - - // Steps 3-4. - // If the Promise isn't available anymore, it has been rejected and the - // reference to it removed to make it eligible for collection. - if (promiseVal.isUndefined()) { + // Steps 3-4 (reordered). + // We use the reference to the reject function as a signal for whether + // the resolve or reject function was already called, at which point + // the references on each of the functions are cleared. + if (!resolve->getExtendedSlot(ResolveFunctionSlot_RejectFunction).isObject()) { args.rval().setUndefined(); return true; } - RootedObject promise(cx, &promiseVal.toObject()); - - // In some cases the Promise reference on the resolution function won't - // have been removed during resolution, so we need to check that here, - // too. - if (promise->is() && - PromiseHasAnyFlag(promise->as(), PROMISE_FLAG_RESOLVED)) - { - args.rval().setUndefined(); - return true; - } + // Steps 1-2 (reordered). + RootedObject promise(cx, &resolve->getExtendedSlot(ResolveFunctionSlot_Promise).toObject()); // Step 5. // Here, we only remove the Promise reference from the resolution // functions. Actually marking it as fulfilled/rejected happens later. ClearResolutionFunctionSlots(resolve); - bool status = ResolvePromiseInternal(cx, promise, resolutionVal); - if (status) - args.rval().setUndefined(); - return status; + // In some cases the Promise reference on the resolution function won't + // have been removed during resolution, so we need to check that here, + // too. + if (promise->is() && + promise->as().state() != JS::PromiseState::Pending) + { + return true; + } + + // Steps 6-13. + if (!ResolvePromiseInternal(cx, promise, resolutionVal)) + return false; + args.rval().setUndefined(); + return true; } static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp); @@ -642,7 +640,7 @@ enum GetCapabilitiesExecutorSlots { }; static MOZ_MUST_USE PromiseObject* -CreatePromiseObjectWithDefaultResolution(JSContext* cx) +CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx) { Rooted promise(cx, CreatePromiseObjectInternal(cx)); if (!promise) @@ -677,7 +675,7 @@ NewPromiseCapability(JSContext* cx, HandleObject C, MutableHandleObject promise, // in the list passed to all/race, which (potentially) means exposing them // to content. if (canOmitResolutionFunctions && IsNativeFunction(cVal, PromiseConstructor)) { - promise.set(CreatePromiseObjectWithDefaultResolution(cx)); + promise.set(CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; return true; @@ -1362,6 +1360,14 @@ PromiseObject::create(JSContext* cx, HandleObject executor, HandleObject proto / return promise; } +// ES2016, 25.4.3.1. skipping creation of resolution functions and executor +// function invocation. +/* static */ PromiseObject* +PromiseObject::createSkippingExecutor(JSContext* cx) +{ + return CreatePromiseObjectWithoutResolutionFunctions(cx); +} + static MOZ_MUST_USE bool PerformPromiseAll(JSContext *cx, JS::ForOfIterator& iterator, HandleObject C, HandleObject promiseObj, HandleObject resolve, HandleObject reject, @@ -2145,7 +2151,7 @@ MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) { // Step 1. - Rooted promise(cx, CreatePromiseObjectWithDefaultResolution(cx)); + Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return nullptr; @@ -2187,7 +2193,7 @@ MOZ_MUST_USE bool js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value) { // Step 2. - Rooted promise(cx, CreatePromiseObjectWithDefaultResolution(cx)); + Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; @@ -2630,6 +2636,9 @@ PromiseObject::resolve(JSContext* cx, Handle promise, HandleValu if (promise->state() != JS::PromiseState::Pending) return true; + if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_RESOLVE_FUNCTION)) + return ResolvePromiseInternal(cx, promise, resolutionValue); + RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise)); RootedValue funVal(cx, ObjectValue(*resolveFun)); @@ -2653,6 +2662,9 @@ PromiseObject::reject(JSContext* cx, Handle promise, HandleValue if (promise->state() != JS::PromiseState::Pending) return true; + if (PromiseHasAnyFlag(*promise, PROMISE_FLAG_DEFAULT_REJECT_FUNCTION)) + return RejectMaybeWrappedPromise(cx, promise, rejectionValue); + RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction)); MOZ_ASSERT(IsCallable(funVal)); diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h index 6a6453e465..8f716dfbc5 100644 --- a/js/src/builtin/Promise.h +++ b/js/src/builtin/Promise.h @@ -44,6 +44,8 @@ class PromiseObject : public NativeObject static PromiseObject* create(JSContext* cx, HandleObject executor, HandleObject proto = nullptr, bool needsWrapping = false); + static PromiseObject* createSkippingExecutor(JSContext* cx); + static JSObject* unforgeableResolve(JSContext* cx, HandleValue value); static JSObject* unforgeableReject(JSContext* cx, HandleValue value); From 4ce0bb5e0afd236ff1a6f481f9ec2876f1215e27 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:21:05 -0500 Subject: [PATCH 04/32] Bug 1336705 - Part 2: Add self-hosting intrinsics for resolving/rejecting Promises and adding reactions. Tag #1287 --- js/src/vm/SelfHosting.cpp | 108 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index ffd707b147..e5d97c7681 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 promise(cx, &args[0].toObject().as()); + 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 promise(cx, &args[0].toObject().as()); + 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, 1, 0), JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0), From 7eb5d46c11e59f2081362a7cf9625a5912a73ce0 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 1 Dec 2019 13:34:22 -0500 Subject: [PATCH 05/32] Bug 1343481 - Part 1: Remove {JSFunction,JSScript,LazyScript}.isGenerator() method. Tag #1287 --- js/src/builtin/ReflectParse.cpp | 8 +++---- js/src/frontend/BytecodeEmitter.cpp | 19 +++++++++------ js/src/frontend/Parser.cpp | 2 +- js/src/frontend/Parser.h | 12 ++++++---- js/src/frontend/SharedContext.h | 23 +++++++++++++++---- js/src/jit/Ion.cpp | 11 +++++++-- js/src/jit/IonAnalysis.cpp | 8 ++++++- js/src/jsfun.cpp | 14 ++++++++--- js/src/jsfun.h | 10 ++++---- js/src/jsscript.cpp | 4 +++- js/src/jsscript.h | 20 ++++++++++------ .../js1_8_5/reflect-parse/PatternBuilders.js | 15 +++++------- js/src/vm/Debugger.cpp | 3 ++- js/src/vm/EnvironmentObject.cpp | 21 +++++++++++++---- js/src/vm/GeneratorObject.cpp | 3 ++- js/src/vm/ObjectGroup.cpp | 2 +- js/src/vm/Probes-inl.h | 5 +++- js/src/vm/SelfHosting.cpp | 3 ++- js/src/vm/Stack-inl.h | 2 +- js/src/vm/Stack.cpp | 4 +++- js/src/vm/Stack.h | 3 ++- js/src/wasm/AsmJS.cpp | 5 +++- 22 files changed, 133 insertions(+), 64 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 8e8bb24174..120970196a 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -3422,10 +3422,10 @@ ASTSerializer::function(ParseNode* pn, ASTType type, MutableHandleValue dst) RootedFunction func(cx, pn->pn_funbox->function()); GeneratorStyle generatorStyle = - pn->pn_funbox->isGenerator() - ? (pn->pn_funbox->isLegacyGenerator() - ? GeneratorStyle::Legacy - : GeneratorStyle::ES6) + pn->pn_funbox->isStarGenerator() + ? GeneratorStyle::ES6 + : pn->pn_funbox->isLegacyGenerator() + ? GeneratorStyle::Legacy : GeneratorStyle::None; bool isAsync = pn->pn_funbox->isAsync(); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 6829751ba4..576c7bab87 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -389,7 +389,10 @@ class BytecodeEmitter::EmitterScope : public Nestable bce->maxFixedSlots) bce->maxFixedSlots = nextFrameSlot_; - MOZ_ASSERT_IF(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->isGenerator(), + MOZ_ASSERT_IF(bce->sc->isFunctionBox() && + (bce->sc->asFunctionBox()->isStarGenerator() || + bce->sc->asFunctionBox()->isLegacyGenerator() || + bce->sc->asFunctionBox()->isAsync()), bce->maxFixedSlots == 0); } @@ -4783,7 +4786,9 @@ BytecodeEmitter::isRunOnceLambda() FunctionBox* funbox = sc->asFunctionBox(); return !funbox->argumentsHasLocalBinding() && - !funbox->isGenerator() && + !funbox->isStarGenerator() && + !funbox->isLegacyGenerator() && + !funbox->isAsync() && !funbox->function()->explicitName(); } @@ -8478,11 +8483,11 @@ BytecodeEmitter::emitReturn(ParseNode* pn) */ ptrdiff_t top = offset(); - bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator(); + bool needsFinalYield = sc->isFunctionBox() && sc->asFunctionBox()->needsFinalYield(); bool isDerivedClassConstructor = sc->isFunctionBox() && sc->asFunctionBox()->isDerivedClassConstructor(); - if (!emit1((isGenerator || isDerivedClassConstructor) ? JSOP_SETRVAL : JSOP_RETURN)) + if (!emit1((needsFinalYield || isDerivedClassConstructor) ? JSOP_SETRVAL : JSOP_RETURN)) return false; // Make sure that we emit this before popping the blocks in prepareForNonLocalJump, @@ -8497,7 +8502,7 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (!nle.prepareForNonLocalJumpToOutermost()) return false; - if (isGenerator) { + if (needsFinalYield) { // We know that .generator is on the function scope, as we just exited // all nested scopes. NameLocation loc = @@ -10313,7 +10318,7 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (!emitTree(funBody)) return false; - if (funbox->isGenerator()) { + if (funbox->needsFinalYield()) { // If we fall off the end of a generator, do a final yield. if (funbox->isStarGenerator() && !emitPrepareIteratorResult()) return false; @@ -10321,7 +10326,7 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (!emit1(JSOP_UNDEFINED)) return false; - if (sc->asFunctionBox()->isStarGenerator() && !emitFinishIteratorResult(true)) + if (funbox->isStarGenerator() && !emitFinishIteratorResult(true)) return false; if (!emit1(JSOP_SETRVAL)) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0c279591f9..64b1f22c2d 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2720,7 +2720,7 @@ Parser::functionBody(InHandling inHandling, YieldHandling yieldHan break; } - if (pc->isGenerator()) { + if (pc->needsDotGeneratorName()) { MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody); if (!declareDotGeneratorName()) return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 88d2dad189..33fe345d63 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -496,10 +496,6 @@ class ParseContext : public Nestable return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator; } - bool isGenerator() const { - return generatorKind() != NotGenerator; - } - bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } @@ -512,6 +508,10 @@ class ParseContext : public Nestable return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync(); } + bool needsDotGeneratorName() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + FunctionAsyncKind asyncKind() const { return isAsync() ? AsyncFunction : SyncFunction; } @@ -818,7 +818,9 @@ class ParserBase : public StrictModeGetter // whether it's prohibited due to strictness, JS version, or occurrence // inside a star generator. bool yieldExpressionsSupported() { - return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync(); + return (versionNumber() >= JSVERSION_1_7 && !pc->isAsync()) || + pc->isStarGenerator() || + pc->isLegacyGenerator(); } virtual bool strictMode() { return pc->sc()->strict(); } diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 013444690c..5766c135a4 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -520,7 +520,9 @@ class FunctionBox : public ObjectBox, public SharedContext return hasExtensibleScope() || needsHomeObject() || isDerivedClassConstructor() || - isGenerator(); + isStarGenerator() || + isLegacyGenerator() || + isAsync(); } bool hasExtraBodyVarScope() const { @@ -531,7 +533,7 @@ class FunctionBox : public ObjectBox, public SharedContext bool needsExtraBodyVarEnvironmentRegardlessOfBindings() const { MOZ_ASSERT(hasParameterExprs); - return hasExtensibleScope() || isGenerator(); + return hasExtensibleScope() || needsDotGeneratorName(); } bool isLikelyConstructorWrapper() const { @@ -539,10 +541,17 @@ class FunctionBox : public ObjectBox, public SharedContext } GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); } - bool isGenerator() const { return generatorKind() != NotGenerator; } bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == StarGenerator; } FunctionAsyncKind asyncKind() const { return AsyncKindFromBits(asyncKindBits_); } + + bool needsFinalYield() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + bool needsDotGeneratorName() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } + bool isAsync() const { return asyncKind() == AsyncFunction; } bool isArrow() const { return function()->isArrow(); } @@ -560,7 +569,7 @@ class FunctionBox : public ObjectBox, public SharedContext // A generator kind can be set at initialization, or when "yield" is // first seen. In both cases the transition can only happen from // NotGenerator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); generatorKindBits_ = GeneratorKindAsBits(kind); } @@ -655,7 +664,11 @@ SharedContext::asModuleContext() inline bool SharedContext::allBindingsClosedOver() { - return bindingsAccessedDynamically() || (isFunctionBox() && asFunctionBox()->isGenerator()); + return bindingsAccessedDynamically() || + (isFunctionBox() && + (asFunctionBox()->isStarGenerator() || + asFunctionBox()->isLegacyGenerator() || + asFunctionBox()->isAsync())); } } // namespace frontend diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index b8a2d2fba5..9337f61505 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2320,7 +2320,9 @@ IonCompile(JSContext* cx, JSScript* script, static bool CheckFrame(JSContext* cx, BaselineFrame* frame) { - MOZ_ASSERT(!frame->script()->isGenerator()); + MOZ_ASSERT(!frame->script()->isStarGenerator()); + MOZ_ASSERT(!frame->script()->isLegacyGenerator()); + MOZ_ASSERT(!frame->script()->isAsync()); MOZ_ASSERT(!frame->isDebuggerEvalFrame()); MOZ_ASSERT(!frame->isEvalFrame()); @@ -2351,11 +2353,16 @@ CheckScript(JSContext* cx, JSScript* script, bool osr) return false; } - if (script->isGenerator()) { + if (script->isStarGenerator() || script->isLegacyGenerator()) { TrackAndSpewIonAbort(cx, script, "generator script"); return false; } + if (script->isAsync()) { + TrackAndSpewIonAbort(cx, script, "async script"); + return false; + } + if (script->hasNonSyntacticScope() && !script->functionNonDelazifying()) { // Support functions with a non-syntactic global scope but not other // scripts. For global scripts, IonBuilder currently uses the global diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index ace6cd81e4..a4724bca4f 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -4402,8 +4402,14 @@ jit::AnalyzeArgumentsUsage(JSContext* cx, JSScript* scriptArg) // direct eval is present. // // FIXME: Don't build arguments for ES6 generator expressions. - if (scriptArg->isDebuggee() || script->isGenerator() || script->bindingsAccessedDynamically()) + if (scriptArg->isDebuggee() || + script->isStarGenerator() || + script->isLegacyGenerator() || + script->isAsync() || + script->bindingsAccessedDynamically()) + { return true; + } if (!jit::IsIonEnabled(cx)) return true; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9edf238efd..9d54fef780 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -131,7 +131,11 @@ IsFunctionInStrictMode(JSFunction* fun) static bool IsNewerTypeFunction(JSFunction* fun) { - return fun->isArrow() || fun->isGenerator() || fun->isAsync() || fun->isMethod(); + return fun->isArrow() || + fun->isStarGenerator() || + fun->isLegacyGenerator() || + fun->isAsync() || + fun->isMethod(); } // Beware: this function can be invoked on *any* function! That includes @@ -442,8 +446,12 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) * - Arrow functions * - Function.prototype */ - if (fun->isBuiltin() || (!fun->isConstructor() && !fun->isGenerator())) + if (fun->isBuiltin()) return true; + if (!fun->isConstructor()) { + if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync()) + return true; + } if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) return false; @@ -1556,7 +1564,7 @@ fun_isGenerator(JSContext* cx, unsigned argc, Value* vp) return true; } - args.rval().setBoolean(fun->isGenerator()); + args.rval().setBoolean(fun->isStarGenerator() || fun->isLegacyGenerator()); return true; } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 234169507d..9aee719e4f 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -146,7 +146,9 @@ class JSFunction : public js::NativeObject MOZ_ASSERT_IF(nonLazyScript()->funHasExtensibleScope() || nonLazyScript()->needsHomeObject() || nonLazyScript()->isDerivedClassConstructor() || - isGenerator(), + isStarGenerator() || + isLegacyGenerator() || + isAsync(), nonLazyScript()->bodyScope()->hasEnvironment()); return nonLazyScript()->bodyScope()->hasEnvironment(); @@ -501,8 +503,6 @@ class JSFunction : public js::NativeObject return js::NotGenerator; } - bool isGenerator() const { return generatorKind() != js::NotGenerator; } - bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } @@ -513,9 +513,9 @@ class JSFunction : public js::NativeObject bool isAsync() const { if (isInterpretedLazy()) - return lazyScript()->asyncKind() == js::AsyncFunction; + return lazyScript()->isAsync(); if (hasScript()) - return nonLazyScript()->asyncKind() == js::AsyncFunction; + return nonLazyScript()->isAsync(); return false; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index fc7438e3b9..6f5284a3bf 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3917,7 +3917,9 @@ JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script) if (script->needsArgsObj()) return true; - MOZ_ASSERT(!script->isGenerator()); + MOZ_ASSERT(!script->isStarGenerator()); + MOZ_ASSERT(!script->isLegacyGenerator()); + MOZ_ASSERT(!script->isAsync()); script->needsArgsObj_ = true; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 85eb2938d3..13eaeff34d 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -1327,13 +1327,12 @@ class JSScript : public js::gc::TenuredCell js::GeneratorKind generatorKind() const { return js::GeneratorKindFromBits(generatorKindBits_); } - bool isGenerator() const { return generatorKind() != js::NotGenerator; } bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } void setGeneratorKind(js::GeneratorKind kind) { // A script only gets its generator kind set as part of initialization, // so it can only transition from not being a generator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); generatorKindBits_ = GeneratorKindAsBits(kind); } @@ -1341,6 +1340,10 @@ class JSScript : public js::gc::TenuredCell return isAsync_ ? js::AsyncFunction : js::SyncFunction; } + bool isAsync() const { + return isAsync_; + } + void setAsyncKind(js::FunctionAsyncKind kind) { isAsync_ = kind == js::AsyncFunction; } @@ -1493,7 +1496,8 @@ class JSScript : public js::gc::TenuredCell bool isRelazifiable() const { return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ && - !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && + !isStarGenerator() && !isLegacyGenerator() && !isAsync() && + !hasBaselineScript() && !hasAnyIonScript() && !isDefaultClassConstructor() && !doNotRelazify_; } @@ -1695,7 +1699,7 @@ class JSScript : public js::gc::TenuredCell bool hasObjects() const { return hasArray(OBJECTS); } bool hasTrynotes() const { return hasArray(TRYNOTES); } bool hasScopeNotes() const { return hasArray(SCOPENOTES); } - bool hasYieldOffsets() const { return isGenerator(); } + bool hasYieldOffsets() const { return isStarGenerator() || isLegacyGenerator() || isAsync(); } #define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0)) @@ -2112,8 +2116,6 @@ class LazyScript : public gc::TenuredCell GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); } - bool isGenerator() const { return generatorKind() != NotGenerator; } - bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } bool isStarGenerator() const { return generatorKind() == StarGenerator; } @@ -2121,7 +2123,7 @@ class LazyScript : public gc::TenuredCell void setGeneratorKind(GeneratorKind kind) { // A script only gets its generator kind set as part of initialization, // so it can only transition from NotGenerator. - MOZ_ASSERT(!isGenerator()); + MOZ_ASSERT(!isStarGenerator() && !isLegacyGenerator()); // Legacy generators cannot currently be lazy. MOZ_ASSERT(kind != LegacyGenerator); p_.generatorKindBits = GeneratorKindAsBits(kind); @@ -2131,6 +2133,10 @@ class LazyScript : public gc::TenuredCell return p_.isAsync ? AsyncFunction : SyncFunction; } + bool isAsync() const { + return p_.isAsync; + } + void setAsyncKind(FunctionAsyncKind kind) { p_.isAsync = kind == AsyncFunction; } diff --git a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js index be65bd76e7..51b7c69265 100644 --- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js +++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js @@ -53,9 +53,8 @@ function asyncFunDecl(id, params, body) { params: params, defaults: [], body: body, - generator: true, - async: true, - style: "es6" }); + generator: false, + async: true }); } function varDecl(decls) { return Pattern({ type: "VariableDeclaration", declarations: decls, kind: "var" }); @@ -181,9 +180,8 @@ function asyncFunExpr(id, args, body) { id: id, params: args, body: body, - generator: true, - async: true, - style: "es6" }); + generator: false, + async: true }); } function arrowExpr(args, body) { return Pattern({ type: "ArrowFunctionExpression", @@ -194,10 +192,9 @@ function asyncArrowExpr(isExpression, args, body) { return Pattern({ type: "ArrowFunctionExpression", params: args, body: body, - generator: true, + generator: false, async: true, - expression: isExpression, - style: "es6" }); + expression: isExpression }); } function metaProperty(meta, property) { diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index d68d1b75eb..b3efeb175b 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -7356,7 +7356,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 c95bb0597a..7834940f14 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(), - !ei.scope().as().canonicalFunction()->isGenerator()); + !ei.scope().as().canonicalFunction()->isStarGenerator() && + !ei.scope().as().canonicalFunction()->isLegacyGenerator() && + !ei.scope().as().canonicalFunction()->isAsync()); if (!CanUseDebugEnvironmentMaps(cx)) return true; @@ -2594,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame) if (!frame.environmentChain()->is()) return; - if (frame.callee()->isGenerator()) + if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() || + frame.callee()->isAsync()) + { return; + } CallObject& callobj = frame.environmentChain()->as(); 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()) { RootedFunction callee(cx, ei.scope().as().canonicalFunction()); // Generators should always reify their scopes. - MOZ_ASSERT(!callee->isGenerator()); + MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() && + !callee->isAsync()); JS::ExposeObjectToActiveJS(callee); Rooted callobj(cx, CallObject::createHollowForDebug(cx, callee)); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index ba28501e61..448bc543d1 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -19,7 +19,8 @@ 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 global(cx, cx->global()); diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp index ec0a7aec19..91070b3f6b 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/Probes-inl.h b/js/src/vm/Probes-inl.h index 347f842b8e..822a8ac598 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 e5d97c7681..2216bf91e9 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -3113,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 11a19d1751..2f2b591e5c 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 c5f2cf5f34..95940eeaff 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 23e6213444..fe04a00f26 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; diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 52b8eeed18..f3eebb85ee 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -8608,9 +8608,12 @@ EstablishPreconditions(ExclusiveContext* cx, AsmJSParser& parser) break; } - if (parser.pc->isGenerator()) + if (parser.pc->isStarGenerator() || parser.pc->isLegacyGenerator()) return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context"); + if (parser.pc->isAsync()) + return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by async context"); + if (parser.pc->isArrowFunction()) return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context"); From 45337018efb146e918eef8ffbcc41f5bc8422bcc Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 1 Dec 2019 18:44:06 -0500 Subject: [PATCH 06/32] Bug 1343481 - Part 2: Stop using StarGegerator for async function. Tag #1287 --- js/src/frontend/BytecodeCompiler.cpp | 2 +- js/src/frontend/BytecodeEmitter.cpp | 23 +++++++----- js/src/frontend/Parser.cpp | 54 +++++++++++++--------------- js/src/frontend/SharedContext.h | 4 +++ js/src/jsfun.cpp | 16 ++++----- js/src/jsfun.h | 6 ++-- js/src/jsscript.cpp | 2 +- js/src/vm/AsyncFunction.cpp | 2 +- js/src/vm/Debugger.cpp | 2 +- js/src/vm/GeneratorObject.cpp | 2 +- 10 files changed, 58 insertions(+), 55 deletions(-) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index a1abbfeda2..7760c4b2eb 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -727,5 +727,5 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope, TraceLogger_ParserCompileFunction); - return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd); + return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd); } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 576c7bab87..efda7a2bef 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8443,7 +8443,8 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (!updateSourceCoordNotes(pn->pn_pos.begin)) return false; - if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) { + bool needsIteratorResult = sc->isFunctionBox() && sc->asFunctionBox()->needsIteratorResult(); + if (needsIteratorResult) { if (!emitPrepareIteratorResult()) return false; } @@ -8458,7 +8459,7 @@ BytecodeEmitter::emitReturn(ParseNode* pn) return false; } - if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) { + if (needsIteratorResult) { if (!emitFinishIteratorResult(true)) return false; } @@ -8530,7 +8531,8 @@ BytecodeEmitter::emitYield(ParseNode* pn) MOZ_ASSERT(sc->isFunctionBox()); if (pn->getOp() == JSOP_YIELD) { - if (sc->asFunctionBox()->isStarGenerator()) { + bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); + if (needsIteratorResult) { if (!emitPrepareIteratorResult()) return false; } @@ -8541,7 +8543,7 @@ BytecodeEmitter::emitYield(ParseNode* pn) if (!emit1(JSOP_UNDEFINED)) return false; } - if (sc->asFunctionBox()->isStarGenerator()) { + if (needsIteratorResult) { if (!emitFinishIteratorResult(false)) return false; } @@ -10320,14 +10322,19 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (funbox->needsFinalYield()) { // If we fall off the end of a generator, do a final yield. - if (funbox->isStarGenerator() && !emitPrepareIteratorResult()) - return false; + bool needsIteratorResult = funbox->needsIteratorResult(); + if (needsIteratorResult) { + if (!emitPrepareIteratorResult()) + return false; + } if (!emit1(JSOP_UNDEFINED)) return false; - if (funbox->isStarGenerator() && !emitFinishIteratorResult(true)) - return false; + if (needsIteratorResult) { + if (!emitFinishIteratorResult(true)) + return false; + } if (!emit1(JSOP_SETRVAL)) return false; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 64b1f22c2d..406d377258 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2474,10 +2474,8 @@ Parser::finishFunction(bool isStandaloneFunction /* = false } static YieldHandling -GetYieldHandling(GeneratorKind generatorKind, FunctionAsyncKind asyncKind) +GetYieldHandling(GeneratorKind generatorKind) { - if (asyncKind == AsyncFunction) - return YieldIsName; if (generatorKind == NotGenerator) return YieldIsName; return YieldIsKeyword; @@ -2541,7 +2539,7 @@ Parser::standaloneFunction(HandleFunction fun, return null(); funpc.setIsStandaloneFunctionBody(); - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); AutoAwaitIsKeyword awaitIsKeyword(this, asyncKind == AsyncFunction); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement, parameterListEnd, /* isStandaloneFunction = */ true)) @@ -2699,7 +2697,7 @@ Parser::functionBody(InHandling inHandling, YieldHandling yieldHan switch (pc->generatorKind()) { case NotGenerator: - MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset); + MOZ_ASSERT_IF(!pc->isAsync(), pc->lastYieldOffset == startYieldOffset); break; case LegacyGenerator: @@ -2715,8 +2713,8 @@ Parser::functionBody(InHandling inHandling, YieldHandling yieldHan break; case StarGenerator: - MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow); - MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody); + MOZ_ASSERT(kind != Arrow); + MOZ_ASSERT(type == StatementListBody); break; } @@ -2761,9 +2759,9 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, #endif switch (kind) { case Expression: - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_LAMBDA - : JSFunction::INTERPRETED_LAMBDA_GENERATOR); + : JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC); break; case Arrow: flags = JSFunction::INTERPRETED_LAMBDA_ARROW; @@ -2771,9 +2769,9 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, break; case Method: MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator); - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_METHOD - : JSFunction::INTERPRETED_METHOD_GENERATOR); + : JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC); allocKind = gc::AllocKind::FUNCTION_EXTENDED; break; case ClassConstructor: @@ -2799,9 +2797,9 @@ Parser::newFunction(HandleAtom atom, FunctionSyntaxKind kind, allocKind = gc::AllocKind::FUNCTION_EXTENDED; } #endif - flags = (generatorKind == NotGenerator + flags = (generatorKind == NotGenerator && asyncKind == SyncFunction ? JSFunction::INTERPRETED_NORMAL - : JSFunction::INTERPRETED_GENERATOR); + : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC); } // We store the async wrapper in a slot for later access. @@ -3324,7 +3322,6 @@ Parser::functionDefinition(uint32_t toStringStart, Node pn, InHand bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == Statement, funName); - MOZ_ASSERT_IF(asyncKind == AsyncFunction, generatorKind == StarGenerator); // When fully parsing a LazyScript, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and @@ -3336,7 +3333,7 @@ Parser::functionDefinition(uint32_t toStringStart, Node pn, InHand } RootedObject proto(context); - if (generatorKind == StarGenerator) { + if (generatorKind == StarGenerator || asyncKind == AsyncFunction) { // If we are off the main thread, the generator meta-objects have // already been created by js::StartOffThreadParseScript, so cx will not // be necessary. @@ -3408,7 +3405,7 @@ Parser::trySyntaxParseInnerFunction(ParseNode* pn, HandleFunct // parse to avoid the overhead of a lazy syntax-only parse. Although // the prediction may be incorrect, IIFEs are common enough that it // pays off for lots of code. - if (pn->isLikelyIIFE() && generatorKind == NotGenerator) + if (pn->isLikelyIIFE() && generatorKind == NotGenerator && asyncKind == SyncFunction) break; Parser* parser = handler.syntaxParser; @@ -3584,7 +3581,7 @@ Parser::standaloneLazyFunction(HandleFunction fun, bool strict if (!tokenStream.peekTokenPos(&pn->pn_pos, modifier)) return null(); - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); FunctionSyntaxKind syntaxKind = Statement; if (fun->isClassConstructor()) syntaxKind = ClassConstructor; @@ -3665,7 +3662,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, return false; uint32_t openedPos = 0; if (tt != TOK_LC) { - if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method || + if (funbox->isStarGenerator() || kind == Method || kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure || IsConstructorKind(kind)) { error(JSMSG_CURLY_BEFORE_BODY); @@ -3695,7 +3692,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. // The same goes when parsing |await| in arrow functions. - YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind(), pc->asyncKind()); + YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind()); Node body; { AutoAwaitIsKeyword awaitIsKeyword(this, funbox->isAsync()); @@ -3786,7 +3783,7 @@ Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHa if (!tokenStream.getToken(&tt)) return null(); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; + GeneratorKind generatorKind = NotGenerator; if (tt == TOK_MUL) { if (asyncKind != SyncFunction) { error(JSMSG_ASYNC_GENERATOR); @@ -3817,7 +3814,7 @@ Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHa MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); - if (!pc->sc()->strict() && generatorKind == NotGenerator) { + if (!pc->sc()->strict() && generatorKind == NotGenerator && asyncKind == SyncFunction) { // In sloppy mode, try Annex B.3.3 semantics. If making an // additional 'var' binding of the same name does not throw an // early error, do so. This 'var' binding would be assigned @@ -3841,7 +3838,7 @@ Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHa if (!pn) return null(); - YieldHandling newYieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling newYieldHandling = GetYieldHandling(generatorKind); return functionDefinition(toStringStart, pn, InAllowed, newYieldHandling, name, Statement, generatorKind, asyncKind, tryAnnexB); } @@ -3854,7 +3851,7 @@ Parser::functionExpr(uint32_t toStringStart, InvokedPrediction inv MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION)); AutoAwaitIsKeyword awaitIsKeyword(this, asyncKind == AsyncFunction); - GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator; + GeneratorKind generatorKind = NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) return null(); @@ -3869,7 +3866,7 @@ Parser::functionExpr(uint32_t toStringStart, InvokedPrediction inv return null(); } - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); RootedPropertyName name(context); if (TokenKindIsPossibleIdentifier(tt)) { @@ -8187,7 +8184,6 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl uint32_t toStringStart = pos().begin; tokenStream.ungetToken(); - GeneratorKind generatorKind = NotGenerator; FunctionAsyncKind asyncKind = SyncFunction; if (next == TOK_ASYNC) { @@ -8200,7 +8196,6 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl if (nextSameLine == TOK_ARROW) { tokenStream.ungetToken(); } else { - generatorKind = StarGenerator; asyncKind = AsyncFunction; } } @@ -8210,7 +8205,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl return null(); Node arrowFunc = functionDefinition(toStringStart, pn, inHandling, yieldHandling, nullptr, - Arrow, generatorKind, asyncKind); + Arrow, NotGenerator, asyncKind); if (!arrowFunc) return null(); @@ -9950,8 +9945,7 @@ Parser::methodDefinition(uint32_t toStringStart, PropertyType prop MOZ_CRASH("Parser: methodDefinition: unexpected property type"); } - GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod || - propType == PropertyType::AsyncMethod) + GeneratorKind generatorKind = propType == PropertyType::GeneratorMethod ? StarGenerator : NotGenerator; @@ -9959,7 +9953,7 @@ Parser::methodDefinition(uint32_t toStringStart, PropertyType prop ? AsyncFunction : SyncFunction; - YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind); + YieldHandling yieldHandling = GetYieldHandling(generatorKind); Node pn = handler.newFunctionExpression(); if (!pn) diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 5766c135a4..2d478308ec 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -552,6 +552,10 @@ class FunctionBox : public ObjectBox, public SharedContext return isStarGenerator() || isLegacyGenerator() || isAsync(); } + bool needsIteratorResult() const { + return isStarGenerator() || isAsync(); + } + bool isAsync() const { return asyncKind() == AsyncFunction; } bool isArrow() const { return function()->isArrow(); } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 9d54fef780..65d44cba7a 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -523,7 +523,7 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleScope enclosingScope, { enum FirstWordFlag { HasAtom = 0x1, - IsStarGenerator = 0x2, + HasStarGeneratorProto = 0x2, IsLazy = 0x4, HasSingletonType = 0x8 }; @@ -552,8 +552,8 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleScope enclosingScope, if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom()) firstword |= HasAtom; - if (fun->isStarGenerator()) - firstword |= IsStarGenerator; + if (fun->isStarGenerator() || fun->isAsync()) + firstword |= HasStarGeneratorProto; if (fun->isInterpretedLazy()) { // Encode a lazy script. @@ -589,7 +589,7 @@ js::XDRInterpretedFunction(XDRState* xdr, HandleScope enclosingScope, if (mode == XDR_DECODE) { RootedObject proto(cx); - if (firstword & IsStarGenerator) { + if (firstword & HasStarGeneratorProto) { proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!proto) return false; @@ -1596,8 +1596,6 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator bool isStarGenerator = generatorKind == StarGenerator; bool isAsync = asyncKind == AsyncFunction; MOZ_ASSERT(generatorKind != LegacyGenerator); - MOZ_ASSERT_IF(isAsync, isStarGenerator); - MOZ_ASSERT_IF(!isStarGenerator, !isAsync); RootedScript maybeScript(cx); const char* filename; @@ -1709,7 +1707,7 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator // Step 4.d, use %Generator% as the fallback prototype. // Also use %Generator% for the unwrapped function of async functions. - if (!proto && isStarGenerator) { + if (!proto && (isStarGenerator || isAsync)) { proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global); if (!proto) return false; @@ -1777,7 +1775,7 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) else newTarget = &args.callee(); - if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction)) + if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction)) return false; // ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077 @@ -1973,7 +1971,7 @@ NewFunctionClone(JSContext* cx, HandleFunction fun, NewObjectKind newKind, gc::AllocKind allocKind, HandleObject proto) { RootedObject cloneProto(cx, proto); - if (!proto && fun->isStarGenerator()) { + if (!proto && (fun->isStarGenerator() || fun->isAsync())) { cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!cloneProto) return nullptr; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 9aee719e4f..9e50387d63 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -89,15 +89,15 @@ class JSFunction : public js::NativeObject ASMJS_CTOR = ASMJS_KIND | NATIVE_CTOR, ASMJS_LAMBDA_CTOR = ASMJS_KIND | NATIVE_CTOR | LAMBDA, INTERPRETED_METHOD = INTERPRETED | METHOD_KIND, - INTERPRETED_METHOD_GENERATOR = INTERPRETED | METHOD_KIND, + INTERPRETED_METHOD_GENERATOR_OR_ASYNC = INTERPRETED | METHOD_KIND, INTERPRETED_CLASS_CONSTRUCTOR = INTERPRETED | CLASSCONSTRUCTOR_KIND | CONSTRUCTOR, INTERPRETED_GETTER = INTERPRETED | GETTER_KIND, INTERPRETED_SETTER = INTERPRETED | SETTER_KIND, INTERPRETED_LAMBDA = INTERPRETED | LAMBDA | CONSTRUCTOR, INTERPRETED_LAMBDA_ARROW = INTERPRETED | LAMBDA | ARROW_KIND, - INTERPRETED_LAMBDA_GENERATOR = INTERPRETED | LAMBDA, + INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC = INTERPRETED | LAMBDA, INTERPRETED_NORMAL = INTERPRETED | CONSTRUCTOR, - INTERPRETED_GENERATOR = INTERPRETED, + INTERPRETED_GENERATOR_OR_ASYNC = INTERPRETED, NO_XDR_FLAGS = RESOLVED_LENGTH | RESOLVED_NAME, STABLE_ACROSS_CLONES = CONSTRUCTOR | HAS_GUESSED_ATOM | LAMBDA | diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 6f5284a3bf..eb74a1ec65 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -3188,7 +3188,7 @@ CloneInnerInterpretedFunction(JSContext* cx, HandleScope enclosingScope, HandleF { /* NB: Keep this in sync with XDRInterpretedFunction. */ RootedObject cloneProto(cx); - if (srcFun->isStarGenerator()) { + if (srcFun->isStarGenerator() || srcFun->isAsync()) { cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global()); if (!cloneProto) return nullptr; diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index e14b77424a..be7a36ecc9 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -109,7 +109,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()."); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index b3efeb175b..7c09c50b06 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1569,7 +1569,7 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const MaybeisStarGenerator()) { + } else if (callee->isStarGenerator() || callee->isAsync()) { if (!CheckStarGeneratorResumptionValue(cx, vp)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD); return false; diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 448bc543d1..c0dff8faae 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -25,7 +25,7 @@ GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) Rooted 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 From a6872d9ea392b3deb84c532e702220ae8992dcf9 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sun, 1 Dec 2019 19:14:11 -0500 Subject: [PATCH 07/32] Bug 1343481 - Part 3: Add JSOP_AWAIT and rename {yieldIndex,yieldOffset} to {yieldAndAwaitIndex,yieldAndAwaitOffset}. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 24 +++++++------- js/src/frontend/BytecodeEmitter.h | 12 +++---- js/src/frontend/FullParseHandler.h | 2 +- js/src/jit/BaselineCompiler.cpp | 49 ++++++++++++++++----------- js/src/jit/BaselineCompiler.h | 9 ++--- js/src/jit/BaselineIC.cpp | 7 ++-- js/src/jit/BaselineJIT.cpp | 6 ++-- js/src/jit/BaselineJIT.h | 2 +- js/src/jit/VMFunctions.cpp | 4 +-- js/src/jsopcode.h | 3 +- js/src/jsscript.cpp | 36 ++++++++++---------- js/src/jsscript.h | 17 ++++++---- js/src/vm/GeneratorObject.cpp | 12 ++++--- js/src/vm/GeneratorObject.h | 51 +++++++++++++++-------------- js/src/vm/Interpreter.cpp | 2 +- js/src/vm/Opcodes.h | 14 ++++++-- 16 files changed, 141 insertions(+), 109 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index efda7a2bef..d934c0c806 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2046,7 +2046,7 @@ class ForOfLoopControl : public LoopControl return false; MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX); - numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldOffsetList.numYields; + numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields; return true; } @@ -2088,7 +2088,7 @@ class ForOfLoopControl : public LoopControl // If any yields were emitted, then this for-of loop is inside a star // generator and must handle the case of Generator.return. Like in // yield*, it is handled with a finally block. - uint32_t numYieldsEmitted = bce->yieldOffsetList.numYields; + uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields; if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) { if (!tryCatch_->emitFinally()) return false; @@ -2190,7 +2190,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, scopeList(cx), tryNoteList(cx), scopeNoteList(cx), - yieldOffsetList(cx), + yieldAndAwaitOffsetList(cx), typesetCount(0), hasSingletons(false), hasTryFinally(false), @@ -4798,26 +4798,26 @@ BytecodeEmitter::emitYieldOp(JSOp op) if (op == JSOP_FINALYIELDRVAL) return emit1(JSOP_FINALYIELDRVAL); - MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD); + MOZ_ASSERT(op == JSOP_INITIALYIELD || op == JSOP_YIELD || op == JSOP_AWAIT); ptrdiff_t off; if (!emitN(op, 3, &off)) return false; - uint32_t yieldIndex = yieldOffsetList.length(); - if (yieldIndex >= JS_BIT(24)) { + uint32_t yieldAndAwaitIndex = yieldAndAwaitOffsetList.length(); + if (yieldAndAwaitIndex >= JS_BIT(24)) { reportError(nullptr, JSMSG_TOO_MANY_YIELDS); return false; } if (op == JSOP_YIELD) - yieldOffsetList.numYields++; + yieldAndAwaitOffsetList.numYields++; else - yieldOffsetList.numAwaits++; + yieldAndAwaitOffsetList.numAwaits++; - SET_UINT24(code(off), yieldIndex); + SET_UINT24(code(off), yieldAndAwaitIndex); - if (!yieldOffsetList.append(offset())) + if (!yieldAndAwaitOffsetList.append(offset())) return false; return emit1(JSOP_DEBUGAFTERYIELD); @@ -8530,7 +8530,7 @@ BytecodeEmitter::emitYield(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); - if (pn->getOp() == JSOP_YIELD) { + if (pn->getOp() == JSOP_YIELD || pn->getOp() == JSOP_AWAIT) { bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); if (needsIteratorResult) { if (!emitPrepareIteratorResult()) @@ -11294,7 +11294,7 @@ CGScopeNoteList::finish(ScopeNoteArray* array, uint32_t prologueLength) } void -CGYieldOffsetList::finish(YieldOffsetArray& array, uint32_t prologueLength) +CGYieldAndAwaitOffsetList::finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength) { MOZ_ASSERT(length() == array.length()); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 38b3c44177..153b078712 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -98,15 +98,15 @@ struct CGScopeNoteList { void finish(ScopeNoteArray* array, uint32_t prologueLength); }; -struct CGYieldOffsetList { +struct CGYieldAndAwaitOffsetList { Vector list; uint32_t numYields; uint32_t numAwaits; - explicit CGYieldOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {} + explicit CGYieldAndAwaitOffsetList(ExclusiveContext* cx) : list(cx), numYields(0), numAwaits(0) {} MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); } size_t length() const { return list.length(); } - void finish(YieldOffsetArray& array, uint32_t prologueLength); + void finish(YieldAndAwaitOffsetArray& array, uint32_t prologueLength); }; static size_t MaxBytecodeLength = INT32_MAX; @@ -237,10 +237,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter CGScopeNoteList scopeNoteList; /* list of emitted block scope notes */ /* - * For each yield op, map the yield index (stored as bytecode operand) to - * the offset of the next op. + * For each yield or await op, map the yield and await index (stored as + * bytecode operand) to the offset of the next op. */ - CGYieldOffsetList yieldOffsetList; + CGYieldAndAwaitOffsetList yieldAndAwaitOffsetList; uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */ diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 2d7f57e1ea..6ab93f3f9d 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -453,7 +453,7 @@ class FullParseHandler ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_(PNK_AWAIT, JSOP_YIELD, pos, value, gen); + return new_(PNK_AWAIT, JSOP_AWAIT, pos, value, gen); } // Statements diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index bad47bcae6..a7d471523b 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -43,7 +43,7 @@ using mozilla::AssertedCast; BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script) : BaselineCompilerSpecific(cx, alloc, script), - yieldOffsets_(cx), + yieldAndAwaitOffsets_(cx), modifiesArguments_(false) { } @@ -210,7 +210,7 @@ BaselineCompiler::compile() pcMappingIndexEntries.length(), pcEntries.length(), bytecodeTypeMapEntries, - yieldOffsets_.length(), + yieldAndAwaitOffsets_.length(), traceLoggerToggleOffsets_.length()), JS::DeletePolicy(cx->runtime())); if (!baselineScript) { @@ -275,7 +275,7 @@ BaselineCompiler::compile() // searches for the sought entry when queries are in linear order. bytecodeMap[script->nTypeSets()] = 0; - baselineScript->copyYieldEntries(script, yieldOffsets_); + baselineScript->copyYieldAndAwaitEntries(script, yieldAndAwaitOffsets_); if (compileDebugInstrumentation_) baselineScript->setHasDebugInstrumentation(); @@ -4180,27 +4180,28 @@ BaselineCompiler::emit_JSOP_GENERATOR() } bool -BaselineCompiler::addYieldOffset() +BaselineCompiler::addYieldAndAwaitOffset() { - MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT); - uint32_t yieldIndex = GET_UINT24(pc); + uint32_t yieldAndAwaitIndex = GET_UINT24(pc); - while (yieldIndex >= yieldOffsets_.length()) { - if (!yieldOffsets_.append(0)) + while (yieldAndAwaitIndex >= yieldAndAwaitOffsets_.length()) { + if (!yieldAndAwaitOffsets_.append(0)) return false; } - static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH, - "code below assumes INITIALYIELD and YIELD have same length"); - yieldOffsets_[yieldIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH); + static_assert(JSOP_INITIALYIELD_LENGTH == JSOP_YIELD_LENGTH && + JSOP_INITIALYIELD_LENGTH == JSOP_AWAIT_LENGTH, + "code below assumes INITIALYIELD and YIELD and AWAIT have same length"); + yieldAndAwaitOffsets_[yieldAndAwaitIndex] = script->pcToOffset(pc + JSOP_YIELD_LENGTH); return true; } bool BaselineCompiler::emit_JSOP_INITIALYIELD() { - if (!addYieldOffset()) + if (!addYieldAndAwaitOffset()) return false; frame.syncStack(0); @@ -4210,7 +4211,8 @@ BaselineCompiler::emit_JSOP_INITIALYIELD() masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), genObj); MOZ_ASSERT(GET_UINT24(pc) == 0); - masm.storeValue(Int32Value(0), Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + masm.storeValue(Int32Value(0), + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); Register envObj = R0.scratchReg(); Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()); @@ -4239,7 +4241,7 @@ static const VMFunction NormalSuspendInfo = bool BaselineCompiler::emit_JSOP_YIELD() { - if (!addYieldOffset()) + if (!addYieldAndAwaitOffset()) return false; // Store generator in R0. @@ -4256,7 +4258,7 @@ BaselineCompiler::emit_JSOP_YIELD() // generator is in the closing state, see GeneratorObject::suspend. masm.storeValue(Int32Value(GET_UINT24(pc)), - Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); Register envObj = R0.scratchReg(); Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()); @@ -4288,6 +4290,12 @@ BaselineCompiler::emit_JSOP_YIELD() return emitReturn(); } +bool +BaselineCompiler::emit_JSOP_AWAIT() +{ + return emit_JSOP_YIELD(); +} + typedef bool (*DebugAfterYieldFn)(JSContext*, BaselineFrame*); static const VMFunction DebugAfterYieldInfo = FunctionInfo(jit::DebugAfterYield, "DebugAfterYield"); @@ -4507,16 +4515,17 @@ BaselineCompiler::emit_JSOP_RESUME() masm.pushValue(retVal); if (resumeKind == GeneratorObject::NEXT) { - // Determine the resume address based on the yieldIndex and the - // yieldIndex -> native table in the BaselineScript. + // Determine the resume address based on the yieldAndAwaitIndex and the + // yieldAndAwaitIndex -> native table in the BaselineScript. masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2); masm.addPtr(scratch2, scratch1); - masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2); + masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()), + scratch2); masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1); // Mark as running and jump to the generator's JIT code. - masm.storeValue(Int32Value(GeneratorObject::YIELD_INDEX_RUNNING), - Address(genObj, GeneratorObject::offsetOfYieldIndexSlot())); + masm.storeValue(Int32Value(GeneratorObject::YIELD_AND_AWAIT_INDEX_RUNNING), + Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot())); masm.jump(scratch1); } else { MOZ_ASSERT(resumeKind == GeneratorObject::THROW || resumeKind == GeneratorObject::CLOSE); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 1f558d7f24..878cb45b9f 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -208,6 +208,7 @@ namespace jit { _(JSOP_GENERATOR) \ _(JSOP_INITIALYIELD) \ _(JSOP_YIELD) \ + _(JSOP_AWAIT) \ _(JSOP_DEBUGAFTERYIELD) \ _(JSOP_FINALYIELDRVAL) \ _(JSOP_RESUME) \ @@ -256,9 +257,9 @@ class BaselineCompiler : public BaselineCompilerSpecific // equivalent positions when debug mode is off. CodeOffset postDebugPrologueOffset_; - // For each INITIALYIELD or YIELD op, this Vector maps the yield index - // to the bytecode offset of the next op. - Vector yieldOffsets_; + // For each INITIALYIELD or YIELD or AWAIT op, this Vector maps the yield + // index to the bytecode offset of the next op. + Vector yieldAndAwaitOffsets_; // Whether any on stack arguments are modified. bool modifiesArguments_; @@ -350,7 +351,7 @@ class BaselineCompiler : public BaselineCompilerSpecific MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry); - MOZ_MUST_USE bool addYieldOffset(); + MOZ_MUST_USE bool addYieldAndAwaitOffset(); void getEnvironmentCoordinateObject(Register reg); Address getEnvironmentCoordinateAddressFromObject(Register objReg, Register reg); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 9530f65fad..e65f10aaca 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -6600,12 +6600,13 @@ ICCall_IsSuspendedStarGenerator::Compiler::generateStubCode(MacroAssembler& masm masm.branchTestObjClass(Assembler::NotEqual, genObj, scratch, &StarGeneratorObject::class_, &returnFalse); - // If the yield index slot holds an int32 value < YIELD_INDEX_CLOSING, + // If the yield index slot holds an int32 value < YIELD_AND_AWAIT_INDEX_CLOSING, // the generator is suspended. - masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), argVal); + masm.loadValue(Address(genObj, GeneratorObject::offsetOfYieldAndAwaitIndexSlot()), argVal); masm.branchTestInt32(Assembler::NotEqual, argVal, &returnFalse); masm.unboxInt32(argVal, scratch); - masm.branch32(Assembler::AboveOrEqual, scratch, Imm32(StarGeneratorObject::YIELD_INDEX_CLOSING), + masm.branch32(Assembler::AboveOrEqual, scratch, + Imm32(StarGeneratorObject::YIELD_AND_AWAIT_INDEX_CLOSING), &returnFalse); masm.moveValue(BooleanValue(true), R0); diff --git a/js/src/jit/BaselineJIT.cpp b/js/src/jit/BaselineJIT.cpp index 5c21926b53..94e0182522 100644 --- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -760,12 +760,12 @@ BaselineScript::icEntryFromReturnAddress(uint8_t* returnAddr) } void -BaselineScript::copyYieldEntries(JSScript* script, Vector& yieldOffsets) +BaselineScript::copyYieldAndAwaitEntries(JSScript* script, Vector& yieldAndAwaitOffsets) { uint8_t** entries = yieldEntryList(); - for (size_t i = 0; i < yieldOffsets.length(); i++) { - uint32_t offset = yieldOffsets[i]; + for (size_t i = 0; i < yieldAndAwaitOffsets.length(); i++) { + uint32_t offset = yieldAndAwaitOffsets[i]; entries[i] = nativeCodeForPC(script, script->offsetToPC(offset)); } } diff --git a/js/src/jit/BaselineJIT.h b/js/src/jit/BaselineJIT.h index 5e7775a61f..b6ccd2ff9b 100644 --- a/js/src/jit/BaselineJIT.h +++ b/js/src/jit/BaselineJIT.h @@ -397,7 +397,7 @@ struct BaselineScript void copyICEntries(JSScript* script, const BaselineICEntry* entries, MacroAssembler& masm); void adoptFallbackStubs(FallbackICStubSpace* stubSpace); - void copyYieldEntries(JSScript* script, Vector& yieldOffsets); + void copyYieldAndAwaitEntries(JSScript* script, Vector& yieldAndAwaitOffsets); PCMappingIndexEntry& pcMappingIndexEntry(size_t index); CompactBufferReader pcMappingReader(size_t indexEntry); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 1ff7adfd16..652c23bf15 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -735,7 +735,7 @@ bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame, jsbytecode* pc, uint32_t stackDepth) { - MOZ_ASSERT(*pc == JSOP_YIELD); + MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT); // Return value is still on the stack. MOZ_ASSERT(stackDepth >= 1); @@ -816,7 +816,7 @@ GeneratorThrowOrClose(JSContext* cx, BaselineFrame* frame, Handlescript(); - uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()]; + uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()]; frame->setOverridePc(script->offsetToPC(offset)); MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame)); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index e56eebb2d4..e417e3a64d 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -655,7 +655,8 @@ IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset); inline bool FlowsIntoNext(JSOp op) { - /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ + // JSOP_YIELD/JSOP_AWAIT is considered to flow into the next instruction, + // like JSOP_CALL. switch (op) { case JSOP_RETRVAL: case JSOP_RETURN: diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index eb74a1ec65..bdd411d043 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -402,8 +402,8 @@ js::XDRScript(XDRState* xdr, HandleScope scriptEnclosingScope, HandleScrip ntrynotes = script->trynotes()->length; if (script->hasScopeNotes()) nscopenotes = script->scopeNotes()->length; - if (script->hasYieldOffsets()) - nyieldoffsets = script->yieldOffsets().length(); + if (script->hasYieldAndAwaitOffsets()) + nyieldoffsets = script->yieldAndAwaitOffsets().length(); nTypeSets = script->nTypeSets(); funLength = script->funLength(); @@ -902,7 +902,7 @@ js::XDRScript(XDRState* xdr, HandleScope scriptEnclosingScope, HandleScrip } for (i = 0; i < nyieldoffsets; ++i) { - uint32_t* offset = &script->yieldOffsets()[i]; + uint32_t* offset = &script->yieldAndAwaitOffsets()[i]; if (!xdr->codeUint32(offset)) return false; } @@ -2456,7 +2456,7 @@ ScriptDataSize(uint32_t nscopes, uint32_t nconsts, uint32_t nobjects, if (nscopenotes != 0) size += sizeof(ScopeNoteArray) + nscopenotes * sizeof(ScopeNote); if (nyieldoffsets != 0) - size += sizeof(YieldOffsetArray) + nyieldoffsets * sizeof(uint32_t); + size += sizeof(YieldAndAwaitOffsetArray) + nyieldoffsets * sizeof(uint32_t); return size; } @@ -2558,10 +2558,10 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco cursor += sizeof(ScopeNoteArray); } - YieldOffsetArray* yieldOffsets = nullptr; + YieldAndAwaitOffsetArray* yieldAndAwaitOffsets = nullptr; if (nyieldoffsets != 0) { - yieldOffsets = reinterpret_cast(cursor); - cursor += sizeof(YieldOffsetArray); + yieldAndAwaitOffsets = reinterpret_cast(cursor); + cursor += sizeof(YieldAndAwaitOffsetArray); } if (nconsts != 0) { @@ -2602,8 +2602,8 @@ JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nsco } if (nyieldoffsets != 0) { - yieldOffsets->init(reinterpret_cast(cursor), nyieldoffsets); - size_t vectorSize = nyieldoffsets * sizeof(script->yieldOffsets()[0]); + yieldAndAwaitOffsets->init(reinterpret_cast(cursor), nyieldoffsets); + size_t vectorSize = nyieldoffsets * sizeof(script->yieldAndAwaitOffsets()[0]); #ifdef DEBUG memset(cursor, 0, vectorSize); #endif @@ -2623,10 +2623,10 @@ JSScript::initFunctionPrototype(ExclusiveContext* cx, Handle script, uint32_t numObjects = 0; uint32_t numTryNotes = 0; uint32_t numScopeNotes = 0; - uint32_t numYieldOffsets = 0; + uint32_t numYieldAndAwaitOffsets = 0; uint32_t numTypeSets = 0; if (!partiallyInit(cx, script, numScopes, numConsts, numObjects, numTryNotes, - numScopeNotes, numYieldOffsets, numTypeSets)) + numScopeNotes, numYieldAndAwaitOffsets, numTypeSets)) { return false; } @@ -2739,7 +2739,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco if (!partiallyInit(cx, script, bce->scopeList.length(), bce->constList.length(), bce->objectList.length, bce->tryNoteList.length(), bce->scopeNoteList.length(), - bce->yieldOffsetList.length(), bce->typesetCount)) + bce->yieldAndAwaitOffsetList.length(), bce->typesetCount)) { return false; } @@ -2794,8 +2794,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, Byteco // Copy yield offsets last, as the generator kind is set in // initFromFunctionBox. - if (bce->yieldOffsetList.length() != 0) - bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength); + if (bce->yieldAndAwaitOffsetList.length() != 0) + bce->yieldAndAwaitOffsetList.finish(script->yieldAndAwaitOffsets(), prologueLength); #ifdef DEBUG script->assertValidJumpTargets(); @@ -3242,7 +3242,7 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, uint32_t nscopes = src->scopes()->length; uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0; uint32_t nscopenotes = src->hasScopeNotes() ? src->scopeNotes()->length : 0; - uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0; + uint32_t nyieldoffsets = src->hasYieldAndAwaitOffsets() ? src->yieldAndAwaitOffsets().length() : 0; /* Script data */ @@ -3380,8 +3380,10 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, dst->trynotes()->vector = Rebase(dst, src, src->trynotes()->vector); if (nscopenotes != 0) dst->scopeNotes()->vector = Rebase(dst, src, src->scopeNotes()->vector); - if (nyieldoffsets != 0) - dst->yieldOffsets().vector_ = Rebase(dst, src, src->yieldOffsets().vector_); + if (nyieldoffsets != 0) { + dst->yieldAndAwaitOffsets().vector_ = + Rebase(dst, src, src->yieldAndAwaitOffsets().vector_); + } /* * Function delazification assumes that their script does not have a diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 13eaeff34d..c19fbfc71d 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -156,7 +156,7 @@ struct ScopeNoteArray { uint32_t length; // Count of indexed try notes. }; -class YieldOffsetArray { +class YieldAndAwaitOffsetArray { friend bool detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst, MutableHandle> scopes); @@ -1699,7 +1699,9 @@ class JSScript : public js::gc::TenuredCell bool hasObjects() const { return hasArray(OBJECTS); } bool hasTrynotes() const { return hasArray(TRYNOTES); } bool hasScopeNotes() const { return hasArray(SCOPENOTES); } - bool hasYieldOffsets() const { return isStarGenerator() || isLegacyGenerator() || isAsync(); } + bool hasYieldAndAwaitOffsets() const { + return isStarGenerator() || isLegacyGenerator() || isAsync(); + } #define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0)) @@ -1708,7 +1710,9 @@ class JSScript : public js::gc::TenuredCell size_t objectsOffset() const { return OFF(constsOffset, hasConsts, js::ConstArray); } size_t trynotesOffset() const { return OFF(objectsOffset, hasObjects, js::ObjectArray); } size_t scopeNotesOffset() const { return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); } - size_t yieldOffsetsOffset() const { return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); } + size_t yieldAndAwaitOffsetsOffset() const { + return OFF(scopeNotesOffset, hasScopeNotes, js::ScopeNoteArray); + } #undef OFF @@ -1738,9 +1742,10 @@ class JSScript : public js::gc::TenuredCell return reinterpret_cast(data + scopeNotesOffset()); } - js::YieldOffsetArray& yieldOffsets() { - MOZ_ASSERT(hasYieldOffsets()); - return *reinterpret_cast(data + yieldOffsetsOffset()); + js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsets() { + MOZ_ASSERT(hasYieldAndAwaitOffsets()); + return *reinterpret_cast(data + + yieldAndAwaitOffsetsOffset()); } bool hasLoops(); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index c0dff8faae..a61d37132d 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -64,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 genObj(cx, &obj->as()); 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()) { RootedValue val(cx, ObjectValue(*frame.callee())); @@ -75,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) { @@ -173,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 diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h index ca1452b347..beb15f790b 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)); + setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_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)); + 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,7 +175,7 @@ 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()); } @@ -187,8 +188,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/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 1cc75e676b..6959e15c2c 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1939,7 +1939,6 @@ CASE(EnableInterruptsPseudoOpcode) CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) CASE(JSOP_UNUSED192) -CASE(JSOP_UNUSED209) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) @@ -3994,6 +3993,7 @@ CASE(JSOP_INITIALYIELD) } CASE(JSOP_YIELD) +CASE(JSOP_AWAIT) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(REGS.fp()->isFunctionFrame()); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index e4b8d7ddaf..e946084f84 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2065,7 +2065,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) \ @@ -2074,7 +2074,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) \ @@ -2129,7 +2129,15 @@ * Stack: => */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ - macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the generator and the return value 'result', stops interpretation + * and returns 'result'. Pushes resolved value onto the stack. + * Category: Statements + * Type: Generator + * Operands: uint24_t yieldAndAwaitIndex + * Stack: result, gen => resolved + */ \ + macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \ /* From bc80cb0d51798fbf9506dba7235c8ff04d8391f6 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Tue, 3 Dec 2019 17:34:06 -0500 Subject: [PATCH 08/32] Bug 1343481 - Part 4: Add Add GeneratorObject.{isAfterYield,isAfterAwait}. Tag #1287 --- js/src/vm/GeneratorObject.cpp | 31 +++++++++++++++++++++++++++++++ js/src/vm/GeneratorObject.h | 7 +++++++ 2 files changed, 38 insertions(+) diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index a61d37132d..11c686f288 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -370,3 +370,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 beb15f790b..f19ca2aac3 100644 --- a/js/src/vm/GeneratorObject.h +++ b/js/src/vm/GeneratorObject.h @@ -179,6 +179,13 @@ class GeneratorObject : public NativeObject setFixedSlot(NEWTARGET_SLOT, NullValue()); } + bool isAfterYield(); + bool isAfterAwait(); + +private: + bool isAfterYieldOrAwait(JSOp op); + +public: static size_t offsetOfCalleeSlot() { return getFixedSlotOffset(CALLEE_SLOT); } From 04ec8aaf1cde871ae51392a4581462c04dcf95bc Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:53:59 -0500 Subject: [PATCH 09/32] Bug 1343481 - Part 5: Rename AsyncFunction-related names in Promise.cpp to explicitly say Async Function. Tag #1287 --- js/src/builtin/Promise.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 884cd6bac9..021f6021f3 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -34,8 +34,8 @@ MillisecondsSinceStartup() enum PromiseHandler { PromiseHandlerIdentity = 0, PromiseHandlerThrower, - PromiseHandlerAwaitFulfilled, - PromiseHandlerAwaitRejected, + PromiseHandlerAsyncFunctionAwaitFulfilled, + PromiseHandlerAsyncFunctionAwaitRejected, }; enum ResolutionMode { @@ -184,7 +184,7 @@ enum ReactionRecordSlots { #define REACTION_FLAG_RESOLVED 0x1 #define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 -#define REACTION_FLAG_AWAIT 0x8 +#define REACTION_FLAG_ASYNC_FUNCTION_AWAIT 0x8 // ES2016, 25.4.1.2. class PromiseReactionRecord : public NativeObject @@ -211,14 +211,14 @@ class PromiseReactionRecord : public NativeObject flags |= REACTION_FLAG_FULFILLED; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - void setIsAwait() { + void setIsAsyncFunctionAwait() { int32_t flags = this->flags(); - flags |= REACTION_FLAG_AWAIT; + flags |= REACTION_FLAG_ASYNC_FUNCTION_AWAIT; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - bool isAwait() { + bool isAsyncFunctionAwait() { int32_t flags = this->flags(); - return flags & REACTION_FLAG_AWAIT; + return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT; } Value handler() { MOZ_ASSERT(targetState() != JS::PromiseState::Pending); @@ -820,10 +820,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat } static MOZ_MUST_USE bool -AwaitPromiseReactionJob(JSContext* cx, Handle reaction, - MutableHandleValue rval) +AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAwait()); + MOZ_ASSERT(reaction->isAsyncFunctionAwait()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); @@ -831,12 +831,12 @@ AwaitPromiseReactionJob(JSContext* cx, Handle reaction, RootedValue generatorVal(cx, resultPromise->getFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PromiseHandlerAwaitFulfilled - || handlerNum == PromiseHandlerAwaitRejected); + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled || + handlerNum == PromiseHandlerAsyncFunctionAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PromiseHandlerAwaitFulfilled) { + if (handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { @@ -891,8 +891,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) // Steps 1-2. Rooted reaction(cx, &reactionObj->as()); - if (reaction->isAwait()) - return AwaitPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncFunctionAwait()) + return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval()); // Step 3. RootedValue handlerVal(cx, reaction->handler()); @@ -2202,8 +2202,8 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand return false; // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAwaitFulfilled)); - RootedValue onRejected(cx, Int32Value(PromiseHandlerAwaitRejected)); + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitRejected)); RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) @@ -2217,7 +2217,7 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand if (!reaction) return false; - reaction->setIsAwait(); + reaction->setIsAsyncFunctionAwait(); // Step 8. return PerformPromiseThenWithReaction(cx, promise, reaction); From 2bbebc6ebc95771d540ecac9d6bdc6b7ef647166 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:55:05 -0500 Subject: [PATCH 10/32] Bug 1343481 - Part 6: Add native functions wrapper for GetInternalError and GetTypeError. Tag #1287 --- js/src/builtin/Promise.cpp | 6 ++---- js/src/jsexn.cpp | 17 +++++++++++++++++ js/src/jsexn.h | 5 +++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 021f6021f3..8fd39e7bbc 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -11,11 +11,11 @@ #include "mozilla/TimeStamp.h" #include "jscntxt.h" +#include "jsexn.h" #include "gc/Heap.h" #include "js/Debug.h" #include "vm/AsyncFunction.h" -#include "vm/SelfHosting.h" #include "jsobjinlines.h" @@ -783,9 +783,7 @@ RejectMaybeWrappedPromise(JSContext *cx, HandleObject promiseObj, HandleValue re // interpreter frame active right now. If a thenable job with a // throwing `then` function got us here, that'll not be the case, // so we add one by throwing the error from self-hosted code. - FixedInvokeArgs<1> getErrorArgs(cx); - getErrorArgs[0].set(Int32Value(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON)); - if (!CallSelfHostedFunction(cx, "GetInternalError", reason, getErrorArgs, &reason)) + if (!GetInternalError(cx, JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON, &reason)) return false; } } diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 3fc9200c1d..72f03ea550 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -32,6 +32,7 @@ #include "vm/ErrorObject.h" #include "vm/GlobalObject.h" #include "vm/SavedStacks.h" +#include "vm/SelfHosting.h" #include "vm/StringBuffer.h" #include "jsobjinlines.h" @@ -1091,3 +1092,19 @@ js::ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& byte return "<>"; return bytes.encodeLatin1(cx, str); } + +bool +js::GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error) +{ + FixedInvokeArgs<1> args(cx); + args[0].set(Int32Value(errorNumber)); + return CallSelfHostedFunction(cx, "GetInternalError", NullHandleValue, args, error); +} + +bool +js::GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error) +{ + FixedInvokeArgs<1> args(cx); + args[0].set(Int32Value(errorNumber)); + return CallSelfHostedFunction(cx, "GetTypeError", NullHandleValue, args, error); +} diff --git a/js/src/jsexn.h b/js/src/jsexn.h index 00120d89c8..ee69e3c500 100644 --- a/js/src/jsexn.h +++ b/js/src/jsexn.h @@ -131,6 +131,11 @@ class AutoAssertNoPendingException extern const char* ValueToSourceForError(JSContext* cx, HandleValue val, JSAutoByteString& bytes); +bool +GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error); +bool +GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error); + } // namespace js #endif /* jsexn_h */ From c1bdfa62fcd54ffdcb9eace0e11a8534a6763472 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:55:24 -0500 Subject: [PATCH 11/32] Bug 1343481 - Part 7: Add BytecodeEmitter::emitDotGenerator and make yield/await nodes unary. Tag #1287 --- js/src/builtin/ReflectParse.cpp | 12 ++-- js/src/frontend/BytecodeEmitter.cpp | 85 +++++++++++++++++----------- js/src/frontend/BytecodeEmitter.h | 5 +- js/src/frontend/FoldConstants.cpp | 25 ++++---- js/src/frontend/FullParseHandler.h | 23 ++++---- js/src/frontend/NameFunctions.cpp | 21 +++---- js/src/frontend/ParseNode.cpp | 28 ++++----- js/src/frontend/ParseNode.h | 6 +- js/src/frontend/Parser.cpp | 33 ++--------- js/src/frontend/Parser.h | 2 - js/src/frontend/SyntaxParseHandler.h | 6 +- 11 files changed, 129 insertions(+), 117 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index 120970196a..22c958d4ca 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -2800,11 +2800,11 @@ ASTSerializer::generatorExpression(ParseNode* pn, MutableHandleValue dst) LOCAL_ASSERT(next->isKind(PNK_SEMI) && next->pn_kid->isKind(PNK_YIELD) && - next->pn_kid->pn_left); + next->pn_kid->pn_kid); RootedValue body(cx); - return expression(next->pn_kid->pn_left, &body) && + return expression(next->pn_kid->pn_kid, &body) && builder.generatorExpression(body, blocks, filter, isLegacy, &pn->pn_pos, dst); } @@ -3146,7 +3146,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_YIELD_STAR: { - MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos)); RootedValue arg(cx); return expression(pn->pn_left, &arg) && @@ -3155,10 +3155,10 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_YIELD: { - MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos)); + MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos)); RootedValue arg(cx); - return optExpression(pn->pn_left, &arg) && + return optExpression(pn->pn_kid, &arg) && builder.yieldExpression(arg, NotDelegating, &pn->pn_pos, dst); } @@ -3480,7 +3480,7 @@ ASTSerializer::functionArgsAndBody(ParseNode* pn, NodeVector& args, NodeVector& ParseNode* pnstart = pnbody->pn_head; // Skip over initial yield in generator. - if (pnstart && pnstart->isKind(PNK_YIELD)) { + if (pnstart && pnstart->isKind(PNK_INITIALYIELD)) { MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD); pnstart = pnstart->pn_next; } diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index d934c0c806..5b1e9229ad 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3175,10 +3175,11 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) *answer = true; return true; + case PNK_INITIALYIELD: case PNK_YIELD_STAR: case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(pn->isArity(PN_BINARY)); + MOZ_ASSERT(pn->isArity(PN_UNARY)); *answer = true; return true; @@ -8526,45 +8527,61 @@ BytecodeEmitter::emitReturn(ParseNode* pn) } bool -BytecodeEmitter::emitYield(ParseNode* pn) +BytecodeEmitter::emitGetDotGenerator() { - MOZ_ASSERT(sc->isFunctionBox()); + NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator); + return emitGetNameAtLocation(cx->names().dotGenerator, loc); +} - if (pn->getOp() == JSOP_YIELD || pn->getOp() == JSOP_AWAIT) { - bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); - if (needsIteratorResult) { - if (!emitPrepareIteratorResult()) - return false; - } - if (pn->pn_left) { - if (!emitTree(pn->pn_left)) - return false; - } else { - if (!emit1(JSOP_UNDEFINED)) - return false; - } - if (needsIteratorResult) { - if (!emitFinishIteratorResult(false)) - return false; - } - } else { - MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD); - } - - if (!emitTree(pn->pn_right)) +bool +BytecodeEmitter::emitInitialYield(ParseNode* pn) +{ + if (!emitTree(pn->pn_kid)) return false; - if (!emitYieldOp(pn->getOp())) + if (!emitYieldOp(JSOP_INITIALYIELD)) return false; - if (pn->getOp() == JSOP_INITIALYIELD && !emit1(JSOP_POP)) + if (!emit1(JSOP_POP)) return false; return true; } bool -BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) +BytecodeEmitter::emitYield(ParseNode* pn) +{ + MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_YIELD || pn->getOp() == JSOP_AWAIT); + + bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); + if (needsIteratorResult) { + if (!emitPrepareIteratorResult()) + return false; + } + if (pn->pn_kid) { + if (!emitTree(pn->pn_kid)) + return false; + } else { + if (!emit1(JSOP_UNDEFINED)) + return false; + } + if (needsIteratorResult) { + if (!emitFinishIteratorResult(false)) + return false; + } + + if (!emitGetDotGenerator()) + return false; + + if (!emitYieldOp(pn->getOp())) + return false; + + return true; +} + +bool +BytecodeEmitter::emitYieldStar(ParseNode* iter) { MOZ_ASSERT(sc->isFunctionBox()); MOZ_ASSERT(sc->asFunctionBox()->isStarGenerator()); @@ -8594,7 +8611,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen) MOZ_ASSERT(this->stackDepth == startDepth); // Load the generator object. - if (!emitTree(gen)) // ITER RESULT GENOBJ + if (!emitGetDotGenerator()) // ITER RESULT GENOBJ return false; // Yield RESULT as-is, without re-boxing. @@ -10339,8 +10356,7 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (!emit1(JSOP_SETRVAL)) return false; - NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator); - if (!emitGetNameAtLocation(cx->names().dotGenerator, loc)) + if (!emitGetDotGenerator()) return false; // No need to check for finally blocks, etc as in EmitReturn. @@ -10600,7 +10616,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_YIELD_STAR: - if (!emitYieldStar(pn->pn_left, pn->pn_right)) + if (!emitYieldStar(pn->pn_kid)) return false; break; @@ -10609,6 +10625,11 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: return false; break; + case PNK_INITIALYIELD: + if (!emitInitialYield(pn)) + return false; + break; + case PNK_YIELD: case PNK_AWAIT: if (!emitYield(pn)) diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 153b078712..32e12b38bb 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -607,9 +607,12 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitFinishIteratorResult(bool done); MOZ_MUST_USE bool iteratorResultShape(unsigned* shape); + MOZ_MUST_USE bool emitGetDotGenerator(); + + MOZ_MUST_USE bool emitInitialYield(ParseNode* pn); MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); - MOZ_MUST_USE bool emitYieldStar(ParseNode* iter, ParseNode* gen); + MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 689fa02b4b..16294c4a81 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -117,9 +117,10 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) // These two aren't statements in the spec, but we sometimes insert them // in statement lists anyway. + case PNK_INITIALYIELD: case PNK_YIELD_STAR: case PNK_YIELD: - MOZ_ASSERT(node->isArity(PN_BINARY)); + MOZ_ASSERT(node->isArity(PN_UNARY)); *result = false; return true; @@ -1775,21 +1776,23 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bo case PNK_GENEXP: return FoldList(cx, pn, parser, inGenexpLambda); + case PNK_INITIALYIELD: + MOZ_ASSERT(pn->isArity(PN_UNARY)); + MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) && + pn->pn_kid->pn_left->isKind(PNK_NAME) && + pn->pn_kid->pn_right->isKind(PNK_GENERATOR)); + return true; + case PNK_YIELD_STAR: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME)); - return Fold(cx, &pn->pn_left, parser, inGenexpLambda); + MOZ_ASSERT(pn->isArity(PN_UNARY)); + return Fold(cx, &pn->pn_kid, parser, inGenexpLambda); case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) || - (pn->pn_right->isKind(PNK_ASSIGN) && - pn->pn_right->pn_left->isKind(PNK_NAME) && - pn->pn_right->pn_right->isKind(PNK_GENERATOR))); - if (!pn->pn_left) + MOZ_ASSERT(pn->isArity(PN_UNARY)); + if (!pn->pn_kid) return true; - return Fold(cx, &pn->pn_left, parser, inGenexpLambda); + return Fold(cx, &pn->pn_kid, parser, inGenexpLambda); case PNK_RETURN: return FoldReturn(cx, pn, parser, inGenexpLambda); diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 6ab93f3f9d..44694298bd 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -440,20 +440,24 @@ class FullParseHandler return true; } - ParseNode* newYieldExpression(uint32_t begin, ParseNode* value, ParseNode* gen, - JSOp op = JSOP_YIELD) { - TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_(PNK_YIELD, op, pos, value, gen); + ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) { + TokenPos pos(begin, begin + 1); + return new_(PNK_INITIALYIELD, JSOP_INITIALYIELD, pos, gen); } - ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { + ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) { + TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); + return new_(PNK_YIELD, JSOP_YIELD, pos, value); + } + + ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value->pn_pos.end); - return new_(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen); + return new_(PNK_YIELD_STAR, JSOP_NOP, pos, value); } - ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value, ParseNode* gen) { + ParseNode* newAwaitExpression(uint32_t begin, ParseNode* value) { TokenPos pos(begin, value ? value->pn_pos.end : begin + 1); - return new_(PNK_AWAIT, JSOP_AWAIT, pos, value, gen); + return new_(PNK_AWAIT, JSOP_AWAIT, pos, value); } // Statements @@ -506,8 +510,7 @@ class FullParseHandler if (!genInit) return false; - ParseNode* initialYield = newYieldExpression(yieldPos.begin, nullptr, genInit, - JSOP_INITIALYIELD); + ParseNode* initialYield = newInitialYieldExpression(yieldPos.begin, genInit); if (!initialYield) return false; diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index dc54d0a88f..376be76242 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -501,24 +501,25 @@ class NameResolver return false; break; + case PNK_INITIALYIELD: + MOZ_ASSERT(cur->pn_kid->isKind(PNK_ASSIGN) && + cur->pn_kid->pn_left->isKind(PNK_NAME) && + cur->pn_kid->pn_right->isKind(PNK_GENERATOR)); + break; + case PNK_YIELD_STAR: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME)); - if (!resolve(cur->pn_left, prefix)) + MOZ_ASSERT(cur->isArity(PN_UNARY)); + if (!resolve(cur->pn_kid, prefix)) return false; break; case PNK_YIELD: case PNK_AWAIT: - MOZ_ASSERT(cur->isArity(PN_BINARY)); - if (cur->pn_left) { - if (!resolve(cur->pn_left, prefix)) + MOZ_ASSERT(cur->isArity(PN_UNARY)); + if (cur->pn_kid) { + if (!resolve(cur->pn_kid, prefix)) return false; } - MOZ_ASSERT(cur->pn_right->isKind(PNK_NAME) || - (cur->pn_right->isKind(PNK_ASSIGN) && - cur->pn_right->pn_left->isKind(PNK_NAME) && - cur->pn_right->pn_right->isKind(PNK_GENERATOR))); break; case PNK_RETURN: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 5fe64e3d36..42ae9451a3 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -286,22 +286,24 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) return PushResult::Recyclable; } - // The left half is the expression being yielded. The right half is - // internal goop: a name reference to the invisible '.generator' local - // variable, or an assignment of a PNK_GENERATOR node to the '.generator' - // local, for a synthesized, prepended initial yield. Yum! + // The child is an assignment of a PNK_GENERATOR node to the + // '.generator' local, for a synthesized, prepended initial yield. + case PNK_INITIALYIELD: { + MOZ_ASSERT(pn->isArity(PN_UNARY)); + MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) && + pn->pn_kid->pn_left->isKind(PNK_NAME) && + pn->pn_kid->pn_right->isKind(PNK_GENERATOR)); + stack->push(pn->pn_kid); + return PushResult::Recyclable; + } + + // The child is the expression being yielded. case PNK_YIELD_STAR: case PNK_YIELD: case PNK_AWAIT: { - MOZ_ASSERT(pn->isArity(PN_BINARY)); - MOZ_ASSERT(pn->pn_right); - MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME) || - (pn->pn_right->isKind(PNK_ASSIGN) && - pn->pn_right->pn_left->isKind(PNK_NAME) && - pn->pn_right->pn_right->isKind(PNK_GENERATOR))); - if (pn->pn_left) - stack->push(pn->pn_left); - stack->push(pn->pn_right); + MOZ_ASSERT(pn->isArity(PN_UNARY)); + if (pn->pn_kid) + stack->push(pn->pn_kid); return PushResult::Recyclable; } diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 1f20f39887..9a435e2701 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -83,6 +83,7 @@ class ObjectBox; F(THROW) \ F(DEBUGGER) \ F(GENERATOR) \ + F(INITIALYIELD) \ F(YIELD) \ F(YIELD_STAR) \ F(GENEXP) \ @@ -418,8 +419,9 @@ IsTypeofKind(ParseNodeKind kind) * PNK_LEXICALSCOPE scope pn_u.scope.bindings: scope bindings * pn_u.scope.body: scope body * PNK_GENERATOR nullary - * PNK_YIELD, binary pn_left: expr or null; pn_right: generator object - * PNK_YIELD_STAR + * PNK_INITIALYIELD unary pn_kid: generator object + * PNK_YIELD, unary pn_kid: expr or null + * PNK_YIELD_STAR, * PNK_ARRAYCOMP list pn_count: 1 * pn_head: list of 1 element, which is block * enclosing for loop(s) and optionally diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 406d377258..3ae5890ac5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6546,29 +6546,6 @@ Parser::returnStatement(YieldHandling yieldHandling) return pn; } -template -typename ParseHandler::Node -Parser::newYieldExpression(uint32_t begin, typename ParseHandler::Node expr, - bool isYieldStar) -{ - Node generator = newDotGeneratorName(); - if (!generator) - return null(); - if (isYieldStar) - return handler.newYieldStarExpression(begin, expr, generator); - return handler.newYieldExpression(begin, expr, generator); -} - -template -typename ParseHandler::Node -Parser::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr) -{ - Node generator = newDotGeneratorName(); - if (!generator) - return null(); - return handler.newAwaitExpression(begin, expr, generator); -} - template typename ParseHandler::Node Parser::yieldExpression(InHandling inHandling) @@ -6617,7 +6594,9 @@ Parser::yieldExpression(InHandling inHandling) if (!exprNode) return null(); } - return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR); + if (kind == PNK_YIELD_STAR) + return handler.newYieldStarExpression(begin, exprNode); + return handler.newYieldExpression(begin, exprNode); } case NotGenerator: @@ -6694,7 +6673,7 @@ Parser::yieldExpression(InHandling inHandling) return null(); } - return newYieldExpression(begin, exprNode); + return handler.newYieldExpression(begin, exprNode); } } @@ -8449,7 +8428,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t if (!kid) return null(); pc->lastAwaitOffset = begin; - return newAwaitExpression(begin, kid); + return handler.newAwaitExpression(begin, kid); } } @@ -8707,7 +8686,7 @@ Parser::comprehensionTail(GeneratorKind comprehensionKind) return handler.newArrayPush(begin, bodyExpr); MOZ_ASSERT(comprehensionKind == StarGenerator); - Node yieldExpr = newYieldExpression(begin, bodyExpr); + Node yieldExpr = handler.newYieldExpression(begin, bodyExpr); if (!yieldExpr) return null(); yieldExpr = handler.parenthesize(yieldExpr); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 33fe345d63..73c2817f45 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1110,8 +1110,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter inline Node newName(PropertyName* name); inline Node newName(PropertyName* name, TokenPos pos); - inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false); - inline Node newAwaitExpression(uint32_t begin, Node expr); inline bool abortIfSyntaxParser(); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index a604b599fb..d919f1354d 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -301,9 +301,9 @@ class SyntaxParseHandler MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } - Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } - Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } - Node newAwaitExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; } + Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; } + Node newYieldStarExpression(uint32_t begin, Node value) { return NodeGeneric; } + Node newAwaitExpression(uint32_t begin, Node value) { return NodeGeneric; } // Statements From c9f1fa2cdaef705ca6e87bea5ff9d603ccc4cca1 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 20:59:32 -0500 Subject: [PATCH 12/32] Bug 1316098 - Optimize out result object allocation for await/return in async function. Tag #1287 --- js/src/doc/Debugger/Conventions.md | 4 +- js/src/frontend/BytecodeEmitter.cpp | 25 ++++- js/src/frontend/BytecodeEmitter.h | 1 + js/src/frontend/SharedContext.h | 2 +- .../onExceptionUnwind-resumption-async.js | 99 +------------------ js/src/js.msg | 1 - js/src/vm/AsyncFunction.cpp | 24 +---- js/src/vm/AsyncFunction.h | 3 - js/src/vm/Debugger.cpp | 12 +-- js/src/vm/Opcodes.h | 6 +- 10 files changed, 39 insertions(+), 138 deletions(-) diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md index e8bd3ee430..5de7bc1715 100644 --- a/js/src/doc/Debugger/Conventions.md +++ b/js/src/doc/Debugger/Conventions.md @@ -110,8 +110,8 @@ resumption value has one of the following forms: the `new` expression returns the frame's `this` value. Similarly, if the function is the constructor for a subclass, then a non-object value may result in a TypeError. - If the frame is a generator or async function, then value must - conform to the iterator protocol: it must be a non-proxy object of the form + If the frame is a generator function, then value must conform to the + iterator protocol: it must be a non-proxy object of the form { done: boolean, value: v }, where both `done` and `value` are ordinary properties. diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 5b1e9229ad..29423fd949 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8552,7 +8552,7 @@ bool BytecodeEmitter::emitYield(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); - MOZ_ASSERT(pn->getOp() == JSOP_YIELD || pn->getOp() == JSOP_AWAIT); + MOZ_ASSERT(pn->getOp() == JSOP_YIELD); bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult(); if (needsIteratorResult) { @@ -8574,12 +8574,27 @@ BytecodeEmitter::emitYield(ParseNode* pn) if (!emitGetDotGenerator()) return false; - if (!emitYieldOp(pn->getOp())) + if (!emitYieldOp(JSOP_YIELD)) return false; return true; } +bool +BytecodeEmitter::emitAwait(ParseNode* pn) +{ + MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); + + if (!emitTree(pn->pn_kid)) + return false; + if (!emitGetDotGenerator()) + return false; + if (!emitYieldOp(JSOP_AWAIT)) + return false; + return true; +} + bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { @@ -10631,11 +10646,15 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_YIELD: - case PNK_AWAIT: if (!emitYield(pn)) return false; break; + case PNK_AWAIT: + if (!emitAwait(pn)) + return false; + break; + case PNK_STATEMENTLIST: if (!emitStatementList(pn)) return false; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 32e12b38bb..d583a50712 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -613,6 +613,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); + MOZ_MUST_USE bool emitAwait(ParseNode* pn); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h index 2d478308ec..8a5f6c0bd3 100644 --- a/js/src/frontend/SharedContext.h +++ b/js/src/frontend/SharedContext.h @@ -553,7 +553,7 @@ class FunctionBox : public ObjectBox, public SharedContext } bool needsIteratorResult() const { - return isStarGenerator() || isAsync(); + return isStarGenerator(); } bool isAsync() const { return asyncKind() == AsyncFunction; } diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js index d4e7e85766..0b2ee766bf 100644 --- a/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js @@ -9,28 +9,6 @@ async function f() { } `); -// To continue testing after uncaught exception, remember the exception and -// return normal completeion. -var currentFrame; -var uncaughtException; -dbg.uncaughtExceptionHook = function(e) { - uncaughtException = e; - return { - return: currentFrame.eval("({ done: true, value: 'uncaught' })").return - }; -}; -function testUncaughtException() { - uncaughtException = undefined; - var val = g.eval(` -var val; -f().then(v => { val = v }); -drainJobQueue(); -val; -`); - assertEq(val, "uncaught"); - assertEq(uncaughtException instanceof TypeError, true); -} - // Just continue dbg.onExceptionUnwind = function(frame) { return undefined; @@ -42,83 +20,10 @@ drainJobQueue(); assertEq(exc instanceof ReferenceError, true); `); -// Should return object. +// Return with resumption value. dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; return { - return: "foo" - }; -}; -testUncaughtException(); - -// The object should have `done` property and `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({})").return - }; -}; -testUncaughtException(); - -// The object should have `done` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ value: 10 })").return - }; -}; -testUncaughtException(); - -// The object should have `value` property. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true })").return - }; -}; -testUncaughtException(); - -// `done` property should be a boolean value. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: 10, value: 10 })").return - }; -}; -testUncaughtException(); - -// `done` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ get done() { return true; }, value: 10 })").return - }; -}; -testUncaughtException(); - -// `value` property shouldn't be an accessor. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true, get value() { return 10; } })").return - }; -}; -testUncaughtException(); - -// The object shouldn't be a Proxy. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("new Proxy({ done: true, value: 10 }, {})").return - }; -}; -testUncaughtException(); - -// Correct resumption value. -dbg.onExceptionUnwind = function(frame) { - currentFrame = frame; - return { - return: frame.eval("({ done: true, value: 10 })").return + return: 10 }; }; var val = g.eval(` diff --git a/js/src/js.msg b/js/src/js.msg index e2d923bc4f..7465129bfb 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -438,7 +438,6 @@ MSG_DEF(JSMSG_SC_SAB_DISABLED, 0, JSEXN_TYPEERR, "SharedArrayBuffer not // Debugger MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null") -MSG_DEF(JSMSG_DEBUG_BAD_AWAIT, 0, JSEXN_TYPEERR, "await expression received invalid value") MSG_DEF(JSMSG_DEBUG_BAD_LINE, 0, JSEXN_TYPEERR, "invalid line number") MSG_DEF(JSMSG_DEBUG_BAD_OFFSET, 0, JSEXN_TYPEERR, "invalid script offset") MSG_DEF(JSMSG_DEBUG_BAD_REFERENT, 2, JSEXN_TYPEERR, "{0} does not refer to {1}") diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index be7a36ecc9..2bcec54657 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -171,22 +171,14 @@ AsyncFunctionResume(JSContext* cx, Handle resultPromise, HandleV : cx->names().StarGeneratorThrow; FixedInvokeArgs<1> args(cx); args[0].set(valueOrReason); - RootedValue result(cx); - if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) + RootedValue value(cx); + if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value)) 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 (generatorVal.toObject().as().isAfterAwait()) + return AsyncFunctionAwait(cx, resultPromise, value); - if (doneVal.toBoolean()) - return AsyncFunctionReturned(cx, resultPromise, value); - - return AsyncFunctionAwait(cx, resultPromise, value); + return AsyncFunctionReturned(cx, resultPromise, value); } // Async Functions proposal 2.2 steps 3-8. @@ -242,9 +234,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 d7f2c13117..de7c87d130 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 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/Debugger.cpp b/js/src/vm/Debugger.cpp index 7c09c50b06..2680699a46 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, value: }. 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() || callee->isAsync()) { + if (callee->isStarGenerator()) { if (!CheckStarGeneratorResumptionValue(cx, vp)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD); return false; diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index e946084f84..0fa63d9762 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2130,12 +2130,12 @@ */ \ macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \ /* - * Pops the generator and the return value 'result', stops interpretation - * and returns 'result'. Pushes resolved value onto the stack. + * 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: result, gen => resolved + * Stack: promise, gen => resolved */ \ macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ From 086d14cb8bfef6c34a11677ad4d476a004927caf Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Tue, 3 Dec 2019 20:00:57 -0500 Subject: [PATCH 13/32] Bug 1344753 - Update for-of stack depth in ControlFlowGenerator::processWhileOrForInLoop. Tag #1287 --- js/src/jit/IonBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index a9bf3519e2..877ec833b7 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -3349,7 +3349,7 @@ IonBuilder::whileOrForInLoop(jssrcnote* sn) unsigned stackPhiCount; if (SN_TYPE(sn) == SRC_FOR_OF) - stackPhiCount = 2; + stackPhiCount = 3; else if (SN_TYPE(sn) == SRC_FOR_IN) stackPhiCount = 1; else From 22cd310e82a0c39a4a796a4ea9bd93374f45e15c Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Wed, 4 Dec 2019 22:11:58 -0500 Subject: [PATCH 14/32] Bug 1317389: Change property attributes for generator and async functions to match ES2015/2017. Tag #1287 --- .../ecma_2017/AsyncFunctions/properties.js | 76 ++++++++++++ js/src/tests/ecma_6/Generators/properties.js | 111 ++++++++++++++++++ js/src/vm/AsyncFunction.cpp | 5 +- js/src/vm/GeneratorObject.cpp | 8 +- js/src/vm/GlobalObject.cpp | 11 +- js/src/vm/GlobalObject.h | 10 +- 6 files changed, 209 insertions(+), 12 deletions(-) create mode 100644 js/src/tests/ecma_2017/AsyncFunctions/properties.js create mode 100644 js/src/tests/ecma_6/Generators/properties.js diff --git a/js/src/tests/ecma_2017/AsyncFunctions/properties.js b/js/src/tests/ecma_2017/AsyncFunctions/properties.js new file mode 100644 index 0000000000..ca383901bb --- /dev/null +++ b/js/src/tests/ecma_2017/AsyncFunctions/properties.js @@ -0,0 +1,76 @@ +/* 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/. */ + +function assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +async function asyncFunc(){} +var AsyncFunctionPrototype = Object.getPrototypeOf(asyncFunc); +var AsyncFunction = AsyncFunctionPrototype.constructor; + + +// ES2017, 25.5.2 Properties of the AsyncFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(AsyncFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunction), []); + +assertOwnDescriptor(AsyncFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "name", { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunction, "prototype", { + value: AsyncFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.5.3 Properties of the AsyncFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(AsyncFunctionPrototype).sort(), ["constructor"]); +assertEqArray(Object.getOwnPropertySymbols(AsyncFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(AsyncFunctionPrototype, "constructor", { + value: AsyncFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(AsyncFunctionPrototype, Symbol.toStringTag, { + value: "AsyncFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.5.4 AsyncFunction Instances + +assertEqArray(Object.getOwnPropertyNames(asyncFunc).sort(), ["length", "name"]); +assertEqArray(Object.getOwnPropertySymbols(asyncFunc), []); + +assertOwnDescriptor(asyncFunc, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(asyncFunc, "name", { + value: "asyncFunc", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Generators/properties.js b/js/src/tests/ecma_6/Generators/properties.js new file mode 100644 index 0000000000..7782e64c06 --- /dev/null +++ b/js/src/tests/ecma_6/Generators/properties.js @@ -0,0 +1,111 @@ +/* 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/. */ + +function assertOwnDescriptor(object, propertyKey, expected) { + var desc = Object.getOwnPropertyDescriptor(object, propertyKey); + if (desc === undefined) { + assertEq(expected, undefined, "Property shouldn't be present"); + return; + } + + assertEq(desc.enumerable, expected.enumerable, `${String(propertyKey)}.[[Enumerable]]`); + assertEq(desc.configurable, expected.configurable, `${String(propertyKey)}.[[Configurable]]`); + + if (Object.prototype.hasOwnProperty.call(desc, "value")) { + assertEq(desc.value, expected.value, `${String(propertyKey)}.[[Value]]`); + assertEq(desc.writable, expected.writable, `${String(propertyKey)}.[[Writable]]`); + } else { + assertEq(desc.get, expected.get, `${String(propertyKey)}.[[Get]]`); + assertEq(desc.set, expected.set, `${String(propertyKey)}.[[Set]]`); + } +} + +function* generator(){} +var GeneratorFunctionPrototype = Object.getPrototypeOf(generator); +var GeneratorFunction = GeneratorFunctionPrototype.constructor; +var GeneratorPrototype = GeneratorFunctionPrototype.prototype; + + +// ES2017, 25.2.2 Properties of the GeneratorFunction Constructor + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunction).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunction), []); + +assertOwnDescriptor(GeneratorFunction, "length", { + value: 1, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "name", { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunction, "prototype", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: false +}); + + +// ES2017, 25.2.3 Properties of the GeneratorFunction Prototype Object + +assertEqArray(Object.getOwnPropertyNames(GeneratorFunctionPrototype).sort(), ["constructor", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorFunctionPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorFunctionPrototype, "constructor", { + value: GeneratorFunction, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, "prototype", { + value: GeneratorPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorFunctionPrototype, Symbol.toStringTag, { + value: "GeneratorFunction", writable: false, enumerable: false, configurable: true +}); + + +// ES2017, 25.2.4 GeneratorFunction Instances + +assertEqArray(Object.getOwnPropertyNames(generator).sort(), ["length", "name", "prototype"]); +assertEqArray(Object.getOwnPropertySymbols(generator), []); + +assertOwnDescriptor(generator, "length", { + value: 0, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "name", { + value: "generator", writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(generator, "prototype", { + value: generator.prototype, writable: true, enumerable: false, configurable: false +}); + + +// ES2017, 25.3.1 Properties of Generator Prototype + +assertEqArray(Object.getOwnPropertyNames(GeneratorPrototype).sort(), ["constructor", "next", "return", "throw"]); +assertEqArray(Object.getOwnPropertySymbols(GeneratorPrototype), [Symbol.toStringTag]); + +assertOwnDescriptor(GeneratorPrototype, "constructor", { + value: GeneratorFunctionPrototype, writable: false, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "next", { + value: GeneratorPrototype.next, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "return", { + value: GeneratorPrototype.return, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, "throw", { + value: GeneratorPrototype.throw, writable: true, enumerable: false, configurable: true +}); + +assertOwnDescriptor(GeneratorPrototype, Symbol.toStringTag, { + value: "Generator", writable: false, enumerable: false, configurable: true +}); + + +if (typeof reportCompare == "function") + reportCompare(true, true); diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp index 2bcec54657..7132ed984d 100644 --- a/js/src/vm/AsyncFunction.cpp +++ b/js/src/vm/AsyncFunction.cpp @@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle 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)); diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp index 11c686f288..2f5c63c285 100644 --- a/js/src/vm/GeneratorObject.cpp +++ b/js/src/vm/GeneratorObject.cpp @@ -316,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle 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; @@ -333,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle 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)); diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 013208f666..70c33c6473 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -586,17 +586,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handlenames().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 9179abbb7d..dd3f231517 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -985,12 +985,14 @@ GlobalObject::createArrayFromBuffer() 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, From e9ad9eab1592f068841f7454639b30c4d70d4fcb Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Wed, 4 Dec 2019 18:47:17 -0500 Subject: [PATCH 15/32] Bug 1331092 - Part 0: Define NOMINMAX to avoid compile error from min/max macro on windows. Tag #1287 --- js/src/moz.build | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/moz.build b/js/src/moz.build index d1e80b4ce8..1546b8351e 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -790,3 +790,6 @@ if CONFIG['GNU_CXX']: # Suppress warnings in third-party code. if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']: SOURCES['jsdtoa.cpp'].flags += ['-Wno-implicit-fallthrough'] + +if CONFIG['OS_ARCH'] == 'WINNT': + DEFINES['NOMINMAX'] = True From c8d569d1b8d46c89404e033fb71b75b08c4266a7 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Wed, 4 Dec 2019 19:57:19 -0500 Subject: [PATCH 16/32] Bug 1331092 - Part 1: Add Symbol.asyncIterator. Tag #1287 --- js/src/jsapi.h | 1 + js/src/tests/ecma_6/Symbol/well-known.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 4dad68e6e2..7795c5e4ca 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -5088,6 +5088,7 @@ GetSymbolDescription(HandleSymbol symbol); macro(toPrimitive) \ macro(toStringTag) \ macro(unscopables) \ + macro(asyncIterator) \ macro(matchAll) enum class SymbolCode : uint32_t { diff --git a/js/src/tests/ecma_6/Symbol/well-known.js b/js/src/tests/ecma_6/Symbol/well-known.js index 8c5de12796..095d333a00 100644 --- a/js/src/tests/ecma_6/Symbol/well-known.js +++ b/js/src/tests/ecma_6/Symbol/well-known.js @@ -11,7 +11,8 @@ var names = [ "hasInstance", "split", "toPrimitive", - "unscopables" + "unscopables", + "asyncIterator" ]; for (var name of names) { From 10b93afff4f0c466583a0da462488512d27d006a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 21:29:23 -0500 Subject: [PATCH 17/32] Bug 1331092 - Part 2: Implement Async Generator except yield*. Tag #1287 --- js/public/Class.h | 2 +- js/src/builtin/AsyncIteration.js | 7 + js/src/builtin/Promise.h | 17 + js/src/frontend/BytecodeCompiler.cpp | 13 + js/src/frontend/BytecodeCompiler.h | 6 + js/src/frontend/BytecodeEmitter.cpp | 26 +- js/src/frontend/BytecodeEmitter.h | 3 +- js/src/frontend/Parser.cpp | 22 +- js/src/js.msg | 3 + js/src/jsapi.cpp | 6 + js/src/jsfun.cpp | 96 +++- js/src/jsfun.h | 3 + js/src/jsiter.cpp | 28 +- js/src/jsiter.h | 4 +- js/src/moz.build | 2 + js/src/shell/js.cpp | 3 + js/src/tests/ecma_7/AsyncFunctions/syntax.js | 3 - js/src/vm/AsyncIteration.cpp | 564 +++++++++++++++++++ js/src/vm/AsyncIteration.h | 204 +++++++ js/src/vm/CommonPropertyNames.h | 3 + js/src/vm/GlobalObject.h | 30 + js/src/vm/Interpreter.cpp | 15 +- js/src/vm/Opcodes.h | 10 +- 23 files changed, 1015 insertions(+), 55 deletions(-) create mode 100644 js/src/builtin/AsyncIteration.js create mode 100644 js/src/vm/AsyncIteration.cpp create mode 100644 js/src/vm/AsyncIteration.h diff --git a/js/public/Class.h b/js/public/Class.h index 67c0cbca85..a7250eac05 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -779,7 +779,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 40) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 44) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/builtin/AsyncIteration.js b/js/src/builtin/AsyncIteration.js new file mode 100644 index 0000000000..029c7c0f81 --- /dev/null +++ b/js/src/builtin/AsyncIteration.js @@ -0,0 +1,7 @@ +/* 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/. */ + +function AsyncIteratorIdentity() { + return this; +} diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h index 8f716dfbc5..680ab2abd9 100644 --- a/js/src/builtin/Promise.h +++ b/js/src/builtin/Promise.h @@ -151,6 +151,23 @@ AsyncFunctionThrown(JSContext* cx, Handle resultPromise); MOZ_MUST_USE bool AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value); +class AsyncGeneratorObject; + +MOZ_MUST_USE bool +AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, HandleValue value); + +MOZ_MUST_USE bool +AsyncGeneratorResolve(JSContext* cx, Handle asyncGenObj, + HandleValue value, bool done); + +MOZ_MUST_USE bool +AsyncGeneratorReject(JSContext* cx, Handle asyncGenObj, + HandleValue exception); + +MOZ_MUST_USE bool +AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind, + HandleValue completionValue, MutableHandleValue result); + /** * A PromiseTask represents a task that can be dispatched to a helper thread * (via StartPromiseTask), executed (by implementing PromiseTask::execute()), diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index 7760c4b2eb..ccfe3cd2e3 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -729,3 +729,16 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu TraceLogger_ParserCompileFunction); return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd); } + +bool +frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + Maybe parameterListEnd) +{ + RootedScope emptyGlobalScope(cx, &cx->global()->emptyGlobalScope()); + + BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope, + TraceLogger_ParserCompileFunction); + return compiler.compileStandaloneFunction(fun, StarGenerator, AsyncFunction, parameterListEnd); +} diff --git a/js/src/frontend/BytecodeCompiler.h b/js/src/frontend/BytecodeCompiler.h index 0bc1ab2abf..029fbe632f 100644 --- a/js/src/frontend/BytecodeCompiler.h +++ b/js/src/frontend/BytecodeCompiler.h @@ -84,6 +84,12 @@ CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun, JS::SourceBufferHolder& srcBuf, mozilla::Maybe parameterListEnd); +MOZ_MUST_USE bool +CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun, + const ReadOnlyCompileOptions& options, + JS::SourceBufferHolder& srcBuf, + mozilla::Maybe parameterListEnd); + MOZ_MUST_USE bool CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun, const ReadOnlyCompileOptions& options, diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 29423fd949..069ba81b1f 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8067,7 +8067,8 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(fun->isArrow() == (pn->getOp() == JSOP_LAMBDA_ARROW)); if (funbox->isAsync()) { MOZ_ASSERT(!needsProto); - return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow()); + return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(), + fun->isStarGenerator()); } if (fun->isArrow()) { @@ -8122,8 +8123,11 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) MOZ_ASSERT(pn->getOp() == JSOP_NOP); switchToPrologue(); if (funbox->isAsync()) { - if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow())) + if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(), + fun->isStarGenerator())) + { return false; + } } else { if (!emitIndex32(JSOP_LAMBDA, index)) return false; @@ -8139,10 +8143,12 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto) // initialize the binding name of the function in the current scope. bool isAsync = funbox->isAsync(); - auto emitLambda = [index, isAsync](BytecodeEmitter* bce, const NameLocation&, bool) { + bool isStarGenerator = funbox->isStarGenerator(); + auto emitLambda = [index, isAsync, isStarGenerator](BytecodeEmitter* bce, + const NameLocation&, bool) { if (isAsync) { return bce->emitAsyncWrapper(index, /* needsHomeObject = */ false, - /* isArrow = */ false); + /* isArrow = */ false, isStarGenerator); } return bce->emitIndexOp(JSOP_LAMBDA, index); }; @@ -8177,7 +8183,8 @@ BytecodeEmitter::emitAsyncWrapperLambda(unsigned index, bool isArrow) { } bool -BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow) +BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow, + bool isStarGenerator) { // needsHomeObject can be true for propertyList for extended class. // In that case push both unwrapped and wrapped function, in order to @@ -8209,8 +8216,13 @@ BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isA if (!emit1(JSOP_DUP)) return false; } - if (!emit1(JSOP_TOASYNC)) - return false; + if (isStarGenerator) { + if (!emit1(JSOP_TOASYNCGEN)) + return false; + } else { + if (!emit1(JSOP_TOASYNC)) + return false; + } return true; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index d583a50712..95445449e1 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -620,7 +620,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn); MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow); - MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow); + MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject, bool isArrow, + bool isStarGenerator); MOZ_MUST_USE bool emitComputedPropertyName(ParseNode* computedPropName); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 3ae5890ac5..7704cf65a5 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3785,10 +3785,12 @@ Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHa GeneratorKind generatorKind = NotGenerator; if (tt == TOK_MUL) { +#ifdef RELEASE_OR_BETA if (asyncKind != SyncFunction) { error(JSMSG_ASYNC_GENERATOR); return null(); } +#endif generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); @@ -3857,10 +3859,12 @@ Parser::functionExpr(uint32_t toStringStart, InvokedPrediction inv return null(); if (tt == TOK_MUL) { +#ifdef RELEASE_OR_BETA if (asyncKind != SyncFunction) { error(JSMSG_ASYNC_GENERATOR); return null(); } +#endif generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); @@ -9468,11 +9472,6 @@ Parser::propertyName(YieldHandling yieldHandling, bool isGenerator = false; bool isAsync = false; - if (ltok == TOK_MUL) { - isGenerator = true; - if (!tokenStream.getToken(<ok)) - return null(); - } if (ltok == TOK_ASYNC) { // AsyncMethod[Yield, Await]: @@ -9500,9 +9499,16 @@ Parser::propertyName(YieldHandling yieldHandling, } } - if (isAsync && isGenerator) { - error(JSMSG_ASYNC_GENERATOR); - return null(); + if (ltok == TOK_MUL) { +#ifdef RELEASE_OR_BETA + if (isAsync) { + error(JSMSG_ASYNC_GENERATOR); + return null(); + } +#endif + isGenerator = true; + if (!tokenStream.getToken(<ok)) + return null(); } propAtom.set(nullptr); diff --git a/js/src/js.msg b/js/src/js.msg index 7465129bfb..2778085c7e 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -593,3 +593,6 @@ MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "P // Iterator MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of iterator is not callable") MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method") + +// Async Iteration +MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index f9a0c6a6bc..6483641f32 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -70,6 +70,7 @@ #include "js/StructuredClone.h" #include "js/Utility.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/DateObject.h" #include "vm/Debugger.h" #include "vm/EnvironmentObject.h" @@ -3591,6 +3592,11 @@ CloneFunctionObject(JSContext* cx, HandleObject funobj, HandleObject env, Handle return nullptr; } + if (IsWrappedAsyncGenerator(fun)) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CLONE_OBJECT); + return nullptr; + } + if (CanReuseScriptForClone(cx->compartment(), fun, env)) { // If the script is to be reused, either the script can already handle // non-syntactic scopes, or there is only the standard global lexical diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 65d44cba7a..ce1ca33fe0 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -41,6 +41,7 @@ #include "js/CallNonGenericMethod.h" #include "js/Proxy.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Debugger.h" #include "vm/GlobalObject.h" #include "vm/Interpreter.h" @@ -312,6 +313,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args) JSFunction* callerFun = &callerObj->as(); if (IsWrappedAsyncFunction(callerFun)) callerFun = GetUnwrappedAsyncFunction(callerFun); + else if (IsWrappedAsyncGenerator(callerFun)) + callerFun = GetUnwrappedAsyncGenerator(callerFun); MOZ_ASSERT(!callerFun->isBuiltin(), "non-builtin iterator returned a builtin?"); if (callerFun->strict()) { @@ -364,13 +367,15 @@ static const JSPropertySpec function_properties[] = { static bool ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId id) { - MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative()); + bool isAsyncGenerator = IsWrappedAsyncGenerator(fun); + + MOZ_ASSERT_IF(!isAsyncGenerator, fun->isInterpreted() || fun->isAsmJSNative()); MOZ_ASSERT(id == NameToId(cx->names().prototype)); // Assert that fun is not a compiler-created function object, which // must never leak to script or embedding code and then be mutated. // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above. - MOZ_ASSERT(!IsInternalFunctionObject(*fun)); + MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun)); MOZ_ASSERT(!fun->isBoundFunction()); // Make the prototype object an instance of Object with the same parent as @@ -380,7 +385,9 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId bool isStarGenerator = fun->isStarGenerator(); Rooted global(cx, &fun->global()); RootedObject objProto(cx); - if (isStarGenerator) + if (isAsyncGenerator) + objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global); + else if (isStarGenerator) objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global); else objProto = GlobalObject::getOrCreateObjectPrototype(cx, global); @@ -396,7 +403,7 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId // non-enumerable, and writable. However, per the 15 July 2013 ES6 draft, // section 15.19.3, the .prototype of a generator function does not link // back with a .constructor. - if (!isStarGenerator) { + if (!isStarGenerator && !isAsyncGenerator) { RootedValue objVal(cx, ObjectValue(*fun)); if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0)) return false; @@ -446,11 +453,13 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) * - Arrow functions * - Function.prototype */ - if (fun->isBuiltin()) - return true; - if (!fun->isConstructor()) { - if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync()) + if (!IsWrappedAsyncGenerator(fun)) { + if (fun->isBuiltin()) return true; + if (!fun->isConstructor()) { + if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync()) + return true; + } } if (!ResolveInterpretedFunctionPrototype(cx, fun, id)) @@ -946,6 +955,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint) return FunctionToString(cx, unwrapped, prettyPrint); } + if (IsWrappedAsyncGenerator(fun)) { + RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun)); + return FunctionToString(cx, unwrapped, prettyPrint); + } + StringBuffer out(cx); RootedScript script(cx); @@ -1606,10 +1620,14 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator &mutedErrors); const char* introductionType = "Function"; - if (isAsync) - introductionType = "AsyncFunction"; - else if (generatorKind != NotGenerator) + if (isAsync) { + if (isStarGenerator) + introductionType = "AsyncGenerator"; + else + introductionType = "AsyncFunction"; + } else if (generatorKind != NotGenerator) { introductionType = "GeneratorFunction"; + } const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) @@ -1737,12 +1755,20 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator : SourceBufferHolder::NoOwnership; bool ok; SourceBufferHolder srcBuf(chars.begin().get(), chars.length(), ownership); - if (isAsync) - ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, parameterListEnd); - else if (isStarGenerator) - ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd); - else - ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd); + if (isAsync) { + if (isStarGenerator) { + ok = frontend::CompileStandaloneAsyncGenerator(cx, &fun, options, srcBuf, + parameterListEnd); + } else { + ok = frontend::CompileStandaloneAsyncFunction(cx, &fun, options, srcBuf, + parameterListEnd); + } + } else { + if (isStarGenerator) + ok = frontend::CompileStandaloneGenerator(cx, &fun, options, srcBuf, parameterListEnd); + else + ok = frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, parameterListEnd); + } // Step 33. args.rval().setObject(*fun); @@ -1768,7 +1794,7 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - // Save the callee before its reset in FunctionConstructor(). + // Save the callee before it's reset in FunctionConstructor(). RootedObject newTarget(cx); if (args.isConstructing()) newTarget = &args.newTarget().toObject(); @@ -1800,6 +1826,40 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp) return true; } +bool +js::AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + // Save the callee before its reset in FunctionConstructor(). + RootedObject newTarget(cx); + if (args.isConstructing()) + newTarget = &args.newTarget().toObject(); + else + newTarget = &args.callee(); + + if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction)) + return false; + + RootedObject proto(cx); + if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) + return false; + + if (!proto) { + proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()); + if (!proto) + return false; + } + + RootedFunction unwrapped(cx, &args.rval().toObject().as()); + RootedObject wrapped(cx, WrapAsyncGeneratorWithProto(cx, unwrapped, proto)); + if (!wrapped) + return false; + + args.rval().setObject(*wrapped); + return true; +} + bool JSFunction::isBuiltinFunctionConstructor() { diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 9e50387d63..ea9e7af9bb 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -660,6 +660,9 @@ Generator(JSContext* cx, unsigned argc, Value* vp); extern bool AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp); +extern bool +AsyncGeneratorConstructor(JSContext* cx, unsigned argc, Value* vp); + // Allocate a new function backed by a JSNative. Note that by default this // creates a singleton object. extern JSFunction* diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index c4da86bdb7..c58f323825 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -916,28 +916,30 @@ js::GetIteratorObject(JSContext* cx, HandleObject obj, uint32_t flags) return iterator; } +// ES 2017 draft 7.4.7. JSObject* -js::CreateItrResultObject(JSContext* cx, HandleValue value, bool done) +js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done) { - // FIXME: We can cache the iterator result object shape somewhere. - AssertHeapIsIdle(cx); + // Step 1 (implicit). - RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, cx->global())); - if (!proto) + // Step 2. + RootedObject resultObj(cx, NewBuiltinClassInstance(cx)); + if (!resultObj) return nullptr; - RootedPlainObject obj(cx, NewObjectWithGivenProto(cx, proto)); - if (!obj) + // Step 3. + if (!DefineProperty(cx, resultObj, cx->names().value, value)) return nullptr; - if (!DefineProperty(cx, obj, cx->names().value, value)) + // Step 4. + if (!DefineProperty(cx, resultObj, cx->names().done, + done ? TrueHandleValue : FalseHandleValue)) + { return nullptr; + } - RootedValue doneBool(cx, BooleanValue(done)); - if (!DefineProperty(cx, obj, cx->names().done, doneBool)) - return nullptr; - - return obj; + // Step 5. + return resultObj; } bool diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 52eb045c5b..17b7df38bf 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -216,10 +216,10 @@ ThrowStopIteration(JSContext* cx); /* * Create an object of the form { value: VALUE, done: DONE }. - * ES6 draft from 2013-09-05, section 25.4.3.4. + * ES 2017 draft 7.4.7. */ extern JSObject* -CreateItrResultObject(JSContext* cx, HandleValue value, bool done); +CreateIterResultObject(JSContext* cx, HandleValue value, bool done); extern JSObject* InitLegacyIteratorClass(JSContext* cx, HandleObject obj); diff --git a/js/src/moz.build b/js/src/moz.build index 1546b8351e..d6c2a426b8 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -305,6 +305,7 @@ UNIFIED_SOURCES += [ 'vm/ArgumentsObject.cpp', 'vm/ArrayBufferObject.cpp', 'vm/AsyncFunction.cpp', + 'vm/AsyncIteration.cpp', 'vm/Caches.cpp', 'vm/CallNonGenericMethod.cpp', 'vm/CharacterEncoding.cpp', @@ -747,6 +748,7 @@ selfhosted.inputs = [ 'builtin/SelfHostingDefines.h', 'builtin/Utilities.js', 'builtin/Array.js', + 'builtin/AsyncIteration.js', 'builtin/Classes.js', 'builtin/Date.js', 'builtin/Error.js', diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 51cd11fe83..33f7d6bdcc 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -85,6 +85,7 @@ #include "threading/Thread.h" #include "vm/ArgumentsObject.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/Compression.h" #include "vm/Debugger.h" #include "vm/HelperThreads.h" @@ -2304,6 +2305,8 @@ ValueToScript(JSContext* cx, HandleValue v, JSFunction** funp = nullptr) // Get unwrapped async function. if (IsWrappedAsyncFunction(fun)) fun = GetUnwrappedAsyncFunction(fun); + if (IsWrappedAsyncGenerator(fun)) + fun = GetUnwrappedAsyncGenerator(fun); if (!fun->isInterpreted()) { JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_SCRIPTS_ONLY); diff --git a/js/src/tests/ecma_7/AsyncFunctions/syntax.js b/js/src/tests/ecma_7/AsyncFunctions/syntax.js index 28e5febc1b..0b4b50af4f 100644 --- a/js/src/tests/ecma_7/AsyncFunctions/syntax.js +++ b/js/src/tests/ecma_7/AsyncFunctions/syntax.js @@ -9,9 +9,6 @@ if (typeof Reflect !== "undefined" && Reflect.parse) { assertEq(Reflect.parse("async function a() {}").body[0].async, true); assertEq(Reflect.parse("() => {}").body[0].async, undefined); - // Async generators are not allowed (with regards to spec) - assertThrows(() => Reflect.parse("async function* a() {}"), SyntaxError); - // No line terminator after async assertEq(Reflect.parse("async\nfunction a(){}").body[0].expression.name, "async"); diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp new file mode 100644 index 0000000000..04effe1b10 --- /dev/null +++ b/js/src/vm/AsyncIteration.cpp @@ -0,0 +1,564 @@ +/* -*- 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 2.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()); + RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)); + RootedFunction unwrapped(cx, &unwrappedVal.toObject().as()); + 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 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* +js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) +{ + MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped)); + JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT) + .toObject().as(); + MOZ_ASSERT(unwrapped->isAsync()); + return unwrapped; +} + +static MOZ_MUST_USE bool +AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument); + +// Async Iteration proposal 5.1.1 Await Fulfilled Functions. +MOZ_MUST_USE bool +js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle asyncGenObj, + HandleValue value) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); +} + +// Async Iteration proposal 5.1.2 Await Rejected Functions. +MOZ_MUST_USE bool +js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + +// Async Iteration proposal 6.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 6.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 6.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 +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()); + + RootedObject obj( + cx, OrdinaryCreateFromConstructor(cx, asyncGen, + GlobalObject::getOrCreateAsyncGeneratorPrototype, + &class_)); + if (!obj) + return nullptr; + + Handle asyncGenObj = obj.as(); + + // 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 +AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle asyncGenObj, + Handle 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()); + + FixedInvokeArgs<1> args(cx); + args[0].setObject(*request); + args.setThis(ObjectValue(*queue)); + return CallJSNative(cx, array_push, args); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle asyncGenObj) +{ + if (asyncGenObj->isSingleQueue()) { + AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest(); + asyncGenObj->clearSingleQueueRequest(); + return request; + } + + RootedArrayObject queue(cx, asyncGenObj->queue()); + + FixedInvokeArgs<0> args(cx); + args.setThis(ObjectValue(*queue)); + if (!CallJSNative(cx, array_shift, args)) + return nullptr; + + return &args.rval().toObject().as(); +} + +/* static */ AsyncGeneratorRequest* +AsyncGeneratorObject::peekRequest(JSContext* cx, Handle 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(); +} + +const Class AsyncGeneratorRequest::class_ = { + "AsyncGeneratorRequest", + JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) +}; + +// Async Iteration proposal 6.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 request = obj.as(); + request->setCompletionKind(completionKind_); + request->setCompletionValue(completionValue_); + request->setPromise(promise_); + return request; +} + +// Async Iteration proposal 6.4.3.2 steps 5.d-g. +static MOZ_MUST_USE bool +AsyncGeneratorReturned(JSContext* cx, Handle 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 6.4.3.2 steps 5.d, f. +static MOZ_MUST_USE bool +AsyncGeneratorThrown(JSContext* cx, Handle 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 6.4.3.5. +MOZ_MUST_USE bool +js::AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isExecuting()); + + // Steps 4-5. + if (asyncGenObj->isQueueEmpty()) + return true; + + // Steps 6-7. + Rooted request( + cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 8. + CompletionKind completionKind = request->completionKind(); + + // Step 9. + if (completionKind != CompletionKind::Normal) { + // Step 9.a. + if (asyncGenObj->isSuspendedStart()) + asyncGenObj->setCompleted(); + + // Step 9.b. + if (asyncGenObj->isCompleted()) { + // Step 9.b.i. + RootedValue value(cx, request->completionValue()); + if (completionKind == CompletionKind::Return) + return AsyncGeneratorResolve(cx, asyncGenObj, value, true); + // Step 9.b.ii. + return AsyncGeneratorReject(cx, asyncGenObj, value); + } + } else if (asyncGenObj->isCompleted()) { + // Step 10. + return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); + } + + // Step 11. + MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); + + // Step 15 (reordered). + asyncGenObj->setExecuting(); + + RootedValue argument(cx, request->completionValue()); + + // Steps 12-14, 16-20. + return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); +} + +// Async Iteration proposal 6.2.1.2 (partially). +// Most steps are done in generator. +static MOZ_MUST_USE bool +AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, + HandleValue value, bool done) +{ + // Step 6. + asyncGenObj->setSuspendedYield(); + + // Step 10.c. + return AsyncGeneratorResolve(cx, asyncGenObj, value, done); +} + +// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20. +// Async Iteration proposal 6.2.1.2 step 10. +// Async Iteration proposal 6.4.3.2 step 5.f-g. +// Async Iteration proposal 5.1 steps 2-9. +// Execution context switching is handled in generator. +static MOZ_MUST_USE bool +AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument) +{ + RootedValue generatorVal(cx, asyncGenObj->generatorVal()); + + // 6.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)) { + // 6.4.3.2 step 5.d, f. + return AsyncGeneratorThrown(cx, asyncGenObj); + } + + // The following code corresponds to the following 3 cases: + // * yield + // * await + // * return + // For await and return, property access is done on an internal result + // object and it's not observable. + // For yield, it's done on an user-provided result object, and it's + // observable, so perform in that order in all cases. + + RootedObject resultObj(cx, &result.toObject()); + RootedValue value(cx); + RootedValue doneVal(cx); + + // 6.2.1.2 step 10.a. + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return false; + + // 6.2.1.2 step 10.b. + if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) + return false; + + // 6.2.1.2 step 10.c. + if (asyncGenObj->generatorObj()->isAfterYield()) + return AsyncGeneratorYield(cx, asyncGenObj, value, ToBoolean(doneVal)); + + // 6.4.3.2 step 5.d-g. + if (ToBoolean(doneVal)) { + MOZ_ASSERT(!asyncGenObj->generatorObj()->isAfterAwait()); + return AsyncGeneratorReturned(cx, asyncGenObj, value); + } + + MOZ_ASSERT(asyncGenObj->generatorObj()->isAfterAwait()); + + // 5.1 steps 2-9. + return AsyncGeneratorAwait(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_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 global) +{ + if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) + return true; + + // Async Iteration proposal 6.1.2 %AsyncIteratorPrototype%. + RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype(cx, global)); + if (!asyncIterProto) + return false; + if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) + return false; + + // Async Iteration proposal 6.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 6.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 6.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_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 0000000000..c0135283fa --- /dev/null +++ b/js/src/vm/AsyncIteration.h @@ -0,0 +1,204 @@ +/* -*- 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 asyncGenObj, + HandleValue value); + +MOZ_MUST_USE bool +AsyncGeneratorAwaitedRejected(JSContext* cx, Handle 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(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(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_Completed + }; + + State state() const { + return static_cast(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(); + } + 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(); + } + + ArrayObject* queue() const { + return &getFixedSlot(Slot_QueueOrRequest).toObject().as(); + } + 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 isCompleted() const { + return state() == State_Completed; + } + + void setSuspendedStart() { + setState(State_SuspendedStart); + } + void setSuspendedYield() { + setState(State_SuspendedYield); + } + void setExecuting() { + setState(State_Executing); + } + void setCompleted() { + setState(State_Completed); + } + + JS::Value generatorVal() const { + return getFixedSlot(Slot_Generator); + } + GeneratorObject* generatorObj() const { + return &getFixedSlot(Slot_Generator).toObject().as(); + } + + static MOZ_MUST_USE bool + enqueueRequest(JSContext* cx, Handle asyncGenObj, + Handle request); + + static AsyncGeneratorRequest* + dequeueRequest(JSContext* cx, Handle asyncGenObj); + + static AsyncGeneratorRequest* + peekRequest(JSContext* cx, Handle asyncGenObj); + + bool isQueueEmpty() const { + if (isSingleQueue()) + return isSingleQueueEmpty(); + return queue()->length() == 0; + } +}; + +MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); + +} // namespace js + +#endif /* vm_AsyncIteration_h */ diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 99cb02e586..4a9c03e884 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -30,6 +30,8 @@ macro(as, as, "as") \ macro(Async, Async, "Async") \ 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 +313,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/GlobalObject.h b/js/src/vm/GlobalObject.h index dd3f231517..ce802daba9 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -100,6 +100,10 @@ class GlobalObject : public NativeObject STAR_GENERATOR_FUNCTION, ASYNC_FUNCTION_PROTO, ASYNC_FUNCTION, + ASYNC_ITERATOR_PROTO, + ASYNC_GENERATOR, + ASYNC_GENERATOR_FUNCTION, + ASYNC_GENERATOR_PROTO, MAP_ITERATOR_PROTO, SET_ITERATOR_PROTO, COLLATOR_PROTO, @@ -624,6 +628,30 @@ class GlobalObject : public NativeObject return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction); } + static NativeObject* + getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle global) + { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + + static NativeObject* + getOrCreateAsyncGenerator(JSContext* cx, Handle global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR, + initAsyncGenerators)); + } + + static JSObject* + getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle global) { + return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators); + } + + static NativeObject* + getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO, + initAsyncGenerators)); + } + static JSObject* getOrCreateMapIteratorPrototype(JSContext* cx, Handle global) { return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto); @@ -782,6 +810,8 @@ class GlobalObject : public NativeObject static bool initAsyncFunction(JSContext* cx, Handle global); + static bool initAsyncGenerators(JSContext* cx, Handle global); + // Implemented in builtin/MapObject.cpp. static bool initMapIteratorProto(JSContext* cx, Handle global); static bool initSetIteratorProto(JSContext* cx, Handle global); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 6959e15c2c..df7318f188 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" @@ -1938,7 +1939,7 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) -CASE(JSOP_UNUSED192) +CASE(JSOP_UNUSED126) CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) @@ -3583,6 +3584,18 @@ CASE(JSOP_TOASYNC) } END_CASE(JSOP_TOASYNC) +CASE(JSOP_TOASYNCGEN) +{ + ReservedRooted unwrapped(&rootFunction0, + ®S.sp[-1].toObject().as()); + JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped); + if (!wrapped) + goto error; + + REGS.sp[-1].setObject(*wrapped); +} +END_CASE(JSOP_TOASYNCGEN) + CASE(JSOP_SETFUNNAME) { MOZ_ASSERT(REGS.stackDepth() >= 2); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index 0fa63d9762..ae2b2520b2 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1962,7 +1962,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 From b5bcbfe58ca423c43987dbcc2be610aad89ccf9a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Fri, 13 Dec 2019 21:48:49 -0500 Subject: [PATCH 18/32] Bug 1331092 - Part 2: Implement Async Generator except yield*. Tag #1287 --- js/src/builtin/Promise.cpp | 253 ++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 2 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 8fd39e7bbc..59e710935e 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -12,10 +12,12 @@ #include "jscntxt.h" #include "jsexn.h" +#include "jsiter.h" #include "gc/Heap.h" #include "js/Debug.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "jsobjinlines.h" @@ -36,6 +38,16 @@ enum PromiseHandler { PromiseHandlerThrower, PromiseHandlerAsyncFunctionAwaitFulfilled, PromiseHandlerAsyncFunctionAwaitRejected, + PromiseHandlerAsyncGeneratorAwaitFulfilled, + PromiseHandlerAsyncGeneratorAwaitRejected, + + // Async Iteration proposal 6.1.1.2.1. + // Async iterator handlers take the resolved value and create new iterator + // objects. To do so it needs to forward whether the iterator is done. In + // spec, this is achieved via the [[Done]] internal slot. We enumerate both + // true and false cases here. + PromiseHandlerAsyncIteratorValueUnwrapDone, + PromiseHandlerAsyncIteratorValueUnwrapNotDone, }; enum ResolutionMode { @@ -178,6 +190,7 @@ enum ReactionRecordSlots { ReactionRecordSlot_IncumbentGlobalObject, ReactionRecordSlot_Flags, ReactionRecordSlot_HandlerArg, + ReactionRecordSlot_Generator, ReactionRecordSlots, }; @@ -185,6 +198,7 @@ enum ReactionRecordSlots { #define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 #define REACTION_FLAG_ASYNC_FUNCTION_AWAIT 0x8 +#define REACTION_FLAG_ASYNC_GENERATOR_AWAIT 0x10 // ES2016, 25.4.1.2. class PromiseReactionRecord : public NativeObject @@ -220,6 +234,22 @@ class PromiseReactionRecord : public NativeObject int32_t flags = this->flags(); return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT; } + void setIsAsyncGeneratorAwait(Handle asyncGenObj) { + int32_t flags = this->flags(); + flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); + + setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj)); + } + bool isAsyncGeneratorAwait() { + int32_t flags = this->flags(); + return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + } + AsyncGeneratorObject* asyncGenerator() { + MOZ_ASSERT(isAsyncGeneratorAwait()); + return &getFixedSlot(ReactionRecordSlot_Generator).toObject() + .as(); + } Value handler() { MOZ_ASSERT(targetState() != JS::PromiseState::Pending); uint32_t slot = targetState() == JS::PromiseState::Fulfilled @@ -846,6 +876,34 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) +{ + MOZ_ASSERT(reaction->isAsyncGeneratorAwait()); + + RootedValue handlerVal(cx, reaction->handler()); + RootedValue argument(cx, reaction->handlerArg()); + Rooted asyncGenObj(cx, reaction->asyncGenerator()); + + int32_t handlerNum = int32_t(handlerVal.toNumber()); + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled || + handlerNum == PromiseHandlerAsyncGeneratorAwaitRejected); + + // Await's handlers don't return a value, nor throw exception. + // They fail only on OOM. + if (handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled) { + if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument)) + return false; + } else { + if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument)) + return false; + } + + rval.setUndefined(); + return true; +} + // ES2016, 25.4.2.1. /** * Callback triggering the fulfill/reject reaction for a resolved Promise, @@ -891,6 +949,8 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) Rooted reaction(cx, &reactionObj->as()); if (reaction->isAsyncFunctionAwait()) return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncGeneratorAwait()) + return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval()); // Step 3. RootedValue handlerVal(cx, reaction->handler()); @@ -907,11 +967,21 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) // Step 4. if (handlerNum == PromiseHandlerIdentity) { handlerResult = argument; - } else { + } else if (handlerNum == PromiseHandlerThrower) { // Step 5. - MOZ_ASSERT(handlerNum == PromiseHandlerThrower); resolutionMode = RejectMode; handlerResult = argument; + } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone || + handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone); + + bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone; + // Async Iteration proposal 6.1.1.2.1 step 1. + RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); + if (!resultObj) + return false; + + handlerResult = ObjectValue(*resultObj); } } else { // Step 6. @@ -2221,6 +2291,185 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand return PerformPromiseThenWithReaction(cx, promise, reaction); } +// Async Iteration proposal 5.1 steps 2-9. +MOZ_MUST_USE bool +js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, + HandleValue value) +{ + // Step 2. + Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!promise) + return false; + + // Steps 3. + if (!ResolvePromiseInternal(cx, promise, value)) + return false; + + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitRejected)); + + RootedObject incumbentGlobal(cx); + if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) + return false; + + // Step 6 (skipped). + + // Steps 7-8. + Rooted reaction(cx, NewReactionRecord(cx, nullptr, + onFulfilled, onRejected, + nullptr, nullptr, + incumbentGlobal)); + if (!reaction) + return false; + + reaction->setIsAsyncGeneratorAwait(asyncGenObj); + + // Step 9. + return PerformPromiseThenWithReaction(cx, promise, reaction); +} + +// Async Iteration proposal 6.4.3.3. +MOZ_MUST_USE bool +js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenObj, + HandleValue value, bool done) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); + + // Step 4. + Rooted request( + cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 5. + RootedObject resultPromise(cx, request->promise()); + + // Step 6. + Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!promise) + return false; + + // Step 7. + if (!ResolvePromiseInternal(cx, promise, value)) + return false; + + // Steps 8-9. + RootedValue onFulfilled(cx, Int32Value(done + ? PromiseHandlerAsyncIteratorValueUnwrapDone + : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); + + RootedObject incumbentGlobal(cx); + if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) + return false; + + // Step 10. + Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, + UndefinedHandleValue, + nullptr, nullptr, + incumbentGlobal)); + if (!reaction) + return false; + + if (!PerformPromiseThenWithReaction(cx, promise, reaction)) + return false; + + // Step 11. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + + // Step 12. + return true; +} + +// Async Iteration proposal 6.4.3.4. +MOZ_MUST_USE bool +js::AsyncGeneratorReject(JSContext* cx, Handle asyncGenObj, + HandleValue exception) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isQueueEmpty()); + + // Step 4. + Rooted request( + cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 5. + RootedObject resultPromise(cx, request->promise()); + + // Step 6. + if (!RejectMaybeWrappedPromise(cx, resultPromise, exception)) + return false; + + // Step 7. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + + // Step 8. + return true; +} + +// Async Iteration proposal 6.4.3.6. +MOZ_MUST_USE bool +js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, + CompletionKind completionKind, HandleValue completionValue, + MutableHandleValue result) +{ + // Step 1 (implicit). + + // Step 2. + RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!resultPromise) + return false; + + // Step 3. + if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is()) { + // Step 3.a. + RootedValue badGeneratorError(cx); + if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError)) + return false; + + // Step 3.b. + if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) + return false; + + // Step 3.c. + result.setObject(*resultPromise); + return true; + } + + Rooted asyncGenObj( + cx, &asyncGenVal.toObject().as()); + + // Step 5 (reordered). + Rooted request( + cx, AsyncGeneratorRequest::create(cx, completionKind, completionValue, resultPromise)); + if (!request) + return false; + + // Steps 4, 6. + if (!AsyncGeneratorObject::enqueueRequest(cx, asyncGenObj, request)) + return false; + + // Step 7. + if (!asyncGenObj->isExecuting()) { + // Step 8. + if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) + return false; + } + + // Step 9. + result.setObject(*resultPromise); + return true; +} + // ES2016, 25.4.5.3. bool js::Promise_then(JSContext* cx, unsigned argc, Value* vp) From 67b8cd621aa63600c299254557e7db9a4f5f57ea Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:13:04 -0500 Subject: [PATCH 19/32] Bug 1331092 - Part 6: Support JSOP_TOASYNCGEN in JIT. Tag #1287 --- js/src/jit/BaselineCompiler.cpp | 23 +++++++++++++++++++++++ js/src/jit/BaselineCompiler.h | 1 + js/src/jit/CodeGenerator.cpp | 12 ++++++++++++ js/src/jit/CodeGenerator.h | 1 + js/src/jit/IonBuilder.cpp | 17 +++++++++++++++++ js/src/jit/IonBuilder.h | 1 + js/src/jit/Lowering.cpp | 8 ++++++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 15 +++++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/shared/LIR-shared.h | 13 +++++++++++++ js/src/jit/shared/LOpcodes-shared.h | 1 + 12 files changed, 94 insertions(+) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index a7d471523b..3856fb63f6 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -25,6 +25,7 @@ #include "jit/VMFunctions.h" #include "js/UniquePtr.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/EnvironmentObject.h" #include "vm/Interpreter.h" #include "vm/TraceLogging.h" @@ -3914,6 +3915,28 @@ BaselineCompiler::emit_JSOP_TOASYNC() return true; } +typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); +static const VMFunction ToAsyncGenInfo = + FunctionInfo(js::WrapAsyncGenerator, "ToAsyncGen"); + +bool +BaselineCompiler::emit_JSOP_TOASYNCGEN() +{ + frame.syncStack(0); + masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(ToAsyncGenInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.pop(); + frame.push(R0); + return true; +} + typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue); static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo(ThrowObjectCoercible, "ThrowObjectCoercible"); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 878cb45b9f..15d0e30645 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -197,6 +197,7 @@ namespace jit { _(JSOP_RUNONCE) \ _(JSOP_REST) \ _(JSOP_TOASYNC) \ + _(JSOP_TOASYNCGEN) \ _(JSOP_TOID) \ _(JSOP_TOSTRING) \ _(JSOP_TABLESWITCH) \ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 205812942a..128947e5ed 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -41,6 +41,7 @@ #include "jit/RangeAnalysis.h" #include "jit/SharedICHelpers.h" #include "vm/AsyncFunction.h" +#include "vm/AsyncIteration.h" #include "vm/MatchPairs.h" #include "vm/RegExpObject.h" #include "vm/RegExpStatics.h" @@ -10213,6 +10214,17 @@ CodeGenerator::visitToAsync(LToAsync* lir) callVM(ToAsyncInfo, lir); } +typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction); +static const VMFunction ToAsyncGenInfo = + FunctionInfo(js::WrapAsyncGenerator, "ToAsyncGen"); + +void +CodeGenerator::visitToAsyncGen(LToAsyncGen* lir) +{ + pushArg(ToRegister(lir->unwrapped())); + callVM(ToAsyncGenInfo, lir); +} + typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue); static const VMFunction ToIdInfo = FunctionInfo(ToIdOperation, "ToIdOperation"); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 12f1238ef3..d2f24dc043 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -286,6 +286,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitTypeOfV(LTypeOfV* lir); void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool); void visitToAsync(LToAsync* lir); + void visitToAsyncGen(LToAsyncGen* lir); void visitToIdV(LToIdV* lir); template void emitLoadElementT(LLoadElementT* lir, const T& source); void visitLoadElementT(LLoadElementT* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 877ec833b7..79214c993d 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2130,6 +2130,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_TOASYNC: return jsop_toasync(); + case JSOP_TOASYNCGEN: + return jsop_toasyncgen(); + case JSOP_TOID: return jsop_toid(); @@ -13193,6 +13196,20 @@ IonBuilder::jsop_toasync() return resumeAfter(ins); } +bool +IonBuilder::jsop_toasyncgen() +{ + MDefinition* unwrapped = current->pop(); + MOZ_ASSERT(unwrapped->type() == MIRType::Object); + + MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped); + + current->add(ins); + current->push(ins); + + return resumeAfter(ins); +} + bool IonBuilder::jsop_toid() { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 1f763a4f2c..48388539fd 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -762,6 +762,7 @@ class IonBuilder MOZ_MUST_USE bool jsop_globalthis(); MOZ_MUST_USE bool jsop_typeof(); MOZ_MUST_USE bool jsop_toasync(); + MOZ_MUST_USE bool jsop_toasyncgen(); MOZ_MUST_USE bool jsop_toid(); MOZ_MUST_USE bool jsop_iter(uint8_t flags); MOZ_MUST_USE bool jsop_itermore(); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 22f1d5f70f..ee59b8ae60 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1175,6 +1175,14 @@ LIRGenerator::visitToAsync(MToAsync* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitToAsyncGen(MToAsyncGen* ins) +{ + LToAsyncGen* lir = new(alloc()) LToAsyncGen(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + void LIRGenerator::visitToId(MToId* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 9342ef471b..1d412040c5 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -121,6 +121,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitCompare(MCompare* comp); void visitTypeOf(MTypeOf* ins); void visitToAsync(MToAsync* ins); + void visitToAsyncGen(MToAsyncGen* ins); void visitToId(MToId* ins); void visitBitNot(MBitNot* ins); void visitBitAnd(MBitAnd* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 6c376d5283..637c09cfdd 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5784,6 +5784,21 @@ class MToAsync TRIVIAL_NEW_WRAPPERS }; +class MToAsyncGen + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MToAsyncGen(MDefinition* unwrapped) + : MUnaryInstruction(unwrapped) + { + setResultType(MIRType::Object); + } + + public: + INSTRUCTION_HEADER(ToAsyncGen) + TRIVIAL_NEW_WRAPPERS +}; + class MToId : public MUnaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 9e460a2ba6..59620e4e76 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -79,6 +79,7 @@ namespace jit { _(BitNot) \ _(TypeOf) \ _(ToAsync) \ + _(ToAsyncGen) \ _(ToId) \ _(BitAnd) \ _(BitOr) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index ecf02816ed..6c8ff899b5 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1606,6 +1606,19 @@ class LToAsync : public LCallInstructionHelper<1, 1, 0> } }; +class LToAsyncGen : public LCallInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(ToAsyncGen) + explicit LToAsyncGen(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* unwrapped() { + return getOperand(0); + } +}; + class LToIdV : public LInstructionHelper { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index e260d7e943..9e79f02660 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -353,6 +353,7 @@ _(Rest) \ _(TypeOfV) \ _(ToAsync) \ + _(ToAsyncGen) \ _(ToIdV) \ _(Floor) \ _(FloorF) \ From 142e8bf444dbb5256acaffdf16e25f6fc2adc55e Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:16:32 -0500 Subject: [PATCH 20/32] Bug 1331092 - Part 7: Implement Async Generator yield*. Tag #1287 --- js/public/Class.h | 2 +- js/src/builtin/Promise.cpp | 159 ++++++++++++++++++++++++++++ js/src/builtin/Promise.h | 3 + js/src/frontend/BytecodeEmitter.cpp | 132 ++++++++++++++++++++--- js/src/frontend/BytecodeEmitter.h | 8 +- js/src/js.msg | 2 + js/src/jsiter.h | 2 + js/src/vm/AsyncIteration.cpp | 125 +++++++++++++++++----- js/src/vm/AsyncIteration.h | 26 +++++ js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/GlobalObject.h | 7 ++ js/src/vm/Interpreter.cpp | 16 ++- js/src/vm/Interpreter.h | 3 +- js/src/vm/Opcodes.h | 10 +- 14 files changed, 452 insertions(+), 44 deletions(-) diff --git a/js/public/Class.h b/js/public/Class.h index a7250eac05..f4fa9ccafb 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -779,7 +779,7 @@ struct JSClass { // application. #define JSCLASS_GLOBAL_APPLICATION_SLOTS 5 #define JSCLASS_GLOBAL_SLOT_COUNT \ - (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 44) + (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 45) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 59e710935e..d2b7b543b1 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -2329,6 +2329,165 @@ js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj return PerformPromiseThenWithReaction(cx, promise, reaction); } +// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next +// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return +// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw +bool +js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) +{ + // Step 1. + RootedValue thisVal(cx, args.thisv()); + + // Step 2. + RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!resultPromise) + return false; + + // Step 3. + if (!thisVal.isObject() || !thisVal.toObject().is()) { + // Step 3.a. + RootedValue badGeneratorError(cx); + if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_ITERATOR, &badGeneratorError)) + return false; + + // Step 3.b. + if (!RejectMaybeWrappedPromise(cx, resultPromise, badGeneratorError)) + return false; + + // Step 3.c. + args.rval().setObject(*resultPromise); + return true; + } + + Rooted asyncIter( + cx, &thisVal.toObject().as()); + + // Step 4. + RootedObject iter(cx, asyncIter->iterator()); + + RootedValue resultVal(cx); + RootedValue func(cx); + if (completionKind == CompletionKind::Normal) { + // 6.1.3.2.1 steps 5-6 (partially). + if (!GetProperty(cx, iter, iter, cx->names().next, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + } else if (completionKind == CompletionKind::Return) { + // 6.1.3.2.2 steps 5-6. + if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7. + if (func.isNullOrUndefined()) { + // Step 7.a. + RootedObject resultObj(cx, CreateIterResultObject(cx, args.get(0), true)); + if (!resultObj) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + RootedValue resultVal(cx, ObjectValue(*resultObj)); + + // Step 7.b. + if (!ResolvePromiseInternal(cx, resultPromise, resultVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7.c. + args.rval().setObject(*resultPromise); + return true; + } + } else { + // 6.1.3.2.3 steps 5-6. + MOZ_ASSERT(completionKind == CompletionKind::Throw); + if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7. + if (func.isNullOrUndefined()) { + // Step 7.a. + if (!RejectMaybeWrappedPromise(cx, resultPromise, args.get(0))) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Step 7.b. + args.rval().setObject(*resultPromise); + return true; + } + } + + // 6.1.3.2.1 steps 5-6 (partially). + // 6.1.3.2.2, 6.1.3.2.3 steps 8-9. + RootedValue iterVal(cx, ObjectValue(*iter)); + FixedInvokeArgs<1> args2(cx); + args2[0].set(args.get(0)); + if (!js::Call(cx, func, iterVal, args2, &resultVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // 6.1.3.2.1 steps 5-6 (partially). + // 6.1.3.2.2, 6.1.3.2.3 steps 10. + if (!resultVal.isObject()) { + CheckIsObjectKind kind; + switch (completionKind) { + case CompletionKind::Normal: + kind = CheckIsObjectKind::IteratorNext; + break; + case CompletionKind::Throw: + kind = CheckIsObjectKind::IteratorThrow; + break; + case CompletionKind::Return: + kind = CheckIsObjectKind::IteratorReturn; + break; + } + MOZ_ALWAYS_FALSE(ThrowCheckIsObject(cx, kind)); + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + } + + RootedObject resultObj(cx, &resultVal.toObject()); + + // Following step numbers are for 6.1.3.2.1. + // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20. + + // Steps 7-8. + RootedValue value(cx); + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + + // Steps 9-10. + RootedValue doneVal(cx); + if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + bool done = ToBoolean(doneVal); + + // Step 11. + Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); + if (!promise) + return false; + + // Step 12. + if (!ResolvePromiseInternal(cx, promise, value)) + return false; + + // Steps 13-14. + RootedValue onFulfilled(cx, Int32Value(done + ? PromiseHandlerAsyncIteratorValueUnwrapDone + : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); + + RootedObject incumbentGlobal(cx); + if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) + return false; + + // Step 15. + Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, + UndefinedHandleValue, + nullptr, nullptr, + incumbentGlobal)); + if (!reaction) + return false; + + if (!PerformPromiseThenWithReaction(cx, promise, reaction)) + return false; + + // Step 16. + args.rval().setObject(*resultPromise); + return true; +} + // Async Iteration proposal 6.4.3.3. MOZ_MUST_USE bool js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenObj, diff --git a/js/src/builtin/Promise.h b/js/src/builtin/Promise.h index 680ab2abd9..b04bd0e2a6 100644 --- a/js/src/builtin/Promise.h +++ b/js/src/builtin/Promise.h @@ -168,6 +168,9 @@ MOZ_MUST_USE bool AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind, HandleValue completionValue, MutableHandleValue result); +bool +AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind); + /** * A PromiseTask represents a task that can be dispatched to a helper thread * (via StartPromiseTask), executed (by implementing PromiseTask::execute()), diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 069ba81b1f..5ee0991514 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2118,7 +2118,7 @@ class ForOfLoopControl : public LoopControl bool emitIteratorClose(BytecodeEmitter* bce, CompletionKind completionKind = CompletionKind::Normal) { ptrdiff_t start = bce->offset(); - if (!bce->emitIteratorClose(completionKind, allowSelfHosted_)) + if (!bce->emitIteratorClose(IteratorKind::Sync, completionKind, allowSelfHosted_)) return false; ptrdiff_t end = bce->offset(); return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); @@ -3721,6 +3721,18 @@ BytecodeEmitter::emitFinishIteratorResult(bool done) return true; } +bool +BytecodeEmitter::emitToIteratorResult(bool done) +{ + if (!emitPrepareIteratorResult()) // VALUE OBJ + return false; + if (!emit1(JSOP_SWAP)) // OBJ VALUE + return false; + if (!emitFinishIteratorResult(done)) // RESULT + return false; + return true; +} + bool BytecodeEmitter::emitGetNameAtLocation(JSAtom* name, const NameLocation& loc, bool callContext) { @@ -5255,7 +5267,8 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false } bool -BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = CompletionKind::Normal */, +BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync */, + CompletionKind completionKind /* = CompletionKind::Normal */, bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, @@ -5340,6 +5353,11 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion return false; checkTypeSet(JSOP_CALL); + if (iterKind == IteratorKind::Async) { + if (!emitAwait()) // ... ... RESULT + return false; + } + if (completionKind == CompletionKind::Throw) { if (!emit1(JSOP_SWAP)) // ... RET ITER RESULT UNDEF return false; @@ -5349,7 +5367,7 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion if (!tryCatch->emitCatch()) // ... RET ITER RESULT return false; - // Just ignore the exception thrown by call. + // Just ignore the exception thrown by call and await. if (!emit1(JSOP_EXCEPTION)) // ... RET ITER RESULT EXC return false; if (!emit1(JSOP_POP)) // ... RET ITER RESULT @@ -6971,6 +6989,63 @@ BytecodeEmitter::emitIterator() return true; } +bool +BytecodeEmitter::emitAsyncIterator() +{ + // Convert iterable to iterator. + if (!emit1(JSOP_DUP)) // OBJ OBJ + return false; + if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::asyncIterator))) // OBJ OBJ @@ASYNCITERATOR + return false; + if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN + return false; + + IfThenElseEmitter ifAsyncIterIsUndefined(this); + if (!emit1(JSOP_DUP)) // OBJ ITERFN ITERFN + return false; + if (!emit1(JSOP_UNDEFINED)) // OBJ ITERFN ITERFN UNDEF + return false; + if (!emit1(JSOP_EQ)) // OBJ ITERFN EQ + return false; + if (!ifAsyncIterIsUndefined.emitIfElse()) // OBJ ITERFN + return false; + + if (!emit1(JSOP_POP)) // OBJ + return false; + if (!emit1(JSOP_DUP)) // OBJ OBJ + return false; + if (!emit2(JSOP_SYMBOL, uint8_t(JS::SymbolCode::iterator))) // OBJ OBJ @@ITERATOR + return false; + if (!emitElemOpBase(JSOP_CALLELEM)) // OBJ ITERFN + return false; + if (!emit1(JSOP_SWAP)) // ITERFN OBJ + return false; + if (!emitCall(JSOP_CALLITER, 0)) // ITER + return false; + checkTypeSet(JSOP_CALLITER); + if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER + return false; + + if (!emit1(JSOP_TOASYNCITER)) // ITER + return false; + + if (!ifAsyncIterIsUndefined.emitElse()) // OBJ ITERFN + return false; + + if (!emit1(JSOP_SWAP)) // ITERFN OBJ + return false; + if (!emitCall(JSOP_CALLITER, 0)) // ITER + return false; + checkTypeSet(JSOP_CALLITER); + if (!emitCheckIsObj(CheckIsObjectKind::GetIterator)) // ITER + return false; + + if (!ifAsyncIterIsUndefined.emitEnd()) // ITER + return false; + + return true; +} + bool BytecodeEmitter::emitSpread(bool allowSelfHosted) { @@ -8593,13 +8668,8 @@ BytecodeEmitter::emitYield(ParseNode* pn) } bool -BytecodeEmitter::emitAwait(ParseNode* pn) +BytecodeEmitter::emitAwait() { - MOZ_ASSERT(sc->isFunctionBox()); - MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); - - if (!emitTree(pn->pn_kid)) - return false; if (!emitGetDotGenerator()) return false; if (!emitYieldOp(JSOP_AWAIT)) @@ -8607,16 +8677,34 @@ BytecodeEmitter::emitAwait(ParseNode* pn) return true; } +bool +BytecodeEmitter::emitAwait(ParseNode* pn) +{ + MOZ_ASSERT(sc->isFunctionBox()); + MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); + + if (!emitTree(pn->pn_kid)) + return false; + return emitAwait(); +} + bool BytecodeEmitter::emitYieldStar(ParseNode* iter) { MOZ_ASSERT(sc->isFunctionBox()); MOZ_ASSERT(sc->asFunctionBox()->isStarGenerator()); + bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); + if (!emitTree(iter)) // ITERABLE return false; - if (!emitIterator()) // ITER - return false; + if (isAsyncGenerator) { + if (!emitAsyncIterator()) // ITER + return false; + } else { + if (!emitIterator()) // ITER + return false; + } // Initial send value is undefined. if (!emit1(JSOP_UNDEFINED)) // ITER RECEIVED @@ -8674,7 +8762,8 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) // // If the iterator does not have a "throw" method, it calls IteratorClose // and then throws a TypeError. - if (!emitIteratorClose()) // ITER RESULT EXCEPTION + IteratorKind iterKind = isAsyncGenerator ? IteratorKind::Async : IteratorKind::Sync; + if (!emitIteratorClose(iterKind)) return false; if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw return false; @@ -8690,6 +8779,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) if (!emitCall(JSOP_CALL, 1, iter)) // ITER OLDRESULT RESULT return false; checkTypeSet(JSOP_CALL); + + if (isAsyncGenerator) { + if (!emitAwait()) // ITER OLDRESULT RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) // ITER OLDRESULT RESULT return false; if (!emit1(JSOP_SWAP)) // ITER RESULT OLDRESULT @@ -8756,6 +8851,11 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) return false; checkTypeSet(JSOP_CALL); + if (iterKind == IteratorKind::Async) { + if (!emitAwait()) // ... FTYPE FVALUE RESULT + return false; + } + // Step v. if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ITER OLDRESULT FTYPE FVALUE RESULT return false; @@ -8830,9 +8930,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) return false; if (!emitCall(JSOP_CALL, 1, iter)) // ITER RESULT return false; + checkTypeSet(JSOP_CALL); + + if (isAsyncGenerator) { + if (!emitAwait()) // ITER RESULT RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ITER RESULT return false; - checkTypeSet(JSOP_CALL); MOZ_ASSERT(this->stackDepth == startDepth); if (!emitJumpTargetAndPatch(checkResult)) // checkResult: diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 95445449e1..1daef71b1a 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -10,6 +10,7 @@ #define frontend_BytecodeEmitter_h #include "jscntxt.h" +#include "jsiter.h" #include "jsopcode.h" #include "jsscript.h" @@ -606,6 +607,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitPrepareIteratorResult(); MOZ_MUST_USE bool emitFinishIteratorResult(bool done); MOZ_MUST_USE bool iteratorResultShape(unsigned* shape); + MOZ_MUST_USE bool emitToIteratorResult(bool done); MOZ_MUST_USE bool emitGetDotGenerator(); @@ -613,6 +615,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); + MOZ_MUST_USE bool emitAwait(); MOZ_MUST_USE bool emitAwait(ParseNode* pn); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); @@ -708,10 +711,13 @@ struct MOZ_STACK_CLASS BytecodeEmitter // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); + MOZ_MUST_USE bool emitAsyncIterator(); + // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); - MOZ_MUST_USE bool emitIteratorClose(CompletionKind completionKind = CompletionKind::Normal, + MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync, + CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false); template diff --git a/js/src/js.msg b/js/src/js.msg index 2778085c7e..d22265ba82 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -596,3 +596,5 @@ MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have // Async Iteration MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator") +MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator") +MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value") diff --git a/js/src/jsiter.h b/js/src/jsiter.h index 17b7df38bf..0b324a0140 100644 --- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -227,6 +227,8 @@ InitLegacyIteratorClass(JSContext* cx, HandleObject obj); extern JSObject* InitStopIterationClass(JSContext* cx, HandleObject obj); +enum class IteratorKind { Sync, Async }; + } /* namespace js */ #endif /* jsiter_h */ diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp index 04effe1b10..74f6389c10 100644 --- a/js/src/vm/AsyncIteration.cpp +++ b/js/src/vm/AsyncIteration.cpp @@ -148,6 +148,69 @@ js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle a return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); } +const Class AsyncFromSyncIteratorObject::class_ = { + "AsyncFromSyncIteratorObject", + JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) +}; + +// Async Iteration proposal 6.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 6.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 asyncIter = obj.as(); + + // Step 3. + asyncIter->setIterator(iter); + + // Step 4. + return asyncIter; +} + +// Async Iteration proposal 6.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 6.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 6.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 6.4.1.2 AsyncGenerator.prototype.next. static bool AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) @@ -413,17 +476,17 @@ js::AsyncGeneratorResumeNext(JSContext* cx, Handle asyncG return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); } -// Async Iteration proposal 6.2.1.2 (partially). +// Async Iteration proposal 6.2.1.3 (partially). // Most steps are done in generator. static MOZ_MUST_USE bool AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, - HandleValue value, bool done) + HandleValue value) { - // Step 6. + // Step 5. asyncGenObj->setSuspendedYield(); - // Step 10.c. - return AsyncGeneratorResolve(cx, asyncGenObj, value, done); + // Step 8. + return AsyncGeneratorResolve(cx, asyncGenObj, value, false); } // Async Iteration proposal 6.4.3.5 steps 12-14, 16-20. @@ -451,41 +514,30 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, return AsyncGeneratorThrown(cx, asyncGenObj); } + if (asyncGenObj->generatorObj()->isAfterAwait()) + return AsyncGeneratorAwait(cx, asyncGenObj, result); + // The following code corresponds to the following 3 cases: // * yield - // * await + // * yield* // * return - // For await and return, property access is done on an internal result + // For yield and return, property access is done on an internal result // object and it's not observable. - // For yield, it's done on an user-provided result object, and it's - // observable, so perform in that order in all cases. + // For yield*, it's done on a possibly user-provided result object, and + // it's observable. + // 2.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. RootedObject resultObj(cx, &result.toObject()); RootedValue value(cx); - RootedValue doneVal(cx); - // 6.2.1.2 step 10.a. if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) return false; - // 6.2.1.2 step 10.b. - if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) - return false; - - // 6.2.1.2 step 10.c. if (asyncGenObj->generatorObj()->isAfterYield()) - return AsyncGeneratorYield(cx, asyncGenObj, value, ToBoolean(doneVal)); + return AsyncGeneratorYield(cx, asyncGenObj, value); // 6.4.3.2 step 5.d-g. - if (ToBoolean(doneVal)) { - MOZ_ASSERT(!asyncGenObj->generatorObj()->isAfterAwait()); - return AsyncGeneratorReturned(cx, asyncGenObj, value); - } - - MOZ_ASSERT(asyncGenObj->generatorObj()->isAfterAwait()); - - // 5.1 steps 2-9. - return AsyncGeneratorAwait(cx, asyncGenObj, value); + return AsyncGeneratorReturned(cx, asyncGenObj, value); } static const JSFunctionSpec async_iterator_proto_methods[] = { @@ -493,6 +545,13 @@ static const JSFunctionSpec async_iterator_proto_methods[] = { 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), @@ -513,6 +572,19 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) return false; + // Async Iteration proposal 6.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 6.4.1 %AsyncGeneratorPrototype%. RootedObject asyncGenProto( cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, @@ -557,6 +629,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) } 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)); diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h index c0135283fa..974c209a01 100644 --- a/js/src/vm/AsyncIteration.h +++ b/js/src/vm/AsyncIteration.h @@ -196,6 +196,32 @@ class AsyncGeneratorObject : public NativeObject } }; +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 AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 4a9c03e884..6a8afb56b5 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -29,6 +29,7 @@ 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") \ diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index ce802daba9..3fd2762f84 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -101,6 +101,7 @@ class GlobalObject : public NativeObject ASYNC_FUNCTION_PROTO, ASYNC_FUNCTION, ASYNC_ITERATOR_PROTO, + ASYNC_FROM_SYNC_ITERATOR_PROTO, ASYNC_GENERATOR, ASYNC_GENERATOR_FUNCTION, ASYNC_GENERATOR_PROTO, @@ -635,6 +636,12 @@ class GlobalObject : public NativeObject initAsyncGenerators)); } + static NativeObject* + getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle global) { + return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO, + initAsyncGenerators)); + } + static NativeObject* getOrCreateAsyncGenerator(JSContext* cx, Handle global) { return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR, diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index df7318f188..5e17d960b5 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1940,7 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode) CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) CASE(JSOP_UNUSED126) -CASE(JSOP_UNUSED210) CASE(JSOP_UNUSED211) CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE) CASE(JSOP_UNUSED221) @@ -3596,6 +3595,17 @@ CASE(JSOP_TOASYNCGEN) } END_CASE(JSOP_TOASYNCGEN) +CASE(JSOP_TOASYNCITER) +{ + ReservedRooted 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); @@ -5139,6 +5149,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 9fefd75ccd..5e7087f7fa 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/Opcodes.h b/js/src/vm/Opcodes.h index ae2b2520b2..b26336815e 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -2146,7 +2146,15 @@ * Stack: promise, gen => resolved */ \ macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \ - macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * 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 From 06b3cb6ccd352946a428d5942c646fc4844225b6 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:22:32 -0500 Subject: [PATCH 21/32] Bug 1331092 - Part 8: Support JSOP_TOASYNCITER in JIT. Tag #1287 --- js/src/jit/BaselineCompiler.cpp | 22 ++++++++++++++++++++++ js/src/jit/BaselineCompiler.h | 1 + js/src/jit/CodeGenerator.cpp | 11 +++++++++++ js/src/jit/CodeGenerator.h | 1 + js/src/jit/IonBuilder.cpp | 17 +++++++++++++++++ js/src/jit/IonBuilder.h | 1 + js/src/jit/Lowering.cpp | 8 ++++++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 15 +++++++++++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/shared/LIR-shared.h | 13 +++++++++++++ js/src/jit/shared/LOpcodes-shared.h | 1 + 12 files changed, 92 insertions(+) diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 3856fb63f6..38202915a4 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -3937,6 +3937,28 @@ BaselineCompiler::emit_JSOP_TOASYNCGEN() return true; } +typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject); +static const VMFunction ToAsyncIterInfo = + FunctionInfo(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); + +bool +BaselineCompiler::emit_JSOP_TOASYNCITER() +{ + frame.syncStack(0); + masm.unboxObject(frame.addressOfStackValue(frame.peek(-1)), R0.scratchReg()); + + prepareVMCall(); + pushArg(R0.scratchReg()); + + if (!callVM(ToAsyncIterInfo)) + return false; + + masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0); + frame.pop(); + frame.push(R0); + return true; +} + typedef bool (*ThrowObjectCoercibleFn)(JSContext*, HandleValue); static const VMFunction ThrowObjectCoercibleInfo = FunctionInfo(ThrowObjectCoercible, "ThrowObjectCoercible"); diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 15d0e30645..7b1af092ae 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -198,6 +198,7 @@ namespace jit { _(JSOP_REST) \ _(JSOP_TOASYNC) \ _(JSOP_TOASYNCGEN) \ + _(JSOP_TOASYNCITER) \ _(JSOP_TOID) \ _(JSOP_TOSTRING) \ _(JSOP_TABLESWITCH) \ diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 128947e5ed..2b1c671d1b 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -10225,6 +10225,17 @@ CodeGenerator::visitToAsyncGen(LToAsyncGen* lir) callVM(ToAsyncGenInfo, lir); } +typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject); +static const VMFunction ToAsyncIterInfo = + FunctionInfo(js::CreateAsyncFromSyncIterator, "ToAsyncIter"); + +void +CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) +{ + pushArg(ToRegister(lir->unwrapped())); + callVM(ToAsyncIterInfo, lir); +} + typedef bool (*ToIdFn)(JSContext*, HandleScript, jsbytecode*, HandleValue, MutableHandleValue); static const VMFunction ToIdInfo = FunctionInfo(ToIdOperation, "ToIdOperation"); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index d2f24dc043..65acfe2740 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -287,6 +287,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool); void visitToAsync(LToAsync* lir); void visitToAsyncGen(LToAsyncGen* lir); + void visitToAsyncIter(LToAsyncIter* lir); void visitToIdV(LToIdV* lir); template void emitLoadElementT(LLoadElementT* lir, const T& source); void visitLoadElementT(LLoadElementT* lir); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 79214c993d..ac56312894 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2133,6 +2133,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_TOASYNCGEN: return jsop_toasyncgen(); + case JSOP_TOASYNCITER: + return jsop_toasynciter(); + case JSOP_TOID: return jsop_toid(); @@ -13210,6 +13213,20 @@ IonBuilder::jsop_toasyncgen() return resumeAfter(ins); } +bool +IonBuilder::jsop_toasynciter() +{ + MDefinition* unwrapped = current->pop(); + MOZ_ASSERT(unwrapped->type() == MIRType::Object); + + MToAsyncIter* ins = MToAsyncIter::New(alloc(), unwrapped); + + current->add(ins); + current->push(ins); + + return resumeAfter(ins); +} + bool IonBuilder::jsop_toid() { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 48388539fd..6a3b61232f 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -763,6 +763,7 @@ class IonBuilder MOZ_MUST_USE bool jsop_typeof(); MOZ_MUST_USE bool jsop_toasync(); MOZ_MUST_USE bool jsop_toasyncgen(); + MOZ_MUST_USE bool jsop_toasynciter(); MOZ_MUST_USE bool jsop_toid(); MOZ_MUST_USE bool jsop_iter(uint8_t flags); MOZ_MUST_USE bool jsop_itermore(); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index ee59b8ae60..1084509833 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1183,6 +1183,14 @@ LIRGenerator::visitToAsyncGen(MToAsyncGen* ins) assignSafepoint(lir, ins); } +void +LIRGenerator::visitToAsyncIter(MToAsyncIter* ins) +{ + LToAsyncIter* lir = new(alloc()) LToAsyncIter(useRegisterAtStart(ins->input())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); +} + void LIRGenerator::visitToId(MToId* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 1d412040c5..81e6abbbbd 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -122,6 +122,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitTypeOf(MTypeOf* ins); void visitToAsync(MToAsync* ins); void visitToAsyncGen(MToAsyncGen* ins); + void visitToAsyncIter(MToAsyncIter* ins); void visitToId(MToId* ins); void visitBitNot(MBitNot* ins); void visitBitAnd(MBitAnd* ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 637c09cfdd..e7186ed305 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5799,6 +5799,21 @@ class MToAsyncGen TRIVIAL_NEW_WRAPPERS }; +class MToAsyncIter + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + explicit MToAsyncIter(MDefinition* unwrapped) + : MUnaryInstruction(unwrapped) + { + setResultType(MIRType::Object); + } + + public: + INSTRUCTION_HEADER(ToAsyncIter) + TRIVIAL_NEW_WRAPPERS +}; + class MToId : public MUnaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 59620e4e76..589dde0774 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -80,6 +80,7 @@ namespace jit { _(TypeOf) \ _(ToAsync) \ _(ToAsyncGen) \ + _(ToAsyncIter) \ _(ToId) \ _(BitAnd) \ _(BitOr) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 6c8ff899b5..ff4915d1a7 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1619,6 +1619,19 @@ class LToAsyncGen : public LCallInstructionHelper<1, 1, 0> } }; +class LToAsyncIter : public LCallInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(ToAsyncIter) + explicit LToAsyncIter(const LAllocation& input) { + setOperand(0, input); + } + + const LAllocation* unwrapped() { + return getOperand(0); + } +}; + class LToIdV : public LInstructionHelper { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 9e79f02660..56b98940af 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -354,6 +354,7 @@ _(TypeOfV) \ _(ToAsync) \ _(ToAsyncGen) \ + _(ToAsyncIter) \ _(ToIdV) \ _(Floor) \ _(FloorF) \ From 9379133c2dadeb19ab92e4d60d0b9a19f7e8f708 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:22:48 -0500 Subject: [PATCH 22/32] Bug 1331092 - Part 9: Implement for-await-of. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 41 ++++++++++++++++++++++------- js/src/frontend/BytecodeEmitter.h | 3 ++- js/src/frontend/Parser.cpp | 22 +++++++++++++++- js/src/frontend/Parser.h | 2 ++ js/src/js.msg | 1 + js/src/jsfriendapi.h | 1 + 6 files changed, 59 insertions(+), 11 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 5ee0991514..87e4d1b90b 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2029,12 +2029,16 @@ class ForOfLoopControl : public LoopControl bool allowSelfHosted_; + IteratorKind iterKind_; + public: - ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted) + ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted, + IteratorKind iterKind) : LoopControl(bce, StatementKind::ForOfLoop), iterDepth_(iterDepth), numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX), - allowSelfHosted_(allowSelfHosted) + allowSelfHosted_(allowSelfHosted), + iterKind_(iterKind) { } @@ -2118,7 +2122,7 @@ class ForOfLoopControl : public LoopControl bool emitIteratorClose(BytecodeEmitter* bce, CompletionKind completionKind = CompletionKind::Normal) { ptrdiff_t start = bce->offset(); - if (!bce->emitIteratorClose(IteratorKind::Sync, completionKind, allowSelfHosted_)) + if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_)) return false; ptrdiff_t end = bce->offset(); return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); @@ -5246,7 +5250,8 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri } bool -BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false */) +BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = IteratorKind::Sync */, + bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".next() iteration is prohibited in self-hosted code because it " @@ -5260,6 +5265,12 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false return false; if (!emitCall(JSOP_CALL, 0, pn)) // ... RESULT return false; + + if (iterKind == IteratorKind::Async) { + if (!emitAwait()) // ... RESULT + return false; + } + if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT return false; checkTypeSet(JSOP_CALL); @@ -7099,7 +7110,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted) if (!emitDupAt(2)) // ITER ARR I ITER return false; - if (!emitIteratorNext(nullptr, allowSelfHosted)) // ITER ARR I RESULT + if (!emitIteratorNext(nullptr, IteratorKind::Sync, allowSelfHosted)) // ITER ARR I RESULT return false; if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT return false; @@ -7199,6 +7210,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte MOZ_ASSERT(forOfHead->isKind(PNK_FOROF)); MOZ_ASSERT(forOfHead->isArity(PN_TERNARY)); + unsigned iflags = forOfLoop->pn_iflags; + IteratorKind iterKind = (iflags & JSITER_FORAWAITOF) + ? IteratorKind::Async + : IteratorKind::Sync; + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()); + MOZ_ASSERT_IF(iterKind == IteratorKind::Async, sc->asFunctionBox()->isAsync()); + ParseNode* forHeadExpr = forOfHead->pn_kid3; // Certain builtins (e.g. Array.from) are implemented in self-hosting @@ -7214,8 +7232,13 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte // Evaluate the expression being iterated. if (!emitTree(forHeadExpr)) // ITERABLE return false; - if (!emitIterator()) // ITER - return false; + if (iterKind == IteratorKind::Async) { + if (!emitAsyncIterator()) // ITER + return false; + } else { + if (!emitIterator()) // ITER + return false; + } int32_t iterDepth = stackDepth; @@ -7226,7 +7249,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emit1(JSOP_UNDEFINED)) // ITER RESULT UNDEF return false; - ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter); + ForOfLoopControl loopInfo(this, iterDepth, allowSelfHostedIter, iterKind); // Annotate so IonMonkey can find the loop-closing jump. unsigned noteIndex; @@ -7322,7 +7345,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte if (!emitDupAt(1)) // ITER UNDEF ITER return false; - if (!emitIteratorNext(forOfHead, allowSelfHostedIter)) // ITER UNDEF RESULT + if (!emitIteratorNext(forOfHead, iterKind, allowSelfHostedIter)) // ITER UNDEF RESULT return false; if (!emit1(JSOP_SWAP)) // ITER RESULT UNDEF diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 1daef71b1a..555c979bf5 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -715,7 +715,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter // Pops iterator from the top of the stack. Pushes the result of |.next()| // onto the stack. - MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync, + bool allowSelfHosted = false); MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync, CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7704cf65a5..2d2f17fd1b 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -5972,6 +5972,7 @@ Parser::matchInOrOf(bool* isForInp, bool* isForOfp) template bool Parser::forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, Maybe& forLoopLexicalScope, @@ -6062,6 +6063,11 @@ Parser::forHeadStart(YieldHandling yieldHandling, if (!matchInOrOf(&isForIn, &isForOf)) return false; + if (iterKind == IteratorKind::Async && !isForOf) { + error(JSMSG_FOR_AWAIT_NOT_OF); + return false; + } + // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled // the init expression; the caller handles the rest. Allow the Operand // modifier when regetting: Operand must be used to examine the ';' in @@ -6135,6 +6141,7 @@ Parser::forStatement(YieldHandling yieldHandling) ParseContext::Statement stmt(pc, StatementKind::ForLoop); bool isForEach = false; + IteratorKind iterKind = IteratorKind::Sync; unsigned iflags = 0; if (allowsForEachIn()) { @@ -6149,6 +6156,19 @@ Parser::forStatement(YieldHandling yieldHandling) } } +#ifndef RELEASE_OR_BETA + if (pc->isAsync()) { + bool matched; + if (!tokenStream.matchToken(&matched, TOK_AWAIT)) + return null(); + + if (matched) { + iflags |= JSITER_FORAWAITOF; + iterKind = IteratorKind::Async; + } + } +#endif + MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); // PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type. @@ -6186,7 +6206,7 @@ Parser::forStatement(YieldHandling yieldHandling) // // In either case the subsequent token can be consistently accessed using // TokenStream::None semantics. - if (!forHeadStart(yieldHandling, &headKind, &startNode, forLoopLexicalScope, + if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope, &iteratedExpr)) { return null(); diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 73c2817f45..243a770835 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -12,6 +12,7 @@ #include "mozilla/Array.h" #include "mozilla/Maybe.h" +#include "jsiter.h" #include "jspubtd.h" #include "frontend/BytecodeCompiler.h" @@ -1196,6 +1197,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter Node forStatement(YieldHandling yieldHandling); bool forHeadStart(YieldHandling yieldHandling, + IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, mozilla::Maybe& forLetImpliedScope, diff --git a/js/src/js.msg b/js/src/js.msg index d22265ba82..f8e5358bb3 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -595,6 +595,7 @@ MSG_DEF(JSMSG_RETURN_NOT_CALLABLE, 0, JSEXN_TYPEERR, "property 'return' of i MSG_DEF(JSMSG_ITERATOR_NO_THROW, 0, JSEXN_TYPEERR, "iterator does not have a 'throw' method") // Async Iteration +MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF, 0, JSEXN_TYPEERR, "'for await' loop should be used with 'of'") MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR, 0, JSEXN_TYPEERR, "Not an async generator") MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR, 0, JSEXN_TYPEERR, "Not an async from sync iterator") MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value") diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 4912154565..7b59689ffd 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -948,6 +948,7 @@ IsObjectInContextCompartment(JSObject* obj, const JSContext* cx); #define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ #define JSITER_SYMBOLS 0x20 /* also include symbol property keys */ #define JSITER_SYMBOLSONLY 0x40 /* exclude string property keys */ +#define JSITER_FORAWAITOF 0x80 /* for-await-of */ JS_FRIEND_API(bool) RunningWithTrustedPrincipals(JSContext* cx); From 9cecb72f378a8386392cd1be70c5af318de5443e Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:25:14 -0500 Subject: [PATCH 23/32] Bug 1331092 - Part 11: Await on the innerResult.value when innerResult.done is true in yield*. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 87e4d1b90b..d7fc86a912 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8896,6 +8896,10 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) return false; if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE return false; + if (isAsyncGenerator) { + if (!emitAwait()) // ITER OLDRESULT FTYPE FVALUE VALUE + return false; + } if (!emitPrepareIteratorResult()) // ITER OLDRESULT FTYPE FVALUE VALUE RESULT return false; if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RESULT VALUE @@ -8988,6 +8992,11 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // VALUE return false; + if (isAsyncGenerator) { + if (!emitAwait()) // VALUE + return false; + } + MOZ_ASSERT(this->stackDepth == startDepth - 1); return true; From 3206741afa56cc2f2292253e4a1b9e482c1c5721 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:57:20 -0500 Subject: [PATCH 24/32] Bug 1355399 - Switch property retrieval in Async-from-Sync Iterator prototype methods. Tag #1287 --- js/src/builtin/Promise.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index d2b7b543b1..6bb2d61d76 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -2444,16 +2444,16 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20. // Steps 7-8. - RootedValue value(cx); - if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) - return AbruptRejectPromise(cx, args, resultPromise, nullptr); - - // Steps 9-10. RootedValue doneVal(cx); if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); bool done = ToBoolean(doneVal); + // Steps 9-10. + RootedValue value(cx); + if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) + return AbruptRejectPromise(cx, args, resultPromise, nullptr); + // Step 11. Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) From ba1cf5608cde46c4c1139dbf31aac281e7fb3d2a Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 09:58:06 -0500 Subject: [PATCH 25/32] Bug 1364608 - Stash rval in AsyncIteratorClose. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index d7fc86a912..1f868ade8d 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5365,7 +5365,14 @@ BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync checkTypeSet(JSOP_CALL); if (iterKind == IteratorKind::Async) { - if (!emitAwait()) // ... ... RESULT + if (completionKind != CompletionKind::Throw) { + // Await clobbers rval, so save the current rval. + if (!emit1(JSOP_GETRVAL)) // ... ... RESULT RVAL + return false; + if (!emit1(JSOP_SWAP)) // ... ... RVAL RESULT + return false; + } + if (!emitAwait()) // ... ... RVAL? RESULT return false; } @@ -5395,8 +5402,15 @@ BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync if (!emit1(JSOP_POP)) // ... RESULT return false; } else { - if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RESULT + if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ... RVAL? RESULT return false; + + if (iterKind == IteratorKind::Async) { + if (!emit1(JSOP_SWAP)) // ... RESULT RVAL + return false; + if (!emit1(JSOP_SETRVAL)) // ... RESULT + return false; + } } if (!ifReturnMethodIsDefined.emitElse()) From 9e8369d01f79c5be5d3a1cfb81ba15bcca59c2cb Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 10:30:54 -0500 Subject: [PATCH 26/32] Bug 1379525 - Part 1: Await on the value before yielding or returning inside async generator. Tag #1287 --- js/src/builtin/Promise.cpp | 399 +++++++++++++++++----------- js/src/frontend/BytecodeEmitter.cpp | 30 ++- js/src/vm/AsyncIteration.cpp | 151 ++++------- js/src/vm/AsyncIteration.h | 34 ++- 4 files changed, 350 insertions(+), 264 deletions(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 6bb2d61d76..62c5eb4295 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -36,18 +36,30 @@ MillisecondsSinceStartup() enum PromiseHandler { PromiseHandlerIdentity = 0, PromiseHandlerThrower, - PromiseHandlerAsyncFunctionAwaitFulfilled, - PromiseHandlerAsyncFunctionAwaitRejected, - PromiseHandlerAsyncGeneratorAwaitFulfilled, - PromiseHandlerAsyncGeneratorAwaitRejected, - // Async Iteration proposal 6.1.1.2.1. - // Async iterator handlers take the resolved value and create new iterator - // objects. To do so it needs to forward whether the iterator is done. In - // spec, this is achieved via the [[Done]] internal slot. We enumerate both - // true and false cases here. - PromiseHandlerAsyncIteratorValueUnwrapDone, - PromiseHandlerAsyncIteratorValueUnwrapNotDone, + // ES 2018 draft 25.5.5.4-5. + PromiseHandlerAsyncFunctionAwaitedFulfilled, + PromiseHandlerAsyncFunctionAwaitedRejected, + + // Async Iteration proposal 4.1. + PromiseHandlerAsyncGeneratorAwaitedFulfilled, + PromiseHandlerAsyncGeneratorAwaitedRejected, + + // Async Iteration proposal 11.4.3.5.1-2. + PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled, + PromiseHandlerAsyncGeneratorResumeNextReturnRejected, + + // Async Iteration proposal 11.4.3.7 steps 8.c-e. + PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled, + PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected, + + // Async Iteration proposal 11.1.3.2.5. + // Async-from-Sync iterator handlers take the resolved value and create new + // iterator objects. To do so it needs to forward whether the iterator is + // done. In spec, this is achieved via the [[Done]] internal slot. We + // enumerate both true and false cases here. + PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone, + PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone, }; enum ResolutionMode { @@ -197,8 +209,8 @@ enum ReactionRecordSlots { #define REACTION_FLAG_RESOLVED 0x1 #define REACTION_FLAG_FULFILLED 0x2 #define REACTION_FLAG_IGNORE_DEFAULT_RESOLUTION 0x4 -#define REACTION_FLAG_ASYNC_FUNCTION_AWAIT 0x8 -#define REACTION_FLAG_ASYNC_GENERATOR_AWAIT 0x10 +#define REACTION_FLAG_ASYNC_FUNCTION 0x8 +#define REACTION_FLAG_ASYNC_GENERATOR 0x10 // ES2016, 25.4.1.2. class PromiseReactionRecord : public NativeObject @@ -225,28 +237,28 @@ class PromiseReactionRecord : public NativeObject flags |= REACTION_FLAG_FULFILLED; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - void setIsAsyncFunctionAwait() { + void setIsAsyncFunction() { int32_t flags = this->flags(); - flags |= REACTION_FLAG_ASYNC_FUNCTION_AWAIT; + flags |= REACTION_FLAG_ASYNC_FUNCTION; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); } - bool isAsyncFunctionAwait() { + bool isAsyncFunction() { int32_t flags = this->flags(); - return flags & REACTION_FLAG_ASYNC_FUNCTION_AWAIT; + return flags & REACTION_FLAG_ASYNC_FUNCTION; } - void setIsAsyncGeneratorAwait(Handle asyncGenObj) { + void setIsAsyncGenerator(Handle asyncGenObj) { int32_t flags = this->flags(); - flags |= REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + flags |= REACTION_FLAG_ASYNC_GENERATOR; setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags)); setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj)); } - bool isAsyncGeneratorAwait() { + bool isAsyncGenerator() { int32_t flags = this->flags(); - return flags & REACTION_FLAG_ASYNC_GENERATOR_AWAIT; + return flags & REACTION_FLAG_ASYNC_GENERATOR; } AsyncGeneratorObject* asyncGenerator() { - MOZ_ASSERT(isAsyncGeneratorAwait()); + MOZ_ASSERT(isAsyncGenerator()); return &getFixedSlot(ReactionRecordSlot_Generator).toObject() .as(); } @@ -848,10 +860,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat } static MOZ_MUST_USE bool -AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, - MutableHandleValue rval) +AsyncFunctionPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAsyncFunctionAwait()); + MOZ_ASSERT(reaction->isAsyncFunction()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); @@ -859,15 +871,14 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, HandlegetFixedSlot(PromiseSlot_AwaitGenerator)); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled || - handlerNum == PromiseHandlerAsyncFunctionAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PromiseHandlerAsyncFunctionAwaitFulfilled) { + if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) { if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument)) return false; } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected); if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument)) return false; } @@ -877,27 +888,48 @@ AsyncFunctionAwaitPromiseReactionJob(JSContext* cx, Handle reaction, - MutableHandleValue rval) +AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle reaction, + MutableHandleValue rval) { - MOZ_ASSERT(reaction->isAsyncGeneratorAwait()); + MOZ_ASSERT(reaction->isAsyncGenerator()); RootedValue handlerVal(cx, reaction->handler()); RootedValue argument(cx, reaction->handlerArg()); Rooted asyncGenObj(cx, reaction->asyncGenerator()); int32_t handlerNum = int32_t(handlerVal.toNumber()); - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled || - handlerNum == PromiseHandlerAsyncGeneratorAwaitRejected); // Await's handlers don't return a value, nor throw exception. // They fail only on OOM. - if (handlerNum == PromiseHandlerAsyncGeneratorAwaitFulfilled) { + if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) { + // 4.1.1. if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument)) return false; - } else { + } else if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedRejected) { + // 4.1.2. if (!AsyncGeneratorAwaitedRejected(cx, asyncGenObj, argument)) return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled) { + asyncGenObj->setCompleted(); + // 11.4.3.5.1 step 1. + if (!AsyncGeneratorResolve(cx, asyncGenObj, argument, true)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorResumeNextReturnRejected) { + asyncGenObj->setCompleted(); + // 11.4.3.5.2 step 1. + if (!AsyncGeneratorReject(cx, asyncGenObj, argument)) + return false; + } else if (handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled) { + asyncGenObj->setExecuting(); + // 11.4.3.7 steps 8.d-e. + if (!AsyncGeneratorYieldReturnAwaitedFulfilled(cx, asyncGenObj, argument)) + return false; + } else { + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected); + asyncGenObj->setExecuting(); + // 11.4.3.7 step 8.c. + if (!AsyncGeneratorYieldReturnAwaitedRejected(cx, asyncGenObj, argument)) + return false; } rval.setUndefined(); @@ -947,10 +979,10 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) // Steps 1-2. Rooted reaction(cx, &reactionObj->as()); - if (reaction->isAsyncFunctionAwait()) - return AsyncFunctionAwaitPromiseReactionJob(cx, reaction, args.rval()); - if (reaction->isAsyncGeneratorAwait()) - return AsyncGeneratorAwaitPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncFunction()) + return AsyncFunctionPromiseReactionJob(cx, reaction, args.rval()); + if (reaction->isAsyncGenerator()) + return AsyncGeneratorPromiseReactionJob(cx, reaction, args.rval()); // Step 3. RootedValue handlerVal(cx, reaction->handler()); @@ -972,11 +1004,11 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp) resolutionMode = RejectMode; handlerResult = argument; } else { - MOZ_ASSERT(handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone || - handlerNum == PromiseHandlerAsyncIteratorValueUnwrapNotDone); + MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone || + handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone); - bool done = handlerNum == PromiseHandlerAsyncIteratorValueUnwrapDone; - // Async Iteration proposal 6.1.1.2.1 step 1. + bool done = handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone; + // Async Iteration proposal 11.1.3.2.5 step 1. RootedObject resultObj(cx, CreateIterResultObject(cx, argument, done)); if (!resultObj) return false; @@ -2214,7 +2246,7 @@ static MOZ_MUST_USE bool PerformPromiseThenWithReaction(JSContext* cx, // Some async/await functions are implemented here instead of // js/src/builtin/AsyncFunction.cpp, to call Promise internal functions. -// Async Functions proposal 1.1.8 and 1.2.14 step 1. +// ES 2018 draft 14.6.11 and 14.7.14 step 1. MOZ_MUST_USE PromiseObject* js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) { @@ -2228,7 +2260,7 @@ js::CreatePromiseObjectForAsync(JSContext* cx, HandleValue generatorVal) return promise; } -// Async Functions proposal 2.2 steps 3.f, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.f, 3.g. MOZ_MUST_USE bool js::AsyncFunctionThrown(JSContext* cx, Handle resultPromise) { @@ -2244,7 +2276,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle resultPromise) return true; } -// Async Functions proposal 2.2 steps 3.d-e, 3.g. +// ES 2018 draft 25.5.5.2 steps 3.d-e, 3.g. MOZ_MUST_USE bool js::AsyncFunctionReturned(JSContext* cx, Handle resultPromise, HandleValue value) { @@ -2256,28 +2288,27 @@ js::AsyncFunctionReturned(JSContext* cx, Handle resultPromise, H return true; } -// Async Functions proposal 2.3 steps 2-8. -MOZ_MUST_USE bool -js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value) +// Helper function that performs the equivalent steps as +// Async Iteration proposal 4.1 Await steps 2-3, 6-9 or similar. +template +static MOZ_MUST_USE bool +InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise, + HandleValue onFulfilled, HandleValue onRejected, T extraStep) { // Step 2. Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) return false; - // Steps 3. + // Step 3. if (!ResolvePromiseInternal(cx, promise, value)) return false; - // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitFulfilled)); - RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitRejected)); - RootedObject incumbentGlobal(cx); if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) return false; - // Steps 6-7. + // Steps 7-8. Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, onRejected, nullptr, nullptr, @@ -2285,53 +2316,47 @@ js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, Hand if (!reaction) return false; - reaction->setIsAsyncFunctionAwait(); - - // Step 8. - return PerformPromiseThenWithReaction(cx, promise, reaction); -} - -// Async Iteration proposal 5.1 steps 2-9. -MOZ_MUST_USE bool -js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, - HandleValue value) -{ - // Step 2. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) - return false; - - // Steps 3. - if (!ResolvePromiseInternal(cx, promise, value)) - return false; - - // Steps 4-5. - RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitFulfilled)); - RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitRejected)); - - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) - return false; - - // Step 6 (skipped). - - // Steps 7-8. - Rooted reaction(cx, NewReactionRecord(cx, nullptr, - onFulfilled, onRejected, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; - - reaction->setIsAsyncGeneratorAwait(asyncGenObj); + // Step 6. + extraStep(reaction); // Step 9. return PerformPromiseThenWithReaction(cx, promise, reaction); } -// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next -// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return -// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw +// ES 2018 draft 25.5.5.3 steps 2-10. +MOZ_MUST_USE bool +js::AsyncFunctionAwait(JSContext* cx, Handle resultPromise, HandleValue value) +{ + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected)); + + // Steps 2-3, 6-10. + auto extra = [](Handle reaction) { + reaction->setIsAsyncFunction(); + }; + return InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra); +} + +// Async Iteration proposal 4.1 Await steps 2-9. +MOZ_MUST_USE bool +js::AsyncGeneratorAwait(JSContext* cx, Handle asyncGenObj, + HandleValue value) +{ + // Steps 4-5. + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected)); + + // Steps 2-3, 6-9. + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); +} + +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw bool js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind completionKind) { @@ -2368,11 +2393,11 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co RootedValue resultVal(cx); RootedValue func(cx); if (completionKind == CompletionKind::Normal) { - // 6.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.1 steps 5-6 (partially). if (!GetProperty(cx, iter, iter, cx->names().next, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); } else if (completionKind == CompletionKind::Return) { - // 6.1.3.2.2 steps 5-6. + // 11.1.3.2.2 steps 5-6. if (!GetProperty(cx, iter, iter, cx->names().return_, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); @@ -2394,7 +2419,7 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co return true; } } else { - // 6.1.3.2.3 steps 5-6. + // 11.1.3.2.3 steps 5-6. MOZ_ASSERT(completionKind == CompletionKind::Throw); if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); @@ -2411,16 +2436,16 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co } } - // 6.1.3.2.1 steps 5-6 (partially). - // 6.1.3.2.2, 6.1.3.2.3 steps 8-9. + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 8-9. RootedValue iterVal(cx, ObjectValue(*iter)); FixedInvokeArgs<1> args2(cx); args2[0].set(args.get(0)); if (!js::Call(cx, func, iterVal, args2, &resultVal)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); - // 6.1.3.2.1 steps 5-6 (partially). - // 6.1.3.2.2, 6.1.3.2.3 steps 10. + // 11.1.3.2.1 steps 5-6 (partially). + // 11.1.3.2.2, 11.1.3.2.3 steps 10. if (!resultVal.isObject()) { CheckIsObjectKind kind; switch (completionKind) { @@ -2440,8 +2465,8 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co RootedObject resultObj(cx, &resultVal.toObject()); - // Following step numbers are for 6.1.3.2.1. - // For 6.1.3.2.2 and 6.1.3.2.3, steps 7-16 corresponds to steps 11-20. + // Following step numbers are for 11.1.3.2.1. + // For 11.1.3.2.2 and 11.1.3.2.3, steps 7-16 corresponds to steps 11-20. // Steps 7-8. RootedValue doneVal(cx); @@ -2454,33 +2479,15 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) return AbruptRejectPromise(cx, args, resultPromise, nullptr); - // Step 11. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) - return false; - - // Step 12. - if (!ResolvePromiseInternal(cx, promise, value)) - return false; - // Steps 13-14. RootedValue onFulfilled(cx, Int32Value(done - ? PromiseHandlerAsyncIteratorValueUnwrapDone - : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); + ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone + : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone)); - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) - return false; - - // Step 15. - Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, - UndefinedHandleValue, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; - - if (!PerformPromiseThenWithReaction(cx, promise, reaction)) + // Steps 11-12, 15. + auto extra = [](Handle reaction) { + }; + if (!InternalAwait(cx, value, resultPromise, onFulfilled, UndefinedHandleValue, extra)) return false; // Step 16. @@ -2488,7 +2495,10 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co return true; } -// Async Iteration proposal 6.4.3.3. +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); + +// Async Iteration proposal 11.4.3.3. MOZ_MUST_USE bool js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenObj, HandleValue value, bool done) @@ -2508,43 +2518,25 @@ js::AsyncGeneratorResolve(JSContext* cx, Handle asyncGenO RootedObject resultPromise(cx, request->promise()); // Step 6. - Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); - if (!promise) + RootedObject resultObj(cx, CreateIterResultObject(cx, value, done)); + if (!resultObj) return false; + RootedValue resultValue(cx, ObjectValue(*resultObj)); + // Step 7. - if (!ResolvePromiseInternal(cx, promise, value)) + if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) return false; - // Steps 8-9. - RootedValue onFulfilled(cx, Int32Value(done - ? PromiseHandlerAsyncIteratorValueUnwrapDone - : PromiseHandlerAsyncIteratorValueUnwrapNotDone)); - - RootedObject incumbentGlobal(cx); - if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal)) - return false; - - // Step 10. - Rooted reaction(cx, NewReactionRecord(cx, resultPromise, onFulfilled, - UndefinedHandleValue, - nullptr, nullptr, - incumbentGlobal)); - if (!reaction) - return false; - - if (!PerformPromiseThenWithReaction(cx, promise, reaction)) - return false; - - // Step 11. + // Step 8. if (!AsyncGeneratorResumeNext(cx, asyncGenObj)) return false; - // Step 12. + // Step 9. return true; } -// Async Iteration proposal 6.4.3.4. +// Async Iteration proposal 11.4.3.4. MOZ_MUST_USE bool js::AsyncGeneratorReject(JSContext* cx, Handle asyncGenObj, HandleValue exception) @@ -2575,7 +2567,98 @@ js::AsyncGeneratorReject(JSContext* cx, Handle asyncGenOb return true; } -// Async Iteration proposal 6.4.3.6. +// Async Iteration proposal 11.4.3.5. +static MOZ_MUST_USE bool +AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj) +{ + // Step 1 (implicit). + + // Steps 2-3. + MOZ_ASSERT(!asyncGenObj->isExecuting()); + + // Step 4. + if (asyncGenObj->isAwaitingYieldReturn() || asyncGenObj->isAwaitingReturn()) + return true; + + // Steps 5-6. + if (asyncGenObj->isQueueEmpty()) + return true; + + // Steps 7-8. + Rooted request( + cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); + if (!request) + return false; + + // Step 9. + CompletionKind completionKind = request->completionKind(); + + // Step 10. + if (completionKind != CompletionKind::Normal) { + // Step 10.a. + if (asyncGenObj->isSuspendedStart()) + asyncGenObj->setCompleted(); + + // Step 10.b. + if (asyncGenObj->isCompleted()) { + RootedValue value(cx, request->completionValue()); + + // Step 10.b.i. + if (completionKind == CompletionKind::Return) { + // Steps 10.b.i.1. + asyncGenObj->setAwaitingReturn(); + + // Steps 10.b.i.4-6 (reordered). + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorResumeNextReturnRejected)); + + // Steps 10.b.i.2-3, 7-10. + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, value, nullptr, onFulfilled, onRejected, extra); + } + + // Step 10.b.ii.1. + MOZ_ASSERT(completionKind == CompletionKind::Throw); + + // Steps 10.b.ii.2-3. + return AsyncGeneratorReject(cx, asyncGenObj, value); + } + } else if (asyncGenObj->isCompleted()) { + // Step 11. + return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); + } + + // Step 12. + MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); + + // Step 16 (reordered). + asyncGenObj->setExecuting(); + + RootedValue argument(cx, request->completionValue()); + + if (completionKind == CompletionKind::Return) { + // 11.4.3.7 AsyncGeneratorYield step 8.b-e. + // Since we don't have the place that handles return from yield + // inside the generator, handle the case here, with extra state + // State_AwaitingYieldReturn. + asyncGenObj->setAwaitingYieldReturn(); + + RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedFulfilled)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorYieldReturnAwaitedRejected)); + + auto extra = [&](Handle reaction) { + reaction->setIsAsyncGenerator(asyncGenObj); + }; + return InternalAwait(cx, argument, nullptr, onFulfilled, onRejected, extra); + } + + // Steps 13-15, 17-21. + return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); +} + +// Async Iteration proposal 11.4.3.6. MOZ_MUST_USE bool js::AsyncGeneratorEnqueue(JSContext* cx, HandleValue asyncGenVal, CompletionKind completionKind, HandleValue completionValue, diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 1f868ade8d..e5afda0df9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -8578,6 +8578,13 @@ BytecodeEmitter::emitReturn(ParseNode* pn) if (ParseNode* pn2 = pn->pn_kid) { if (!emitTree(pn2)) return false; + + bool isAsyncGenerator = sc->asFunctionBox()->isAsync() && + sc->asFunctionBox()->isStarGenerator(); + if (isAsyncGenerator) { + if (!emitAwait()) + return false; + } } else { /* No explicit return value provided */ if (!emit1(JSOP_UNDEFINED)) @@ -8690,6 +8697,14 @@ BytecodeEmitter::emitYield(ParseNode* pn) if (!emit1(JSOP_UNDEFINED)) return false; } + + // 11.4.3.7 AsyncGeneratorYield step 5. + bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); + if (isAsyncGenerator) { + if (!emitAwait()) // RESULT + return false; + } + if (needsIteratorResult) { if (!emitFinishIteratorResult(false)) return false; @@ -8762,6 +8777,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) MOZ_ASSERT(this->stackDepth == startDepth); + // 11.4.3.7 AsyncGeneratorYield step 5. + if (isAsyncGenerator) { + if (!emitAwait()) // ITER RESULT + return false; + } + // Load the generator object. if (!emitGetDotGenerator()) // ITER RESULT GENOBJ return false; @@ -8910,10 +8931,6 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) return false; if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ITER OLDRESULT FTYPE FVALUE VALUE return false; - if (isAsyncGenerator) { - if (!emitAwait()) // ITER OLDRESULT FTYPE FVALUE VALUE - return false; - } if (!emitPrepareIteratorResult()) // ITER OLDRESULT FTYPE FVALUE VALUE RESULT return false; if (!emit1(JSOP_SWAP)) // ITER OLDRESULT FTYPE FVALUE RESULT VALUE @@ -9006,11 +9023,6 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // VALUE return false; - if (isAsyncGenerator) { - if (!emitAwait()) // VALUE - return false; - } - MOZ_ASSERT(this->stackDepth == startDepth - 1); return true; diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp index 74f6389c10..256f247b09 100644 --- a/js/src/vm/AsyncIteration.cpp +++ b/js/src/vm/AsyncIteration.cpp @@ -26,7 +26,7 @@ using namespace js::gc; #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1 #define WRAPPED_ASYNC_UNWRAPPED_SLOT 0 -// Async Iteration proposal 2.3.10 Runtime Semantics: EvaluateBody. +// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody. static bool WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp) { @@ -128,11 +128,7 @@ js::GetUnwrappedAsyncGenerator(JSFunction* wrapped) return unwrapped; } -static MOZ_MUST_USE bool -AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, - CompletionKind completionKind, HandleValue argument); - -// Async Iteration proposal 5.1.1 Await Fulfilled Functions. +// Async Iteration proposal 4.1.1 Await Fulfilled Functions. MOZ_MUST_USE bool js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle asyncGenObj, HandleValue value) @@ -140,7 +136,7 @@ js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value); } -// Async Iteration proposal 5.1.2 Await Rejected Functions. +// Async Iteration proposal 4.1.2 Await Rejected Functions. MOZ_MUST_USE bool js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle asyncGenObj, HandleValue reason) @@ -148,12 +144,30 @@ js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle a 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 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 asyncGenObj, + HandleValue reason) +{ + return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason); +} + const Class AsyncFromSyncIteratorObject::class_ = { "AsyncFromSyncIteratorObject", JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots) }; -// Async Iteration proposal 6.1.3.1. +// Async Iteration proposal 11.1.3.1. JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) { @@ -164,7 +178,7 @@ js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter) return AsyncFromSyncIteratorObject::create(cx, iter); } -// Async Iteration proposal 6.1.3.1 steps 2-4. +// Async Iteration proposal 11.1.3.1 steps 2-4. /* static */ JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) { @@ -187,7 +201,7 @@ AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter) return asyncIter; } -// Async Iteration proposal 6.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. +// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next. static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) { @@ -195,7 +209,7 @@ AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal); } -// Async Iteration proposal 6.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. +// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return. static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) { @@ -203,7 +217,7 @@ AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return); } -// Async Iteration proposal 6.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. +// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw. static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) { @@ -211,7 +225,7 @@ AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp) return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw); } -// Async Iteration proposal 6.4.1.2 AsyncGenerator.prototype.next. +// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next. static bool AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) { @@ -222,7 +236,7 @@ AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) args.rval()); } -// Async Iteration proposal 6.4.1.3 AsyncGenerator.prototype.return. +// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return. static bool AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) { @@ -233,7 +247,7 @@ AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) args.rval()); } -// Async Iteration proposal 6.4.1.4 AsyncGenerator.prototype.throw. +// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw. static bool AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) { @@ -371,7 +385,7 @@ const Class AsyncGeneratorRequest::class_ = { JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots) }; -// Async Iteration proposal 6.4.3.1. +// Async Iteration proposal 11.4.3.1. /* static */ AsyncGeneratorRequest* AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, HandleValue completionValue_, HandleObject promise_) @@ -387,7 +401,7 @@ AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_, return request; } -// Async Iteration proposal 6.4.3.2 steps 5.d-g. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g. static MOZ_MUST_USE bool AsyncGeneratorReturned(JSContext* cx, Handle asyncGenObj, HandleValue value) @@ -402,7 +416,7 @@ AsyncGeneratorReturned(JSContext* cx, Handle asyncGenObj, return AsyncGeneratorResolve(cx, asyncGenObj, value, true); } -// Async Iteration proposal 6.4.3.2 steps 5.d, f. +// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f. static MOZ_MUST_USE bool AsyncGeneratorThrown(JSContext* cx, Handle asyncGenObj) { @@ -422,85 +436,33 @@ AsyncGeneratorThrown(JSContext* cx, Handle asyncGenObj) return AsyncGeneratorReject(cx, asyncGenObj, value); } -// Async Iteration proposal 6.4.3.5. -MOZ_MUST_USE bool -js::AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj) -{ - // Step 1 (implicit). - - // Steps 2-3. - MOZ_ASSERT(!asyncGenObj->isExecuting()); - - // Steps 4-5. - if (asyncGenObj->isQueueEmpty()) - return true; - - // Steps 6-7. - Rooted request( - cx, AsyncGeneratorObject::peekRequest(cx, asyncGenObj)); - if (!request) - return false; - - // Step 8. - CompletionKind completionKind = request->completionKind(); - - // Step 9. - if (completionKind != CompletionKind::Normal) { - // Step 9.a. - if (asyncGenObj->isSuspendedStart()) - asyncGenObj->setCompleted(); - - // Step 9.b. - if (asyncGenObj->isCompleted()) { - // Step 9.b.i. - RootedValue value(cx, request->completionValue()); - if (completionKind == CompletionKind::Return) - return AsyncGeneratorResolve(cx, asyncGenObj, value, true); - // Step 9.b.ii. - return AsyncGeneratorReject(cx, asyncGenObj, value); - } - } else if (asyncGenObj->isCompleted()) { - // Step 10. - return AsyncGeneratorResolve(cx, asyncGenObj, UndefinedHandleValue, true); - } - - // Step 11. - MOZ_ASSERT(asyncGenObj->isSuspendedStart() || asyncGenObj->isSuspendedYield()); - - // Step 15 (reordered). - asyncGenObj->setExecuting(); - - RootedValue argument(cx, request->completionValue()); - - // Steps 12-14, 16-20. - return AsyncGeneratorResume(cx, asyncGenObj, completionKind, argument); -} - -// Async Iteration proposal 6.2.1.3 (partially). +// Async Iteration proposal 11.4.3.7 (partially). // Most steps are done in generator. static MOZ_MUST_USE bool -AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, - HandleValue value) +AsyncGeneratorYield(JSContext* cx, Handle asyncGenObj, HandleValue value) { - // Step 5. + // Step 5 is done in bytecode. + + // Step 6. asyncGenObj->setSuspendedYield(); - // Step 8. + // Step 9. return AsyncGeneratorResolve(cx, asyncGenObj, value, false); } -// Async Iteration proposal 6.4.3.5 steps 12-14, 16-20. -// Async Iteration proposal 6.2.1.2 step 10. -// Async Iteration proposal 6.4.3.2 step 5.f-g. -// Async Iteration proposal 5.1 steps 2-9. +// 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. -static MOZ_MUST_USE bool -AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, - CompletionKind completionKind, HandleValue argument) +MOZ_MUST_USE bool +js::AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument) { RootedValue generatorVal(cx, asyncGenObj->generatorVal()); - // 6.4.3.5 steps 12-14, 16-20. + // 11.4.3.5 steps 12-14, 16-20. HandlePropertyName funName = completionKind == CompletionKind::Normal ? cx->names().StarGeneratorNext : completionKind == CompletionKind::Throw @@ -510,10 +472,11 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, args[0].set(argument); RootedValue result(cx); if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) { - // 6.4.3.2 step 5.d, f. + // 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); @@ -525,8 +488,10 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, // 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. - // 2.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. + // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix. RootedObject resultObj(cx, &result.toObject()); RootedValue value(cx); @@ -536,7 +501,7 @@ AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, if (asyncGenObj->generatorObj()->isAfterYield()) return AsyncGeneratorYield(cx, asyncGenObj, value); - // 6.4.3.2 step 5.d-g. + // 11.4.3.2 step 5.d-g. return AsyncGeneratorReturned(cx, asyncGenObj, value); } @@ -565,14 +530,14 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject()) return true; - // Async Iteration proposal 6.1.2 %AsyncIteratorPrototype%. + // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%. RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype(cx, global)); if (!asyncIterProto) return false; if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods)) return false; - // Async Iteration proposal 6.1.3.2 %AsyncFromSyncIteratorPrototype%. + // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%. RootedObject asyncFromSyncIterProto( cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, asyncIterProto)); @@ -585,7 +550,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) return false; } - // Async Iteration proposal 6.4.1 %AsyncGeneratorPrototype%. + // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%. RootedObject asyncGenProto( cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_, asyncIterProto)); @@ -597,7 +562,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) return false; } - // Async Iteration proposal 6.3.3 %AsyncGenerator%. + // Async Iteration proposal 11.3.3 %AsyncGenerator%. RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global)); if (!asyncGenerator) return false; @@ -616,7 +581,7 @@ GlobalObject::initAsyncGenerators(JSContext* cx, Handle global) RootedObject proto(cx, &function.toObject()); RootedAtom name(cx, cx->names().AsyncGeneratorFunction); - // Async Iteration proposal 6.3.2 %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)); diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h index 974c209a01..58c43131b4 100644 --- a/js/src/vm/AsyncIteration.h +++ b/js/src/vm/AsyncIteration.h @@ -38,11 +38,18 @@ GetUnwrappedAsyncGenerator(JSFunction* wrapped); MOZ_MUST_USE bool AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle asyncGenObj, - HandleValue value); - + HandleValue value); MOZ_MUST_USE bool AsyncGeneratorAwaitedRejected(JSContext* cx, Handle asyncGenObj, - HandleValue reason); + HandleValue reason); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx, + Handle asyncGenObj, + HandleValue value); +MOZ_MUST_USE bool +AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx, + Handle asyncGenObj, + HandleValue reason); class AsyncGeneratorRequest : public NativeObject { @@ -97,6 +104,12 @@ class AsyncGeneratorObject : public NativeObject 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 }; @@ -155,6 +168,12 @@ class AsyncGeneratorObject : public NativeObject 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; } @@ -168,6 +187,12 @@ class AsyncGeneratorObject : public NativeObject void setExecuting() { setState(State_Executing); } + void setAwaitingYieldReturn() { + setState(State_AwaitingYieldReturn); + } + void setAwaitingReturn() { + setState(State_AwaitingReturn); + } void setCompleted() { setState(State_Completed); } @@ -223,7 +248,8 @@ class AsyncFromSyncIteratorObject : public NativeObject }; MOZ_MUST_USE bool -AsyncGeneratorResumeNext(JSContext* cx, Handle asyncGenObj); +AsyncGeneratorResume(JSContext* cx, Handle asyncGenObj, + CompletionKind completionKind, HandleValue argument); } // namespace js From 586cd0b71b31ca591ca94c420020b5af9c257cb5 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 10:32:39 -0500 Subject: [PATCH 27/32] Bug 1379525 - Part 2: Properly handle rejection in async-from-sync iteration. Tag #1287 --- js/src/builtin/Promise.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index 62c5eb4295..4900a91cb5 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -2295,6 +2295,9 @@ static MOZ_MUST_USE bool InternalAwait(JSContext* cx, HandleValue value, HandleObject resultPromise, HandleValue onFulfilled, HandleValue onRejected, T extraStep) { + MOZ_ASSERT(onFulfilled.isNumber() || onFulfilled.isObject()); + MOZ_ASSERT(onRejected.isNumber() || onRejected.isObject()); + // Step 2. Rooted promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx)); if (!promise) @@ -2483,11 +2486,12 @@ js::AsyncFromSyncIteratorMethod(JSContext* cx, CallArgs& args, CompletionKind co RootedValue onFulfilled(cx, Int32Value(done ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone)); + RootedValue onRejected(cx, Int32Value(PromiseHandlerThrower)); // Steps 11-12, 15. auto extra = [](Handle reaction) { }; - if (!InternalAwait(cx, value, resultPromise, onFulfilled, UndefinedHandleValue, extra)) + if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra)) return false; // Step 16. From f08c44f010ffbcc3ec503d2069b49692cd92ffdf Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 10:35:26 -0500 Subject: [PATCH 28/32] Bug 1390082 - Implement AsyncGeneratorQueue with simpler array operations. Tag #1287 Note: Without ReadableStream implementation --- js/src/vm/AsyncIteration.cpp | 62 ++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp index 256f247b09..3001793745 100644 --- a/js/src/vm/AsyncIteration.cpp +++ b/js/src/vm/AsyncIteration.cpp @@ -315,6 +315,53 @@ AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue 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 asyncGenObj, Handle request) @@ -339,11 +386,8 @@ AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handlequeue()); - - FixedInvokeArgs<1> args(cx); - args[0].setObject(*request); - args.setThis(ObjectValue(*queue)); - return CallJSNative(cx, array_push, args); + RootedValue requestVal(cx, ObjectValue(*request)); + return InternalEnqueue(cx, queue, requestVal); } /* static */ AsyncGeneratorRequest* @@ -356,13 +400,11 @@ AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handlequeue()); - - FixedInvokeArgs<0> args(cx); - args.setThis(ObjectValue(*queue)); - if (!CallJSNative(cx, array_shift, args)) + RootedValue requestVal(cx); + if (!InternalDequeue(cx, queue, &requestVal)) return nullptr; - return &args.rval().toObject().as(); + return &requestVal.toObject().as(); } /* static */ AsyncGeneratorRequest* From b476f2e713f43808a8bfed2dfc1f85a7f290a56b Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 14 Dec 2019 10:42:18 -0500 Subject: [PATCH 29/32] Bug 1352312 - Enable Async Iteration. Tag #1287 --- js/src/frontend/Parser.cpp | 20 -------------------- js/src/js.msg | 1 - 2 files changed, 21 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 2d2f17fd1b..cf9f1e27c9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3785,12 +3785,6 @@ Parser::functionStmt(uint32_t toStringStart, YieldHandling yieldHa GeneratorKind generatorKind = NotGenerator; if (tt == TOK_MUL) { -#ifdef RELEASE_OR_BETA - if (asyncKind != SyncFunction) { - error(JSMSG_ASYNC_GENERATOR); - return null(); - } -#endif generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); @@ -3859,12 +3853,6 @@ Parser::functionExpr(uint32_t toStringStart, InvokedPrediction inv return null(); if (tt == TOK_MUL) { -#ifdef RELEASE_OR_BETA - if (asyncKind != SyncFunction) { - error(JSMSG_ASYNC_GENERATOR); - return null(); - } -#endif generatorKind = StarGenerator; if (!tokenStream.getToken(&tt)) return null(); @@ -6156,7 +6144,6 @@ Parser::forStatement(YieldHandling yieldHandling) } } -#ifndef RELEASE_OR_BETA if (pc->isAsync()) { bool matched; if (!tokenStream.matchToken(&matched, TOK_AWAIT)) @@ -6167,7 +6154,6 @@ Parser::forStatement(YieldHandling yieldHandling) iterKind = IteratorKind::Async; } } -#endif MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); @@ -9520,12 +9506,6 @@ Parser::propertyName(YieldHandling yieldHandling, } if (ltok == TOK_MUL) { -#ifdef RELEASE_OR_BETA - if (isAsync) { - error(JSMSG_ASYNC_GENERATOR); - return null(); - } -#endif isGenerator = true; if (!tokenStream.getToken(<ok)) return null(); diff --git a/js/src/js.msg b/js/src/js.msg index f8e5358bb3..9dc5f4e9f9 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -185,7 +185,6 @@ MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 0, JSEXN_SYNTAXERR, "invalid array compre MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 0, JSEXN_INTERNALERR, "array initializer too large") MSG_DEF(JSMSG_AS_AFTER_IMPORT_STAR, 0, JSEXN_SYNTAXERR, "missing keyword 'as' after import *") MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD, 1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'") -MSG_DEF(JSMSG_ASYNC_GENERATOR, 0, JSEXN_SYNTAXERR, "generator function or method can't be async") MSG_DEF(JSMSG_AWAIT_IN_DEFAULT, 0, JSEXN_SYNTAXERR, "await can't be used in default expression") MSG_DEF(JSMSG_AWAIT_OUTSIDE_ASYNC, 0, JSEXN_SYNTAXERR, "await is only valid in async functions") MSG_DEF(JSMSG_BAD_ARROW_ARGS, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)") From 85d4d59f0497bbd2a44316f262d1483ba0bfdd54 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Thu, 12 Dec 2019 22:26:46 -0500 Subject: [PATCH 30/32] Bug 1454285 - Part 1: Specify the current scope when emitting await and .generator. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 106 +++++++++++++++++----------- js/src/frontend/BytecodeEmitter.h | 26 +++++-- 2 files changed, 82 insertions(+), 50 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index e5afda0df9..862eaa4877 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1988,6 +1988,8 @@ class MOZ_STACK_CLASS IfThenElseEmitter class ForOfLoopControl : public LoopControl { + using EmitterScope = BytecodeEmitter::EmitterScope; + // The stack depth of the iterator. int32_t iterDepth_; @@ -2080,8 +2082,8 @@ class ForOfLoopControl : public LoopControl MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_)); if (!bce->emitDupAt(slotFromTop)) // ITER ... EXCEPTION ITER return false; - if (!emitIteratorClose(bce, CompletionKind::Throw)) // ITER ... EXCEPTION - return false; + if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Throw)) + return false; // ITER ... EXCEPTION if (!ifIteratorIsNotClosed.emitEnd()) // ITER ... EXCEPTION return false; @@ -2104,8 +2106,8 @@ class ForOfLoopControl : public LoopControl return false; if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER return false; - if (!emitIteratorClose(bce, CompletionKind::Normal)) // ITER ... FTYPE FVALUE - return false; + if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Normal)) + return false; // ITER ... FTYPE FVALUE if (!ifGeneratorClosing.emitEnd()) // ITER ... FTYPE FVALUE return false; } @@ -2119,16 +2121,27 @@ class ForOfLoopControl : public LoopControl return true; } - bool emitIteratorClose(BytecodeEmitter* bce, - CompletionKind completionKind = CompletionKind::Normal) { + bool emitIteratorCloseInInnermostScope(BytecodeEmitter* bce, + CompletionKind completionKind = CompletionKind::Normal) { + return emitIteratorCloseInScope(bce, *bce->innermostEmitterScope, completionKind); + } + + bool emitIteratorCloseInScope(BytecodeEmitter* bce, + EmitterScope& currentScope, + CompletionKind completionKind = CompletionKind::Normal) { ptrdiff_t start = bce->offset(); - if (!bce->emitIteratorClose(iterKind_, completionKind, allowSelfHosted_)) + if (!bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind, + allowSelfHosted_)) + { return false; + } ptrdiff_t end = bce->offset(); return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end); } - bool emitPrepareForNonLocalJump(BytecodeEmitter* bce, bool isTarget) { + bool emitPrepareForNonLocalJumpFromScope(BytecodeEmitter* bce, + EmitterScope& currentScope, + bool isTarget) { // Pop unnecessary values from the stack. Effectively this means // leaving try-catch block. However, the performing IteratorClose can // reach the depth for try-catch, and effectively re-enter the @@ -2145,7 +2158,7 @@ class ForOfLoopControl : public LoopControl if (!bce->emit1(JSOP_SWAP)) // UNDEF ITER return false; - if (!emitIteratorClose(bce)) // UNDEF + if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) // UNDEF return false; if (isTarget) { @@ -2812,8 +2825,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta return false; ForOfLoopControl& loopinfo = control->as(); - if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ... + if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es, + /* isTarget = */ false)) + { // ... return false; + } } else { npops += 3; } @@ -2840,8 +2856,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta if (target && emitIteratorCloseAtTarget && target->is()) { ForOfLoopControl& loopinfo = target->as(); - if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ true)) // ... UNDEF UNDEF UNDEF + if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es, + /* isTarget = */ true)) + { // ... UNDEF UNDEF UNDEF return false; + } } EmitterScope* targetEmitterScope = target ? target->emitterScope() : bce_->varEmitterScope; @@ -5267,7 +5286,7 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = Iter return false; if (iterKind == IteratorKind::Async) { - if (!emitAwait()) // ... RESULT + if (!emitAwaitInInnermostScope()) // ... RESULT return false; } @@ -5278,9 +5297,10 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, IteratorKind iterKind /* = Iter } bool -BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync */, - CompletionKind completionKind /* = CompletionKind::Normal */, - bool allowSelfHosted /* = false */) +BytecodeEmitter::emitIteratorCloseInScope(EmitterScope& currentScope, + IteratorKind iterKind /* = IteratorKind::Sync */, + CompletionKind completionKind /* = CompletionKind::Normal */, + bool allowSelfHosted /* = false */) { MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting, ".close() on iterators is prohibited in self-hosted code because it " @@ -5372,7 +5392,7 @@ BytecodeEmitter::emitIteratorClose(IteratorKind iterKind /* = IteratorKind::Sync if (!emit1(JSOP_SWAP)) // ... ... RVAL RESULT return false; } - if (!emitAwait()) // ... ... RVAL? RESULT + if (!emitAwaitInScope(currentScope)) // ... ... RVAL? RESULT return false; } @@ -5636,7 +5656,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // For an empty pattern [], call IteratorClose unconditionally. Nothing // else needs to be done. if (!pattern->pn_head) - return emitIteratorClose(); // ... OBJ + return emitIteratorCloseInInnermostScope(); // ... OBJ // Push an initial FALSE value for DONE. if (!emit1(JSOP_FALSE)) // ... OBJ ITER FALSE @@ -5837,7 +5857,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav return false; if (!ifDone.emitElse()) // ... OBJ ITER return false; - if (!emitIteratorClose()) // ... OBJ + if (!emitIteratorCloseInInnermostScope()) // ... OBJ return false; if (!ifDone.emitEnd()) return false; @@ -8582,7 +8602,7 @@ BytecodeEmitter::emitReturn(ParseNode* pn) bool isAsyncGenerator = sc->asFunctionBox()->isAsync() && sc->asFunctionBox()->isStarGenerator(); if (isAsyncGenerator) { - if (!emitAwait()) + if (!emitAwaitInInnermostScope()) return false; } } else { @@ -8658,9 +8678,9 @@ BytecodeEmitter::emitReturn(ParseNode* pn) } bool -BytecodeEmitter::emitGetDotGenerator() +BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope) { - NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator); + NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator, ¤tScope); return emitGetNameAtLocation(cx->names().dotGenerator, loc); } @@ -8701,7 +8721,7 @@ BytecodeEmitter::emitYield(ParseNode* pn) // 11.4.3.7 AsyncGeneratorYield step 5. bool isAsyncGenerator = sc->asFunctionBox()->isAsync(); if (isAsyncGenerator) { - if (!emitAwait()) // RESULT + if (!emitAwaitInInnermostScope()) // RESULT return false; } @@ -8710,7 +8730,7 @@ BytecodeEmitter::emitYield(ParseNode* pn) return false; } - if (!emitGetDotGenerator()) + if (!emitGetDotGeneratorInInnermostScope()) return false; if (!emitYieldOp(JSOP_YIELD)) @@ -8720,24 +8740,24 @@ BytecodeEmitter::emitYield(ParseNode* pn) } bool -BytecodeEmitter::emitAwait() -{ - if (!emitGetDotGenerator()) - return false; - if (!emitYieldOp(JSOP_AWAIT)) - return false; - return true; -} - -bool -BytecodeEmitter::emitAwait(ParseNode* pn) +BytecodeEmitter::emitAwaitInInnermostScope(ParseNode* pn) { MOZ_ASSERT(sc->isFunctionBox()); MOZ_ASSERT(pn->getOp() == JSOP_AWAIT); if (!emitTree(pn->pn_kid)) return false; - return emitAwait(); + return emitAwaitInInnermostScope(); +} + +bool +BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope) +{ + if (!emitGetDotGeneratorInScope(currentScope)) + return false; + if (!emitYieldOp(JSOP_AWAIT)) + return false; + return true; } bool @@ -8779,12 +8799,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) // 11.4.3.7 AsyncGeneratorYield step 5. if (isAsyncGenerator) { - if (!emitAwait()) // ITER RESULT + if (!emitAwaitInInnermostScope()) // NEXT ITER RESULT return false; } // Load the generator object. - if (!emitGetDotGenerator()) // ITER RESULT GENOBJ + if (!emitGetDotGeneratorInInnermostScope()) // NEXT ITER RESULT GENOBJ return false; // Yield RESULT as-is, without re-boxing. @@ -8821,7 +8841,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) // If the iterator does not have a "throw" method, it calls IteratorClose // and then throws a TypeError. IteratorKind iterKind = isAsyncGenerator ? IteratorKind::Async : IteratorKind::Sync; - if (!emitIteratorClose(iterKind)) + if (!emitIteratorCloseInInnermostScope(iterKind)) // NEXT ITER RESULT EXCEPTION return false; if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw return false; @@ -8839,7 +8859,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) checkTypeSet(JSOP_CALL); if (isAsyncGenerator) { - if (!emitAwait()) // ITER OLDRESULT RESULT + if (!emitAwaitInInnermostScope()) // NEXT ITER OLDRESULT RESULT return false; } @@ -8910,7 +8930,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) checkTypeSet(JSOP_CALL); if (iterKind == IteratorKind::Async) { - if (!emitAwait()) // ... FTYPE FVALUE RESULT + if (!emitAwaitInInnermostScope()) // ... FTYPE FVALUE RESULT return false; } @@ -8991,7 +9011,7 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter) checkTypeSet(JSOP_CALL); if (isAsyncGenerator) { - if (!emitAwait()) // ITER RESULT RESULT + if (!emitAwaitInInnermostScope()) // NEXT ITER RESULT RESULT return false; } @@ -10547,7 +10567,7 @@ BytecodeEmitter::emitFunctionBody(ParseNode* funBody) if (!emit1(JSOP_SETRVAL)) return false; - if (!emitGetDotGenerator()) + if (!emitGetDotGeneratorInInnermostScope()) return false; // No need to check for finally blocks, etc as in EmitReturn. @@ -10827,7 +10847,7 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage:: break; case PNK_AWAIT: - if (!emitAwait(pn)) + if (!emitAwaitInInnermostScope(pn)) return false; break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 555c979bf5..32431cfd11 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -609,15 +609,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool iteratorResultShape(unsigned* shape); MOZ_MUST_USE bool emitToIteratorResult(bool done); - MOZ_MUST_USE bool emitGetDotGenerator(); + MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() { + return emitGetDotGeneratorInScope(*innermostEmitterScope); + } + MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope); MOZ_MUST_USE bool emitInitialYield(ParseNode* pn); MOZ_MUST_USE bool emitYield(ParseNode* pn); MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); - MOZ_MUST_USE bool emitAwait(); - MOZ_MUST_USE bool emitAwait(ParseNode* pn); - + MOZ_MUST_USE bool emitAwaitInInnermostScope() { + return emitAwaitInScope(*innermostEmitterScope); + } + MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn); + MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope); MOZ_MUST_USE bool emitPropLHS(ParseNode* pn); MOZ_MUST_USE bool emitPropOp(ParseNode* pn, JSOp op); MOZ_MUST_USE bool emitPropIncDec(ParseNode* pn); @@ -717,9 +722,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter // onto the stack. MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync, bool allowSelfHosted = false); - MOZ_MUST_USE bool emitIteratorClose(IteratorKind iterKind = IteratorKind::Sync, - CompletionKind completionKind = CompletionKind::Normal, - bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorCloseInScope(EmitterScope& currentScope, + IteratorKind iterKind = IteratorKind::Sync, + CompletionKind completionKind = CompletionKind::Normal, + bool allowSelfHosted = false); + MOZ_MUST_USE bool emitIteratorCloseInInnermostScope(IteratorKind iterKind = IteratorKind::Sync, + CompletionKind completionKind = CompletionKind::Normal, + bool allowSelfHosted = false) { + return emitIteratorCloseInScope(*innermostEmitterScope, iterKind, completionKind, + allowSelfHosted); + } template MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth, From d16b90c09994d4a2d447f110ce7e7f18600f69e5 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Thu, 12 Dec 2019 22:41:21 -0500 Subject: [PATCH 31/32] Bug 1454285 - Part 2: Disallow using innermostEmitterScope while the value does not match the bytecode environment. Tag #1287 --- js/src/frontend/BytecodeEmitter.cpp | 59 ++++++++++++++++------------- js/src/frontend/BytecodeEmitter.h | 49 +++++++++++++++++++++--- 2 files changed, 76 insertions(+), 32 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 862eaa4877..a9dd32c96f 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -100,7 +100,7 @@ class BytecodeEmitter::NestableControl : public Nestable(&bce->innermostNestableControl), kind_(kind), - emitterScope_(bce->innermostEmitterScope) + emitterScope_(bce->innermostEmitterScopeNoCheck()) { } public: @@ -426,7 +426,7 @@ class BytecodeEmitter::EmitterScope : public Nestableparent) { *bce = (*bce)->parent; - return (*bce)->innermostEmitterScope; + return (*bce)->innermostEmitterScopeNoCheck(); } return nullptr; @@ -460,7 +460,7 @@ class BytecodeEmitter::EmitterScope : public Nestable(&bce->innermostEmitterScope), + : Nestable(&bce->innermostEmitterScope_), nameCache_(bce->cx->frontendCollectionPool()), hasEnvironment_(false), environmentChainLength_(0), @@ -866,7 +866,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind Handle bindings) { MOZ_ASSERT(kind != ScopeKind::NamedLambda && kind != ScopeKind::StrictNamedLambda); - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -935,7 +935,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind bool BytecodeEmitter::EmitterScope::enterNamedLambda(BytecodeEmitter* bce, FunctionBox* funbox) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); MOZ_ASSERT(funbox->namedLambdaBindings()); if (!ensureCache(bce)) @@ -1002,7 +1002,7 @@ BytecodeEmitter::EmitterScope::enterComprehensionFor(BytecodeEmitter* bce, bool BytecodeEmitter::EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -1035,7 +1035,7 @@ BytecodeEmitter::EmitterScope::enterParameterExpressionVar(BytecodeEmitter* bce) bool BytecodeEmitter::EmitterScope::enterFunction(BytecodeEmitter* bce, FunctionBox* funbox) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); // If there are parameter expressions, there is an extra var scope. if (!funbox->hasExtraBodyVarScope()) @@ -1126,7 +1126,7 @@ BytecodeEmitter::EmitterScope::enterFunctionExtraBodyVar(BytecodeEmitter* bce, F MOZ_ASSERT(funbox->hasParameterExprs); MOZ_ASSERT(funbox->extraVarScopeBindings() || funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings()); - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); // The extra var scope is never popped once it's entered. It replaces the // function scope as the var emitter scope. @@ -1212,7 +1212,7 @@ class DynamicBindingIter : public BindingIter bool BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedContext* globalsc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1272,7 +1272,7 @@ BytecodeEmitter::EmitterScope::enterGlobal(BytecodeEmitter* bce, GlobalSharedCon bool BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext* evalsc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1327,7 +1327,7 @@ BytecodeEmitter::EmitterScope::enterEval(BytecodeEmitter* bce, EvalSharedContext bool BytecodeEmitter::EmitterScope::enterModule(BytecodeEmitter* bce, ModuleSharedContext* modulesc) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); bce->setVarEmitterScope(this); @@ -1384,7 +1384,7 @@ BytecodeEmitter::EmitterScope::enterModule(BytecodeEmitter* bce, ModuleSharedCon bool BytecodeEmitter::EmitterScope::enterWith(BytecodeEmitter* bce) { - MOZ_ASSERT(this == bce->innermostEmitterScope); + MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck()); if (!ensureCache(bce)) return false; @@ -1412,7 +1412,7 @@ BytecodeEmitter::EmitterScope::leave(BytecodeEmitter* bce, bool nonLocal) { // If we aren't leaving the scope due to a non-local jump (e.g., break), // we must be the innermost scope. - MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScope); + MOZ_ASSERT_IF(!nonLocal, this == bce->innermostEmitterScopeNoCheck()); ScopeKind kind = scope(bce)->kind(); switch (kind) { @@ -2123,7 +2123,7 @@ class ForOfLoopControl : public LoopControl bool emitIteratorCloseInInnermostScope(BytecodeEmitter* bce, CompletionKind completionKind = CompletionKind::Normal) { - return emitIteratorCloseInScope(bce, *bce->innermostEmitterScope, completionKind); + return emitIteratorCloseInScope(bce, *bce->innermostEmitterScope(), completionKind); } bool emitIteratorCloseInScope(BytecodeEmitter* bce, @@ -2201,8 +2201,11 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent, bodyScopeIndex(UINT32_MAX), varEmitterScope(nullptr), innermostNestableControl(nullptr), - innermostEmitterScope(nullptr), + innermostEmitterScope_(nullptr), innermostTDZCheckCache(nullptr), +#ifdef DEBUG + unstableEmitterScope(false), +#endif constList(cx), scopeList(cx), tryNoteList(cx), @@ -2259,13 +2262,13 @@ BytecodeEmitter::findInnermostNestableControl(Predicate predicate) const NameLocation BytecodeEmitter::lookupName(JSAtom* name) { - return innermostEmitterScope->lookup(this, name); + return innermostEmitterScope()->lookup(this, name); } Maybe BytecodeEmitter::locationOfNameBoundInScope(JSAtom* name, EmitterScope* target) { - return innermostEmitterScope->locationBoundInScope(this, name, target); + return innermostEmitterScope()->locationBoundInScope(this, name, target); } Maybe @@ -2726,7 +2729,7 @@ class NonLocalExitControl : bce_(bce), savedScopeNoteIndex_(bce->scopeNoteList.length()), savedDepth_(bce->stackDepth), - openScopeNoteIndex_(bce->innermostEmitterScope->noteIndex()), + openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()), kind_(kind) { } @@ -2772,9 +2775,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta using NestableControl = BytecodeEmitter::NestableControl; using EmitterScope = BytecodeEmitter::EmitterScope; - EmitterScope* es = bce_->innermostEmitterScope; + EmitterScope* es = bce_->innermostEmitterScope(); int npops = 0; + AutoCheckUnstableEmitterScope cues(bce_); + // For 'continue', 'break', and 'return' statements, emit IteratorClose // bytecode inline. 'continue' statements do not call IteratorClose for // the loop they are continuing. @@ -2895,7 +2900,7 @@ BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist, SrcNoteTy Scope* BytecodeEmitter::innermostScope() const { - return innermostEmitterScope->scope(this); + return innermostEmitterScope()->scope(this); } bool @@ -3555,7 +3560,7 @@ BytecodeEmitter::needsImplicitThis() return true; // Otherwise see if the current point is under a 'with'. - for (EmitterScope* es = innermostEmitterScope; es; es = es->enclosingInFrame()) { + for (EmitterScope* es = innermostEmitterScope(); es; es = es->enclosingInFrame()) { if (es->scope(this)->kind() == ScopeKind::With) return true; } @@ -5206,7 +5211,7 @@ BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, Destructuri // destructuring declaration needs to initialize the name in // the function scope. The innermost scope is the var scope, // and its enclosing scope is the function scope. - EmitterScope* funScope = innermostEmitterScope->enclosingInFrame(); + EmitterScope* funScope = innermostEmitterScope()->enclosingInFrame(); NameLocation paramLoc = *locationOfNameBoundInScope(name, funScope); if (!emitSetOrInitializeNameAtLocation(name, paramLoc, emitSwapScopeAndRhs, true)) return false; @@ -7308,7 +7313,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte // bindings inducing an environment, recreate the current environment. DebugOnly forOfTarget = forOfHead->pn_kid1; MOZ_ASSERT(forOfTarget->isKind(PNK_LET) || forOfTarget->isKind(PNK_CONST)); - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7490,7 +7495,7 @@ BytecodeEmitter::emitForIn(ParseNode* forInLoop, EmitterScope* headLexicalEmitte // it must be the innermost one. If that scope has closed-over // bindings inducing an environment, recreate the current environment. MOZ_ASSERT(forInTarget->isKind(PNK_LET) || forInTarget->isKind(PNK_CONST)); - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7619,7 +7624,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // exists for the head, it must be the innermost one. If that scope // has closed-over bindings inducing an environment, recreate the // current environment. - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -7667,7 +7672,7 @@ BytecodeEmitter::emitCStyleFor(ParseNode* pn, EmitterScope* headLexicalEmitterSc // ES 13.7.4.8 step 3.e. The per-iteration freshening. if (forLoopRequiresFreshening) { - MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope); + MOZ_ASSERT(headLexicalEmitterScope == innermostEmitterScope()); MOZ_ASSERT(headLexicalEmitterScope->scope(this)->kind() == ScopeKind::Lexical); if (headLexicalEmitterScope->hasEnvironment()) { @@ -10367,7 +10372,7 @@ BytecodeEmitter::emitFunctionFormalParameters(ParseNode* pn) { ParseNode* funBody = pn->last(); FunctionBox* funbox = sc->asFunctionBox(); - EmitterScope* funScope = innermostEmitterScope; + EmitterScope* funScope = innermostEmitterScope(); bool hasParameterExprs = funbox->hasParameterExprs; bool hasRest = funbox->hasRest(); diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 32431cfd11..77f77599c3 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -228,9 +228,23 @@ struct MOZ_STACK_CLASS BytecodeEmitter EmitterScope* varEmitterScope; NestableControl* innermostNestableControl; - EmitterScope* innermostEmitterScope; + EmitterScope* innermostEmitterScope_; TDZCheckCache* innermostTDZCheckCache; +#ifdef DEBUG + bool unstableEmitterScope; + + friend class AutoCheckUnstableEmitterScope; +#endif + + EmitterScope* innermostEmitterScope() const { + MOZ_ASSERT(!unstableEmitterScope); + return innermostEmitterScopeNoCheck(); + } + EmitterScope* innermostEmitterScopeNoCheck() const { + return innermostEmitterScope_; + } + CGConstList constList; /* constants to be included with the script */ CGObjectList objectList; /* list of emitted objects */ CGScopeList scopeList; /* list of emitted scopes */ @@ -319,7 +333,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter EmitterScope* source); mozilla::Maybe locationOfNameBoundInFunctionScope(JSAtom* name) { - return locationOfNameBoundInFunctionScope(name, innermostEmitterScope); + return locationOfNameBoundInFunctionScope(name, innermostEmitterScope()); } void setVarEmitterScope(EmitterScope* emitterScope) { @@ -610,7 +624,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitToIteratorResult(bool done); MOZ_MUST_USE bool emitGetDotGeneratorInInnermostScope() { - return emitGetDotGeneratorInScope(*innermostEmitterScope); + return emitGetDotGeneratorInScope(*innermostEmitterScope()); } MOZ_MUST_USE bool emitGetDotGeneratorInScope(EmitterScope& currentScope); @@ -619,7 +633,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitYieldOp(JSOp op); MOZ_MUST_USE bool emitYieldStar(ParseNode* iter); MOZ_MUST_USE bool emitAwaitInInnermostScope() { - return emitAwaitInScope(*innermostEmitterScope); + return emitAwaitInScope(*innermostEmitterScope()); } MOZ_MUST_USE bool emitAwaitInInnermostScope(ParseNode* pn); MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope); @@ -729,7 +743,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitIteratorCloseInInnermostScope(IteratorKind iterKind = IteratorKind::Sync, CompletionKind completionKind = CompletionKind::Normal, bool allowSelfHosted = false) { - return emitIteratorCloseInScope(*innermostEmitterScope, iterKind, completionKind, + return emitIteratorCloseInScope(*innermostEmitterScope(), iterKind, completionKind, allowSelfHosted); } @@ -829,6 +843,31 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false); }; +class MOZ_RAII AutoCheckUnstableEmitterScope { +#ifdef DEBUG + bool prev_; + BytecodeEmitter* bce_; +#endif + + public: + AutoCheckUnstableEmitterScope() = delete; + explicit AutoCheckUnstableEmitterScope(BytecodeEmitter* bce) +#ifdef DEBUG + : bce_(bce) +#endif + { +#ifdef DEBUG + prev_ = bce_->unstableEmitterScope; + bce_->unstableEmitterScope = true; +#endif + } + ~AutoCheckUnstableEmitterScope() { +#ifdef DEBUG + bce_->unstableEmitterScope = prev_; +#endif + } +}; + } /* namespace frontend */ } /* namespace js */ From 0b1090f40eb7f8abc77edb0dd7d1569feac9eeff Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Tue, 17 Dec 2019 19:06:03 -0500 Subject: [PATCH 32/32] No Issue - Fix indentation and account for system libevent in ipc/chromium/moz.build --- ipc/chromium/moz.build | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipc/chromium/moz.build b/ipc/chromium/moz.build index ba5f4e5126..dc5b4dca40 100644 --- a/ipc/chromium/moz.build +++ b/ipc/chromium/moz.build @@ -139,13 +139,14 @@ if os_bsd or os_linux or os_solaris: ] if os_solaris: - SOURCES += [ + SOURCES += [ 'src/base/atomicops_internals_x86_gcc.cc', 'src/base/process_util_linux.cc', 'src/base/time_posix.cc', ] - -LOCAL_INCLUDES += ['src/third_party/libevent/linux'] + +elif not CONFIG['MOZ_SYSTEM_LIBEVENT']: + LOCAL_INCLUDES += ['src/third_party/libevent/linux'] ost = CONFIG['OS_TEST'] if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost: