summaryrefslogtreecommitdiffstats
path: root/js/src/vm
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 /js/src/vm
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
Diffstat (limited to 'js/src/vm')
-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
20 files changed, 1247 insertions, 97 deletions
diff --git a/js/src/vm/AsyncFunction.cpp b/js/src/vm/AsyncFunction.cpp
index e14b77424..7132ed984 100644
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -40,8 +40,11 @@ GlobalObject::initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global)
proto));
if (!asyncFunction)
return false;
- if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto))
+ if (!LinkConstructorAndPrototype(cx, asyncFunction, asyncFunctionProto,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
return false;
+ }
global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
global->setReservedSlot(ASYNC_FUNCTION_PROTO, ObjectValue(*asyncFunctionProto));
@@ -109,7 +112,7 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
JSObject*
js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
{
- MOZ_ASSERT(unwrapped->isStarGenerator());
+ MOZ_ASSERT(unwrapped->isAsync());
MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
"%FunctionPrototype% fallback in NewFunctionWithProto().");
@@ -171,22 +174,14 @@ AsyncFunctionResume(JSContext* cx, Handle<PromiseObject*> resultPromise, HandleV
: cx->names().StarGeneratorThrow;
FixedInvokeArgs<1> args(cx);
args[0].set(valueOrReason);
- RootedValue result(cx);
- if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result))
- return AsyncFunctionThrown(cx, resultPromise);
-
- RootedObject resultObj(cx, &result.toObject());
- RootedValue doneVal(cx);
RootedValue value(cx);
- if (!GetProperty(cx, resultObj, resultObj, cx->names().done, &doneVal))
- return false;
- if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
- return false;
+ if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &value))
+ return AsyncFunctionThrown(cx, resultPromise);
- if (doneVal.toBoolean())
- return AsyncFunctionReturned(cx, resultPromise, value);
+ if (generatorVal.toObject().as<GeneratorObject>().isAfterAwait())
+ return AsyncFunctionAwait(cx, resultPromise, value);
- return AsyncFunctionAwait(cx, resultPromise, value);
+ return AsyncFunctionReturned(cx, resultPromise, value);
}
// Async Functions proposal 2.2 steps 3-8.
@@ -242,9 +237,3 @@ js::IsWrappedAsyncFunction(JSFunction* fun)
{
return fun->maybeNative() == WrappedAsyncFunction;
}
-
-MOZ_MUST_USE bool
-js::CheckAsyncResumptionValue(JSContext* cx, HandleValue v)
-{
- return CheckStarGeneratorResumptionValue(cx, v);
-}
diff --git a/js/src/vm/AsyncFunction.h b/js/src/vm/AsyncFunction.h
index d7f2c1311..de7c87d13 100644
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -35,9 +35,6 @@ MOZ_MUST_USE bool
AsyncFunctionAwaitedRejected(JSContext* cx, Handle<PromiseObject*> resultPromise,
HandleValue generatorVal, HandleValue reason);
-MOZ_MUST_USE bool
-CheckAsyncResumptionValue(JSContext* cx, HandleValue v);
-
} // namespace js
#endif /* vm_AsyncFunction_h */
diff --git a/js/src/vm/AsyncIteration.cpp b/js/src/vm/AsyncIteration.cpp
new file mode 100644
index 000000000..300179374
--- /dev/null
+++ b/js/src/vm/AsyncIteration.cpp
@@ -0,0 +1,644 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "vm/AsyncIteration.h"
+
+#include "jsarray.h"
+#include "jscompartment.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+#include "vm/GlobalObject.h"
+#include "vm/Interpreter.h"
+#include "vm/SelfHosting.h"
+
+#include "jscntxtinlines.h"
+#include "jsobjinlines.h"
+
+#include "vm/NativeObject-inl.h"
+
+using namespace js;
+using namespace js::gc;
+
+#define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
+#define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
+
+// Async Iteration proposal 8.3.10 Runtime Semantics: EvaluateBody.
+static bool
+WrappedAsyncGenerator(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ RootedFunction wrapped(cx, &args.callee().as<JSFunction>());
+ RootedValue unwrappedVal(cx, wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT));
+ RootedFunction unwrapped(cx, &unwrappedVal.toObject().as<JSFunction>());
+ RootedValue thisValue(cx, args.thisv());
+
+ // Step 1.
+ RootedValue generatorVal(cx);
+ InvokeArgs args2(cx);
+ if (!args2.init(cx, argc))
+ return false;
+ for (size_t i = 0, len = argc; i < len; i++)
+ args2[i].set(args[i]);
+ if (!Call(cx, unwrappedVal, thisValue, args2, &generatorVal))
+ return false;
+
+ // Step 2.
+ Rooted<AsyncGeneratorObject*> asyncGenObj(
+ cx, AsyncGeneratorObject::create(cx, wrapped, generatorVal));
+ if (!asyncGenObj)
+ return false;
+
+ // Step 3 (skipped).
+ // Done in AsyncGeneratorObject::create and generator.
+
+ // Step 4.
+ args.rval().setObject(*asyncGenObj);
+ return true;
+}
+
+JSObject*
+js::WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
+{
+ MOZ_ASSERT(unwrapped->isAsync());
+ MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
+ "%FunctionPrototype% fallback in NewFunctionWithProto().");
+
+ // Create a new function with AsyncGeneratorPrototype, reusing the name and
+ // the length of `unwrapped`.
+
+ RootedAtom funName(cx, unwrapped->explicitName());
+ uint16_t length;
+ if (!JSFunction::getLength(cx, unwrapped, &length))
+ return nullptr;
+
+ RootedFunction wrapped(cx, NewFunctionWithProto(cx, WrappedAsyncGenerator, length,
+ JSFunction::NATIVE_FUN, nullptr,
+ funName, proto,
+ AllocKind::FUNCTION_EXTENDED,
+ TenuredObject));
+ if (!wrapped)
+ return nullptr;
+
+ if (unwrapped->hasCompileTimeName())
+ wrapped->setCompileTimeName(unwrapped->compileTimeName());
+
+ // Link them to each other to make GetWrappedAsyncGenerator and
+ // GetUnwrappedAsyncGenerator work.
+ unwrapped->setExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT, ObjectValue(*wrapped));
+ wrapped->setExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT, ObjectValue(*unwrapped));
+
+ return wrapped;
+}
+
+JSObject*
+js::WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped)
+{
+ RootedObject proto(cx, GlobalObject::getOrCreateAsyncGenerator(cx, cx->global()));
+ if (!proto)
+ return nullptr;
+
+ return WrapAsyncGeneratorWithProto(cx, unwrapped, proto);
+}
+
+bool
+js::IsWrappedAsyncGenerator(JSFunction* fun)
+{
+ return fun->maybeNative() == WrappedAsyncGenerator;
+}
+
+JSFunction*
+js::GetWrappedAsyncGenerator(JSFunction* unwrapped)
+{
+ MOZ_ASSERT(unwrapped->isAsync());
+ return &unwrapped->getExtendedSlot(UNWRAPPED_ASYNC_WRAPPED_SLOT).toObject().as<JSFunction>();
+}
+
+JSFunction*
+js::GetUnwrappedAsyncGenerator(JSFunction* wrapped)
+{
+ MOZ_ASSERT(IsWrappedAsyncGenerator(wrapped));
+ JSFunction* unwrapped = &wrapped->getExtendedSlot(WRAPPED_ASYNC_UNWRAPPED_SLOT)
+ .toObject().as<JSFunction>();
+ MOZ_ASSERT(unwrapped->isAsync());
+ return unwrapped;
+}
+
+// Async Iteration proposal 4.1.1 Await Fulfilled Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Normal, value);
+}
+
+// Async Iteration proposal 4.1.2 Await Rejected Functions.
+MOZ_MUST_USE bool
+js::AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
+}
+
+// Async Iteration proposal 11.4.3.7 step 8.d-e.
+MOZ_MUST_USE bool
+js::AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Return, value);
+}
+
+// Async Iteration proposal 11.4.3.7 step 8.d-e.
+MOZ_MUST_USE bool
+js::AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason)
+{
+ return AsyncGeneratorResume(cx, asyncGenObj, CompletionKind::Throw, reason);
+}
+
+const Class AsyncFromSyncIteratorObject::class_ = {
+ "AsyncFromSyncIteratorObject",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots)
+};
+
+// Async Iteration proposal 11.1.3.1.
+JSObject*
+js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter)
+{
+ // Step 1 (implicit).
+ // Done in bytecode emitted by emitAsyncIterator.
+
+ // Steps 2-4.
+ return AsyncFromSyncIteratorObject::create(cx, iter);
+}
+
+// Async Iteration proposal 11.1.3.1 steps 2-4.
+/* static */ JSObject*
+AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter)
+{
+ // Step 2.
+ RootedObject proto(cx, GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(cx,
+ cx->global()));
+ if (!proto)
+ return nullptr;
+
+ RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, proto));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncFromSyncIteratorObject*> asyncIter = obj.as<AsyncFromSyncIteratorObject>();
+
+ // Step 3.
+ asyncIter->setIterator(iter);
+
+ // Step 4.
+ return asyncIter;
+}
+
+// Async Iteration proposal 11.1.3.2.1 %AsyncFromSyncIteratorPrototype%.next.
+static bool
+AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
+}
+
+// Async Iteration proposal 11.1.3.2.2 %AsyncFromSyncIteratorPrototype%.return.
+static bool
+AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
+}
+
+// Async Iteration proposal 11.1.3.2.3 %AsyncFromSyncIteratorPrototype%.throw.
+static bool
+AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
+}
+
+// Async Iteration proposal 11.4.1.2 AsyncGenerator.prototype.next.
+static bool
+AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Normal, args.get(0),
+ args.rval());
+}
+
+// Async Iteration proposal 11.4.1.3 AsyncGenerator.prototype.return.
+static bool
+AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Return, args.get(0),
+ args.rval());
+}
+
+// Async Iteration proposal 11.4.1.4 AsyncGenerator.prototype.throw.
+static bool
+AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // Steps 1-3.
+ return AsyncGeneratorEnqueue(cx, args.thisv(), CompletionKind::Throw, args.get(0),
+ args.rval());
+}
+
+const Class AsyncGeneratorObject::class_ = {
+ "AsyncGenerator",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots)
+};
+
+// ES 2017 draft 9.1.13.
+template <typename ProtoGetter>
+static JSObject*
+OrdinaryCreateFromConstructor(JSContext* cx, HandleFunction fun,
+ ProtoGetter protoGetter, const Class* clasp)
+{
+ // Step 1 (skipped).
+
+ // Step 2.
+ RootedValue protoVal(cx);
+ if (!GetProperty(cx, fun, fun, cx->names().prototype, &protoVal))
+ return nullptr;
+
+ RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
+ if (!proto) {
+ proto = protoGetter(cx, cx->global());
+ if (!proto)
+ return nullptr;
+ }
+
+ // Step 3.
+ return NewNativeObjectWithGivenProto(cx, clasp, proto);
+}
+
+/* static */ AsyncGeneratorObject*
+AsyncGeneratorObject::create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal)
+{
+ MOZ_ASSERT(generatorVal.isObject());
+ MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>());
+
+ RootedObject obj(
+ cx, OrdinaryCreateFromConstructor(cx, asyncGen,
+ GlobalObject::getOrCreateAsyncGeneratorPrototype,
+ &class_));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncGeneratorObject*> asyncGenObj = obj.as<AsyncGeneratorObject>();
+
+ // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
+ // Step 6.
+ asyncGenObj->setGenerator(generatorVal);
+
+ // Step 7.
+ asyncGenObj->setSuspendedStart();
+
+ // Step 8.
+ asyncGenObj->clearSingleQueueRequest();
+
+ return asyncGenObj;
+}
+
+static MOZ_MUST_USE bool
+InternalEnqueue(JSContext* cx, HandleArrayObject queue, HandleValue val)
+{
+ uint32_t length;
+ if (!GetLengthProperty(cx, queue, &length))
+ return false;
+
+ if (length >= MAX_ARRAY_INDEX) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ if (!DefineElement(cx, queue, length, val))
+ return false;
+ return SetLengthProperty(cx, queue, length + 1);
+}
+
+static MOZ_MUST_USE bool
+InternalDequeue(JSContext* cx, HandleArrayObject queue, MutableHandleValue val)
+{
+ uint32_t length;
+ if (!GetLengthProperty(cx, queue, &length))
+ return false;
+
+ MOZ_ASSERT(length != 0, "Queue should not be empty here");
+
+ if (!GetElement(cx, queue, queue, 0, val))
+ return false;
+
+ uint32_t newlength = length - 1;
+ RootedValue tmp(cx);
+ for (uint32_t i = 0; i < newlength; i++) {
+ if (!GetElement(cx, queue, queue, i + 1, &tmp))
+ return false;
+ if (!DefineElement(cx, queue, i, tmp))
+ return false;
+ }
+ ObjectOpResult result;
+ if (!DeleteElement(cx, queue, newlength, result))
+ return false;
+ if (!result) {
+ RootedId id(cx, INT_TO_JSID(newlength));
+ return result.reportError(cx, queue, id);
+ }
+ return SetLengthProperty(cx, queue, newlength);
+}
+
+/* static */ MOZ_MUST_USE bool
+AsyncGeneratorObject::enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ Handle<AsyncGeneratorRequest*> request)
+{
+ if (asyncGenObj->isSingleQueue()) {
+ if (asyncGenObj->isSingleQueueEmpty()) {
+ asyncGenObj->setSingleQueueRequest(request);
+ return true;
+ }
+
+ RootedArrayObject queue(cx, NewDenseEmptyArray(cx));
+ if (!queue)
+ return false;
+
+ if (!NewbornArrayPush(cx, queue, ObjectValue(*asyncGenObj->singleQueueRequest())))
+ return false;
+ if (!NewbornArrayPush(cx, queue, ObjectValue(*request)))
+ return false;
+
+ asyncGenObj->setQueue(queue);
+ return true;
+ }
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+ RootedValue requestVal(cx, ObjectValue(*request));
+ return InternalEnqueue(cx, queue, requestVal);
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ if (asyncGenObj->isSingleQueue()) {
+ AsyncGeneratorRequest* request = asyncGenObj->singleQueueRequest();
+ asyncGenObj->clearSingleQueueRequest();
+ return request;
+ }
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+ RootedValue requestVal(cx);
+ if (!InternalDequeue(cx, queue, &requestVal))
+ return nullptr;
+
+ return &requestVal.toObject().as<AsyncGeneratorRequest>();
+}
+
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorObject::peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ if (asyncGenObj->isSingleQueue())
+ return asyncGenObj->singleQueueRequest();
+
+ RootedArrayObject queue(cx, asyncGenObj->queue());
+
+ RootedValue requestVal(cx);
+ if (!GetElement(cx, queue, queue, 0, &requestVal))
+ return nullptr;
+
+ return &requestVal.toObject().as<AsyncGeneratorRequest>();
+}
+
+const Class AsyncGeneratorRequest::class_ = {
+ "AsyncGeneratorRequest",
+ JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots)
+};
+
+// Async Iteration proposal 11.4.3.1.
+/* static */ AsyncGeneratorRequest*
+AsyncGeneratorRequest::create(JSContext* cx, CompletionKind completionKind_,
+ HandleValue completionValue_, HandleObject promise_)
+{
+ RootedObject obj(cx, NewNativeObjectWithGivenProto(cx, &class_, nullptr));
+ if (!obj)
+ return nullptr;
+
+ Handle<AsyncGeneratorRequest*> request = obj.as<AsyncGeneratorRequest>();
+ request->setCompletionKind(completionKind_);
+ request->setCompletionValue(completionValue_);
+ request->setPromise(promise_);
+ return request;
+}
+
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d-g.
+static MOZ_MUST_USE bool
+AsyncGeneratorReturned(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value)
+{
+ // Step 5.d.
+ asyncGenObj->setCompleted();
+
+ // Step 5.e (done in bytecode).
+ // Step 5.f.i (implicit).
+
+ // Step 5.g.
+ return AsyncGeneratorResolve(cx, asyncGenObj, value, true);
+}
+
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart steps 5.d, f.
+static MOZ_MUST_USE bool
+AsyncGeneratorThrown(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj)
+{
+ // Step 5.d.
+ asyncGenObj->setCompleted();
+
+ // Not much we can do about uncatchable exceptions, so just bail.
+ if (!cx->isExceptionPending())
+ return false;
+
+ // Step 5.f.i.
+ RootedValue value(cx);
+ if (!GetAndClearException(cx, &value))
+ return false;
+
+ // Step 5.f.ii.
+ return AsyncGeneratorReject(cx, asyncGenObj, value);
+}
+
+// Async Iteration proposal 11.4.3.7 (partially).
+// Most steps are done in generator.
+static MOZ_MUST_USE bool
+AsyncGeneratorYield(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj, HandleValue value)
+{
+ // Step 5 is done in bytecode.
+
+ // Step 6.
+ asyncGenObj->setSuspendedYield();
+
+ // Step 9.
+ return AsyncGeneratorResolve(cx, asyncGenObj, value, false);
+}
+
+// Async Iteration proposal 4.1 Await steps 2-9.
+// Async Iteration proposal 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
+// Async Iteration proposal 11.4.3.2 AsyncGeneratorStart step 5.f-g.
+// Async Iteration proposal 11.4.3.5 AsyncGeneratorResumeNext
+// steps 12-14, 16-20.
+// Execution context switching is handled in generator.
+MOZ_MUST_USE bool
+js::AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ CompletionKind completionKind, HandleValue argument)
+{
+ RootedValue generatorVal(cx, asyncGenObj->generatorVal());
+
+ // 11.4.3.5 steps 12-14, 16-20.
+ HandlePropertyName funName = completionKind == CompletionKind::Normal
+ ? cx->names().StarGeneratorNext
+ : completionKind == CompletionKind::Throw
+ ? cx->names().StarGeneratorThrow
+ : cx->names().StarGeneratorReturn;
+ FixedInvokeArgs<1> args(cx);
+ args[0].set(argument);
+ RootedValue result(cx);
+ if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) {
+ // 11.4.3.2 step 5.d, f.
+ return AsyncGeneratorThrown(cx, asyncGenObj);
+ }
+
+ // 4.1 steps 2-9.
+ if (asyncGenObj->generatorObj()->isAfterAwait())
+ return AsyncGeneratorAwait(cx, asyncGenObj, result);
+
+ // The following code corresponds to the following 3 cases:
+ // * yield
+ // * yield*
+ // * return
+ // For yield and return, property access is done on an internal result
+ // object and it's not observable.
+ // For yield*, it's done on a possibly user-provided result object, and
+ // it's observable.
+ //
+ // Note that IteratorComplete steps in 8.2.1 are done in bytecode.
+
+ // 8.2.1 yield* steps 6.a.vii, 6.b.ii.7, 6.c.ix.
+ RootedObject resultObj(cx, &result.toObject());
+ RootedValue value(cx);
+
+ if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value))
+ return false;
+
+ if (asyncGenObj->generatorObj()->isAfterYield())
+ return AsyncGeneratorYield(cx, asyncGenObj, value);
+
+ // 11.4.3.2 step 5.d-g.
+ return AsyncGeneratorReturned(cx, asyncGenObj, value);
+}
+
+static const JSFunctionSpec async_iterator_proto_methods[] = {
+ JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity", 0, 0),
+ JS_FS_END
+};
+
+static const JSFunctionSpec async_from_sync_iter_methods[] = {
+ JS_FN("next", AsyncFromSyncIteratorNext, 1, 0),
+ JS_FN("throw", AsyncFromSyncIteratorThrow, 1, 0),
+ JS_FN("return", AsyncFromSyncIteratorReturn, 1, 0),
+ JS_FS_END
+};
+
+static const JSFunctionSpec async_generator_methods[] = {
+ JS_FN("next", AsyncGeneratorNext, 1, 0),
+ JS_FN("throw", AsyncGeneratorThrow, 1, 0),
+ JS_FN("return", AsyncGeneratorReturn, 1, 0),
+ JS_FS_END
+};
+
+/* static */ MOZ_MUST_USE bool
+GlobalObject::initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global)
+{
+ if (global->getReservedSlot(ASYNC_ITERATOR_PROTO).isObject())
+ return true;
+
+ // Async Iteration proposal 11.1.2 %AsyncIteratorPrototype%.
+ RootedObject asyncIterProto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
+ if (!asyncIterProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr, async_iterator_proto_methods))
+ return false;
+
+ // Async Iteration proposal 11.1.3.2 %AsyncFromSyncIteratorPrototype%.
+ RootedObject asyncFromSyncIterProto(
+ cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+ asyncIterProto));
+ if (!asyncFromSyncIterProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
+ async_from_sync_iter_methods) ||
+ !DefineToStringTag(cx, asyncFromSyncIterProto, cx->names().AsyncFromSyncIterator))
+ {
+ return false;
+ }
+
+ // Async Iteration proposal 11.4.1 %AsyncGeneratorPrototype%.
+ RootedObject asyncGenProto(
+ cx, GlobalObject::createBlankPrototypeInheriting(cx, global, &PlainObject::class_,
+ asyncIterProto));
+ if (!asyncGenProto)
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr, async_generator_methods) ||
+ !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator))
+ {
+ return false;
+ }
+
+ // Async Iteration proposal 11.3.3 %AsyncGenerator%.
+ RootedObject asyncGenerator(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
+ if (!asyncGenerator)
+ return false;
+ if (!JSObject::setDelegate(cx, asyncGenerator))
+ return false;
+ if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto, JSPROP_READONLY,
+ JSPROP_READONLY) ||
+ !DefineToStringTag(cx, asyncGenerator, cx->names().AsyncGeneratorFunction))
+ {
+ return false;
+ }
+
+ RootedValue function(cx, global->getConstructor(JSProto_Function));
+ if (!function.toObjectOrNull())
+ return false;
+ RootedObject proto(cx, &function.toObject());
+ RootedAtom name(cx, cx->names().AsyncGeneratorFunction);
+
+ // Async Iteration proposal 11.3.2 %AsyncGeneratorFunction%.
+ RootedObject asyncGenFunction(
+ cx, NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1, JSFunction::NATIVE_CTOR,
+ nullptr, name, proto, gc::AllocKind::FUNCTION, SingletonObject));
+ if (!asyncGenFunction)
+ return false;
+ if (!LinkConstructorAndPrototype(cx, asyncGenFunction, asyncGenerator,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
+ return false;
+ }
+
+ global->setReservedSlot(ASYNC_ITERATOR_PROTO, ObjectValue(*asyncIterProto));
+ global->setReservedSlot(ASYNC_FROM_SYNC_ITERATOR_PROTO, ObjectValue(*asyncFromSyncIterProto));
+ global->setReservedSlot(ASYNC_GENERATOR, ObjectValue(*asyncGenerator));
+ global->setReservedSlot(ASYNC_GENERATOR_FUNCTION, ObjectValue(*asyncGenFunction));
+ global->setReservedSlot(ASYNC_GENERATOR_PROTO, ObjectValue(*asyncGenProto));
+ return true;
+}
diff --git a/js/src/vm/AsyncIteration.h b/js/src/vm/AsyncIteration.h
new file mode 100644
index 000000000..58c43131b
--- /dev/null
+++ b/js/src/vm/AsyncIteration.h
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_AsyncIteration_h
+#define vm_AsyncIteration_h
+
+#include "jscntxt.h"
+#include "jsobj.h"
+
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+
+namespace js {
+
+// Async generator consists of 2 functions, |wrapped| and |unwrapped|.
+// |unwrapped| is a generator function compiled from async generator script,
+// |await| behaves just like |yield| there. |unwrapped| isn't exposed to user
+// script.
+// |wrapped| is a native function that is the value of async generator.
+
+JSObject*
+WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto);
+
+JSObject*
+WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped);
+
+bool
+IsWrappedAsyncGenerator(JSFunction* fun);
+
+JSFunction*
+GetWrappedAsyncGenerator(JSFunction* unwrapped);
+
+JSFunction*
+GetUnwrappedAsyncGenerator(JSFunction* wrapped);
+
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedFulfilled(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value);
+MOZ_MUST_USE bool
+AsyncGeneratorAwaitedRejected(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason);
+MOZ_MUST_USE bool
+AsyncGeneratorYieldReturnAwaitedFulfilled(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue value);
+MOZ_MUST_USE bool
+AsyncGeneratorYieldReturnAwaitedRejected(JSContext* cx,
+ Handle<AsyncGeneratorObject*> asyncGenObj,
+ HandleValue reason);
+
+class AsyncGeneratorRequest : public NativeObject
+{
+ private:
+ enum AsyncGeneratorRequestSlots {
+ Slot_CompletionKind = 0,
+ Slot_CompletionValue,
+ Slot_Promise,
+ Slots,
+ };
+
+ void setCompletionKind(CompletionKind completionKind_) {
+ setFixedSlot(Slot_CompletionKind,
+ Int32Value(static_cast<int32_t>(completionKind_)));
+ }
+ void setCompletionValue(HandleValue completionValue_) {
+ setFixedSlot(Slot_CompletionValue, completionValue_);
+ }
+ void setPromise(HandleObject promise_) {
+ setFixedSlot(Slot_Promise, ObjectValue(*promise_));
+ }
+
+ public:
+ static const Class class_;
+
+ static AsyncGeneratorRequest*
+ create(JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
+ HandleObject promise);
+
+ CompletionKind completionKind() const {
+ return static_cast<CompletionKind>(getFixedSlot(Slot_CompletionKind).toInt32());
+ }
+ JS::Value completionValue() const {
+ return getFixedSlot(Slot_CompletionValue);
+ }
+ JSObject* promise() const {
+ return &getFixedSlot(Slot_Promise).toObject();
+ }
+};
+
+class AsyncGeneratorObject : public NativeObject
+{
+ private:
+ enum AsyncGeneratorObjectSlots {
+ Slot_State = 0,
+ Slot_Generator,
+ Slot_QueueOrRequest,
+ Slots
+ };
+
+ enum State {
+ State_SuspendedStart,
+ State_SuspendedYield,
+ State_Executing,
+ // State_AwaitingYieldReturn corresponds to the case that
+ // AsyncGenerator#return is called while State_Executing,
+ // just like the case that AsyncGenerator#return is called
+ // while State_Completed.
+ State_AwaitingYieldReturn,
+ State_AwaitingReturn,
+ State_Completed
+ };
+
+ State state() const {
+ return static_cast<State>(getFixedSlot(Slot_State).toInt32());
+ }
+ void setState(State state_) {
+ setFixedSlot(Slot_State, Int32Value(state_));
+ }
+
+ void setGenerator(const Value& value) {
+ setFixedSlot(Slot_Generator, value);
+ }
+
+ // Queue is implemented in 2 ways. If only one request is queued ever,
+ // request is stored directly to the slot. Once 2 requests are queued, an
+ // array is created and requests are pushed into it, and the array is
+ // stored to the slot.
+
+ bool isSingleQueue() const {
+ return getFixedSlot(Slot_QueueOrRequest).isNull() ||
+ getFixedSlot(Slot_QueueOrRequest).toObject().is<AsyncGeneratorRequest>();
+ }
+ bool isSingleQueueEmpty() const {
+ return getFixedSlot(Slot_QueueOrRequest).isNull();
+ }
+ void setSingleQueueRequest(AsyncGeneratorRequest* request) {
+ setFixedSlot(Slot_QueueOrRequest, ObjectValue(*request));
+ }
+ void clearSingleQueueRequest() {
+ setFixedSlot(Slot_QueueOrRequest, NullHandleValue);
+ }
+ AsyncGeneratorRequest* singleQueueRequest() const {
+ return &getFixedSlot(Slot_QueueOrRequest).toObject().as<AsyncGeneratorRequest>();
+ }
+
+ ArrayObject* queue() const {
+ return &getFixedSlot(Slot_QueueOrRequest).toObject().as<ArrayObject>();
+ }
+ void setQueue(JSObject* queue_) {
+ setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
+ }
+
+ public:
+ static const Class class_;
+
+ static AsyncGeneratorObject*
+ create(JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal);
+
+ bool isSuspendedStart() const {
+ return state() == State_SuspendedStart;
+ }
+ bool isSuspendedYield() const {
+ return state() == State_SuspendedYield;
+ }
+ bool isExecuting() const {
+ return state() == State_Executing;
+ }
+ bool isAwaitingYieldReturn() const {
+ return state() == State_AwaitingYieldReturn;
+ }
+ bool isAwaitingReturn() const {
+ return state() == State_AwaitingReturn;
+ }
+ bool isCompleted() const {
+ return state() == State_Completed;
+ }
+
+ void setSuspendedStart() {
+ setState(State_SuspendedStart);
+ }
+ void setSuspendedYield() {
+ setState(State_SuspendedYield);
+ }
+ void setExecuting() {
+ setState(State_Executing);
+ }
+ void setAwaitingYieldReturn() {
+ setState(State_AwaitingYieldReturn);
+ }
+ void setAwaitingReturn() {
+ setState(State_AwaitingReturn);
+ }
+ void setCompleted() {
+ setState(State_Completed);
+ }
+
+ JS::Value generatorVal() const {
+ return getFixedSlot(Slot_Generator);
+ }
+ GeneratorObject* generatorObj() const {
+ return &getFixedSlot(Slot_Generator).toObject().as<GeneratorObject>();
+ }
+
+ static MOZ_MUST_USE bool
+ enqueueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ Handle<AsyncGeneratorRequest*> request);
+
+ static AsyncGeneratorRequest*
+ dequeueRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+ static AsyncGeneratorRequest*
+ peekRequest(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
+
+ bool isQueueEmpty() const {
+ if (isSingleQueue())
+ return isSingleQueueEmpty();
+ return queue()->length() == 0;
+ }
+};
+
+JSObject*
+CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter);
+
+class AsyncFromSyncIteratorObject : public NativeObject
+{
+ private:
+ enum AsyncFromSyncIteratorObjectSlots {
+ Slot_Iterator = 0,
+ Slots
+ };
+
+ void setIterator(HandleObject iterator_) {
+ setFixedSlot(Slot_Iterator, ObjectValue(*iterator_));
+ }
+
+ public:
+ static const Class class_;
+
+ static JSObject*
+ create(JSContext* cx, HandleObject iter);
+
+ JSObject* iterator() const {
+ return &getFixedSlot(Slot_Iterator).toObject();
+ }
+};
+
+MOZ_MUST_USE bool
+AsyncGeneratorResume(JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
+ CompletionKind completionKind, HandleValue argument);
+
+} // namespace js
+
+#endif /* vm_AsyncIteration_h */
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index 99cb02e58..6a8afb56b 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -29,7 +29,10 @@
macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
macro(as, as, "as") \
macro(Async, Async, "Async") \
+ macro(AsyncFromSyncIterator, AsyncFromSyncIterator, "Async-from-Sync Iterator") \
macro(AsyncFunction, AsyncFunction, "AsyncFunction") \
+ macro(AsyncGenerator, AsyncGenerator, "AsyncGenerator") \
+ macro(AsyncGeneratorFunction, AsyncGeneratorFunction, "AsyncGeneratorFunction") \
macro(AsyncWrapped, AsyncWrapped, "AsyncWrapped") \
macro(async, async, "async") \
macro(await, await, "await") \
@@ -311,6 +314,7 @@
macro(star, star, "*") \
macro(starDefaultStar, starDefaultStar, "*default*") \
macro(StarGeneratorNext, StarGeneratorNext, "StarGeneratorNext") \
+ macro(StarGeneratorReturn, StarGeneratorReturn, "StarGeneratorReturn") \
macro(StarGeneratorThrow, StarGeneratorThrow, "StarGeneratorThrow") \
macro(startTimestamp, startTimestamp, "startTimestamp") \
macro(state, state, "state") \
diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp
index d68d1b75e..2680699a4 100644
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -32,7 +32,6 @@
#include "js/Vector.h"
#include "proxy/ScriptedProxyHandler.h"
#include "vm/ArgumentsObject.h"
-#include "vm/AsyncFunction.h"
#include "vm/DebuggerMemory.h"
#include "vm/GeneratorObject.h"
#include "vm/SPSProfiler.h"
@@ -1560,16 +1559,11 @@ CheckResumptionValue(JSContext* cx, AbstractFramePtr frame, const Maybe<HandleVa
JSTrapStatus status, MutableHandleValue vp)
{
if (status == JSTRAP_RETURN && frame && frame.isFunctionFrame()) {
- // Don't let a { return: ... } resumption value make a generator or
- // async function violate the iterator protocol. The return value from
+ // Don't let a { return: ... } resumption value make a generator
+ // function violate the iterator protocol. The return value from
// such a frame must have the form { done: <bool>, value: <anything> }.
RootedFunction callee(cx, frame.callee());
- if (callee->isAsync()) {
- if (!CheckAsyncResumptionValue(cx, vp)) {
- JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_AWAIT);
- return false;
- }
- } else if (callee->isStarGenerator()) {
+ if (callee->isStarGenerator()) {
if (!CheckStarGeneratorResumptionValue(cx, vp)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_YIELD);
return false;
@@ -7356,7 +7350,8 @@ DebuggerFrame::getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
/* static */ bool
DebuggerFrame::getIsGenerator(HandleDebuggerFrame frame)
{
- return DebuggerFrame::getReferent(frame).script()->isGenerator();
+ return DebuggerFrame::getReferent(frame).script()->isStarGenerator() ||
+ DebuggerFrame::getReferent(frame).script()->isLegacyGenerator();
}
/* static */ bool
diff --git a/js/src/vm/EnvironmentObject.cpp b/js/src/vm/EnvironmentObject.cpp
index c95bb0597..7834940f1 100644
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -2448,7 +2448,9 @@ DebugEnvironments::addDebugEnvironment(JSContext* cx, const EnvironmentIter& ei,
MOZ_ASSERT(cx->compartment() == debugEnv->compartment());
// Generators should always have environments.
MOZ_ASSERT_IF(ei.scope().is<FunctionScope>(),
- !ei.scope().as<FunctionScope>().canonicalFunction()->isGenerator());
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isStarGenerator() &&
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isLegacyGenerator() &&
+ !ei.scope().as<FunctionScope>().canonicalFunction()->isAsync());
if (!CanUseDebugEnvironmentMaps(cx))
return true;
@@ -2594,8 +2596,11 @@ DebugEnvironments::onPopCall(JSContext* cx, AbstractFramePtr frame)
if (!frame.environmentChain()->is<CallObject>())
return;
- if (frame.callee()->isGenerator())
+ if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
+ frame.callee()->isAsync())
+ {
return;
+ }
CallObject& callobj = frame.environmentChain()->as<CallObject>();
envs->liveEnvs.remove(&callobj);
@@ -2726,8 +2731,13 @@ DebugEnvironments::updateLiveEnvironments(JSContext* cx)
if (frame.environmentChain()->compartment() != cx->compartment())
continue;
- if (frame.isFunctionFrame() && frame.callee()->isGenerator())
- continue;
+ if (frame.isFunctionFrame()) {
+ if (frame.callee()->isStarGenerator() || frame.callee()->isLegacyGenerator() ||
+ frame.callee()->isAsync())
+ {
+ continue;
+ }
+ }
if (!frame.isDebuggee())
continue;
@@ -2882,7 +2892,8 @@ GetDebugEnvironmentForMissing(JSContext* cx, const EnvironmentIter& ei)
if (ei.scope().is<FunctionScope>()) {
RootedFunction callee(cx, ei.scope().as<FunctionScope>().canonicalFunction());
// Generators should always reify their scopes.
- MOZ_ASSERT(!callee->isGenerator());
+ MOZ_ASSERT(!callee->isStarGenerator() && !callee->isLegacyGenerator() &&
+ !callee->isAsync());
JS::ExposeObjectToActiveJS(callee);
Rooted<CallObject*> callobj(cx, CallObject::createHollowForDebug(cx, callee));
diff --git a/js/src/vm/GeneratorObject.cpp b/js/src/vm/GeneratorObject.cpp
index ba28501e6..2f5c63c28 100644
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -19,12 +19,13 @@ using namespace js;
JSObject*
GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
{
- MOZ_ASSERT(frame.script()->isGenerator());
+ MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() ||
+ frame.script()->isAsync());
MOZ_ASSERT(frame.script()->nfixed() == 0);
Rooted<GlobalObject*> global(cx, cx->global());
RootedNativeObject obj(cx);
- if (frame.script()->isStarGenerator()) {
+ if (frame.script()->isStarGenerator() || frame.script()->isAsync()) {
RootedValue pval(cx);
RootedObject fun(cx, frame.callee());
// FIXME: This would be faster if we could avoid doing a lookup to get
@@ -63,10 +64,14 @@ bool
GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
Value* vp, unsigned nvalues)
{
- MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD);
+ MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
MOZ_ASSERT(!genObj->hasExpressionStack());
+ MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
+ MOZ_ASSERT_IF(*pc == JSOP_YIELD,
+ genObj->callee().isStarGenerator() ||
+ genObj->callee().isLegacyGenerator());
if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) {
RootedValue val(cx, ObjectValue(*frame.callee()));
@@ -74,8 +79,8 @@ GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame
return false;
}
- uint32_t yieldIndex = GET_UINT24(pc);
- genObj->setYieldIndex(yieldIndex);
+ uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
+ genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex);
genObj->setEnvironmentChain(*frame.environmentChain());
if (nvalues) {
@@ -172,7 +177,7 @@ GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
}
JSScript* script = callee->nonLazyScript();
- uint32_t offset = script->yieldOffsets()[genObj->yieldIndex()];
+ uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()];
activation.regs().pc = script->offsetToPC(offset);
// Always push on a value, even if we are raising an exception. In the
@@ -311,7 +316,8 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto))
return false;
- if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto) ||
+ if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY,
+ JSPROP_READONLY) ||
!DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
{
return false;
@@ -328,8 +334,11 @@ GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
SingletonObject));
if (!genFunction)
return false;
- if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
+ if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto,
+ JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
+ {
return false;
+ }
global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction));
@@ -365,3 +374,34 @@ js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
return true;
}
+
+bool
+GeneratorObject::isAfterYield()
+{
+ return isAfterYieldOrAwait(JSOP_YIELD);
+}
+
+bool
+GeneratorObject::isAfterAwait()
+{
+ return isAfterYieldOrAwait(JSOP_AWAIT);
+}
+
+bool
+GeneratorObject::isAfterYieldOrAwait(JSOp op)
+{
+ if (isClosed() || isClosing() || isRunning())
+ return false;
+
+ JSScript* script = callee().nonLazyScript();
+ jsbytecode* code = script->code();
+ uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()];
+ if (code[nextOffset] != JSOP_DEBUGAFTERYIELD)
+ return false;
+
+ uint32_t offset = nextOffset - JSOP_YIELD_LENGTH;
+ MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
+ code[offset] == JSOP_AWAIT);
+
+ return code[offset] == op;
+}
diff --git a/js/src/vm/GeneratorObject.h b/js/src/vm/GeneratorObject.h
index ca1452b34..f19ca2aac 100644
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -21,15 +21,15 @@ class GeneratorObject : public NativeObject
public:
// Magic values stored in the yield index slot when the generator is
// running or closing. See the yield index comment below.
- static const int32_t YIELD_INDEX_RUNNING = INT32_MAX;
- static const int32_t YIELD_INDEX_CLOSING = INT32_MAX - 1;
+ static const int32_t YIELD_AND_AWAIT_INDEX_RUNNING = INT32_MAX;
+ static const int32_t YIELD_AND_AWAIT_INDEX_CLOSING = INT32_MAX - 1;
enum {
CALLEE_SLOT = 0,
ENV_CHAIN_SLOT,
ARGS_OBJ_SLOT,
EXPRESSION_STACK_SLOT,
- YIELD_INDEX_SLOT,
+ YIELD_AND_AWAIT_INDEX_SLOT,
NEWTARGET_SLOT,
RESERVED_SLOTS
};
@@ -124,47 +124,48 @@ class GeneratorObject : public NativeObject
// The yield index slot is abused for a few purposes. It's undefined if
// it hasn't been set yet (before the initial yield), and null if the
// generator is closed. If the generator is running, the yield index is
- // YIELD_INDEX_RUNNING. If the generator is in that bizarre "closing"
- // state, the yield index is YIELD_INDEX_CLOSING.
+ // YIELD_AND_AWAIT_INDEX_RUNNING. If the generator is in that bizarre
+ // "closing" state, the yield index is YIELD_AND_AWAIT_INDEX_CLOSING.
//
// If the generator is suspended, it's the yield index (stored as
- // JSOP_INITIALYIELD/JSOP_YIELD operand) of the yield instruction that
- // suspended the generator. The yield index can be mapped to the bytecode
- // offset (interpreter) or to the native code offset (JIT).
+ // JSOP_INITIALYIELD/JSOP_YIELD/JSOP_AWAIT operand) of the yield
+ // instruction that suspended the generator. The yield index can be mapped
+ // to the bytecode offset (interpreter) or to the native code offset (JIT).
bool isRunning() const {
MOZ_ASSERT(!isClosed());
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_RUNNING;
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_RUNNING;
}
bool isClosing() const {
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() == YIELD_INDEX_CLOSING;
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() == YIELD_AND_AWAIT_INDEX_CLOSING;
}
bool isSuspended() const {
// Note: also update Baseline's IsSuspendedStarGenerator code if this
// changes.
MOZ_ASSERT(!isClosed());
- static_assert(YIELD_INDEX_CLOSING < YIELD_INDEX_RUNNING,
- "test below should return false for YIELD_INDEX_RUNNING");
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32() < YIELD_INDEX_CLOSING;
+ static_assert(YIELD_AND_AWAIT_INDEX_CLOSING < YIELD_AND_AWAIT_INDEX_RUNNING,
+ "test below should return false for YIELD_AND_AWAIT_INDEX_RUNNING");
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32() < YIELD_AND_AWAIT_INDEX_CLOSING;
}
void setRunning() {
MOZ_ASSERT(isSuspended());
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_RUNNING));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_RUNNING));
}
void setClosing() {
MOZ_ASSERT(isSuspended());
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(YIELD_INDEX_CLOSING));
- }
- void setYieldIndex(uint32_t yieldIndex) {
- MOZ_ASSERT_IF(yieldIndex == 0, getFixedSlot(YIELD_INDEX_SLOT).isUndefined());
- MOZ_ASSERT_IF(yieldIndex != 0, isRunning() || isClosing());
- MOZ_ASSERT(yieldIndex < uint32_t(YIELD_INDEX_CLOSING));
- setFixedSlot(YIELD_INDEX_SLOT, Int32Value(yieldIndex));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(YIELD_AND_AWAIT_INDEX_CLOSING));
+ }
+ void setYieldAndAwaitIndex(uint32_t yieldAndAwaitIndex) {
+ MOZ_ASSERT_IF(yieldAndAwaitIndex == 0,
+ getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).isUndefined());
+ MOZ_ASSERT_IF(yieldAndAwaitIndex != 0, isRunning() || isClosing());
+ MOZ_ASSERT(yieldAndAwaitIndex < uint32_t(YIELD_AND_AWAIT_INDEX_CLOSING));
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, Int32Value(yieldAndAwaitIndex));
MOZ_ASSERT(isSuspended());
}
- uint32_t yieldIndex() const {
+ uint32_t yieldAndAwaitIndex() const {
MOZ_ASSERT(isSuspended());
- return getFixedSlot(YIELD_INDEX_SLOT).toInt32();
+ return getFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT).toInt32();
}
bool isClosed() const {
return getFixedSlot(CALLEE_SLOT).isNull();
@@ -174,10 +175,17 @@ class GeneratorObject : public NativeObject
setFixedSlot(ENV_CHAIN_SLOT, NullValue());
setFixedSlot(ARGS_OBJ_SLOT, NullValue());
setFixedSlot(EXPRESSION_STACK_SLOT, NullValue());
- setFixedSlot(YIELD_INDEX_SLOT, NullValue());
+ setFixedSlot(YIELD_AND_AWAIT_INDEX_SLOT, NullValue());
setFixedSlot(NEWTARGET_SLOT, NullValue());
}
+ bool isAfterYield();
+ bool isAfterAwait();
+
+private:
+ bool isAfterYieldOrAwait(JSOp op);
+
+public:
static size_t offsetOfCalleeSlot() {
return getFixedSlotOffset(CALLEE_SLOT);
}
@@ -187,8 +195,8 @@ class GeneratorObject : public NativeObject
static size_t offsetOfArgsObjSlot() {
return getFixedSlotOffset(ARGS_OBJ_SLOT);
}
- static size_t offsetOfYieldIndexSlot() {
- return getFixedSlotOffset(YIELD_INDEX_SLOT);
+ static size_t offsetOfYieldAndAwaitIndexSlot() {
+ return getFixedSlotOffset(YIELD_AND_AWAIT_INDEX_SLOT);
}
static size_t offsetOfExpressionStackSlot() {
return getFixedSlotOffset(EXPRESSION_STACK_SLOT);
diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
index 013208f66..70c33c647 100644
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -586,17 +586,18 @@ GlobalObject::createBlankPrototypeInheriting(JSContext* cx, Handle<GlobalObject*
}
bool
-js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_)
+js::LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor_, JSObject* proto_,
+ unsigned prototypeAttrs, unsigned constructorAttrs)
{
RootedObject ctor(cx, ctor_), proto(cx, proto_);
RootedValue protoVal(cx, ObjectValue(*proto));
RootedValue ctorVal(cx, ObjectValue(*ctor));
- return DefineProperty(cx, ctor, cx->names().prototype, protoVal,
- nullptr, nullptr, JSPROP_PERMANENT | JSPROP_READONLY) &&
- DefineProperty(cx, proto, cx->names().constructor, ctorVal,
- nullptr, nullptr, 0);
+ return DefineProperty(cx, ctor, cx->names().prototype, protoVal, nullptr, nullptr,
+ prototypeAttrs) &&
+ DefineProperty(cx, proto, cx->names().constructor, ctorVal, nullptr, nullptr,
+ constructorAttrs);
}
bool
diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
index 9179abbb7..3fd2762f8 100644
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -100,6 +100,11 @@ class GlobalObject : public NativeObject
STAR_GENERATOR_FUNCTION,
ASYNC_FUNCTION_PROTO,
ASYNC_FUNCTION,
+ ASYNC_ITERATOR_PROTO,
+ ASYNC_FROM_SYNC_ITERATOR_PROTO,
+ ASYNC_GENERATOR,
+ ASYNC_GENERATOR_FUNCTION,
+ ASYNC_GENERATOR_PROTO,
MAP_ITERATOR_PROTO,
SET_ITERATOR_PROTO,
COLLATOR_PROTO,
@@ -624,6 +629,36 @@ class GlobalObject : public NativeObject
return getOrCreateObject(cx, global, ASYNC_FUNCTION, initAsyncFunction);
}
+ static NativeObject*
+ getOrCreateAsyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
+ {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_ITERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
+ static NativeObject*
+ getOrCreateAsyncFromSyncIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_FROM_SYNC_ITERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
+ static NativeObject*
+ getOrCreateAsyncGenerator(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR,
+ initAsyncGenerators));
+ }
+
+ static JSObject*
+ getOrCreateAsyncGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
+ return getOrCreateObject(cx, global, ASYNC_GENERATOR_FUNCTION, initAsyncGenerators);
+ }
+
+ static NativeObject*
+ getOrCreateAsyncGeneratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+ return MaybeNativeObject(getOrCreateObject(cx, global, ASYNC_GENERATOR_PROTO,
+ initAsyncGenerators));
+ }
+
static JSObject*
getOrCreateMapIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
return getOrCreateObject(cx, global, MAP_ITERATOR_PROTO, initMapIteratorProto);
@@ -782,6 +817,8 @@ class GlobalObject : public NativeObject
static bool initAsyncFunction(JSContext* cx, Handle<GlobalObject*> global);
+ static bool initAsyncGenerators(JSContext* cx, Handle<GlobalObject*> global);
+
// Implemented in builtin/MapObject.cpp.
static bool initMapIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
static bool initSetIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
@@ -985,12 +1022,14 @@ GlobalObject::createArrayFromBuffer<uint8_clamped>() const
}
/*
- * Define ctor.prototype = proto as non-enumerable, non-configurable, and
- * non-writable; define proto.constructor = ctor as non-enumerable but
- * configurable and writable.
+ * Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
+ * non-configurable, and non-writable; and define proto.constructor = ctor as
+ * non-enumerable but configurable and writable.
*/
extern bool
-LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto);
+LinkConstructorAndPrototype(JSContext* cx, JSObject* ctor, JSObject* proto,
+ unsigned prototypeAttrs = JSPROP_PERMANENT | JSPROP_READONLY,
+ unsigned constructorAttrs = 0);
/*
* Define properties and/or functions on any object. Either ps or fs, or both,
diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp
index 030f0f3b6..3cf9b57f6 100644
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -39,6 +39,7 @@
#include "jit/Ion.h"
#include "jit/IonAnalysis.h"
#include "vm/AsyncFunction.h"
+#include "vm/AsyncIteration.h"
#include "vm/Debugger.h"
#include "vm/GeneratorObject.h"
#include "vm/Opcodes.h"
@@ -1939,9 +1940,6 @@ CASE(EnableInterruptsPseudoOpcode)
CASE(JSOP_NOP)
CASE(JSOP_NOP_DESTRUCTURING)
CASE(JSOP_UNUSED126)
-CASE(JSOP_UNUSED192)
-CASE(JSOP_UNUSED209)
-CASE(JSOP_UNUSED210)
CASE(JSOP_UNUSED211)
CASE(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
CASE(JSOP_UNUSED221)
@@ -3585,6 +3583,29 @@ CASE(JSOP_TOASYNC)
}
END_CASE(JSOP_TOASYNC)
+CASE(JSOP_TOASYNCGEN)
+{
+ ReservedRooted<JSFunction*> unwrapped(&rootFunction0,
+ &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;