Bug 1530324 - Part 3: Add separate AbstractGeneratorObject subclasses for async functions and async generators. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 25 Feb 2019 05:04:52 -0800
changeset 519555 e125746cec9579271e48e5570ab9fec66222633d
parent 519554 90d0e91224a9f061e631f88a4aed499108349b5d
child 519556 135c13d4ceba69ab9b70c4803198b107c8f0cc06
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1530324
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1530324 - Part 3: Add separate AbstractGeneratorObject subclasses for async functions and async generators. r=arai
js/src/builtin/AsyncFunction.js
js/src/builtin/AsyncIteration.js
js/src/builtin/Promise.cpp
js/src/builtin/Promise.h
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-01.js
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-06.js
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js
js/src/moz.build
js/src/vm/AsyncFunction.cpp
js/src/vm/AsyncFunction.h
js/src/vm/AsyncIteration.cpp
js/src/vm/AsyncIteration.h
js/src/vm/CommonPropertyNames.h
js/src/vm/GeneratorObject.cpp
js/src/vm/GeneratorObject.h
js/src/vm/SelfHosting.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/AsyncFunction.js
@@ -0,0 +1,15 @@
+/* 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 AsyncFunctionNext(val) {
+    assert(IsAsyncFunctionGeneratorObject(this),
+           "ThisArgument must be a generator object for async functions");
+    return resumeGenerator(this, val, "next");
+}
+
+function AsyncFunctionThrow(val) {
+    assert(IsAsyncFunctionGeneratorObject(this),
+           "ThisArgument must be a generator object for async functions");
+    return resumeGenerator(this, val, "throw");
+}
--- a/js/src/builtin/AsyncIteration.js
+++ b/js/src/builtin/AsyncIteration.js
@@ -1,7 +1,26 @@
 /* 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;
 }
+
+function AsyncGeneratorNext(val) {
+    assert(IsAsyncGeneratorGeneratorObject(this),
+           "ThisArgument must be a generator object for async generators");
+    return resumeGenerator(this, val, "next");
+}
+
+function AsyncGeneratorThrow(val) {
+    assert(IsAsyncGeneratorGeneratorObject(this),
+           "ThisArgument must be a generator object for async generators");
+    return resumeGenerator(this, val, "throw");
+}
+
+function AsyncGeneratorReturn(val) {
+    assert(IsAsyncGeneratorGeneratorObject(this),
+           "ThisArgument must be a generator object for async generators");
+    var rval = { value: val, done: true };
+    return resumeGenerator(this, rval, "return");
+}
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -617,30 +617,30 @@ class PromiseReactionRecord : public Nat
     return flags & REACTION_FLAG_DEFAULT_RESOLVING_HANDLER;
   }
   PromiseObject* defaultResolvingPromise() {
     MOZ_ASSERT(isDefaultResolvingHandler());
     const Value& promiseToResolve =
         getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
     return &promiseToResolve.toObject().as<PromiseObject>();
   }
-  void setIsAsyncFunction(GeneratorObject* genObj) {
+  void setIsAsyncFunction(AsyncFunctionGeneratorObject* genObj) {
     setFlagOnInitialState(REACTION_FLAG_ASYNC_FUNCTION);
     setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
                  ObjectValue(*genObj));
   }
   bool isAsyncFunction() {
     int32_t flags = this->flags();
     return flags & REACTION_FLAG_ASYNC_FUNCTION;
   }
-  GeneratorObject* asyncFunctionGenerator() {
+  AsyncFunctionGeneratorObject* asyncFunctionGenerator() {
     MOZ_ASSERT(isAsyncFunction());
     const Value& generator =
         getFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve);
-    return &generator.toObject().as<GeneratorObject>();
+    return &generator.toObject().as<AsyncFunctionGeneratorObject>();
   }
   void setIsAsyncGenerator(AsyncGeneratorObject* asyncGenObj) {
     setFlagOnInitialState(REACTION_FLAG_ASYNC_GENERATOR);
     setFixedSlot(ReactionRecordSlot_GeneratorOrPromiseToResolve,
                  ObjectValue(*asyncGenObj));
   }
   bool isAsyncGenerator() {
     int32_t flags = this->flags();
@@ -1477,17 +1477,18 @@ static MOZ_MUST_USE bool AsyncFunctionPr
     JSContext* cx, Handle<PromiseReactionRecord*> reaction,
     MutableHandleValue rval) {
   MOZ_ASSERT(reaction->isAsyncFunction());
 
   RootedValue handlerVal(cx, reaction->handler());
   RootedValue argument(cx, reaction->handlerArg());
   Rooted<PromiseObject*> resultPromise(
       cx, &reaction->promise()->as<PromiseObject>());
-  Rooted<GeneratorObject*> generator(cx, reaction->asyncFunctionGenerator());
+  Rooted<AsyncFunctionGeneratorObject*> generator(
+      cx, reaction->asyncFunctionGenerator());
 
   int32_t handlerNum = handlerVal.toInt32();
 
   // Await's handlers don't return a value, nor throw exception.
   // They fail only on OOM.
   if (handlerNum == PromiseHandlerAsyncFunctionAwaitedFulfilled) {
     if (!AsyncFunctionAwaitedFulfilled(cx, resultPromise, generator,
                                        argument)) {
@@ -3579,20 +3580,19 @@ static MOZ_MUST_USE bool InternalAwait(J
   // Step 6.
   extraStep(reaction);
 
   // 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<GeneratorObject*> genObj,
-                                         Handle<PromiseObject*> resultPromise,
-                                         HandleValue value) {
+MOZ_MUST_USE bool js::AsyncFunctionAwait(
+    JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
+    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) {
@@ -4840,26 +4840,28 @@ static MOZ_MUST_USE bool IsTopMostAsyncF
   if (!iter.calleeTemplate()) {
     return false;
   }
   MOZ_ASSERT(iter.calleeTemplate()->isAsync());
 
   ++iter;
 
   // The parent frame should be the `next` function of the generator that is
-  // internally called in AsyncFunctionResume.
+  // internally called in AsyncFunctionResume resp. AsyncGeneratorResume.
   if (iter.done()) {
     return false;
   }
   if (!iter.calleeTemplate()) {
     return false;
   }
 
   if (!IsSelfHostedFunctionWithName(iter.calleeTemplate(),
-                                    cx->names().GeneratorNext)) {
+                                    cx->names().AsyncFunctionNext) &&
+      !IsSelfHostedFunctionWithName(iter.calleeTemplate(),
+                                    cx->names().AsyncGeneratorNext)) {
     return false;
   }
 
   ++iter;
 
   // There should be no more frames.
   if (iter.done()) {
     return true;
--- a/js/src/builtin/Promise.h
+++ b/js/src/builtin/Promise.h
@@ -238,29 +238,28 @@ MOZ_MUST_USE bool RejectPromiseWithPendi
 MOZ_MUST_USE PromiseObject* CreatePromiseObjectForAsync(JSContext* cx);
 
 /**
  * Returns true if the given object is a promise created by
  * CreatePromiseObjectForAsync function.
  */
 MOZ_MUST_USE bool IsPromiseForAsync(JSObject* promise);
 
