summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2019-12-17 21:47:18 +0000
committerGitHub <noreply@github.com>2019-12-17 21:47:18 +0000
commit07d0bcbf112a4e274905837c6ea0b0212b51e4e3 (patch)
tree3b43cb63b33d82d4965d402aca39028836983bb4
parente2de507e0261c9b138cd3cf5356c21eca3e7a28d (diff)
parent6c3e42ac6427fabaf83b5acc7877aa3d15117125 (diff)
downloadUXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar
UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.gz
UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.lz
UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.tar.xz
UXP-07d0bcbf112a4e274905837c6ea0b0212b51e4e3.zip
Merge pull request #1327 from g4jc/async_iteration
Implement Async Iteration in SpiderMonkey This resolves #1287
-rw-r--r--js/public/Class.h2
-rw-r--r--js/src/builtin/AsyncIteration.js7
-rw-r--r--js/src/builtin/Promise.cpp738
-rw-r--r--js/src/builtin/Promise.h22
-rw-r--r--js/src/builtin/ReflectParse.cpp20
-rw-r--r--js/src/doc/Debugger/Conventions.md4
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp13
-rw-r--r--js/src/frontend/BytecodeCompiler.h6
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp477
-rw-r--r--js/src/frontend/BytecodeEmitter.h91
-rw-r--r--js/src/frontend/FoldConstants.cpp25
-rw-r--r--js/src/frontend/FullParseHandler.h21
-rw-r--r--js/src/frontend/NameFunctions.cpp21
-rw-r--r--js/src/frontend/ParseNode.cpp28
-rw-r--r--js/src/frontend/ParseNode.h6
-rw-r--r--js/src/frontend/Parser.cpp129
-rw-r--r--js/src/frontend/Parser.h16
-rw-r--r--js/src/frontend/SharedContext.h27
-rw-r--r--js/src/frontend/SyntaxParseHandler.h6
-rw-r--r--js/src/jit-test/tests/debug/onExceptionUnwind-resumption-async.js99
-rw-r--r--js/src/jit/BaselineCompiler.cpp94
-rw-r--r--js/src/jit/BaselineCompiler.h11
-rw-r--r--js/src/jit/BaselineIC.cpp7
-rw-r--r--js/src/jit/BaselineJIT.cpp6
-rw-r--r--js/src/jit/BaselineJIT.h2
-rw-r--r--js/src/jit/CodeGenerator.cpp23
-rw-r--r--js/src/jit/CodeGenerator.h2
-rw-r--r--js/src/jit/Ion.cpp11
-rw-r--r--js/src/jit/IonAnalysis.cpp8
-rw-r--r--js/src/jit/IonBuilder.cpp36
-rw-r--r--js/src/jit/IonBuilder.h2
-rw-r--r--js/src/jit/Lowering.cpp16
-rw-r--r--js/src/jit/Lowering.h2
-rw-r--r--js/src/jit/MIR.h30
-rw-r--r--js/src/jit/MOpcodes.h2
-rw-r--r--js/src/jit/VMFunctions.cpp4
-rw-r--r--js/src/jit/shared/LIR-shared.h26
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h2
-rw-r--r--js/src/js.msg8
-rw-r--r--js/src/jsapi.cpp6
-rw-r--r--js/src/jsapi.h1
-rw-r--r--js/src/jsexn.cpp17
-rw-r--r--js/src/jsexn.h5
-rw-r--r--js/src/jsfriendapi.h1
-rw-r--r--js/src/jsfun.cpp120
-rw-r--r--js/src/jsfun.h19
-rw-r--r--js/src/jsiter.cpp28
-rw-r--r--js/src/jsiter.h6
-rw-r--r--js/src/jsopcode.h3
-rw-r--r--js/src/jsscript.cpp42
-rw-r--r--js/src/jsscript.h35
-rw-r--r--js/src/moz.build5
-rw-r--r--js/src/shell/js.cpp3
-rw-r--r--js/src/tests/ecma_2017/AsyncFunctions/properties.js76
-rw-r--r--js/src/tests/ecma_6/Generators/properties.js111
-rw-r--r--js/src/tests/ecma_6/Promise/self-resolve.js20
-rw-r--r--js/src/tests/ecma_6/Symbol/well-known.js3
-rw-r--r--js/src/tests/ecma_7/AsyncFunctions/syntax.js3
-rw-r--r--js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js15
-rw-r--r--js/src/vm/AsyncFunction.cpp31
-rw-r--r--js/src/vm/AsyncFunction.h3
-rw-r--r--js/src/vm/AsyncIteration.cpp644
-rw-r--r--js/src/vm/AsyncIteration.h256
-rw-r--r--js/src/vm/CommonPropertyNames.h4
-rw-r--r--js/src/vm/Debugger.cpp15
-rw-r--r--js/src/vm/EnvironmentObject.cpp21
-rw-r--r--js/src/vm/GeneratorObject.cpp56
-rw-r--r--js/src/vm/GeneratorObject.h60
-rw-r--r--js/src/vm/GlobalObject.cpp11
-rw-r--r--js/src/vm/GlobalObject.h47
-rw-r--r--js/src/vm/Interpreter.cpp32
-rw-r--r--js/src/vm/Interpreter.h3
-rw-r--r--js/src/vm/ObjectGroup.cpp2
-rw-r--r--js/src/vm/Opcodes.h34
-rw-r--r--js/src/vm/Probes-inl.h5
-rw-r--r--js/src/vm/SelfHosting.cpp111
-rw-r--r--js/src/vm/Stack-inl.h2
-rw-r--r--js/src/vm/Stack.cpp4
-rw-r--r--js/src/vm/Stack.h3
-rw-r--r--js/src/wasm/AsmJS.cpp5
80 files changed, 3191 insertions, 727 deletions
diff --git a/js/public/Class.h b/js/public/Class.h
index 67c0cbca8..f4fa9ccaf 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 + 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/AsyncIteration.js b/js/src/builtin/AsyncIteration.js
new file mode 100644
index 000000000..029c7c0f8
--- /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.cpp b/js/src/builtin/Promise.cpp
index 1d068f8c6..4900a91cb 100644
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -11,11 +11,13 @@
#include "mozilla/TimeStamp.h"
#include "jscntxt.h"
+#include "jsexn.h"
+#include "jsiter.h"
#include "gc/Heap.h"
#include "js/Debug.h"
#include "vm/AsyncFunction.h"
-#include "vm/SelfHosting.h"
+#include "vm/AsyncIteration.h"
#include "jsobjinlines.h"
@@ -31,10 +33,34 @@ 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,
+
+ // 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 {
ResolveMode,
@@ -47,9 +73,8 @@ enum ResolveFunctionSlots {
};
enum RejectFunctionSlots {
- RejectFunctionSlot_Promise = ResolveFunctionSlot_Promise,
+ RejectFunctionSlot_Promise = 0,
RejectFunctionSlot_ResolveFunction,
- RejectFunctionSlot_PromiseAllData = RejectFunctionSlot_ResolveFunction,
};
enum PromiseAllResolveElementFunctionSlots {
@@ -86,7 +111,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 +151,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;
}
@@ -177,13 +202,15 @@ enum ReactionRecordSlots {
ReactionRecordSlot_IncumbentGlobalObject,
ReactionRecordSlot_Flags,
ReactionRecordSlot_HandlerArg,
+ ReactionRecordSlot_Generator,
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 0x8
+#define REACTION_FLAG_ASYNC_GENERATOR 0x10
// ES2016, 25.4.1.2.
class PromiseReactionRecord : public NativeObject
@@ -210,14 +237,30 @@ class PromiseReactionRecord : public NativeObject
flags |= REACTION_FLAG_FULFILLED;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
}
- void setIsAwait() {
+ void setIsAsyncFunction() {
+ int32_t flags = this->flags();
+ flags |= REACTION_FLAG_ASYNC_FUNCTION;
+ setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
+ }
+ bool isAsyncFunction() {
int32_t flags = this->flags();
- flags |= REACTION_FLAG_AWAIT;
+ return flags & REACTION_FLAG_ASYNC_FUNCTION;
+ }
+ void setIsAsyncGenerator(Handle<AsyncGeneratorObject*> asyncGenObj) {
+ int32_t flags = this->flags();
+ flags |= REACTION_FLAG_ASYNC_GENERATOR;
setFixedSlot(ReactionRecordSlot_Flags, Int32Value(flags));
+
+ setFixedSlot(ReactionRecordSlot_Generator, ObjectValue(*asyncGenObj));
}
- bool isAwait() {
+ bool isAsyncGenerator() {
int32_t flags = this->flags();
- return flags & REACTION_FLAG_AWAIT;
+ return flags & REACTION_FLAG_ASYNC_GENERATOR;
+ }
+ AsyncGeneratorObject* asyncGenerator() {
+ MOZ_ASSERT(isAsyncGenerator());
+ return &getFixedSlot(ReactionRecordSlot_Generator).toObject()
+ .as<AsyncGeneratorObject>();
}
Value handler() {
MOZ_ASSERT(targetState() != JS::PromiseState::Pending);
@@ -277,12 +320,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));
@@ -319,28 +356,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<PromiseObject>() &&
- PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED))
+ promise->as<PromiseObject>().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,
@@ -351,16 +387,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);
@@ -398,10 +446,7 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
RootedFunction resolve(cx, &args.callee().as<JSFunction>());
RootedValue resolutionVal(cx, args.get(0));
- // Steps 1-2.
- RootedValue promiseVal(cx, resolve->getExtendedSlot(ResolveFunctionSlot_Promise));
-
- // Steps 3-4.
+ // 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.
@@ -410,43 +455,28 @@ ResolvePromiseFunction(JSContext* cx, unsigned argc, Value* vp)
return true;
}
- RootedObject promise(cx, &promiseVal.toObject());
+ // 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);
// 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<PromiseObject>() &&
- PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_RESOLVED))
+ promise->as<PromiseObject>().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(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();
- return status;
+ // Steps 6-13.
+ if (!ResolvePromiseInternal(cx, promise, resolutionVal))
+ return false;
+ args.rval().setUndefined();
+ return true;
}
static bool PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp);
@@ -652,7 +682,7 @@ enum GetCapabilitiesExecutorSlots {
};
static MOZ_MUST_USE PromiseObject*
-CreatePromiseObjectWithDefaultResolution(JSContext* cx)
+CreatePromiseObjectWithoutResolutionFunctions(JSContext* cx)
{
Rooted<PromiseObject*> promise(cx, CreatePromiseObjectInternal(cx));
if (!promise)
@@ -687,7 +717,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;
@@ -795,9 +825,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;
}
}
@@ -832,10 +860,10 @@ TriggerPromiseReactions(JSContext* cx, HandleValue reactionsVal, JS::PromiseStat
}
static MOZ_MUST_USE bool
-AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
- MutableHandleValue rval)
+AsyncFunctionPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
+ MutableHandleValue rval)
{
- MOZ_ASSERT(reaction->isAwait());
+ MOZ_ASSERT(reaction->isAsyncFunction());
RootedValue handlerVal(cx, reaction->handler());
RootedValue argument(cx, reaction->handlerArg());
@@ -843,15 +871,14 @@ AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> 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);
// Await's handlers don't return a value, nor throw exception.
// They fail only on OOM.
- if (handlerNum == PROMISE_HANDLER_AWAIT_FULFILLED) {
+ if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generatorVal, argument))
return false;
} else {
+ MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFunctionAwaitedRejected);
if (!AsyncFunctionAwaitedRejected(cx, resultPromise, generatorVal, argument))
return false;
}
@@ -860,6 +887,55 @@ AwaitPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
return true;
}
+static MOZ_MUST_USE bool
+AsyncGeneratorPromiseReactionJob(JSContext* cx, Handle<PromiseReactionRecord*> reaction,
+ MutableHandleValue rval)
+{
+ MOZ_ASSERT(reaction->isAsyncGenerator());
+
+ RootedValue handlerVal(cx, reaction->handler());
+ RootedValue argument(cx, reaction->handlerArg());
+ Rooted<AsyncGeneratorObject*> asyncGenObj(cx, reaction->asyncGenerator());
+
+ int32_t handlerNum = int32_t(handlerVal.toNumber());
+
+ // Await's handlers don't return a value, nor throw exception.
+ // They fail only on OOM.
+ if (handlerNum == PromiseHandlerAsyncGeneratorAwaitedFulfilled) {
+ // 4.1.1.
+ if (!AsyncGeneratorAwaitedFulfilled(cx, asyncGenObj, argument))
+ return false;
+ } 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();
+ return true;
+}
+
// ES2016, 25.4.2.1.
/**
* Callback triggering the fulfill/reject reaction for a resolved Promise,
@@ -903,8 +979,10 @@ PromiseReactionJob(JSContext* cx, unsigned argc, Value* vp)
// Steps 1-2.
Rooted<PromiseReactionRecord*> reaction(cx, &reactionObj->as<PromiseReactionRecord>());
- if (reaction->isAwait())
- return AwaitPromiseReactionJob(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());
@@ -919,13 +997,23 @@ 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 {
+ } else if (handlerNum == PromiseHandlerThrower) {
// Step 5.
- MOZ_ASSERT(handlerNum == PROMISE_HANDLER_THROWER);
resolutionMode = RejectMode;
handlerResult = argument;
+ } else {
+ MOZ_ASSERT(handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone ||
+ handlerNum == PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone);
+
+ 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;
+
+ handlerResult = ObjectValue(*resultObj);
}
} else {
// Step 6.
@@ -964,8 +1052,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 +1188,17 @@ GetResolveFunctionFromReject(JSFunction* reject)
{
MOZ_ASSERT(reject->maybeNative() == RejectPromiseFunction);
Value resolveFunVal = reject->getExtendedSlot(RejectFunctionSlot_ResolveFunction);
- if (IsNativeFunction(resolveFunVal, ResolvePromiseFunction))
- return &resolveFunVal.toObject().as<JSFunction>();
+ MOZ_ASSERT(IsNativeFunction(resolveFunVal, ResolvePromiseFunction));
+ return &resolveFunVal.toObject().as<JSFunction>();
+}
- PromiseAllDataHolder* resolveFunObj = &resolveFunVal.toObject().as<PromiseAllDataHolder>();
- return &resolveFunObj->resolveObj()->as<JSFunction>();
+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<JSFunction>();
}
static JSFunction*
@@ -1140,8 +1234,7 @@ ClearResolutionFunctionSlots(JSFunction* resolutionFun)
JSFunction* reject;
if (resolutionFun->maybeNative() == ResolvePromiseFunction) {
resolve = resolutionFun;
- reject = &resolutionFun->getExtendedSlot(ResolveFunctionSlot_RejectFunction)
- .toObject().as<JSFunction>();
+ reject = GetRejectFunctionFromResolve(resolutionFun);
} else {
resolve = GetResolveFunctionFromReject(resolutionFun);
reject = resolutionFun;
@@ -1367,6 +1460,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,
@@ -1585,7 +1686,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 +1695,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 +1768,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 +1789,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 +1913,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 +1995,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.
@@ -2155,12 +2246,12 @@ 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)
{
// Step 1.
- Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
+ Rooted<PromiseObject*> promise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
if (!promise)
return nullptr;
@@ -2169,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<PromiseObject*> resultPromise)
{
@@ -2185,7 +2276,7 @@ js::AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> 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<PromiseObject*> resultPromise, HandleValue value)
{
@@ -2197,28 +2288,30 @@ js::AsyncFunctionReturned(JSContext* cx, Handle<PromiseObject*> resultPromise, H
return true;
}
-// Async Functions proposal 2.3 steps 2-8.
-MOZ_MUST_USE bool
-js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> 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 <typename T>
+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<PromiseObject*> promise(cx, CreatePromiseObjectWithDefaultResolution(cx));
+ Rooted<PromiseObject*> 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(PROMISE_HANDLER_AWAIT_FULFILLED));
- RootedValue onRejected(cx, Int32Value(PROMISE_HANDLER_AWAIT_REJECTED));
-
RootedObject incumbentGlobal(cx);
if (!GetObjectFromIncumbentGlobal(cx, &incumbentGlobal))
return false;
- // Steps 6-7.
+ // Steps 7-8.
Rooted<PromiseReactionRecord*> reaction(cx, NewReactionRecord(cx, resultPromise,
onFulfilled, onRejected,
nullptr, nullptr,
@@ -2226,12 +2319,403 @@ js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, Hand
if (!reaction)
return false;
- reaction->setIsAwait();
+ // Step 6.
+ extraStep(reaction);
- // Step 8.
+ // Step 9.
return PerformPromiseThenWithReaction(cx, promise, reaction);
}
+// ES 2018 draft 25.5.5.3 steps 2-10.
+MOZ_MUST_USE bool
+js::AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value)
+{
+ // Steps 4-5.
+ RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedFulfilled));
+ RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncFunctionAwaitedRejected));
+
+ // Steps 2-3, 6-10.
+ auto extra = [](Handle<PromiseReactionRecord*> 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<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ // Steps 4-5.
+ RootedValue onFulfilled(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedFulfilled));
+ RootedValue onRejected(cx, Int32Value(PromiseHandlerAsyncGeneratorAwaitedRejected));
+
+ // Steps 2-3, 6-9.
+ auto extra = [&](Handle<PromiseReactionRecord*> 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)
+{
+ // 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<AsyncFromSyncIteratorObject>()) {
+ // 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<AsyncFromSyncIteratorObject*> asyncIter(
+ cx, &thisVal.toObject().as<AsyncFromSyncIteratorObject>());
+
+ // Step 4.
+ RootedObject iter(cx, asyncIter->iterator());
+
+ RootedValue resultVal(cx);
+ RootedValue func(cx);
+ if (completionKind == CompletionKind::Normal) {
+ // 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) {
+ // 11.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 {
+ // 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);
+
+ // 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;
+ }
+ }
+
+ // 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);
+
+ // 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) {
+ 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 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);
+ 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);
+
+ // Steps 13-14.
+ RootedValue onFulfilled(cx, Int32Value(done
+ ? PromiseHandlerAsyncFromSyncIteratorValueUnwrapDone
+ : PromiseHandlerAsyncFromSyncIteratorValueUnwrapNotDone));
+ RootedValue onRejected(cx, Int32Value(PromiseHandlerThrower));
+
+ // Steps 11-12, 15.
+ auto extra = [](Handle<PromiseReactionRecord*> reaction) {
+ };
+ if (!InternalAwait(cx, value, resultPromise, onFulfilled, onRejected, extra))
+ return false;
+
+ // Step 16.
+ args.rval().setObject(*resultPromise);
+ return true;
+}
+
+static MOZ_MUST_USE bool
+AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+// Async Iteration proposal 11.4.3.3.
+MOZ_MUST_USE bool
+js::AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value, bool done)
+{
+ // Step 1 (implicit).
+
+ // Steps 2-3.
+ MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
+
+ // Step 4.
+ Rooted<AsyncGeneratorRequest*> request(
+ cx, AsyncGeneratorObject::dequeueRequest(cx, asyncGenObj));
+ if (!request)
+ return false;
+
+ // Step 5.
+ RootedObject resultPromise(cx, request->promise());
+
+ // Step 6.
+ RootedObject resultObj(cx, CreateIterResultObject(cx, value, done));
+ if (!resultObj)
+ return false;
+
+ RootedValue resultValue(cx, ObjectValue(*resultObj));
+
+ // Step 7.
+ if (!ResolvePromiseInternal(cx, resultPromise, resultValue))
+ return false;
+
+ // Step 8.
+ if (!AsyncGeneratorResumeNext(cx, asyncGenObj))
+ return false;
+
+ // Step 9.
+ return true;
+}
+
+// Async Iteration proposal 11.4.3.4.
+MOZ_MUST_USE bool
+js::AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue exception)
+{
+ // Step 1 (implicit).
+
+ // Steps 2-3.
+ MOZ_ASSERT(!asyncGenObj->isQueueEmpty());
+
+ // Step 4.
+ Rooted<AsyncGeneratorRequest*> 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 11.4.3.5.
+static MOZ_MUST_USE bool
+AsyncGeneratorResumeNext(JSContext* cx, Handle<AsyncGeneratorObject*> 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<AsyncGeneratorRequest*> 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<PromiseReactionRecord*> 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<PromiseReactionRecord*> 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,
+ MutableHandleValue result)
+{
+ // Step 1 (implicit).
+
+ // Step 2.
+ RootedObject resultPromise(cx, CreatePromiseObjectWithoutResolutionFunctions(cx));
+ if (!resultPromise)
+ return false;
+
+ // Step 3.
+ if (!asyncGenVal.isObject() || !asyncGenVal.toObject().is<AsyncGeneratorObject>()) {
+ // 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<AsyncGeneratorObject*> asyncGenObj(
+ cx, &asyncGenVal.toObject().as<AsyncGeneratorObject>());
+
+ // Step 5 (reordered).
+ Rooted<AsyncGeneratorRequest*> 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)
@@ -2291,12 +2775,12 @@ PerformPromiseThen(JSContext* cx, Handle<PromiseObject*> 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))
@@ -2645,6 +3129,9 @@ PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> 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));
@@ -2668,6 +3155,9 @@ PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> 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 6a6453e46..b04bd0e2a 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);
@@ -149,6 +151,26 @@ AsyncFunctionThrown(JSContext* cx, Handle<PromiseObject*> resultPromise);
MOZ_MUST_USE bool
AsyncFunctionAwait(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value);
+class AsyncGeneratorObject;
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwait(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value);
+
+MOZ_MUST_USE bool
+AsyncGeneratorResolve(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value, bool done);
+
+MOZ_MUST_USE bool
+AsyncGeneratorReject(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue exception);
+
+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/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp
index 8e8bb2417..22c958d4c 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);
}
@@ -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();
@@ -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/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md
index e8bd3ee43..5de7bc171 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 <i>value</i> must
- conform to the iterator protocol: it must be a non-proxy object of the form
+ If the frame is a generator function, then <i>value</i> must conform to the
+ iterator protocol: it must be a non-proxy object of the form
<code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where
both `done` and `value` are ordinary properties.
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index a1abbfeda..ccfe3cd2e 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -727,5 +727,18 @@ frontend::CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fu
BytecodeCompiler compiler(cx, cx->tempLifoAlloc(), options, srcBuf, emptyGlobalScope,
TraceLogger_ParserCompileFunction);
+ return compiler.compileStandaloneFunction(fun, NotGenerator, AsyncFunction, parameterListEnd);
+}
+
+bool
+frontend::CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ JS::SourceBufferHolder& srcBuf,
+ Maybe<uint32_t> 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 0bc1ab2ab..029fbe632 100644
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -85,6 +85,12 @@ CompileStandaloneAsyncFunction(JSContext* cx, MutableHandleFunction fun,
mozilla::Maybe<uint32_t> parameterListEnd);
MOZ_MUST_USE bool
+CompileStandaloneAsyncGenerator(JSContext* cx, MutableHandleFunction fun,
+ const ReadOnlyCompileOptions& options,
+ JS::SourceBufferHolder& srcBuf,
+ mozilla::Maybe<uint32_t> parameterListEnd);
+
+MOZ_MUST_USE bool
CompileAsyncFunctionBody(JSContext* cx, MutableHandleFunction fun,
const ReadOnlyCompileOptions& options,
Handle<PropertyNameVector> formals, JS::SourceBufferHolder& srcBuf);
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 309d6c290..f4574b248 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -100,7 +100,7 @@ class BytecodeEmitter::NestableControl : public Nestable<BytecodeEmitter::Nestab
NestableControl(BytecodeEmitter* bce, StatementKind kind)
: Nestable<NestableControl>(&bce->innermostNestableControl),
kind_(kind),
- emitterScope_(bce->innermostEmitterScope)
+ emitterScope_(bce->innermostEmitterScopeNoCheck())
{ }
public:
@@ -389,7 +389,10 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc
nextFrameSlot_ = bi.nextFrameSlot();
if (nextFrameSlot_ > 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);
}
@@ -423,7 +426,7 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc
// enclosing BCE.
if ((*bce)->parent) {
*bce = (*bce)->parent;
- return (*bce)->innermostEmitterScope;
+ return (*bce)->innermostEmitterScopeNoCheck();
}
return nullptr;
@@ -457,7 +460,7 @@ class BytecodeEmitter::EmitterScope : public Nestable<BytecodeEmitter::EmitterSc
public:
explicit EmitterScope(BytecodeEmitter* bce)
- : Nestable<EmitterScope>(&bce->innermostEmitterScope),
+ : Nestable<EmitterScope>(&bce->innermostEmitterScope_),
nameCache_(bce->cx->frontendCollectionPool()),
hasEnvironment_(false),
environmentChainLength_(0),
@@ -863,7 +866,7 @@ BytecodeEmitter::EmitterScope::enterLexical(BytecodeEmitter* bce, ScopeKind kind
Handle<LexicalScope::Data*> bindings)
{
MOZ_ASSERT(kind != ScopeKind::NamedLambda && kind != ScopeKind::StrictNamedLambda);
- MOZ_ASSERT(this == bce->innermostEmitterScope);
+ MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());
if (!ensureCache(bce))
return false;
@@ -932,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))
@@ -999,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;
@@ -1032,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())
@@ -1123,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.
@@ -1209,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);
@@ -1269,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);
@@ -1324,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);
@@ -1381,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;
@@ -1409,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) {
@@ -1985,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_;
@@ -2026,12 +2031,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)
{
}
@@ -2043,7 +2052,7 @@ class ForOfLoopControl : public LoopControl
return false;
MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
- numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldOffsetList.numYields;
+ numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields;
return true;
}
@@ -2073,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;
@@ -2085,7 +2094,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;
@@ -2097,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;
}
@@ -2112,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(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
@@ -2138,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) {
@@ -2181,13 +2201,16 @@ 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),
scopeNoteList(cx),
- yieldOffsetList(cx),
+ yieldAndAwaitOffsetList(cx),
typesetCount(0),
hasSingletons(false),
hasTryFinally(false),
@@ -2239,13 +2262,13 @@ BytecodeEmitter::findInnermostNestableControl(Predicate predicate) const
NameLocation
BytecodeEmitter::lookupName(JSAtom* name)
{
- return innermostEmitterScope->lookup(this, name);
+ return innermostEmitterScope()->lookup(this, name);
}
Maybe<NameLocation>
BytecodeEmitter::locationOfNameBoundInScope(JSAtom* name, EmitterScope* target)
{
- return innermostEmitterScope->locationBoundInScope(this, name, target);
+ return innermostEmitterScope()->locationBoundInScope(this, name, target);
}
Maybe<NameLocation>
@@ -2706,7 +2729,7 @@ class NonLocalExitControl
: bce_(bce),
savedScopeNoteIndex_(bce->scopeNoteList.length()),
savedDepth_(bce->stackDepth),
- openScopeNoteIndex_(bce->innermostEmitterScope->noteIndex()),
+ openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()),
kind_(kind)
{ }
@@ -2752,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.
@@ -2805,8 +2830,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
return false;
ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
- if (!loopinfo.emitPrepareForNonLocalJump(bce_, /* isTarget = */ false)) // ...
+ if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es,
+ /* isTarget = */ false))
+ { // ...
return false;
+ }
} else {
npops += 3;
}
@@ -2833,8 +2861,11 @@ NonLocalExitControl::prepareForNonLocalJump(BytecodeEmitter::NestableControl* ta
if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
- 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;
@@ -2869,7 +2900,7 @@ BytecodeEmitter::emitGoto(NestableControl* target, JumpList* jumplist, SrcNoteTy
Scope*
BytecodeEmitter::innermostScope() const
{
- return innermostEmitterScope->scope(this);
+ return innermostEmitterScope()->scope(this);
}
bool
@@ -3172,10 +3203,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;
@@ -3528,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;
}
@@ -3718,6 +3750,18 @@ BytecodeEmitter::emitFinishIteratorResult(bool done)
}
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)
{
switch (loc.kind()) {
@@ -4783,7 +4827,9 @@ BytecodeEmitter::isRunOnceLambda()
FunctionBox* funbox = sc->asFunctionBox();
return !funbox->argumentsHasLocalBinding() &&
- !funbox->isGenerator() &&
+ !funbox->isStarGenerator() &&
+ !funbox->isLegacyGenerator() &&
+ !funbox->isAsync() &&
!funbox->function()->explicitName();
}
@@ -4793,26 +4839,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);
@@ -5165,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;
@@ -5228,7 +5274,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 "
@@ -5242,6 +5289,12 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false
return false;
if (!emitCall(JSOP_CALL, 0, pn)) // ... RESULT
return false;
+
+ if (iterKind == IteratorKind::Async) {
+ if (!emitAwaitInInnermostScope()) // ... RESULT
+ return false;
+ }
+
if (!emitCheckIsObj(CheckIsObjectKind::IteratorNext)) // ... RESULT
return false;
checkTypeSet(JSOP_CALL);
@@ -5249,8 +5302,10 @@ BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted /* = false
}
bool
-BytecodeEmitter::emitIteratorClose(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 "
@@ -5334,6 +5389,18 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion
return false;
checkTypeSet(JSOP_CALL);
+ if (iterKind == IteratorKind::Async) {
+ 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 (!emitAwaitInScope(currentScope)) // ... ... RVAL? RESULT
+ return false;
+ }
+
if (completionKind == CompletionKind::Throw) {
if (!emit1(JSOP_SWAP)) // ... RET ITER RESULT UNDEF
return false;
@@ -5343,7 +5410,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
@@ -5360,8 +5427,15 @@ BytecodeEmitter::emitIteratorClose(CompletionKind completionKind /* = Completion
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())
@@ -5587,7 +5661,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
@@ -5788,7 +5862,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;
@@ -6966,6 +7040,63 @@ BytecodeEmitter::emitIterator()
}
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)
{
LoopControl loopInfo(this, StatementKind::Spread);
@@ -7018,7 +7149,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;
@@ -7118,6 +7249,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
@@ -7133,8 +7271,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;
@@ -7145,7 +7288,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;
@@ -7170,7 +7313,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
// bindings inducing an environment, recreate the current environment.
DebugOnly<ParseNode*> 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()) {
@@ -7241,7 +7384,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
@@ -7352,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()) {
@@ -7481,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()) {
@@ -7529,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()) {
@@ -8061,7 +8204,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()) {
@@ -8116,8 +8260,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;
@@ -8133,10 +8280,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);
};
@@ -8171,7 +8320,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
@@ -8203,8 +8353,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;
}
@@ -8438,7 +8593,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;
}
@@ -8447,13 +8603,20 @@ 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 (!emitAwaitInInnermostScope())
+ return false;
+ }
} else {
/* No explicit return value provided */
if (!emit1(JSOP_UNDEFINED))
return false;
}
- if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) {
+ if (needsIteratorResult) {
if (!emitFinishIteratorResult(true))
return false;
}
@@ -8478,11 +8641,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 +8660,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 =
@@ -8520,52 +8683,105 @@ BytecodeEmitter::emitReturn(ParseNode* pn)
}
bool
+BytecodeEmitter::emitGetDotGeneratorInScope(EmitterScope& currentScope)
+{
+ NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator, &currentScope);
+ return emitGetNameAtLocation(cx->names().dotGenerator, loc);
+}
+
+bool
+BytecodeEmitter::emitInitialYield(ParseNode* pn)
+{
+ if (!emitTree(pn->pn_kid))
+ return false;
+
+ if (!emitYieldOp(JSOP_INITIALYIELD))
+ return false;
+
+ if (!emit1(JSOP_POP))
+ return false;
+
+ return true;
+}
+
+bool
BytecodeEmitter::emitYield(ParseNode* pn)
{
MOZ_ASSERT(sc->isFunctionBox());
+ MOZ_ASSERT(pn->getOp() == JSOP_YIELD);
- if (pn->getOp() == JSOP_YIELD) {
- if (sc->asFunctionBox()->isStarGenerator()) {
- if (!emitPrepareIteratorResult())
- return false;
- }
- if (pn->pn_left) {
- if (!emitTree(pn->pn_left))
- return false;
- } else {
- if (!emit1(JSOP_UNDEFINED))
- return false;
- }
- if (sc->asFunctionBox()->isStarGenerator()) {
- if (!emitFinishIteratorResult(false))
- return false;
- }
+ bool needsIteratorResult = sc->asFunctionBox()->needsIteratorResult();
+ if (needsIteratorResult) {
+ if (!emitPrepareIteratorResult())
+ return false;
+ }
+ if (pn->pn_kid) {
+ if (!emitTree(pn->pn_kid))
+ return false;
} else {
- MOZ_ASSERT(pn->getOp() == JSOP_INITIALYIELD);
+ if (!emit1(JSOP_UNDEFINED))
+ return false;
}
- if (!emitTree(pn->pn_right))
+ // 11.4.3.7 AsyncGeneratorYield step 5.
+ bool isAsyncGenerator = sc->asFunctionBox()->isAsync();
+ if (isAsyncGenerator) {
+ if (!emitAwaitInInnermostScope()) // RESULT
+ return false;
+ }
+
+ if (needsIteratorResult) {
+ if (!emitFinishIteratorResult(false))
+ return false;
+ }
+
+ if (!emitGetDotGeneratorInInnermostScope())
return false;
- if (!emitYieldOp(pn->getOp()))
+ if (!emitYieldOp(JSOP_YIELD))
return false;
- if (pn->getOp() == JSOP_INITIALYIELD && !emit1(JSOP_POP))
+ return true;
+}
+
+bool
+BytecodeEmitter::emitAwaitInInnermostScope(ParseNode* pn)
+{
+ MOZ_ASSERT(sc->isFunctionBox());
+ MOZ_ASSERT(pn->getOp() == JSOP_AWAIT);
+
+ if (!emitTree(pn->pn_kid))
return false;
+ return emitAwaitInInnermostScope();
+}
+bool
+BytecodeEmitter::emitAwaitInScope(EmitterScope& currentScope)
+{
+ if (!emitGetDotGeneratorInScope(currentScope))
+ return false;
+ if (!emitYieldOp(JSOP_AWAIT))
+ return false;
return true;
}
bool
-BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
+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
@@ -8586,8 +8802,14 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
MOZ_ASSERT(this->stackDepth == startDepth);
+ // 11.4.3.7 AsyncGeneratorYield step 5.
+ if (isAsyncGenerator) {
+ if (!emitAwaitInInnermostScope()) // NEXT ITER RESULT
+ return false;
+ }
+
// Load the generator object.
- if (!emitTree(gen)) // ITER RESULT GENOBJ
+ if (!emitGetDotGeneratorInInnermostScope()) // NEXT ITER RESULT GENOBJ
return false;
// Yield RESULT as-is, without re-boxing.
@@ -8623,7 +8845,8 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
//
// 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 (!emitIteratorCloseInInnermostScope(iterKind)) // NEXT ITER RESULT EXCEPTION
return false;
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_ITERATOR_NO_THROW)) // throw
return false;
@@ -8639,6 +8862,12 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
if (!emitCall(JSOP_CALL, 1, iter)) // ITER OLDRESULT RESULT
return false;
checkTypeSet(JSOP_CALL);
+
+ if (isAsyncGenerator) {
+ if (!emitAwaitInInnermostScope()) // NEXT ITER OLDRESULT RESULT
+ return false;
+ }
+
if (!emitCheckIsObj(CheckIsObjectKind::IteratorThrow)) // ITER OLDRESULT RESULT
return false;
if (!emit1(JSOP_SWAP)) // ITER RESULT OLDRESULT
@@ -8705,6 +8934,11 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
return false;
checkTypeSet(JSOP_CALL);
+ if (iterKind == IteratorKind::Async) {
+ if (!emitAwaitInInnermostScope()) // ... FTYPE FVALUE RESULT
+ return false;
+ }
+
// Step v.
if (!emitCheckIsObj(CheckIsObjectKind::IteratorReturn)) // ITER OLDRESULT FTYPE FVALUE RESULT
return false;
@@ -8779,9 +9013,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
return false;
if (!emitCall(JSOP_CALL, 1, iter)) // ITER RESULT
return false;
+ checkTypeSet(JSOP_CALL);
+
+ if (isAsyncGenerator) {
+ if (!emitAwaitInInnermostScope()) // NEXT 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:
@@ -10131,7 +10371,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();
@@ -10312,22 +10552,26 @@ 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;
+ bool needsIteratorResult = funbox->needsIteratorResult();
+ if (needsIteratorResult) {
+ if (!emitPrepareIteratorResult())
+ return false;
+ }
if (!emit1(JSOP_UNDEFINED))
return false;
- if (sc->asFunctionBox()->isStarGenerator() && !emitFinishIteratorResult(true))
- return false;
+ if (needsIteratorResult) {
+ if (!emitFinishIteratorResult(true))
+ return false;
+ }
if (!emit1(JSOP_SETRVAL))
return false;
- NameLocation loc = *locationOfNameBoundInFunctionScope(cx->names().dotGenerator);
- if (!emitGetNameAtLocation(cx->names().dotGenerator, loc))
+ if (!emitGetDotGeneratorInInnermostScope())
return false;
// No need to check for finally blocks, etc as in EmitReturn.
@@ -10587,7 +10831,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;
@@ -10596,12 +10840,21 @@ 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))
return false;
break;
+ case PNK_AWAIT:
+ if (!emitAwaitInInnermostScope(pn))
+ return false;
+ break;
+
case PNK_STATEMENTLIST:
if (!emitStatementList(pn))
return false;
@@ -11281,7 +11534,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 595ee6405..8ad409c11 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"
@@ -98,15 +99,15 @@ struct CGScopeNoteList {
void finish(ScopeNoteArray* array, uint32_t prologueLength);
};
-struct CGYieldOffsetList {
+struct CGYieldAndAwaitOffsetList {
Vector<uint32_t> 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;
@@ -227,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 */
@@ -237,10 +252,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 */
@@ -318,7 +333,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
EmitterScope* source);
mozilla::Maybe<NameLocation> locationOfNameBoundInFunctionScope(JSAtom* name) {
- return locationOfNameBoundInFunctionScope(name, innermostEmitterScope);
+ return locationOfNameBoundInFunctionScope(name, innermostEmitterScope());
}
void setVarEmitterScope(EmitterScope* emitterScope) {
@@ -606,17 +621,29 @@ 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 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, ParseNode* gen);
-
+ MOZ_MUST_USE bool emitYieldStar(ParseNode* iter);
+ 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);
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);
@@ -703,11 +730,22 @@ 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,
- bool allowSelfHosted = false);
+ MOZ_MUST_USE bool emitIteratorNext(ParseNode* pn, IteratorKind kind = IteratorKind::Sync,
+ 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 <typename InnerEmitter>
MOZ_MUST_USE bool wrapWithDestructuringIteratorCloseTryNote(int32_t iterDepth,
@@ -805,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 */
diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp
index 689fa02b4..16294c4a8 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<FullParseHandler>& 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 2d7f57e1e..44694298b 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) {
+ ParseNode* newInitialYieldExpression(uint32_t begin, ParseNode* gen) {
+ TokenPos pos(begin, begin + 1);
+ return new_<UnaryNode>(PNK_INITIALYIELD, JSOP_INITIALYIELD, pos, gen);
+ }
+
+ ParseNode* newYieldExpression(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
- return new_<BinaryNode>(PNK_YIELD, op, pos, value, gen);
+ return new_<UnaryNode>(PNK_YIELD, JSOP_YIELD, pos, value);
}
- ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value, ParseNode* gen) {
+ ParseNode* newYieldStarExpression(uint32_t begin, ParseNode* value) {
TokenPos pos(begin, value->pn_pos.end);
- return new_<BinaryNode>(PNK_YIELD_STAR, JSOP_NOP, pos, value, gen);
+ return new_<UnaryNode>(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_<BinaryNode>(PNK_AWAIT, JSOP_YIELD, pos, value, gen);
+ return new_<UnaryNode>(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 dc54d0a88..376be7624 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 5fe64e3d3..42ae9451a 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 1f20f3988..9a435e270 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 0c279591f..cf9f1e27c 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2474,10 +2474,8 @@ Parser<SyntaxParseHandler>::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<FullParseHandler>::standaloneFunction(HandleFunction fun,
return null();
funpc.setIsStandaloneFunctionBody();
- YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind);
AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
parameterListEnd, /* isStandaloneFunction = */ true))
@@ -2699,7 +2697,7 @@ Parser<ParseHandler>::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,12 +2713,12 @@ Parser<ParseHandler>::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;
}
- if (pc->isGenerator()) {
+ if (pc->needsDotGeneratorName()) {
MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
if (!declareDotGeneratorName())
return null();
@@ -2761,9 +2759,9 @@ Parser<ParseHandler>::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<ParseHandler>::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<ParseHandler>::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<ParseHandler>::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<ParseHandler>::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<FullParseHandler>::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<SyntaxParseHandler>* parser = handler.syntaxParser;
@@ -3584,7 +3581,7 @@ Parser<FullParseHandler>::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<ParseHandler>::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<ParseHandler>::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<ParseHandler> awaitIsKeyword(this, funbox->isAsync());
@@ -3786,12 +3783,8 @@ Parser<ParseHandler>::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);
- return null();
- }
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
@@ -3817,7 +3810,7 @@ Parser<ParseHandler>::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 +3834,7 @@ Parser<ParseHandler>::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,22 +3847,18 @@ Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction inv
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
- GeneratorKind generatorKind = asyncKind == AsyncFunction ? StarGenerator : NotGenerator;
+ GeneratorKind generatorKind = NotGenerator;
TokenKind tt;
if (!tokenStream.getToken(&tt))
return null();
if (tt == TOK_MUL) {
- if (asyncKind != SyncFunction) {
- error(JSMSG_ASYNC_GENERATOR);
- return null();
- }
generatorKind = StarGenerator;
if (!tokenStream.getToken(&tt))
return null();
}
- YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
+ YieldHandling yieldHandling = GetYieldHandling(generatorKind);
RootedPropertyName name(context);
if (TokenKindIsPossibleIdentifier(tt)) {
@@ -5971,6 +5960,7 @@ Parser<ParseHandler>::matchInOrOf(bool* isForInp, bool* isForOfp)
template <class ParseHandler>
bool
Parser<ParseHandler>::forHeadStart(YieldHandling yieldHandling,
+ IteratorKind iterKind,
ParseNodeKind* forHeadKind,
Node* forInitialPart,
Maybe<ParseContext::Scope>& forLoopLexicalScope,
@@ -6061,6 +6051,11 @@ Parser<ParseHandler>::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
@@ -6134,6 +6129,7 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
ParseContext::Statement stmt(pc, StatementKind::ForLoop);
bool isForEach = false;
+ IteratorKind iterKind = IteratorKind::Sync;
unsigned iflags = 0;
if (allowsForEachIn()) {
@@ -6148,6 +6144,17 @@ Parser<ParseHandler>::forStatement(YieldHandling yieldHandling)
}
}
+ if (pc->isAsync()) {
+ bool matched;
+ if (!tokenStream.matchToken(&matched, TOK_AWAIT))
+ return null();
+
+ if (matched) {
+ iflags |= JSITER_FORAWAITOF;
+ iterKind = IteratorKind::Async;
+ }
+ }
+
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
// PNK_FORHEAD, PNK_FORIN, or PNK_FOROF depending on the loop type.
@@ -6185,7 +6192,7 @@ Parser<ParseHandler>::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();
@@ -6551,29 +6558,6 @@ Parser<ParseHandler>::returnStatement(YieldHandling yieldHandling)
template <typename ParseHandler>
typename ParseHandler::Node
-Parser<ParseHandler>::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>
-typename ParseHandler::Node
-Parser<ParseHandler>::newAwaitExpression(uint32_t begin, typename ParseHandler::Node expr)
-{
- Node generator = newDotGeneratorName();
- if (!generator)
- return null();
- return handler.newAwaitExpression(begin, expr, generator);
-}
-
-template <typename ParseHandler>
-typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression(InHandling inHandling)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
@@ -6620,7 +6604,9 @@ Parser<ParseHandler>::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:
@@ -6697,7 +6683,7 @@ Parser<ParseHandler>::yieldExpression(InHandling inHandling)
return null();
}
- return newYieldExpression(begin, exprNode);
+ return handler.newYieldExpression(begin, exprNode);
}
}
@@ -8187,7 +8173,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
uint32_t toStringStart = pos().begin;
tokenStream.ungetToken();
- GeneratorKind generatorKind = NotGenerator;
FunctionAsyncKind asyncKind = SyncFunction;
if (next == TOK_ASYNC) {
@@ -8200,7 +8185,6 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
if (nextSameLine == TOK_ARROW) {
tokenStream.ungetToken();
} else {
- generatorKind = StarGenerator;
asyncKind = AsyncFunction;
}
}
@@ -8210,7 +8194,7 @@ Parser<ParseHandler>::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();
@@ -8454,7 +8438,7 @@ Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling t
if (!kid)
return null();
pc->lastAwaitOffset = begin;
- return newAwaitExpression(begin, kid);
+ return handler.newAwaitExpression(begin, kid);
}
}
@@ -8712,7 +8696,7 @@ Parser<ParseHandler>::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);
@@ -9494,11 +9478,6 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
bool isGenerator = false;
bool isAsync = false;
- if (ltok == TOK_MUL) {
- isGenerator = true;
- if (!tokenStream.getToken(&ltok))
- return null();
- }
if (ltok == TOK_ASYNC) {
// AsyncMethod[Yield, Await]:
@@ -9526,9 +9505,10 @@ Parser<ParseHandler>::propertyName(YieldHandling yieldHandling,
}
}
- if (isAsync && isGenerator) {
- error(JSMSG_ASYNC_GENERATOR);
- return null();
+ if (ltok == TOK_MUL) {
+ isGenerator = true;
+ if (!tokenStream.getToken(&ltok))
+ return null();
}
propAtom.set(nullptr);
@@ -9950,8 +9930,7 @@ Parser<ParseHandler>::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 +9938,7 @@ Parser<ParseHandler>::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/Parser.h b/js/src/frontend/Parser.h
index 88d2dad18..243a77083 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"
@@ -496,10 +497,6 @@ class ParseContext : public Nestable<ParseContext>
return sc_->isFunctionBox() ? sc_->asFunctionBox()->generatorKind() : NotGenerator;
}
- bool isGenerator() const {
- return generatorKind() != NotGenerator;
- }
-
bool isLegacyGenerator() const {
return generatorKind() == LegacyGenerator;
}
@@ -512,6 +509,10 @@ class ParseContext : public Nestable<ParseContext>
return sc_->isFunctionBox() && sc_->asFunctionBox()->isAsync();
}
+ bool needsDotGeneratorName() const {
+ return isStarGenerator() || isLegacyGenerator() || isAsync();
+ }
+
FunctionAsyncKind asyncKind() const {
return isAsync() ? AsyncFunction : SyncFunction;
}
@@ -818,7 +819,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(); }
@@ -1108,8 +1111,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();
@@ -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<ParseContext::Scope>& forLetImpliedScope,
diff --git a/js/src/frontend/SharedContext.h b/js/src/frontend/SharedContext.h
index 013444690..8a5f6c0bd 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,21 @@ 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 needsIteratorResult() const {
+ return isStarGenerator();
+ }
+
bool isAsync() const { return asyncKind() == AsyncFunction; }
bool isArrow() const { return function()->isArrow(); }
@@ -560,7 +573,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 +668,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/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h
index a604b599f..d919f1354 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
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 d4e7e8576..0b2ee766b 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.
-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.
+// Return with 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/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index b2f9d3b23..d254b9826 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"
@@ -43,7 +44,7 @@ using mozilla::AssertedCast;
BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
: BaselineCompilerSpecific(cx, alloc, script),
- yieldOffsets_(cx),
+ yieldAndAwaitOffsets_(cx),
modifiesArguments_(false)
{
}
@@ -210,7 +211,7 @@ BaselineCompiler::compile()
pcMappingIndexEntries.length(),
pcEntries.length(),
bytecodeTypeMapEntries,
- yieldOffsets_.length(),
+ yieldAndAwaitOffsets_.length(),
traceLoggerToggleOffsets_.length()),
JS::DeletePolicy<BaselineScript>(cx->runtime()));
if (!baselineScript) {
@@ -275,7 +276,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();
@@ -3908,6 +3909,50 @@ BaselineCompiler::emit_JSOP_TOASYNC()
return true;
}
+typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
+static const VMFunction ToAsyncGenInfo =
+ FunctionInfo<ToAsyncGenFn>(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 JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
+static const VMFunction ToAsyncIterInfo =
+ FunctionInfo<ToAsyncIterFn>(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<ThrowObjectCoercibleFn>(ThrowObjectCoercible, "ThrowObjectCoercible");
@@ -4174,27 +4219,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);
@@ -4204,7 +4250,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());
@@ -4233,7 +4280,7 @@ static const VMFunction NormalSuspendInfo =
bool
BaselineCompiler::emit_JSOP_YIELD()
{
- if (!addYieldOffset())
+ if (!addYieldAndAwaitOffset())
return false;
// Store generator in R0.
@@ -4250,7 +4297,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());
@@ -4282,6 +4329,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<DebugAfterYieldFn>(jit::DebugAfterYield, "DebugAfterYield");
@@ -4501,16 +4554,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 95e0c77ad..a200f7ab9 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -196,6 +196,8 @@ namespace jit {
_(JSOP_RUNONCE) \
_(JSOP_REST) \
_(JSOP_TOASYNC) \
+ _(JSOP_TOASYNCGEN) \
+ _(JSOP_TOASYNCITER) \
_(JSOP_TOID) \
_(JSOP_TOSTRING) \
_(JSOP_TABLESWITCH) \
@@ -207,6 +209,7 @@ namespace jit {
_(JSOP_GENERATOR) \
_(JSOP_INITIALYIELD) \
_(JSOP_YIELD) \
+ _(JSOP_AWAIT) \
_(JSOP_DEBUGAFTERYIELD) \
_(JSOP_FINALYIELDRVAL) \
_(JSOP_RESUME) \
@@ -255,9 +258,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<uint32_t> yieldOffsets_;
+ // For each INITIALYIELD or YIELD or AWAIT op, this Vector maps the yield
+ // index to the bytecode offset of the next op.
+ Vector<uint32_t> yieldAndAwaitOffsets_;
// Whether any on stack arguments are modified.
bool modifiesArguments_;
@@ -349,7 +352,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 9530f65fa..e65f10aac 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 5c21926b5..94e018252 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<uint32_t>& yieldOffsets)
+BaselineScript::copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& 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 5e7775a61..b6ccd2ff9 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<uint32_t>& yieldOffsets);
+ void copyYieldAndAwaitEntries(JSScript* script, Vector<uint32_t>& yieldAndAwaitOffsets);
PCMappingIndexEntry& pcMappingIndexEntry(size_t index);
CompactBufferReader pcMappingReader(size_t indexEntry);
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 205812942..2b1c671d1 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,28 @@ CodeGenerator::visitToAsync(LToAsync* lir)
callVM(ToAsyncInfo, lir);
}
+typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
+static const VMFunction ToAsyncGenInfo =
+ FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
+
+void
+CodeGenerator::visitToAsyncGen(LToAsyncGen* lir)
+{
+ pushArg(ToRegister(lir->unwrapped()));
+ callVM(ToAsyncGenInfo, lir);
+}
+
+typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject);
+static const VMFunction ToAsyncIterInfo =
+ FunctionInfo<ToAsyncIterFn>(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<ToIdFn>(ToIdOperation, "ToIdOperation");
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index 12f1238ef..65acfe274 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -286,6 +286,8 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitTypeOfV(LTypeOfV* lir);
void visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool);
void visitToAsync(LToAsync* lir);
+ void visitToAsyncGen(LToAsyncGen* lir);
+ void visitToAsyncIter(LToAsyncIter* lir);
void visitToIdV(LToIdV* lir);
template<typename T> void emitLoadElementT(LLoadElementT* lir, const T& source);
void visitLoadElementT(LLoadElementT* lir);
diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp
index b8a2d2fba..9337f6150 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 ace6cd81e..a4724bca4 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/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 0c69729a4..1e12f5dbe 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2130,6 +2130,12 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_TOASYNC:
return jsop_toasync();
+ case JSOP_TOASYNCGEN:
+ return jsop_toasyncgen();
+
+ case JSOP_TOASYNCITER:
+ return jsop_toasynciter();
+
case JSOP_TOID:
return jsop_toid();
@@ -3351,7 +3357,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
@@ -13196,6 +13202,34 @@ IonBuilder::jsop_toasync()
}
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_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()
{
// No-op if the index is an integer.
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 1f763a4f2..6a3b61232 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -762,6 +762,8 @@ 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_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 22f1d5f70..108450983 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1176,6 +1176,22 @@ LIRGenerator::visitToAsync(MToAsync* ins)
}
void
+LIRGenerator::visitToAsyncGen(MToAsyncGen* ins)
+{
+ LToAsyncGen* lir = new(alloc()) LToAsyncGen(useRegisterAtStart(ins->input()));
+ defineReturn(lir, 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)
{
LToIdV* lir = new(alloc()) LToIdV(useBox(ins->input()), tempDouble());
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 9342ef471..81e6abbbb 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -121,6 +121,8 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCompare(MCompare* comp);
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 6c376d528..e7186ed30 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -5784,6 +5784,36 @@ 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 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 9e460a2ba..589dde077 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -79,6 +79,8 @@ namespace jit {
_(BitNot) \
_(TypeOf) \
_(ToAsync) \
+ _(ToAsyncGen) \
+ _(ToAsyncIter) \
_(ToId) \
_(BitAnd) \
_(BitOr) \
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index 1ff7adfd1..652c23bf1 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, Handle<GeneratorObjec
// work. This function always returns false, so we're guaranteed to enter
// the exception handler where we will clear the pc.
JSScript* script = frame->script();
- 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/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index ecf02816e..ff4915d1a 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1606,6 +1606,32 @@ 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 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<BOX_PIECES, BOX_PIECES, 1>
{
public:
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index e260d7e94..56b98940a 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -353,6 +353,8 @@
_(Rest) \
_(TypeOfV) \
_(ToAsync) \
+ _(ToAsyncGen) \
+ _(ToAsyncIter) \
_(ToIdV) \
_(Floor) \
_(FloorF) \
diff --git a/js/src/js.msg b/js/src/js.msg
index e2d923bc4..9dc5f4e9f 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)")
@@ -438,7 +437,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}")
@@ -594,3 +592,9 @@ 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_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/jsapi.cpp b/js/src/jsapi.cpp
index f9a0c6a6b..6483641f3 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/jsapi.h b/js/src/jsapi.h
index 4dad68e6e..7795c5e4c 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/jsexn.cpp b/js/src/jsexn.cpp
index 3fc9200c1..72f03ea55 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 "<<error converting value to string>>";
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 00120d89c..ee69e3c50 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 */
diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h
index 491215456..7b59689ff 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);
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index 9edf238ef..ce1ca33fe 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"
@@ -131,7 +132,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
@@ -308,6 +313,8 @@ CallerGetterImpl(JSContext* cx, const CallArgs& args)
JSFunction* callerFun = &callerObj->as<JSFunction>();
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()) {
@@ -360,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
@@ -376,7 +385,9 @@ ResolveInterpretedFunctionPrototype(JSContext* cx, HandleFunction fun, HandleId
bool isStarGenerator = fun->isStarGenerator();
Rooted<GlobalObject*> 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);
@@ -392,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;
@@ -442,8 +453,14 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
* - Arrow functions
* - Function.prototype
*/
- if (fun->isBuiltin() || (!fun->isConstructor() && !fun->isGenerator()))
- return true;
+ 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))
return false;
@@ -515,7 +532,7 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
{
enum FirstWordFlag {
HasAtom = 0x1,
- IsStarGenerator = 0x2,
+ HasStarGeneratorProto = 0x2,
IsLazy = 0x4,
HasSingletonType = 0x8
};
@@ -544,8 +561,8 @@ js::XDRInterpretedFunction(XDRState<mode>* 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.
@@ -581,7 +598,7 @@ js::XDRInterpretedFunction(XDRState<mode>* 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;
@@ -938,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);
@@ -1556,7 +1578,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;
}
@@ -1588,8 +1610,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;
@@ -1600,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())
@@ -1701,7 +1725,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;
@@ -1731,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);
@@ -1762,14 +1794,14 @@ 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();
else
newTarget = &args.callee();
- if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
+ if (!FunctionConstructor(cx, args, NotGenerator, AsyncFunction))
return false;
// ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077
@@ -1795,6 +1827,40 @@ js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
}
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<JSFunction>());
+ RootedObject wrapped(cx, WrapAsyncGeneratorWithProto(cx, unwrapped, proto));
+ if (!wrapped)
+ return false;
+
+ args.rval().setObject(*wrapped);
+ return true;
+}
+
+bool
JSFunction::isBuiltinFunctionConstructor()
{
return maybeNative() == Function || maybeNative() == Generator;
@@ -1965,7 +2031,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 234169507..ea9e7af9b 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 |
@@ -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;
}
@@ -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 c4da86bdb..c58f32382 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<PlainObject>(cx));
+ if (!resultObj)
return nullptr;
- RootedPlainObject obj(cx, NewObjectWithGivenProto<PlainObject>(cx, proto));
- if (!obj)
- return nullptr;
-
- if (!DefineProperty(cx, obj, cx->names().value, value))
+ // Step 3.
+ if (!DefineProperty(cx, resultObj, cx->names().value, value))
return nullptr;
- RootedValue doneBool(cx, BooleanValue(done));
- if (!DefineProperty(cx, obj, cx->names().done, doneBool))
+ // Step 4.
+ if (!DefineProperty(cx, resultObj, cx->names().done,
+ done ? TrueHandleValue : FalseHandleValue))
+ {
return nullptr;
+ }
- return obj;
+ // Step 5.
+ return resultObj;
}
bool
diff --git a/js/src/jsiter.h b/js/src/jsiter.h
index 52eb045c5..0b324a014 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);
@@ -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/jsopcode.h b/js/src/jsopcode.h
index e56eebb2d..e417e3a64 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 fc7438e3b..bdd411d04 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -402,8 +402,8 @@ js::XDRScript(XDRState<mode>* 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<mode>* 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<YieldOffsetArray*>(cursor);
- cursor += sizeof(YieldOffsetArray);
+ yieldAndAwaitOffsets = reinterpret_cast<YieldAndAwaitOffsetArray*>(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<uint32_t*>(cursor), nyieldoffsets);
- size_t vectorSize = nyieldoffsets * sizeof(script->yieldOffsets()[0]);
+ yieldAndAwaitOffsets->init(reinterpret_cast<uint32_t*>(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<JSScript*> 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();
@@ -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;
@@ -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<JSTryNote>(dst, src, src->trynotes()->vector);
if (nscopenotes != 0)
dst->scopeNotes()->vector = Rebase<ScopeNote>(dst, src, src->scopeNotes()->vector);
- if (nyieldoffsets != 0)
- dst->yieldOffsets().vector_ = Rebase<uint32_t>(dst, src, src->yieldOffsets().vector_);
+ if (nyieldoffsets != 0) {
+ dst->yieldAndAwaitOffsets().vector_ =
+ Rebase<uint32_t>(dst, src, src->yieldAndAwaitOffsets().vector_);
+ }
/*
* Function delazification assumes that their script does not have a
@@ -3917,7 +3919,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 85eb2938d..c19fbfc71 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<GCVector<Scope*>> scopes);
@@ -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,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 isGenerator(); }
+ bool hasYieldAndAwaitOffsets() const {
+ return isStarGenerator() || isLegacyGenerator() || isAsync();
+ }
#define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0))
@@ -1704,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
@@ -1734,9 +1742,10 @@ class JSScript : public js::gc::TenuredCell
return reinterpret_cast<js::ScopeNoteArray*>(data + scopeNotesOffset());
}
- js::YieldOffsetArray& yieldOffsets() {
- MOZ_ASSERT(hasYieldOffsets());
- return *reinterpret_cast<js::YieldOffsetArray*>(data + yieldOffsetsOffset());
+ js::YieldAndAwaitOffsetArray& yieldAndAwaitOffsets() {
+ MOZ_ASSERT(hasYieldAndAwaitOffsets());
+ return *reinterpret_cast<js::YieldAndAwaitOffsetArray*>(data +
+ yieldAndAwaitOffsetsOffset());
}
bool hasLoops();
@@ -2112,8 +2121,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 +2128,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 +2138,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/moz.build b/js/src/moz.build
index d1e80b4ce..d6c2a426b 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',
@@ -790,3 +792,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
diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp
index 51cd11fe8..33f7d6bdc 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_2017/AsyncFunctions/properties.js b/js/src/tests/ecma_2017/AsyncFunctions/properties.js
new file mode 100644
index 000000000..ca383901b
--- /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 000000000..7782e64c0
--- /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/tests/ecma_6/Promise/self-resolve.js b/js/src/tests/ecma_6/Promise/self-resolve.js
index e16a2ceb3..4e7e36c6c 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");
diff --git a/js/src/tests/ecma_6/Symbol/well-known.js b/js/src/tests/ecma_6/Symbol/well-known.js
index 8c5de1279..095d333a0 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) {
diff --git a/js/src/tests/ecma_7/AsyncFunctions/syntax.js b/js/src/tests/ecma_7/AsyncFunctions/syntax.js
index 28e5febc1..0b4b50af4 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/tests/js1_8_5/reflect-parse/PatternBuilders.js b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
index be65bd76e..51b7c6926 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/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp
index e14b77424..7132ed984 100644
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
proto));
if (!asyncFunction)
return false;
- if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto))
+ if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
return false;
+ }
global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
@@ -109,7 +112,7 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
JSObject*
js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
{
- MOZ_ASSERT(unwrapped->isStarGenerator());
+ MOZ_ASSERT(unwrapped->isAsync());
MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
"%FunctionPrototype% fallback in NewFunctionWithProto().");
@@ -171,22 +174,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV
: cx->names().StarGeneratorThrow;
FixedInvokeArgs<1> args(cx);
args[0].set(valueOrReason);
- RootedValue result(cx);
- if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
- return AsyncFunctionThrown(cx, resultPromise);
-
- RootedObject resultObj(cx, &result.toObject());
- RootedValue doneVal(cx);
RootedValue value(cx);
- if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
- return false;
- if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
- return false;
+ if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value))
+ return AsyncFunctionThrown(cx, resultPromise);
- if (doneVal.toBoolean())
- return AsyncFunctionReturned(cx, resultPromise, value);
+ if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait())
+ return AsyncFunctionAwait(cx, resultPromise, value);
- return AsyncFunctionAwait(cx, resultPromise, value);
+ return AsyncFunctionReturned(cx, resultPromise, value);
}
// Async Functions proposal 2.2 steps 3-8.
@@ -242,9 +237,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun)
{
return fun->maybeNative() == WrappedAsyncFunction;
}
-
-MOZ_MUST_USE bool
-js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v)
-{
- return CheckStarGeneratorResumptionValue(cx, v);
-}
diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h
index d7f2c1311..de7c87d13 100644
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -35,9 +35,6 @@ MOZ_MUST_USE bool
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason);
-MOZ_MUST_USE bool
-CheckAsyncResumptionValue(JSContext* cx, HandleValue v);
-
} // namespace js
#endif /* vm_AsyncFunction_h */
diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp
new file mode 100644
index 000000000..300179374
--- /dev/null
+++ b/js/src/vm/AsyncIteration.cpp
@@ -0,0 +1,644 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/AsyncIteration.h"
+
+#include "jsarray.h"
+#include "jscompartment.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/SelfHosting.h"
+
+#include "jscntxtinlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
+#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
+
+// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody.
+static bool
+WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
+ RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
+ RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
+ RootedValue thisValue(cx, args.thisv());
+
+ // Step 1.
+ RootedValue generatorVal(cx);
+ InvokeArgs args2(cx);
+ if (!args2.init(cx, argc))
+ return false;
+ for (size_t i = 0, len = argc; i < len; i++)
+ args2[i].set(args[i]);
+ if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal))
+ return false;
+
+ // Step 2.
+ Rooted<AsyncGeneratorObject*> asyncGenObj(
+ cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal));
+ if (!asyncGenObj)
+ return false;
+
+ // Step 3 (skipped).
+ // Done in AsyncGeneratorObject::create and generator.
+
+ // Step 4.
+ args.rval().setObject(*asyncGenObj);
+ return true;
+}
+
+JSObject*
+js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
+{
+ MOZ_ASSERT(unwrapped->isAsync());
+ MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
+ "%FunctionPrototype% fallback in NewFunctionWithProto().");
+
+ // Create a new function with AsyncGeneratorPrototype, reusing the name and
+ // the length of `unwrapped`.
+
+ RootedAtom funName(cx, unwrapped->explicitName());
+ uint16_t length;
+ if (!JSFunction::getLength(cx, unwrapped, &length))
+ return nullptr;
+
+ RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length,
+ JSFunction::NATIVE_FUN, nullptr,
+ funName, proto,
+ AllocKind::FUNCTION_EXTENDED,
+ TenuredObject));
+ if (!wrapped)
+ return nullptr;
+
+ if (unwrapped->hasCompileTimeName())
+ wrapped->setCompileTimeName(unwrapped->compileTimeName());
+
+ // Link them to each other to make GetWrappedAsyncGenerator and
+ // GetUnwrappedAsyncGenerator work.
+ unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
+ wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
+
+ return wrapped;
+}
+
+JSObject*
+js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped)
+{
+ RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()));
+ if (!proto)
+ return nullptr;
+
+ return WrapAsyncGeneratorWithProto(cx, unwrapped, proto);
+}
+
+bool
+js::IsWrappedAsyncGenerator(JSFunction* fun)
+{
+ return fun->maybeNative() == WrappedAsyncGenerator;
+}
+
+JSFunction*
+js::GetWrappedAsyncGenerator(JSFunction* unwrapped)
+{
+ MOZ_ASSERT(unwrapped->isAsync());
+ return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
+}
+
+JSFunction*
+js::GetUnwrappedAsyncGenerator(JSFunction* wrapped)
+{
+ MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped));
+ JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)
+ .toObject().as<JSFunction>();
+ MOZ_ASSERT(unwrapped->isAsync());
+ return unwrapped;
+}
+
+// Async Iteration proposal 4.1.1 Await Fulfilled Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value);
+}
+
+// Async Iteration proposal 4.1.2 Await Rejected Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
+}
+
+// Async Iteration proposal 11.4.3.7 step 8.d-e.
+MOZ_MUST_USE bool
+js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value);
+}
+
+// Async Iteration proposal 11.4.3.7 step 8.d-e.
+MOZ_MUST_USE bool
+js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
+}
+
+const Class AsyncFromSyncIteratorObject::class_ = {
+ "AsyncFromSyncIteratorObject",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)
+};
+
+// Async Iteration proposal 11.1.3.1.
+JSObject*
+js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
+{
+ // Step 1 (implicit).
+ // Done in bytecode emitted by emitAsyncIterator.
+
+ // Steps 2-4.
+ return AsyncFromSyncIteratorObject::create(cx, iter);
+}
+
+// Async Iteration proposal 11.1.3.1 steps 2-4.
+/* static */ JSObject*
+AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
+{
+ // Step 2.
+ RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx,
+ cx->global()));
+ if (!proto)
+ return nullptr;
+
+ RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>();
+
+ // Step 3.
+ asyncIter->setIterator(iter);
+
+ // Step 4.
+ return asyncIter;
+}
+
+// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
+static bool
+AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
+}
+
+// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return.
+static bool
+AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
+}
+
+// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw.
+static bool
+AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
+}
+
+// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next.
+static bool
+AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, args.get(0),
+ args.rval());
+}
+
+// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return.
+static bool
+AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, args.get(0),
+ args.rval());
+}
+
+// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw.
+static bool
+AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, args.get(0),
+ args.rval());
+}
+
+const Class AsyncGeneratorObject::class_ = {
+ "AsyncGenerator",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots)
+};
+
+// ES 2017 draft 9.1.13.
+template <typename ProtoGetter>
+static JSObject*
+OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun,
+ ProtoGetter protoGetter, const Class* clasp)
+{
+ // Step 1 (skipped).
+
+ // Step 2.
+ RootedValue protoVal(cx);
+ if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal))
+ return nullptr;
+
+ RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
+ if (!proto) {
+ proto = protoGetter(cx, cx->global());
+ if (!proto)
+ return nullptr;
+ }
+
+ // Step 3.
+ return NewNativeObjectWithGivenProto(cx, clasp, proto);
+}
+
+/* static */ AsyncGeneratorObject*
+AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal)
+{
+ MOZ_ASSERT(generatorVal.isObject());
+ MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>());
+
+ RootedObject obj(
+ cx, OrdinaryCreateFromConstructor(cx, asyncGen,
+ GlobalObject::getOrCreateAsyncGeneratorPrototype,
+ &class_));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>();
+
+ // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
+ // Step 6.
+ asyncGenObj->setGenerator(generatorVal);
+
+ // Step 7.
+ asyncGenObj->setSuspendedStart();
+
+ // Step 8.
+ asyncGenObj->clearSingleQueueRequest();
+
+ return asyncGenObj;
+}
+
+static MOZ_MUST_USE bool
+InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val)
+{
+ uint32_t length;
+ if (!GetLengthProperty(cx, queue, &length))
+ return false;
+
+ if (length >= MAX_ARRAY_INDEX) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (!DefineElement(cx, queue, length, val))
+ return false;
+ return SetLengthProperty(cx, queue, length + 1);
+}
+
+static MOZ_MUST_USE bool
+InternalDequeue(JSContext* cx, HandleArrayObject queue, MutableHandleValue val)
+{
+ uint32_t length;
+ if (!GetLengthProperty(cx, queue, &length))
+ return false;
+
+ MOZ_ASSERT(length != 0, "Queue should not be empty here");
+
+ if (!GetElement(cx, queue, queue, 0, val))
+ return false;
+
+ uint32_t newlength = length - 1;
+ RootedValue tmp(cx);
+ for (uint32_t i = 0; i < newlength; i++) {
+ if (!GetElement(cx, queue, queue, i + 1, &tmp))
+ return false;
+ if (!DefineElement(cx, queue, i, tmp))
+ return false;
+ }
+ ObjectOpResult result;
+ if (!DeleteElement(cx, queue, newlength, result))
+ return false;
+ if (!result) {
+ RootedId id(cx, INT_TO_JSID(newlength));
+ return result.reportError(cx, queue, id);
+ }
+ return SetLengthProperty(cx, queue, newlength);
+}
+
+/* static */ MOZ_MUST_USE bool
+AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ Handle<AsyncGeneratorRequest*> request)
+{
+ if (asyncGenObj->isSingleQueue()) {
+ if (asyncGenObj->isSingleQueueEmpty()) {
+ asyncGenObj->setSingleQueueRequest(request);
+ return true;
+ }
+
+ RootedArrayObject queue(cx, NewDenseEmptyArray(cx));
+ if (!queue)
+ return false;
+
+ if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest())))
+ return false;
+ if (!NewbornArrayPush(cx, queue, ObjectValue(*request)))
+ return false;
+
+ asyncGenObj->setQueue(queue);
+ return true;
+ }
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+ RootedValue requestVal(cx, ObjectValue(*request));
+ return InternalEnqueue(cx, queue, requestVal);
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ if (asyncGenObj->isSingleQueue()) {
+ AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest();
+ asyncGenObj->clearSingleQueueRequest();
+ return request;
+ }
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+ RootedValue requestVal(cx);
+ if (!InternalDequeue(cx, queue, &requestVal))
+ return nullptr;
+
+ return &requestVal.toObject().as<AsyncGeneratorRequest>();
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ if (asyncGenObj->isSingleQueue())
+ return asyncGenObj->singleQueueRequest();
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+
+ RootedValue requestVal(cx);
+ if (!GetElement(cx, queue, queue, 0, &requestVal))
+ return nullptr;
+
+ return &requestVal.toObject().as<AsyncGeneratorRequest>();
+}
+
+const Class AsyncGeneratorRequest::class_ = {
+ "AsyncGeneratorRequest",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)
+};
+
+// Async Iteration proposal 11.4.3.1.
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
+ HandleValue completionValue_, HandleObject promise_)
+{
+ RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>();
+ request->setCompletionKind(completionKind_);
+ request->setCompletionValue(completionValue_);
+ request->setPromise(promise_);
+ return request;
+}
+
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g.
+static MOZ_MUST_USE bool
+AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ // Step 5.d.
+ asyncGenObj->setCompleted();
+
+ // Step 5.e (done in bytecode).
+ // Step 5.f.i (implicit).
+
+ // Step 5.g.
+ return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
+}
+
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f.
+static MOZ_MUST_USE bool
+AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ // Step 5.d.
+ asyncGenObj->setCompleted();
+
+ // Not much we can do about uncatchable exceptions, so just bail.
+ if (!cx->isExceptionPending())
+ return false;
+
+ // Step 5.f.i.
+ RootedValue value(cx);
+ if (!GetAndClearException(cx, &value))
+ return false;
+
+ // Step 5.f.ii.
+ return AsyncGeneratorReject(cx, asyncGenObj, value);
+}
+
+// Async Iteration proposal 11.4.3.7 (partially).
+// Most steps are done in generator.
+static MOZ_MUST_USE bool
+AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value)
+{
+ // Step 5 is done in bytecode.
+
+ // Step 6.
+ asyncGenObj->setSuspendedYield();
+
+ // Step 9.
+ return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
+}
+
+// Async Iteration proposal 4.1 Await steps 2-9.
+// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g.
+// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext
+// steps 12-14, 16-20.
+// Execution context switching is handled in generator.
+MOZ_MUST_USE bool
+js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ CompletionKind completionKind, HandleValue argument)
+{
+ RootedValue generatorVal(cx, asyncGenObj->generatorVal());
+
+ // 11.4.3.5 steps 12-14, 16-20.
+ HandlePropertyName funName = completionKind == CompletionKind::Normal
+ ? cx->names().StarGeneratorNext
+ : completionKind == CompletionKind::Throw
+ ? cx->names().StarGeneratorThrow
+ : cx->names().StarGeneratorReturn;
+ FixedInvokeArgs<1> args(cx);
+ args[0].set(argument);
+ RootedValue result(cx);
+ if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) {
+ // 11.4.3.2 step 5.d, f.
+ return AsyncGeneratorThrown(cx, asyncGenObj);
+ }
+
+ // 4.1 steps 2-9.
+ if (asyncGenObj->generatorObj()->isAfterAwait())
+ return AsyncGeneratorAwait(cx, asyncGenObj, result);
+
+ // The following code corresponds to the following 3 cases:
+ // * yield
+ // * yield*
+ // * return
+ // For yield and return, property access is done on an internal result
+ // object and it's not observable.
+ // For yield*, it's done on a possibly user-provided result object, and
+ // it's observable.
+ //
+ // Note that IteratorComplete steps in 8.2.1 are done in bytecode.
+
+ // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
+ RootedObject resultObj(cx, &result.toObject());
+ RootedValue value(cx);
+
+ if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+ return false;
+
+ if (asyncGenObj->generatorObj()->isAfterYield())
+ return AsyncGeneratorYield(cx, asyncGenObj, value);
+
+ // 11.4.3.2 step 5.d-g.
+ return AsyncGeneratorReturned(cx, asyncGenObj, value);
+}
+
+static const JSFunctionSpec async_iterator_proto_methods[] = {
+ JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
+ JS_FS_END
+};
+
+static const JSFunctionSpec async_from_sync_iter_methods[] = {
+ JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
+ JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
+ JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0),
+ JS_FS_END
+};
+
+static const JSFunctionSpec async_generator_methods[] = {
+ JS_FN("next", AsyncGeneratorNext, 1, 0),
+ JS_FN("throw", AsyncGeneratorThrow, 1, 0),
+ JS_FN("return", AsyncGeneratorReturn, 1, 0),
+ JS_FS_END
+};
+
+/* static */ MOZ_MUST_USE bool
+GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
+{
+ if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject())
+ return true;
+
+ // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%.
+ RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
+ if (!asyncIterProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
+ return false;
+
+ // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%.
+ RootedObject asyncFromSyncIterProto(
+ cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+ asyncIterProto));
+ if (!asyncFromSyncIterProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
+ async_from_sync_iter_methods) ||
+ !DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator))
+ {
+ return false;
+ }
+
+ // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%.
+ RootedObject asyncGenProto(
+ cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+ asyncIterProto));
+ if (!asyncGenProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) ||
+ !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator))
+ {
+ return false;
+ }
+
+ // Async Iteration proposal 11.3.3 %AsyncGenerator%.
+ RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+ if (!asyncGenerator)
+ return false;
+ if (!JSObject::setDelegate(cx, asyncGenerator))
+ return false;
+ if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY,
+ JSPROP_READONLY) ||
+ !DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction))
+ {
+ return false;
+ }
+
+ RootedValue function(cx, global->getConstructor(JSProto_Function));
+ if (!function.toObjectOrNull())
+ return false;
+ RootedObject proto(cx, &function.toObject());
+ RootedAtom name(cx, cx->names().AsyncGeneratorFunction);
+
+ // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%.
+ RootedObject asyncGenFunction(
+ cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR,
+ nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject));
+ if (!asyncGenFunction)
+ return false;
+ if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
+ return false;
+ }
+
+ global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
+ global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto));
+ global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator));
+ global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction));
+ global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto));
+ return true;
+}
diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h
new file mode 100644
index 000000000..58c43131b
--- /dev/null
+++ b/js/src/vm/AsyncIteration.h
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_AsyncIteration_h
+#define vm_AsyncIteration_h
+
+#include "jscntxt.h"
+#include "jsobj.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+
+namespace js {
+
+// Async generator consists of 2 functions, |wrapped| and |unwrapped|.
+// |unwrapped| is a generator function compiled from async generator script,
+// |await| behaves just like |yield| there. |unwrapped| isn't exposed to user
+// script.
+// |wrapped| is a native function that is the value of async generator.
+
+JSObject*
+WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto);
+
+JSObject*
+WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped);
+
+bool
+IsWrappedAsyncGenerator(JSFunction* fun);
+
+JSFunction*
+GetWrappedAsyncGenerator(JSFunction* unwrapped);
+
+JSFunction*
+GetUnwrappedAsyncGenerator(JSFunction* wrapped);
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value);
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason);
+MOZ_MUST_USE bool
+AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value);
+MOZ_MUST_USE bool
+AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason);
+
+class AsyncGeneratorRequest : public NativeObject
+{
+ private:
+ enum AsyncGeneratorRequestSlots {
+ Slot_CompletionKind = 0,
+ Slot_CompletionValue,
+ Slot_Promise,
+ Slots,
+ };
+
+ void setCompletionKind(CompletionKind completionKind_) {
+ setFixedSlot(Slot_CompletionKind,
+ Int32Value(static_cast<int32_t>(completionKind_)));
+ }
+ void setCompletionValue(HandleValue completionValue_) {
+ setFixedSlot(Slot_CompletionValue, completionValue_);
+ }
+ void setPromise(HandleObject promise_) {
+ setFixedSlot(Slot_Promise, ObjectValue(*promise_));
+ }
+
+ public:
+ static const Class class_;
+
+ static AsyncGeneratorRequest*
+ create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
+ HandleObject promise);
+
+ CompletionKind completionKind() const {
+ return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
+ }
+ JS::Value completionValue() const {
+ return getFixedSlot(Slot_CompletionValue);
+ }
+ JSObject* promise() const {
+ return &getFixedSlot(Slot_Promise).toObject();
+ }
+};
+
+class AsyncGeneratorObject : public NativeObject
+{
+ private:
+ enum AsyncGeneratorObjectSlots {
+ Slot_State = 0,
+ Slot_Generator,
+ Slot_QueueOrRequest,
+ Slots
+ };
+
+ enum State {
+ State_SuspendedStart,
+ State_SuspendedYield,
+ State_Executing,
+ // State_AwaitingYieldReturn corresponds to the case that
+ // AsyncGenerator#return is called while State_Executing,
+ // just like the case that AsyncGenerator#return is called
+ // while State_Completed.
+ State_AwaitingYieldReturn,
+ State_AwaitingReturn,
+ State_Completed
+ };
+
+ State state() const {
+ return static_cast<State>(getFixedSlot(Slot_State).toInt32());
+ }
+ void setState(State state_) {
+ setFixedSlot(Slot_State, Int32Value(state_));
+ }
+
+ void setGenerator(const Value& value) {
+ setFixedSlot(Slot_Generator, value);
+ }
+
+ // Queue is implemented in 2 ways. If only one request is queued ever,
+ // request is stored directly to the slot. Once 2 requests are queued, an
+ // array is created and requests are pushed into it, and the array is
+ // stored to the slot.
+
+ bool isSingleQueue() const {
+ return getFixedSlot(Slot_QueueOrRequest).isNull() ||
+ getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>();
+ }
+ bool isSingleQueueEmpty() const {
+ return getFixedSlot(Slot_QueueOrRequest).isNull();
+ }
+ void setSingleQueueRequest(AsyncGeneratorRequest* request) {
+ setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
+ }
+ void clearSingleQueueRequest() {
+ setFixedSlot(Slot_QueueOrRequest, NullHandleValue);
+ }
+ AsyncGeneratorRequest* singleQueueRequest() const {
+ return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
+ }
+
+ ArrayObject* queue() const {
+ return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>();
+ }
+ void setQueue(JSObject* queue_) {
+ setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
+ }
+
+ public:
+ static const Class class_;
+
+ static AsyncGeneratorObject*
+ create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal);
+
+ bool isSuspendedStart() const {
+ return state() == State_SuspendedStart;
+ }
+ bool isSuspendedYield() const {
+ return state() == State_SuspendedYield;
+ }
+ bool isExecuting() const {
+ return state() == State_Executing;
+ }
+ bool isAwaitingYieldReturn() const {
+ return state() == State_AwaitingYieldReturn;
+ }
+ bool isAwaitingReturn() const {
+ return state() == State_AwaitingReturn;
+ }
+ bool isCompleted() const {
+ return state() == State_Completed;
+ }
+
+ void setSuspendedStart() {
+ setState(State_SuspendedStart);
+ }
+ void setSuspendedYield() {
+ setState(State_SuspendedYield);
+ }
+ void setExecuting() {
+ setState(State_Executing);
+ }
+ void setAwaitingYieldReturn() {
+ setState(State_AwaitingYieldReturn);
+ }
+ void setAwaitingReturn() {
+ setState(State_AwaitingReturn);
+ }
+ void setCompleted() {
+ setState(State_Completed);
+ }
+
+ JS::Value generatorVal() const {
+ return getFixedSlot(Slot_Generator);
+ }
+ GeneratorObject* generatorObj() const {
+ return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>();
+ }
+
+ static MOZ_MUST_USE bool
+ enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ Handle<AsyncGeneratorRequest*> request);
+
+ static AsyncGeneratorRequest*
+ dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+ static AsyncGeneratorRequest*
+ peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+ bool isQueueEmpty() const {
+ if (isSingleQueue())
+ return isSingleQueueEmpty();
+ return queue()->length() == 0;
+ }
+};
+
+JSObject*
+CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter);
+
+class AsyncFromSyncIteratorObject : public NativeObject
+{
+ private:
+ enum AsyncFromSyncIteratorObjectSlots {
+ Slot_Iterator = 0,
+ Slots
+ };
+
+ void setIterator(HandleObject iterator_) {
+ setFixedSlot(Slot_Iterator, ObjectValue(*iterator_));
+ }
+
+ public:
+ static const Class class_;
+
+ static JSObject*
+ create(JSContext* cx, HandleObject iter);
+
+ JSObject* iterator() const {
+ return &getFixedSlot(Slot_Iterator).toObject();
+ }
+};
+
+MOZ_MUST_USE bool
+AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ CompletionKind completionKind, HandleValue argument);
+
+} // namespace js
+
+#endif /* vm_AsyncIteration_h */
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index 99cb02e58..6a8afb56b 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -29,7 +29,10 @@
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(as, as, "as") \
macro(Async, Async, "Async") \
+ macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \
macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
+ macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \
+ macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \
macro(await, await, "await") \
@@ -311,6 +314,7 @@
macro(star, star, "*") \
macro(starDefaultStar, starDefaultStar, "*default*") \
macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
+ macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(state, state, "state") \
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index d68d1b75e..2680699a4 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -32,7 +32,6 @@
#include "js/Vector.h"
#include "proxy/ScriptedProxyHandler.h"
#include "vm/ArgumentsObject.h"
-#include "vm/AsyncFunction.h"
#include "vm/DebuggerMemory.h"
#include "vm/GeneratorObject.h"
#include "vm/SPSProfiler.h"
@@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleVa
JSTrapStatus status, MutableHandleValue vp)
{
if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
- // Don't let a { return: ... } resumption value make a generator or
- // async function violate the iterator protocol. The return value from
+ // Don't let a { return: ... } resumption value make a generator
+ // function violate the iterator protocol. The return value from
// such a frame must have the form { done: <bool>, value: <anything> }.
RootedFunction callee(cx, frame.callee());
- if (callee->isAsync()) {
- if (!CheckAsyncResumptionValue(cx, vp)) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT);
- return false;
- }
- } else if (callee->isStarGenerator()) {
+ if (callee->isStarGenerator()) {
if (!CheckStarGeneratorResumptionValue(cx, vp)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
return false;
@@ -7356,7 +7350,8 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
/* static */ bool
DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
{
- return DebuggerFrame::getReferent(frame).script()->isGenerator();
+ return DebuggerFrame::getReferent(frame).script()->isStarGenerator() ||
+ DebuggerFrame::getReferent(frame).script()->isLegacyGenerator();
}
/* static */ bool
diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
index c95bb0597..7834940f1 100644
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2448,7 +2448,9 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
// Generators should always have environments.
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
- !ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator());
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isStarGenerator() &&
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isLegacyGenerator() &&
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isAsync());
if (!CanUseDebugEnvironmentMaps(cx))
return true;
@@ -2594,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
if (!frame.environmentChain()->is<CallObject>())
return;
- if (frame.callee()->isGenerator())
+ if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
+ frame.callee()->isAsync())
+ {
return;
+ }
CallObject& callobj = frame.environmentChain()->as<CallObject>();
envs->liveEnvs.remove(&callobj);
@@ -2726,8 +2731,13 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
if (frame.environmentChain()->compartment() != cx->compartment())
continue;
- if (frame.isFunctionFrame() && frame.callee()->isGenerator())
- continue;
+ if (frame.isFunctionFrame()) {
+ if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
+ frame.callee()->isAsync())
+ {
+ continue;
+ }
+ }
if (!frame.isDebuggee())
continue;
@@ -2882,7 +2892,8 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
if (ei.scope().is<FunctionScope>()) {
RootedFunction callee(cx, ei.scope().as<FunctionScope>().canonicalFunction());
// Generators should always reify their scopes.
- MOZ_ASSERT(!callee->isGenerator());
+ MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() &&
+ !callee->isAsync());
JS::ExposeObjectToActiveJS(callee);
Rooted<CallObject*> callobj(cx, CallObject::createHollowForDebug(cx, callee));
diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp
index ba28501e6..2f5c63c28 100644
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -19,12 +19,13 @@ using namespace js;
JSObject*
GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
{
- MOZ_ASSERT(frame.script()->isGenerator());
+ MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() ||
+ frame.script()->isAsync());
MOZ_ASSERT(frame.script()->nfixed() == 0);
Rooted<GlobalObject*> global(cx, cx->global());
RootedNativeObject obj(cx);
- if (frame.script()->isStarGenerator()) {
+ if (frame.script()->isStarGenerator() || frame.script()->isAsync()) {
RootedValue pval(cx);
RootedObject fun(cx, frame.callee());
// FIXME: This would be faster if we could avoid doing a lookup to get
@@ -63,10 +64,14 @@ bool
GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
Value* vp, unsigned nvalues)
{
- MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
+ MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
MOZ_ASSERT(!genObj->hasExpressionStack());
+ MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
+ MOZ_ASSERT_IF(*pc == JSOP_YIELD,
+ genObj->callee().isStarGenerator() ||
+ genObj->callee().isLegacyGenerator());
if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) {
RootedValue val(cx, ObjectValue(*frame.callee()));
@@ -74,8 +79,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
return false;
}
- uint32_t yieldIndex = GET_UINT24(pc);
- genObj->setYieldIndex(yieldIndex);
+ uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
+ genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex);
genObj->setEnvironmentChain(*frame.environmentChain());
if (nvalues) {
@@ -172,7 +177,7 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
}
JSScript* script = callee->nonLazyScript();
- uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
+ uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()];
activation.regs().pc = script->offsetToPC(offset);
// Always push on a value, even if we are raising an exception. In the
@@ -311,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto))
return false;
- if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
+ if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY,
+ JSPROP_READONLY) ||
!DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
{
return false;
@@ -328,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
SingletonObject));
if (!genFunction)
return false;
- if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
+ if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
return false;
+ }
global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction));
@@ -365,3 +374,34 @@ js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
return true;
}
+
+bool
+GeneratorObject::isAfterYield()
+{
+ return isAfterYieldOrAwait(JSOP_YIELD);
+}
+
+bool
+GeneratorObject::isAfterAwait()
+{
+ return isAfterYieldOrAwait(JSOP_AWAIT);
+}
+
+bool
+GeneratorObject::isAfterYieldOrAwait(JSOp op)
+{
+ if (isClosed() || isClosing() || isRunning())
+ return false;
+
+ JSScript* script = callee().nonLazyScript();
+ jsbytecode* code = script->code();
+ uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()];
+ if (code[nextOffset] != JSOP_DEBUGAFTERYIELD)
+ return false;
+
+ uint32_t offset = nextOffset - JSOP_YIELD_LENGTH;
+ MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
+ code[offset] == JSOP_AWAIT);
+
+ return code[offset] == op;
+}
diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h
index ca1452b34..f19ca2aac 100644
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -21,15 +21,15 @@ class GeneratorObject : public NativeObject
public:
// Magic values stored in the yield index slot when the generator is
// running or closing. See the yield index comment below.
- static const int32_t YIELD_INDEX_RUNNING = INT32_MAX;
- static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1;
+ static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX;
+ static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1;
enum {
CALLEE_SLOT = 0,
ENV_CHAIN_SLOT,
ARGS_OBJ_SLOT,
EXPRESSION_STACK_SLOT,
- YIELD_INDEX_SLOT,
+ YIELD_AND_AWAIT_INDEX_SLOT,
NEWTARGET_SLOT,
RESERVED_SLOTS
};
@@ -124,47 +124,48 @@ class GeneratorObject : public NativeObject
// The yield index slot is abused for a few purposes. It's undefined if
// it hasn't been set yet (before the initial yield), and null if the
// generator is closed. If the generator is running, the yield index is
- // YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing"
- // state, the yield index is YIELD_INDEX_CLOSING.
+ // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre
+ // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING.
//
// If the generator is suspended, it's the yield index (stored as
- // JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction that
- // suspended the generator. The yield index can be mapped to the bytecode
- // offset (interpreter) or to the native code offset (JIT).
+ // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield
+ // instruction that suspended the generator. The yield index can be mapped
+ // to the bytecode offset (interpreter) or to the native code offset (JIT).
bool isRunning() const {
MOZ_ASSERT(!isClosed());
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING;
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING;
}
bool isClosing() const {
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING;
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING;
}
bool isSuspended() const {
// Note: also update Baseline's IsSuspendedStarGenerator code if this
// changes.
MOZ_ASSERT(!isClosed());
- static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING,
- "test below should return false for YIELD_INDEX_RUNNING");
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING;
+ static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING,
+ "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING");
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING;
}
void setRunning() {
MOZ_ASSERT(isSuspended());
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING));
}
void setClosing() {
MOZ_ASSERT(isSuspended());
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING));
- }
- void setYieldIndex(uint32_t yieldIndex) {
- MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined());
- MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing());
- MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING));
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING));
+ }
+ void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) {
+ MOZ_ASSERT_IF(yieldAndAwaitIndex == 0,
+ getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined());
+ MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing());
+ MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex));
MOZ_ASSERT(isSuspended());
}
- uint32_t yieldIndex() const {
+ uint32_t yieldAndAwaitIndex() const {
MOZ_ASSERT(isSuspended());
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32();
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32();
}
bool isClosed() const {
return getFixedSlot(CALLEE_SLOT).isNull();
@@ -174,10 +175,17 @@ class GeneratorObject : public NativeObject
setFixedSlot(ENV_CHAIN_SLOT, NullValue());
setFixedSlot(ARGS_OBJ_SLOT, NullValue());
setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
- setFixedSlot(YIELD_INDEX_SLOT, NullValue());
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue());
setFixedSlot(NEWTARGET_SLOT, NullValue());
}
+ bool isAfterYield();
+ bool isAfterAwait();
+
+private:
+ bool isAfterYieldOrAwait(JSOp op);
+
+public:
static size_t offsetOfCalleeSlot() {
return getFixedSlotOffset(CALLEE_SLOT);
}
@@ -187,8 +195,8 @@ class GeneratorObject : public NativeObject
static size_t offsetOfArgsObjSlot() {
return getFixedSlotOffset(ARGS_OBJ_SLOT);
}
- static size_t offsetOfYieldIndexSlot() {
- return getFixedSlotOffset(YIELD_INDEX_SLOT);
+ static size_t offsetOfYieldAndAwaitIndexSlot() {
+ return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT);
}
static size_t offsetOfExpressionStackSlot() {
return getFixedSlotOffset(EXPRESSION_STACK_SLOT);
diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
index 013208f66..70c33c647 100644
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -586,17 +586,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*
}
bool
-js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_)
+js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_,
+ unsigned prototypeAttrs, unsigned constructorAttrs)
{
RootedObject ctor(cx, ctor_), proto(cx, proto_);
RootedValue protoVal(cx, ObjectValue(*proto));
RootedValue ctorVal(cx, ObjectValue(*ctor));
- return DefineProperty(cx, ctor, cx->names().prototype, protoVal,
- nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) &&
- DefineProperty(cx, proto, cx->names().constructor, ctorVal,
- nullptr, nullptr, 0);
+ return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr,
+ prototypeAttrs) &&
+ DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr,
+ constructorAttrs);
}
bool
diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
index 9179abbb7..3fd2762f8 100644
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -100,6 +100,11 @@ class GlobalObject : public NativeObject
STAR_GENERATOR_FUNCTION,
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
+ ASYNC_ITERATOR_PROTO,
+ ASYNC_FROM_SYNC_ITERATOR_PROTO,
+ ASYNC_GENERATOR,
+ ASYNC_GENERATOR_FUNCTION,
+ ASYNC_GENERATOR_PROTO,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
COLLATOR_PROTO,
@@ -624,6 +629,36 @@ class GlobalObject : public NativeObject
return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction);
}
+ static NativeObject*
+ getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
+ {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
+ static NativeObject*
+ getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
+ static NativeObject*
+ getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR,
+ initAsyncGenerators));
+ }
+
+ static JSObject*
+ getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators);
+ }
+
+ static NativeObject*
+ getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
static JSObject*
getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto);
@@ -782,6 +817,8 @@ class GlobalObject : public NativeObject
static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global);
+ static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global);
+
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
@@ -985,12 +1022,14 @@ GlobalObject::createArrayFromBuffer<uint8_clamped>() const
}
/*
- * Define ctor.prototype = proto as non-enumerable, non-configurable, and
- * non-writable; define proto.constructor = ctor as non-enumerable but
- * configurable and writable.
+ * Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
+ * non-configurable, and non-writable; and define proto.constructor = ctor as
+ * non-enumerable but configurable and writable.
*/
extern bool
-LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto);
+LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto,
+ unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY,
+ unsigned constructorAttrs = 0);
/*
* Define properties and/or functions on any object. Either ps or fs, or both,
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 030f0f3b6..3cf9b57f6 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -39,6 +39,7 @@
#include "jit/Ion.h"
#include "jit/IonAnalysis.h"
#include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
#include "vm/Debugger.h"
#include "vm/GeneratorObject.h"
#include "vm/Opcodes.h"
@@ -1939,9 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode)
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
CASE(JSOP_UNUSED126)
-CASE(JSOP_UNUSED192)
-CASE(JSOP_UNUSED209)
-CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
CASE(JSOP_UNUSED221)
@@ -3585,6 +3583,29 @@ CASE(JSOP_TOASYNC)
}
END_CASE(JSOP_TOASYNC)
+CASE(JSOP_TOASYNCGEN)
+{
+ ReservedRooted<JSFunction*> unwrapped(&rootFunction0,
+ &REGS.sp[-1].toObject().as<JSFunction>());
+ JSObject* wrapped = WrapAsyncGenerator(cx, unwrapped);
+ if (!wrapped)
+ goto error;
+
+ REGS.sp[-1].setObject(*wrapped);
+}
+END_CASE(JSOP_TOASYNCGEN)
+
+CASE(JSOP_TOASYNCITER)
+{
+ ReservedRooted<JSObject*> iter(&rootObject1, &REGS.sp[-1].toObject());
+ JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter);
+ if (!asyncIter)
+ goto error;
+
+ REGS.sp[-1].setObject(*asyncIter);
+}
+END_CASE(JSOP_TOASYNCITER)
+
CASE(JSOP_SETFUNNAME)
{
MOZ_ASSERT(REGS.stackDepth() >= 2);
@@ -3994,6 +4015,7 @@ CASE(JSOP_INITIALYIELD)
}
CASE(JSOP_YIELD)
+CASE(JSOP_AWAIT)
{
MOZ_ASSERT(!cx->isExceptionPending());
MOZ_ASSERT(REGS.fp()->isFunctionFrame());
@@ -5126,6 +5148,10 @@ js::ThrowCheckIsObject(JSContext* cx, CheckIsObjectKind kind)
case CheckIsObjectKind::GetIterator:
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_GET_ITER_RETURNED_PRIMITIVE);
break;
+ case CheckIsObjectKind::GetAsyncIterator:
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+ JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE);
+ break;
default:
MOZ_CRASH("Unknown kind");
}
diff --git a/js/src/vm/Interpreter.h b/js/src/vm/Interpreter.h
index 9fefd75cc..5e7087f7f 100644
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -561,7 +561,8 @@ enum class CheckIsObjectKind : uint8_t {
IteratorNext,
IteratorReturn,
IteratorThrow,
- GetIterator
+ GetIterator,
+ GetAsyncIterator
};
bool
diff --git a/js/src/vm/ObjectGroup.cpp b/js/src/vm/ObjectGroup.cpp
index ec0a7aec1..91070b3f6 100644
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -184,7 +184,7 @@ ObjectGroup::useSingletonForNewObject(JSContext* cx, JSScript* script, jsbytecod
* Sub2 lets us continue to distinguish the two subclasses and any extra
* properties added to those prototype objects.
*/
- if (script->isGenerator())
+ if (script->isStarGenerator() || script->isLegacyGenerator() || script->isAsync())
return false;
if (JSOp(*pc) != JSOP_NEW)
return false;
diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h
index 3c4d61a67..e13cd6221 100644
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1952,7 +1952,15 @@
* Stack: this => this
*/ \
macro(JSOP_CHECKTHISREINIT,191,"checkthisreinit",NULL,1, 1, 1, JOF_BYTE) \
- macro(JSOP_UNUSED192, 192,"unused192", NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Pops the top of stack value as 'unwrapped', converts it to async
+ * generator 'wrapped', and pushes 'wrapped' back on the stack.
+ * Category: Statements
+ * Type: Generator
+ * Operands:
+ * Stack: unwrapped => wrapped
+ */ \
+ macro(JSOP_TOASYNCGEN, 192, "toasyncgen", NULL, 1, 1, 1, JOF_BYTE) \
\
/*
* Pops the top two values on the stack as 'propval' and 'obj', pushes
@@ -2055,7 +2063,7 @@
* interpretation.
* Category: Statements
* Type: Generator
- * Operands: uint24_t yieldIndex
+ * Operands: uint24_t yieldAndAwaitIndex
* Stack: generator =>
*/ \
macro(JSOP_INITIALYIELD, 202,"initialyield", NULL, 4, 1, 1, JOF_UINT24) \
@@ -2064,7 +2072,7 @@
* returns 'rval1'. Pushes sent value from 'send()' onto the stack.
* Category: Statements
* Type: Generator
- * Operands: uint24_t yieldIndex
+ * Operands: uint24_t yieldAndAwaitIndex
* Stack: rval1, gen => rval2
*/ \
macro(JSOP_YIELD, 203,"yield", NULL, 4, 2, 1, JOF_UINT24) \
@@ -2119,8 +2127,24 @@
* Stack: =>
*/ \
macro(JSOP_DEBUGAFTERYIELD, 208, "debugafteryield", NULL, 1, 0, 0, JOF_BYTE) \
- macro(JSOP_UNUSED209, 209, "unused209", NULL, 1, 0, 0, JOF_BYTE) \
- macro(JSOP_UNUSED210, 210, "unused210", NULL, 1, 0, 0, JOF_BYTE) \
+ /*
+ * Pops the generator and the return value 'promise', stops interpretation
+ * and returns 'promise'. Pushes resolved value onto the stack.
+ * Category: Statements
+ * Type: Generator
+ * Operands: uint24_t yieldAndAwaitIndex
+ * Stack: promise, gen => resolved
+ */ \
+ macro(JSOP_AWAIT, 209, "await", NULL, 4, 2, 1, JOF_UINT24) \
+ /*
+ * Pops the iterator from the top of the stack, and create async iterator
+ * from it and push the async iterator back onto the stack.
+ * Category: Statements
+ * Type: Generator
+ * Operands:
+ * Stack: iter => asynciter
+ */ \
+ macro(JSOP_TOASYNCITER, 210, "toasynciter", NULL, 1, 1, 1, JOF_BYTE) \
macro(JSOP_UNUSED211, 211, "unused211", NULL, 1, 0, 0, JOF_BYTE) \
/*
* Initializes generator frame, creates a generator and pushes it on the
diff --git a/js/src/vm/Probes-inl.h b/js/src/vm/Probes-inl.h
index 347f842b8..822a8ac59 100644
--- a/js/src/vm/Probes-inl.h
+++ b/js/src/vm/Probes-inl.h
@@ -41,7 +41,10 @@ probes::EnterScript(JSContext* cx, JSScript* script, JSFunction* maybeFun,
if (rt->spsProfiler.enabled()) {
if (!rt->spsProfiler.enter(cx, script, maybeFun))
return false;
- MOZ_ASSERT_IF(!fp->script()->isGenerator(), !fp->hasPushedSPSFrame());
+ MOZ_ASSERT_IF(!fp->script()->isStarGenerator() &&
+ !fp->script()->isLegacyGenerator() &&
+ !fp->script()->isAsync(),
+ !fp->hasPushedSPSFrame());
fp->setPushedSPSFrame();
}
diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp
index ffd707b14..2216bf91e 100644
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -27,6 +27,7 @@
#include "builtin/MapObject.h"
#include "builtin/ModuleObject.h"
#include "builtin/Object.h"
+#include "builtin/Promise.h"
#include "builtin/Reflect.h"
#include "builtin/SelfHostingDefines.h"
#include "builtin/SIMD.h"
@@ -2135,6 +2136,105 @@ intrinsic_PromiseResolve(JSContext* cx, unsigned argc, Value* vp)
return true;
}
+static bool
+intrinsic_CreatePendingPromise(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 0);
+ RootedObject promise(cx, PromiseObject::createSkippingExecutor(cx));
+ if (!promise)
+ return false;
+ args.rval().setObject(*promise);
+ return true;
+}
+
+static bool
+intrinsic_CreatePromiseResolvedWith(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 1);
+ RootedObject promise(cx, PromiseObject::unforgeableResolve(cx, args[0]));
+ if (!promise)
+ return false;
+ args.rval().setObject(*promise);
+ return true;
+}
+
+static bool
+intrinsic_CreatePromiseRejectedWith(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 1);
+ RootedObject promise(cx, PromiseObject::unforgeableReject(cx, args[0]));
+ if (!promise)
+ return false;
+ args.rval().setObject(*promise);
+ return true;
+}
+
+static bool
+intrinsic_ResolvePromise(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 2);
+ Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
+ if (!PromiseObject::resolve(cx, promise, args[1]))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+intrinsic_RejectPromise(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() == 2);
+ Rooted<PromiseObject*> promise(cx, &args[0].toObject().as<PromiseObject>());
+ if (!PromiseObject::reject(cx, promise, args[1]))
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+intrinsic_CallOriginalPromiseThen(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() >= 2);
+
+ RootedObject promise(cx, &args[0].toObject());
+ Value val = args[1];
+ RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
+ val = args.get(2);
+ RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
+
+ RootedObject resultPromise(cx, JS::CallOriginalPromiseThen(cx, promise, onResolvedObj,
+ onRejectedObj));
+ if (!resultPromise)
+ return false;
+ args.rval().setObject(*resultPromise);
+ return true;
+}
+
+static bool
+intrinsic_AddPromiseReactions(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(args.length() >= 2);
+
+ RootedObject promise(cx, &args[0].toObject());
+ Value val = args[1];
+ RootedObject onResolvedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
+ val = args.get(2);
+ RootedObject onRejectedObj(cx, val.isUndefined() ? nullptr : val.toObjectOrNull());
+
+ bool result = JS::AddPromiseReactions(cx, promise, onResolvedObj, onRejectedObj);
+ if (!result)
+ return false;
+ args.rval().setUndefined();
+ return true;
+}
+
// The self-hosting global isn't initialized with the normal set of builtins.
// Instead, individual C++-implemented functions that're required by
// self-hosted code are defined as global functions. Accessing these
@@ -2536,6 +2636,14 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("AddModuleNamespaceBinding", intrinsic_AddModuleNamespaceBinding, 4, 0),
JS_FN("ModuleNamespaceExports", intrinsic_ModuleNamespaceExports, 1, 0),
+ JS_FN("CreatePendingPromise", intrinsic_CreatePendingPromise, 0, 0),
+ JS_FN("CreatePromiseResolvedWith", intrinsic_CreatePromiseResolvedWith, 1, 0),
+ JS_FN("CreatePromiseRejectedWith", intrinsic_CreatePromiseRejectedWith, 1, 0),
+ JS_FN("ResolvePromise", intrinsic_ResolvePromise, 2, 0),
+ JS_FN("RejectPromise", intrinsic_RejectPromise, 2, 0),
+ JS_FN("AddPromiseReactions", intrinsic_AddPromiseReactions, 3, 0),
+ JS_FN("CallOriginalPromiseThen", intrinsic_CallOriginalPromiseThen, 3, 0),
+
JS_FN("IsPromiseObject", intrinsic_IsInstanceOfBuiltin<PromiseObject>, 1, 0),
JS_FN("CallPromiseMethodIfWrapped", CallNonGenericSelfhostedMethod<Is<PromiseObject>>, 2, 0),
JS_FN("PromiseResolve", intrinsic_PromiseResolve, 2, 0),
@@ -3005,7 +3113,8 @@ JSRuntime::cloneSelfHostedFunctionScript(JSContext* cx, HandlePropertyName name,
return false;
// JSFunction::generatorKind can't handle lazy self-hosted functions, so we make sure there
// aren't any.
- MOZ_ASSERT(!sourceFun->isGenerator());
+ MOZ_ASSERT(!sourceFun->isStarGenerator() && !sourceFun->isLegacyGenerator() &&
+ !sourceFun->isAsync());
MOZ_ASSERT(targetFun->isExtended());
MOZ_ASSERT(targetFun->isInterpretedLazy());
MOZ_ASSERT(targetFun->isSelfHostedBuiltin());
diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h
index 11a19d175..2f2b591e5 100644
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -335,7 +335,7 @@ InterpreterStack::resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs,
HandleFunction callee, HandleValue newTarget,
HandleObject envChain)
{
- MOZ_ASSERT(callee->isGenerator());
+ MOZ_ASSERT(callee->isStarGenerator() || callee->isLegacyGenerator() || callee->isAsync());
RootedScript script(cx, JSFunction::getOrCreateScript(cx, callee));
InterpreterFrame* prev = regs.fp();
jsbytecode* prevpc = regs.pc;
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index c5f2cf5f3..95940eeaf 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -270,7 +270,9 @@ InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc)
UnwindAllEnvironmentsInFrame(cx, ei);
if (isFunctionFrame()) {
- if (!callee().isGenerator() &&
+ if (!callee().isStarGenerator() &&
+ !callee().isLegacyGenerator() &&
+ !callee().isAsync() &&
isConstructing() &&
thisArgument().isObject() &&
returnValue().isPrimitive())
diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h
index 23e621344..fe04a00f2 100644
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -693,7 +693,8 @@ class InterpreterFrame
}
void resumeGeneratorFrame(JSObject* envChain) {
- MOZ_ASSERT(script()->isGenerator());
+ MOZ_ASSERT(script()->isStarGenerator() || script()->isLegacyGenerator() ||
+ script()->isAsync());
MOZ_ASSERT(isFunctionFrame());
flags_ |= HAS_INITIAL_ENV;
envChain_ = envChain;
diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp
index 52b8eeed1..f3eebb85e 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");