-class GeneratorObject;
+class AsyncFunctionGeneratorObject;
 
 MOZ_MUST_USE bool AsyncFunctionReturned(JSContext* cx,
                                         Handle<PromiseObject*> resultPromise,
                                         HandleValue value);
 
 MOZ_MUST_USE bool AsyncFunctionThrown(JSContext* cx,
                                       Handle<PromiseObject*> resultPromise);
 
-MOZ_MUST_USE bool AsyncFunctionAwait(JSContext* cx,
-                                     Handle<GeneratorObject*> genObj,
-                                     Handle<PromiseObject*> resultPromise,
-                                     HandleValue value);
+MOZ_MUST_USE bool AsyncFunctionAwait(
+    JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
+    Handle<PromiseObject*> resultPromise, HandleValue value);
 
 // If the await operation can be skipped and the resolution value for `val` can
 // be acquired, stored the resolved value to `resolved` and `true` to
 // `*canSkip`.  Otherwise, stores `false` to `*canSkip`.
 MOZ_MUST_USE bool TrySkipAwait(JSContext* cx, HandleValue val, bool* canSkip,
                                MutableHandleValue resolved);
 
 class AsyncGeneratorObject;
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-01.js
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-01.js
@@ -14,22 +14,25 @@ let resumption = undefined;
 dbg.onEnterFrame = frame => {
     if (frame.type == "call" && frame.callee.name === "f") {
         frame.onPop = completion => {
             assertEq(completion.return, resumption.return);
             hits++;
         };
 
         // Don't tell anyone, but if we force-return a generator object here,
-        // the robots accept it as one of their own and plug it right into the
-        // async function machinery. This may be handy against Skynet someday.
-        resumption = frame.eval(`(function* f2() { hit2 = true; throw "fit"; })()`);
+        // the robots will still detect it and throw an error. No protection
+        // against Skynet, for us poor humans!
+        resumption = frame.eval(`(function* f2() { hit2 = true; })()`);
         assertEq(resumption.return.class, "Generator");
         return resumption;
     }
 };
 
-let p = g.f(0);
+let error;
+try {
+    g.f(0);
+} catch (e) {
+    error = e;
+}
 assertEq(hits, 1);
-let pw = gw.makeDebuggeeValue(p);
-assertEq(pw.isPromise, true);
-assertEq(pw.promiseState, "rejected");
-assertEq(pw.promiseReason, "fit");
+assertEq(error instanceof g.Error, true);
+assertEq(g.hit2, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js
@@ -0,0 +1,38 @@
+// A Debugger can {return:} from the first onEnterFrame for an async generator.
+// (The exact behavior is undocumented; we're testing that it doesn't crash.)
+
+ignoreUnhandledRejections();
+
+let g = newGlobal({newCompartment: true});
+g.hit2 = false;
+g.eval(`async function* f(x) { await x; return "ponies"; }`);
+
+let dbg = new Debugger;
+let gw = dbg.addDebuggee(g);
+let hits = 0;
+let resumption = undefined;
+dbg.onEnterFrame = frame => {
+    if (frame.type == "call" && frame.callee.name === "f") {
+        frame.onPop = completion => {
+            assertEq(completion.return, resumption.return);
+            hits++;
+        };
+
+        // Don't tell anyone, but if we force-return a generator object here,
+        // the robots will still detect it and throw an error. No protection
+        // against Skynet, for us poor humans!
+        resumption = frame.eval(`(function* f2() { hit2 = true; })()`);
+        assertEq(resumption.return.class, "Generator");
+        return resumption;
+    }
+};
+
+let error;
+try {
+    g.f(0);
+} catch (e) {
+    error = e;
+}
+assertEq(hits, 1);
+assertEq(error instanceof g.Error, true);
+assertEq(g.hit2, false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-06.js
@@ -0,0 +1,49 @@
+// A Debugger can {return:} from the first onEnterFrame for an async function.
+// (The exact behavior is undocumented; we're testing that it doesn't crash.)
+
+ignoreUnhandledRejections();
+
+let g = newGlobal({newCompartment: true});
+g.eval(`async function f(x) { await x; return "ponies"; }`);
+g.eval(`async function f2(x) { await x; return "moar ponies"; }`);
+
+let dbg = new Debugger;
+let gw = dbg.addDebuggee(g);
+let hits = 0;
+let resumption = undefined;
+let savedAsyncGen = undefined;
+dbg.onEnterFrame = frame => {
+    if (frame.type == "call" && frame.callee.name === "f2") {
+        frame.onPop = completion => {
+            if (savedAsyncGen === undefined) {
+                savedAsyncGen = completion.return;
+            }
+        };
+    }
+    if (frame.type == "call" && frame.callee.name === "f") {
+        frame.onPop = completion => {
+            hits++;
+        };
+
+        return {return: savedAsyncGen};
+    }
+};
+
+let p2 = g.f2(0);
+let p = g.f(0);
+
+assertEq(hits, 1);
+
+drainJobQueue();
+
+assertEq(hits, 1);
+
+let pw2 = gw.makeDebuggeeValue(p2);
+assertEq(pw2.isPromise, true);
+assertEq(pw2.promiseState, "fulfilled");
+assertEq(pw2.promiseValue, undefined);
+
+let pw = gw.makeDebuggeeValue(p);
+assertEq(pw.isPromise, true);
+assertEq(pw.promiseState, "fulfilled");
+assertEq(pw.promiseValue, "moar ponies");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js
@@ -0,0 +1,54 @@
+// A Debugger can {return:} from the first onEnterFrame for an async generator.
+// (The exact behavior is undocumented; we're testing that it doesn't crash.)
+
+ignoreUnhandledRejections();
+
+let g = newGlobal({newCompartment: true});
+g.eval(`async function* f(x) { await x; return "ponies"; }`);
+g.eval(`async function* f2(x) { await x; return "moar ponies"; }`);
+
+let dbg = new Debugger;
+let gw = dbg.addDebuggee(g);
+let hits = 0;
+let resumption = undefined;
+let savedAsyncGen = undefined;
+dbg.onEnterFrame = frame => {
+    if (frame.type == "call" && frame.callee.name === "f2") {
+        frame.onPop = completion => {
+            if (savedAsyncGen === undefined) {
+                savedAsyncGen = completion.return;
+            }
+        };
+    }
+    if (frame.type == "call" && frame.callee.name === "f") {
+        frame.onPop = completion => {
+            hits++;
+        };
+
+        return {return: savedAsyncGen};
+    }
+};
+
+let it2 = g.f2(123);
+let it = g.f(0);
+
+let p2 = it2.next();
+let p = it.next();
+
+assertEq(hits, 1);
+
+drainJobQueue();
+
+assertEq(hits, 1);
+
+let pw2 = gw.makeDebuggeeValue(p2);
+assertEq(pw2.isPromise, true);
+assertEq(pw2.promiseState, "fulfilled");
+assertEq(pw2.promiseValue.getProperty("value").return, 123);
+assertEq(pw2.promiseValue.getProperty("done").return, true);
+
+let pw = gw.makeDebuggeeValue(p);
+assertEq(pw.isPromise, true);
+assertEq(pw.promiseState, "fulfilled");
+assertEq(pw.promiseValue.getProperty("value").return, undefined);
+assertEq(pw.promiseValue.getProperty("done").return, true);
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -421,16 +421,17 @@ GENERATED_FILES += [('selfhosted.out.h',
 selfhosted = GENERATED_FILES[('selfhosted.out.h', 'selfhosted.js')]
 selfhosted.script = 'builtin/embedjs.py:generate_selfhosted'
 selfhosted.inputs = [
     'js.msg',
     'builtin/TypedObjectConstants.h',
     'builtin/SelfHostingDefines.h',
     'builtin/Utilities.js',
     'builtin/Array.js',
+    'builtin/AsyncFunction.js',
     'builtin/AsyncIteration.js',
     'builtin/Classes.js',
     'builtin/Date.js',
     'builtin/Error.js',
     'builtin/Function.js',
     'builtin/Generator.js',
     'builtin/intl/Collator.js',
     'builtin/intl/CommonFunctions.js',
--- a/js/src/vm/AsyncFunction.cpp
+++ b/js/src/vm/AsyncFunction.cpp
@@ -56,17 +56,17 @@ using mozilla::Maybe;
   global->setReservedSlot(ASYNC_FUNCTION, ObjectValue(*asyncFunction));
   global->setReservedSlot(ASYNC_FUNCTION_PROTO,
                           ObjectValue(*asyncFunctionProto));
   return true;
 }
 
 static MOZ_MUST_USE bool AsyncFunctionStart(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator);
+    Handle<AsyncFunctionGeneratorObject*> generator);
 
 #define UNWRAPPED_ASYNC_WRAPPED_SLOT 1
 #define WRAPPED_ASYNC_UNWRAPPED_SLOT 0
 
 // Async Functions proposal 1.1.8 and 1.2.14.
 static bool WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -78,25 +78,34 @@ static bool WrappedAsyncFunction(JSConte
   // Also does a part of 2.2 steps 1-2.
   InvokeArgs args2(cx);
   if (!FillArgumentsFromArraylike(cx, args2, args)) {
     return false;
   }
 
   RootedValue generatorVal(cx);
   if (Call(cx, unwrappedVal, args.thisv(), args2, &generatorVal)) {
+    // Handle the case when the debugger force-returned an unexpected value.
+    if (!generatorVal.isObject() ||
+        !generatorVal.toObject().is<AsyncFunctionGeneratorObject>()) {
+      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                                JSMSG_UNEXPECTED_TYPE, "return value",
+                                JS::InformalValueTypeName(generatorVal));
+      return false;
+    }
+
     // Step 1.
     Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
     if (!resultPromise) {
       return false;
     }
 
     // Step 3.
-    Rooted<GeneratorObject*> generator(
-        cx, &generatorVal.toObject().as<GeneratorObject>());
+    Rooted<AsyncFunctionGeneratorObject*> generator(
+        cx, &generatorVal.toObject().as<AsyncFunctionGeneratorObject>());
     if (!AsyncFunctionStart(cx, resultPromise, generator)) {
       return false;
     }
 
     // Step 5.
     args.rval().setObject(*resultPromise);
     return true;
   }
@@ -178,71 +187,81 @@ enum class ResumeKind { Normal, Throw };
 
 // Async Functions proposal 2.2 steps 3.f, 3.g.
 // Async Functions proposal 2.2 steps 3.d-e, 3.g.
 // Implemented in js/src/builtin/Promise.cpp
 
 // Async Functions proposal 2.2 steps 3-8, 2.4 steps 2-7, 2.5 steps 2-7.
 static bool AsyncFunctionResume(JSContext* cx,
                                 Handle<PromiseObject*> resultPromise,
-                                Handle<GeneratorObject*> generator,
+                                Handle<AsyncFunctionGeneratorObject*> generator,
                                 ResumeKind kind, HandleValue valueOrReason) {
   RootedObject stack(cx, resultPromise->allocationSite());
   Maybe<JS::AutoSetAsyncStackForNewCalls> asyncStack;
   if (stack) {
     asyncStack.emplace(
         cx, stack, "async",
         JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
   }
 
+  // This case can only happen when the debugger force-returned the same
+  // generator object for two async function calls. It doesn't matter how we
+  // handle it, as long as we don't crash.
+  if (generator->isClosed() || !generator->isSuspended()) {
+    return AsyncFunctionReturned(cx, resultPromise, UndefinedHandleValue);
+  }
+
   // Execution context switching is handled in generator.
   HandlePropertyName funName = kind == ResumeKind::Normal
-                                   ? cx->names().GeneratorNext
-                                   : cx->names().GeneratorThrow;
+                                   ? cx->names().AsyncFunctionNext
+                                   : cx->names().AsyncFunctionThrow;
   FixedInvokeArgs<1> args(cx);
   args[0].set(valueOrReason);
   RootedValue generatorOrValue(cx, ObjectValue(*generator));
   if (!CallSelfHostedFunction(cx, funName, generatorOrValue, args,
                               &generatorOrValue)) {
+    if (!generator->isClosed()) {
+      generator->setClosed();
+    }
     return AsyncFunctionThrown(cx, resultPromise);
   }
 
   if (generator->isAfterAwait()) {
     return AsyncFunctionAwait(cx, generator, resultPromise, generatorOrValue);
   }
 
   return AsyncFunctionReturned(cx, resultPromise, generatorOrValue);
 }
 
 // Async Functions proposal 2.2 steps 3-8.
 static MOZ_MUST_USE bool AsyncFunctionStart(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator) {
+    Handle<AsyncFunctionGeneratorObject*> generator) {
   return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Normal,
                              UndefinedHandleValue);
 }
 
 // Async Functions proposal 2.3 steps 1-8.
 // Implemented in js/src/builtin/Promise.cpp
 
 // Async Functions proposal 2.4.
 MOZ_MUST_USE bool js::AsyncFunctionAwaitedFulfilled(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator, HandleValue value) {
+    Handle<AsyncFunctionGeneratorObject*> generator, HandleValue value) {
   // Step 1 (implicit).
 
   // Steps 2-7.
   return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Normal,
                              value);
 }
 
 // Async Functions proposal 2.5.
 MOZ_MUST_USE bool js::AsyncFunctionAwaitedRejected(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator, HandleValue reason) {
+    Handle<AsyncFunctionGeneratorObject*> generator, HandleValue reason) {
   // Step 1 (implicit).
 
   // Step 2-7.
   return AsyncFunctionResume(cx, resultPromise, generator, ResumeKind::Throw,
                              reason);
 }
 
 JSFunction* js::GetWrappedAsyncFunction(JSFunction* unwrapped) {
--- a/js/src/vm/AsyncFunction.h
+++ b/js/src/vm/AsyncFunction.h
@@ -7,17 +7,17 @@
 #ifndef vm_AsyncFunction_h
 #define vm_AsyncFunction_h
 
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 
 namespace js {
 
-class GeneratorObject;
+class AsyncFunctionGeneratorObject;
 
 // An async function is implemented using two function objects, which are
 // referred to as the "unwrapped" and the "wrapped" async function object.
 // The unwrapped function is a generator function compiled from the async
 // function's script. |await| expressions within the async function are
 // compiled like |yield| expression for the generator function with dedicated
 // opcode,. The unwrapped function is never exposed to user script.
 // The wrapped function is a native function which wraps the generator function,
@@ -45,17 +45,17 @@ JSObject* WrapAsyncFunctionWithProto(JSC
 // prototype object.
 JSObject* WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped);
 
 // Resume the async function when the `await` operand resolves.
 // Split into two functions depending on whether the awaited value was
 // fulfilled or rejected.
 MOZ_MUST_USE bool AsyncFunctionAwaitedFulfilled(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator, HandleValue value);
+    Handle<AsyncFunctionGeneratorObject*> generator, HandleValue value);
 
 MOZ_MUST_USE bool AsyncFunctionAwaitedRejected(
     JSContext* cx, Handle<PromiseObject*> resultPromise,
-    Handle<GeneratorObject*> generator, HandleValue reason);
+    Handle<AsyncFunctionGeneratorObject*> generator, HandleValue reason);
 
 }  // namespace js
 
 #endif /* vm_AsyncFunction_h */
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.cpp
@@ -39,16 +39,25 @@ static bool WrappedAsyncGenerator(JSCont
     return false;
   }
 
   RootedValue generatorVal(cx);
   if (!Call(cx, unwrappedVal, args.thisv(), args2, &generatorVal)) {
     return false;
   }
 
+  // Handle the case when the debugger force-returned an unexpected value.
+  if (!generatorVal.isObject() ||
+      !generatorVal.toObject().is<AsyncGeneratorGeneratorObject>()) {
+    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
+                              JSMSG_UNEXPECTED_TYPE, "return value",
+                              JS::InformalValueTypeName(generatorVal));
+    return false;
+  }
+
   // Step 2.
   AsyncGeneratorObject* asyncGenObj =
       AsyncGeneratorObject::create(cx, wrapped, generatorVal);
   if (!asyncGenObj) {
     return false;
   }
 
   // Step 3 (skipped).
@@ -266,17 +275,17 @@ static AsyncGeneratorObject* OrdinaryCre
 
   // Step 3.
   return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto);
 }
 
 /* static */ AsyncGeneratorObject* AsyncGeneratorObject::create(
     JSContext* cx, HandleFunction asyncGen, HandleValue generatorVal) {
   MOZ_ASSERT(generatorVal.isObject());
-  MOZ_ASSERT(generatorVal.toObject().is<GeneratorObject>());
+  MOZ_ASSERT(generatorVal.toObject().is<AsyncGeneratorGeneratorObject>());
 
   AsyncGeneratorObject* asyncGenObj =
       OrdinaryCreateFromConstructorAsynGen(cx, asyncGen);
   if (!asyncGenObj) {
     return nullptr;
   }
 
   // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
@@ -434,27 +443,38 @@ static MOZ_MUST_USE bool AsyncGeneratorY
 // 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());
 
+  // This case can only happen when the debugger force-returned the same
+  // generator object for two async generator calls. It doesn't matter how we
+  // handle it, as long as we don't crash.
+  if (generatorVal.toObject().as<AbstractGeneratorObject>().isClosed() ||
+      !generatorVal.toObject().as<AbstractGeneratorObject>().isSuspended()) {
+    return AsyncGeneratorReturned(cx, asyncGenObj, UndefinedHandleValue);
+  }
+
   // 11.4.3.5 steps 12-14, 16-20.
   HandlePropertyName funName = completionKind == CompletionKind::Normal
-                                   ? cx->names().GeneratorNext
+                                   ? cx->names().AsyncGeneratorNext
                                    : completionKind == CompletionKind::Throw
-                                         ? cx->names().GeneratorThrow
-                                         : cx->names().GeneratorReturn;
+                                         ? cx->names().AsyncGeneratorThrow
+                                         : cx->names().AsyncGeneratorReturn;
   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.
+    if (!generatorVal.toObject().as<AbstractGeneratorObject>().isClosed()) {
+      generatorVal.toObject().as<AbstractGeneratorObject>().setClosed();
+    }
     return AsyncGeneratorThrown(cx, asyncGenObj);
   }
 
   // 4.1 steps 2-9.
   if (asyncGenObj->generatorObj()->isAfterAwait()) {
     return AsyncGeneratorAwait(cx, asyncGenObj, result);
   }
 
--- a/js/src/vm/AsyncIteration.h
+++ b/js/src/vm/AsyncIteration.h
@@ -236,18 +236,20 @@ class AsyncGeneratorObject : public Nati
   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>();
+  AsyncGeneratorGeneratorObject* generatorObj() const {
+    return &getFixedSlot(Slot_Generator)
+                .toObject()
+                .as<AsyncGeneratorGeneratorObject>();
   }
 
   static MOZ_MUST_USE bool enqueueRequest(
       JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
       Handle<AsyncGeneratorRequest*> request);
 
   static AsyncGeneratorRequest* dequeueRequest(
       JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -27,19 +27,24 @@
   MACRO(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString")       \
   MACRO(ArrayType, ArrayType, "ArrayType")                                     \
   MACRO(ArrayValues, ArrayValues, "ArrayValues")                               \
   MACRO(as, as, "as")                                                          \
   MACRO(Async, Async, "Async")                                                 \
   MACRO(AsyncFromSyncIterator, AsyncFromSyncIterator,                          \
         "Async-from-Sync Iterator")                                            \
   MACRO(AsyncFunction, AsyncFunction, "AsyncFunction")                         \
+  MACRO(AsyncFunctionNext, AsyncFunctionNext, "AsyncFunctionNext")             \
+  MACRO(AsyncFunctionThrow, AsyncFunctionThrow, "AsyncFunctionThrow")          \
   MACRO(AsyncGenerator, AsyncGenerator, "AsyncGenerator")                      \
   MACRO(AsyncGeneratorFunction, AsyncGeneratorFunction,                        \
         "AsyncGeneratorFunction")                                              \
+  MACRO(AsyncGeneratorNext, AsyncGeneratorNext, "AsyncGeneratorNext")          \
+  MACRO(AsyncGeneratorReturn, AsyncGeneratorReturn, "AsyncGeneratorReturn")    \
+  MACRO(AsyncGeneratorThrow, AsyncGeneratorThrow, "AsyncGeneratorThrow")       \
   MACRO(AsyncWrapped, AsyncWrapped, "AsyncWrapped")                            \
   MACRO(async, async, "async")                                                 \
   MACRO(autoAllocateChunkSize, autoAllocateChunkSize, "autoAllocateChunkSize") \
   MACRO(await, await, "await")                                                 \
   MACRO(Bool8x16, Bool8x16, "Bool8x16")                                        \
   MACRO(Bool16x8, Bool16x8, "Bool16x8")                                        \
   MACRO(Bool32x4, Bool32x4, "Bool32x4")                                        \
   MACRO(Bool64x2, Bool64x2, "Bool64x2")                                        \
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -45,20 +45,25 @@ JSObject* AbstractGeneratorObject::creat
         return nullptr;
       }
     }
 
     genObj = NewObjectWithGivenProto<GeneratorObject>(cx, proto);
     if (!genObj) {
       return nullptr;
     }
+  } else if (fun->isGenerator()) {
+    RootedObject proto(cx, nullptr);
+    genObj = NewObjectWithGivenProto<AsyncGeneratorGeneratorObject>(cx, proto);
+    if (!genObj) {
+      return nullptr;
+    }
   } else {
-    // Internal generator instance.
     RootedObject proto(cx, nullptr);
-    genObj = NewObjectWithGivenProto<GeneratorObject>(cx, proto);
+    genObj = NewObjectWithGivenProto<AsyncFunctionGeneratorObject>(cx, proto);
     if (!genObj) {
       return nullptr;
     }
   }
 
   genObj->setCallee(*frame.callee());
   genObj->setEnvironmentChain(*frame.environmentChain());
   if (frame.script()->needsArgsObj()) {
@@ -216,16 +221,24 @@ bool AbstractGeneratorObject::resume(JSC
 const Class GeneratorObject::class_ = {
     "Generator", JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)};
 
 static const JSFunctionSpec generator_methods[] = {
     JS_SELF_HOSTED_FN("next", "GeneratorNext", 1, 0),
     JS_SELF_HOSTED_FN("throw", "GeneratorThrow", 1, 0),
     JS_SELF_HOSTED_FN("return", "GeneratorReturn", 1, 0), JS_FS_END};
 
+const Class AsyncFunctionGeneratorObject::class_ = {
+    "AsyncFunctionGenerator",
+    JSCLASS_HAS_RESERVED_SLOTS(AsyncFunctionGeneratorObject::RESERVED_SLOTS)};
+
+const Class AsyncGeneratorGeneratorObject::class_ = {
+    "AsyncGeneratorGenerator",
+    JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorGeneratorObject::RESERVED_SLOTS)};
+
 JSObject* js::NewSingletonObjectWithFunctionPrototype(
     JSContext* cx, Handle<GlobalObject*> global) {
   RootedObject proto(cx,
                      GlobalObject::getOrCreateFunctionPrototype(cx, global));
   if (!proto) {
     return nullptr;
   }
   return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
@@ -315,10 +328,11 @@ bool AbstractGeneratorObject::isAfterYie
   MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
              code[offset] == JSOP_AWAIT);
 
   return code[offset] == op;
 }
 
 template <>
 bool JSObject::is<js::AbstractGeneratorObject>() const {
-  return is<GeneratorObject>();
+  return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() ||
+         is<AsyncGeneratorGeneratorObject>();
 }
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -203,16 +203,30 @@ class AbstractGeneratorObject : public N
 
 class GeneratorObject : public AbstractGeneratorObject {
  public:
   enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS };
 
   static const Class class_;
 };
 
+class AsyncFunctionGeneratorObject : public AbstractGeneratorObject {
+ public:
+  enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS };
+
+  static const Class class_;
+};
+
+class AsyncGeneratorGeneratorObject : public AbstractGeneratorObject {
+ public:
+  enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS };
+
+  static const Class class_;
+};
+
 bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
                             Handle<AbstractGeneratorObject*> obj,
                             HandleValue val, uint32_t resumeKind);
 
 /**
  * Return the generator object associated with the given frame. The frame must
  * be a call frame for a generator. If the generator object hasn't been created
  * yet, or hasn't been stored in the stack slot yet, this returns null.
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2553,16 +2553,21 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("IsGeneratorObject", intrinsic_IsInstanceOfBuiltin<GeneratorObject>,
           1, 0),
     JS_FN("GeneratorObjectIsClosed", intrinsic_GeneratorObjectIsClosed, 1, 0),
     JS_FN("IsSuspendedGenerator", intrinsic_IsSuspendedGenerator, 1, 0),
 
     JS_FN("GeneratorIsRunning", intrinsic_GeneratorIsRunning, 1, 0),
     JS_FN("GeneratorSetClosed", intrinsic_GeneratorSetClosed, 1, 0),
 
+    JS_FN("IsAsyncFunctionGeneratorObject",
+          intrinsic_IsInstanceOfBuiltin<AsyncFunctionGeneratorObject>, 1, 0),
+    JS_FN("IsAsyncGeneratorGeneratorObject",
+          intrinsic_IsInstanceOfBuiltin<AsyncGeneratorGeneratorObject>, 1, 0),
+
     JS_INLINABLE_FN("GuardToArrayBuffer",
                     intrinsic_GuardToBuiltin<ArrayBufferObject>, 1, 0,
                     IntrinsicGuardToArrayBuffer),
     JS_INLINABLE_FN("GuardToSharedArrayBuffer",
                     intrinsic_GuardToBuiltin<SharedArrayBufferObject>, 1, 0,
                     IntrinsicGuardToSharedArrayBuffer),
     JS_FN("IsWrappedArrayBuffer",
           intrinsic_IsWrappedInstanceOfBuiltin<ArrayBufferObject>, 1, 0),