Bug 1530324 - Part 4: Remove wrapper function for async generators. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 25 Feb 2019 05:09:51 -0800
changeset 461674 135c13d4ceba69ab9b70c4803198b107c8f0cc06
parent 461673 e125746cec9579271e48e5570ab9fec66222633d
child 461675 48fb1e2b6e97f1f089c259d4f3209ee26a274e74
push id35626
push usercsabou@mozilla.com
push dateThu, 28 Feb 2019 11:31:08 +0000
treeherdermozilla-central@2ea0c1db7e60 [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 4: Remove wrapper function for async generators. r=arai
js/src/builtin/AsyncIteration.js
js/src/builtin/ModuleObject.cpp
js/src/builtin/Object.cpp
js/src/builtin/TestingFunctions.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/ObjectEmitter.cpp
js/src/frontend/ObjectEmitter.h
js/src/frontend/Parser.cpp
js/src/jit-test/tests/basic/offThreadCompileScript-03.js
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js
js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.h
js/src/jit/shared/LIR-shared.h
js/src/vm/ArgumentsObject.cpp
js/src/vm/AsyncIteration.cpp
js/src/vm/AsyncIteration.h
js/src/vm/BytecodeUtil.cpp
js/src/vm/Debugger.cpp
js/src/vm/EnvironmentObject.cpp
js/src/vm/GeneratorObject.cpp
js/src/vm/GeneratorObject.h
js/src/vm/GlobalObject.cpp
js/src/vm/HelperThreads.cpp
js/src/vm/Interpreter.cpp
js/src/vm/JSFunction.cpp
js/src/vm/JSScript.cpp
js/src/vm/Opcodes.h
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/AsyncIteration.js
+++ b/js/src/builtin/AsyncIteration.js
@@ -2,25 +2,25 @@
  * 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),
+    assert(IsAsyncGeneratorObject(this),
            "ThisArgument must be a generator object for async generators");
     return resumeGenerator(this, val, "next");
 }
 
 function AsyncGeneratorThrow(val) {
-    assert(IsAsyncGeneratorGeneratorObject(this),
+    assert(IsAsyncGeneratorObject(this),
            "ThisArgument must be a generator object for async generators");
     return resumeGenerator(this, val, "throw");
 }
 
 function AsyncGeneratorReturn(val) {
-    assert(IsAsyncGeneratorGeneratorObject(this),
+    assert(IsAsyncGeneratorObject(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/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -995,22 +995,18 @@ bool ModuleObject::noteFunctionDeclarati
 
   for (const auto& funDecl : *funDecls) {
     fun = funDecl.fun;
     obj = Lambda(cx, fun, env);
     if (!obj) {
       return false;
     }
 
-    if (fun->isAsync()) {
-      if (fun->isGenerator()) {
-        obj = WrapAsyncGenerator(cx, obj.as<JSFunction>());
-      } else {
-        obj = WrapAsyncFunction(cx, obj.as<JSFunction>());
-      }
+    if (fun->isAsync() && !fun->isGenerator()) {
+      obj = WrapAsyncFunction(cx, obj.as<JSFunction>());
     }
 
     if (!obj) {
       return false;
     }
 
     value = ObjectValue(*obj);
     if (!SetProperty(cx, env, funDecl.name->asPropertyName(), value)) {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -363,17 +363,17 @@ JSString* js::ObjectToSource(JSContext* 
         if (!buf.append("get ")) {
           return false;
         }
       } else if (kind == PropertyKind::Setter) {
         if (!buf.append("set ")) {
           return false;
         }
       } else if (kind == PropertyKind::Method && fun) {
-        if (IsWrappedAsyncFunction(fun)) {
+        if (IsWrappedAsyncFunction(fun) || fun->isAsync()) {
           if (!buf.append("async ")) {
             return false;
           }
         }
 
         if (fun->isGenerator()) {
           if (!buf.append('*')) {
             return false;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -5438,19 +5438,16 @@ JSScript* js::TestingFunctionArgumentToS
       break;
     }
   }
 
   // Get unwrapped async function.
   if (IsWrappedAsyncFunction(fun)) {
     fun = GetUnwrappedAsyncFunction(fun);
   }
-  if (IsWrappedAsyncGenerator(fun)) {
-    fun = GetUnwrappedAsyncGenerator(fun);
-  }
 
   if (!fun->isInterpreted()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_TESTING_SCRIPTS_ONLY);
     return nullptr;
   }
 
   JSScript* script = JSFunction::getOrCreateScript(cx, fun);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3193,26 +3193,27 @@ bool BytecodeEmitter::emitAnonymousFunct
 }
 
 bool BytecodeEmitter::emitAnonymousFunctionWithComputedName(
     ParseNode* node, FunctionPrefixKind prefixKind) {
   MOZ_ASSERT(node->isDirectRHSAnonFunction());
 
   if (node->is<FunctionNode>()) {
     if (!emitTree(node)) {
-      //            [stack] # !isAsync || !needsHomeObject
+      //            [stack] # !isAsync || isGenerator || !needsHomeObject
       //            [stack] NAME FUN
-      //            [stack] # isAsync && needsHomeObject
+      //            [stack] # isAsync && !isGenerator && needsHomeObject
       //            [stack] NAME UNWRAPPED WRAPPED
       return false;
     }
     unsigned depth = 1;
     FunctionNode* funNode = &node->as<FunctionNode>();
     FunctionBox* funbox = funNode->funbox();
-    if (funbox->isAsync() && funbox->needsHomeObject()) {
+    if (funbox->isAsync() && !funbox->isGenerator() &&
+        funbox->needsHomeObject()) {
       depth = 2;
     }
     if (!emitDupAt(depth)) {
       //            [stack] # !isAsync || !needsHomeObject
       //            [stack] NAME FUN NAME
       //            [stack] # isAsync && needsHomeObject
       //            [stack] NAME UNWRAPPED WRAPPED NAME
       return false;
@@ -5802,20 +5803,19 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
   // Make the function object a literal in the outer script's pool.
   unsigned index = objectList.add(funNode->funbox());
 
   // Non-hoisted functions simply emit their respective op.
   if (!funNode->functionIsHoisted()) {
     // JSOP_LAMBDA_ARROW is always preceded by a new.target
     MOZ_ASSERT(fun->isArrow() ==
                (funNode->syntaxKind() == FunctionSyntaxKind::Arrow));
-    if (funbox->isAsync()) {
+    if (funbox->isAsync() && !funbox->isGenerator()) {
       MOZ_ASSERT(!needsProto);
-      return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
-                              fun->isGenerator());
+      return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow());
     }
 
     if (fun->isArrow()) {
       if (sc->allowNewTarget()) {
         if (!emit1(JSOP_NEWTARGET)) {
           return false;
         }
       } else {
@@ -5862,19 +5862,18 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
       RootedModuleObject module(cx, sc->asModuleContext()->module());
       if (!module->noteFunctionDeclaration(cx, name, fun)) {
         return false;
       }
     } else {
       MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
       MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::Statement);
       MOZ_ASSERT(inPrologue());
-      if (funbox->isAsync()) {
-        if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
-                              fun->isGenerator())) {
+      if (funbox->isAsync() && !funbox->isGenerator()) {
+        if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow())) {
           return false;
         }
       } else {
         if (!emitIndex32(JSOP_LAMBDA, index)) {
           return false;
         }
       }
       if (!emit1(JSOP_DEFFUN)) {
@@ -5884,19 +5883,19 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::e
   } else {
     // For functions nested within functions and blocks, make a lambda and
     // initialize the binding name of the function in the current scope.
 
     NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
     if (!noe.prepareForRhs()) {
       return false;
     }
-    if (funbox->isAsync()) {
+    if (funbox->isAsync() && !funbox->isGenerator()) {
       if (!emitAsyncWrapper(index, /* needsHomeObject = */ false,
-                            /* isArrow = */ false, funbox->isGenerator())) {
+                            /* isArrow = */ false)) {
         return false;
       }
     } else {
       if (!emitIndexOp(JSOP_LAMBDA, index)) {
         return false;
       }
     }
     if (!noe.emitAssignment()) {
@@ -5929,17 +5928,17 @@ bool BytecodeEmitter::emitAsyncWrapperLa
       return false;
     }
   }
 
   return true;
 }
 
 bool BytecodeEmitter::emitAsyncWrapper(unsigned index, bool needsHomeObject,
-                                       bool isArrow, bool isGenerator) {
+                                       bool isArrow) {
   // needsHomeObject can be true for propertyList for extended class.
   // In that case push both unwrapped and wrapped function, in order to
   // initialize home object of unwrapped function, and set wrapped function
   // as a property.
   //
   //   lambda       // unwrapped
   //   dup          // unwrapped unwrapped
   //   toasync      // unwrapped wrapped
@@ -5964,24 +5963,18 @@ bool BytecodeEmitter::emitAsyncWrapper(u
   if (!emitAsyncWrapperLambda(index, isArrow)) {
     return false;
   }
   if (needsHomeObject) {
     if (!emit1(JSOP_DUP)) {
       return false;
     }
   }
-  if (isGenerator) {
-    if (!emit1(JSOP_TOASYNCGEN)) {
-      return false;
-    }
-  } else {
-    if (!emit1(JSOP_TOASYNC)) {
-      return false;
-    }
+  if (!emit1(JSOP_TOASYNC)) {
+    return false;
   }
   return true;
 }
 
 bool BytecodeEmitter::emitDo(BinaryNode* doNode) {
   ParseNode* bodyNode = doNode->left();
 
   DoWhileEmitter doWhile(this);
@@ -7897,17 +7890,18 @@ bool BytecodeEmitter::emitPropertyList(L
         }
       }
 
       if (propVal->is<FunctionNode>() &&
           propVal->as<FunctionNode>().funbox()->needsHomeObject()) {
         FunctionBox* funbox = propVal->as<FunctionNode>().funbox();
         MOZ_ASSERT(funbox->function()->allowSuperProperty());
 
-        if (!pe.emitInitHomeObject(funbox->asyncKind())) {
+        bool isAsyncNonGenerator = funbox->isAsync() && !funbox->isGenerator();
+        if (!pe.emitInitHomeObject(isAsyncNonGenerator)) {
           //        [stack] CTOR? OBJ CTOR? KEY? FUN
           return false;
         }
       }
       return true;
     };
 
     PropertyEmitter::Kind kind =
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -651,17 +651,17 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
   MOZ_MUST_USE bool emitAwaitInInnermostScope(UnaryNode* awaitNode);
   MOZ_MUST_USE bool emitAwaitInScope(EmitterScope& currentScope);
 
   MOZ_MUST_USE bool emitPropLHS(PropertyAccess* prop);
   MOZ_MUST_USE bool emitPropIncDec(UnaryNode* incDec);
 
   MOZ_MUST_USE bool emitAsyncWrapperLambda(unsigned index, bool isArrow);
   MOZ_MUST_USE bool emitAsyncWrapper(unsigned index, bool needsHomeObject,
-                                     bool isArrow, bool isGenerator);
+                                     bool isArrow);
 
   MOZ_MUST_USE bool emitComputedPropertyName(UnaryNode* computedPropName);
 
   // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM
   // opcode onto the stack in the right order. In the case of SETELEM, the
   // value to be assigned must already be pushed.
   enum class EmitElemOption { Get, Call, IncDec, CompoundAssign, Ref };
   MOZ_MUST_USE bool emitElemOperands(PropertyByValue* elem,
--- a/js/src/frontend/ObjectEmitter.cpp
+++ b/js/src/frontend/ObjectEmitter.cpp
@@ -223,25 +223,24 @@ bool PropertyEmitter::prepareForComputed
 
 #ifdef DEBUG
   propertyState_ = PropertyState::ComputedValue;
 #endif
   return true;
 }
 
 bool PropertyEmitter::emitInitHomeObject(
-    FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */) {
+    bool isAsyncNonGenerator /* = false */) {
   MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
              propertyState_ == PropertyState::IndexValue ||
              propertyState_ == PropertyState::ComputedValue);
 
   //                [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
 
-  bool isAsync = kind == FunctionAsyncKind::AsyncFunction;
-  if (isAsync) {
+  if (isAsyncNonGenerator) {
     //              [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED
     if (!bce_->emit1(JSOP_SWAP)) {
       //            [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED
       return false;
     }
   }
 
   // There are the following values on the stack conditionally, between
@@ -250,28 +249,28 @@ bool PropertyEmitter::emitInitHomeObject
   //   * KEY if isIndexOrComputed_
   //   * WRAPPED if isAsync
   //
   // JSOP_INITHOMEOBJECT uses one of the following:
   //   * HOMEOBJ if !isStatic_
   //     (`super.foo` points the super prototype property)
   //   * the 2nd CTOR if isStatic_
   //     (`super.foo` points the super constructor property)
-  if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsync)) {
+  if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsyncNonGenerator)) {
     //              [stack] # non-static method
     //              [stack] CTOR? HOMEOBJ CTOR KEY? WRAPPED? FUN CTOR
     //              [stack] # static method
     //              [stack] CTOR? HOMEOBJ KEY? WRAPPED? FUN HOMEOBJ
     return false;
   }
   if (!bce_->emit1(JSOP_INITHOMEOBJECT)) {
     //              [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN
     return false;
   }
-  if (isAsync) {
+  if (isAsyncNonGenerator) {
     if (!bce_->emit1(JSOP_POP)) {
       //            [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED
       return false;
     }
   }
 
 #ifdef DEBUG
   if (propertyState_ == PropertyState::PropValue) {
--- a/js/src/frontend/ObjectEmitter.h
+++ b/js/src/frontend/ObjectEmitter.h
@@ -13,18 +13,16 @@
 #include <stddef.h>  // size_t, ptrdiff_t
 #include <stdint.h>  // uint32_t
 
 #include "frontend/EmitterScope.h"   // EmitterScope
 #include "frontend/TDZCheckCache.h"  // TDZCheckCache
 #include "js/RootingAPI.h"           // JS::Handle, JS::Rooted
 #include "vm/BytecodeUtil.h"         // JSOp
 #include "vm/JSAtom.h"               // JSAtom
-#include "vm/JSFunction.h"           // JSFunction, FunctionPrefixKind
-#include "vm/JSScript.h"             // FunctionAsyncKind
 #include "vm/NativeObject.h"         // PlainObject
 #include "vm/Scope.h"                // LexicalScope
 
 namespace js {
 
 namespace frontend {
 
 struct BytecodeEmitter;
@@ -239,18 +237,17 @@ class MOZ_STACK_CLASS PropertyEmitter {
   // { [ key ]: value }
   //   ^
   //   |
   //   keyPos
   MOZ_MUST_USE bool prepareForComputedPropKey(
       const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
   MOZ_MUST_USE bool prepareForComputedPropValue();
 
-  MOZ_MUST_USE bool emitInitHomeObject(
-      FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction);
+  MOZ_MUST_USE bool emitInitHomeObject(bool isAsyncNonGenerator = false);
 
   // @param key
   //        Property key
   MOZ_MUST_USE bool emitInitProp(JS::Handle<JSAtom*> key);
   MOZ_MUST_USE bool emitInitGetter(JS::Handle<JSAtom*> key);
   MOZ_MUST_USE bool emitInitSetter(JS::Handle<JSAtom*> key);
 
   MOZ_MUST_USE bool emitInitIndexProp();
@@ -513,17 +510,17 @@ class MOZ_RAII AutoSaveLocalStrictMode {
 //     emit(function_for_m);
 //     ce.emitInitHomeObject();
 //     ce.emitInitProp(atom_of_m);
 //
 //   `async m() { super.f(); }` in class
 //     // after emitInitConstructor/emitInitDefaultConstructor
 //     ce.prepareForPropValue(Some(offset_of_m));
 //     emit(function_for_m);
-//     ce.emitInitHomeObject(FunctionAsyncKind::Async);
+//     ce.emitInitHomeObject(true);
 //     ce.emitInitProp(atom_of_m);
 //
 //   `get p() { super.f(); }` in class
 //     // after emitInitConstructor/emitInitDefaultConstructor
 //     ce.prepareForPropValue(Some(offset_of_p));
 //     emit(function_for_p);
 //     ce.emitInitHomeObject();
 //     ce.emitInitGetter(atom_of_m);
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1980,17 +1980,18 @@ JSFunction* AllocNewFunction(JSContext* 
 #endif
       flags = (generatorKind == GeneratorKind::NotGenerator &&
                        asyncKind == FunctionAsyncKind::SyncFunction
                    ? JSFunction::INTERPRETED_NORMAL
                    : JSFunction::INTERPRETED_GENERATOR_OR_ASYNC);
   }
 
   // We store the async wrapper in a slot for later access.
-  if (asyncKind == FunctionAsyncKind::AsyncFunction) {
+  if (asyncKind == FunctionAsyncKind::AsyncFunction &&
+      generatorKind == GeneratorKind::NotGenerator) {
     allocKind = gc::AllocKind::FUNCTION_EXTENDED;
   }
 
   fun = NewFunctionWithProto(cx, nullptr, 0, flags, nullptr, atom, proto,
                              allocKind, TenuredObject);
   if (!fun) {
     return nullptr;
   }
@@ -2561,18 +2562,23 @@ GeneralParser<ParseHandler, Unit>::funct
     }
 
     return funNode;
   }
 
   RootedObject proto(cx_);
   if (generatorKind == GeneratorKind::Generator ||
       asyncKind == FunctionAsyncKind::AsyncFunction) {
-    proto =
-        GlobalObject::getOrCreateGeneratorFunctionPrototype(cx_, cx_->global());
+    if (generatorKind == GeneratorKind::Generator &&
+        asyncKind == FunctionAsyncKind::AsyncFunction) {
+      proto = GlobalObject::getOrCreateAsyncGenerator(cx_, cx_->global());
+    } else {
+      proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(
+          cx_, cx_->global());
+    }
     if (!proto) {
       return null();
     }
   }
   RootedFunction fun(
       cx_, newFunction(funName, kind, generatorKind, asyncKind, proto));
   if (!fun) {
     return null();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/offThreadCompileScript-03.js
@@ -0,0 +1,50 @@
+// |jit-test| skip-if: helperThreadCount() === 0
+
+// Test offThreadCompileScript for different function types.
+
+load(libdir + 'asserts.js');
+
+var id;
+
+id = offThreadCompileScript(`
+  function f() { return "pass"; }
+  f();
+`);
+assertEq(runOffThreadScript(id), "pass");
+
+id = offThreadCompileScript(`
+  function* f() { return "pass"; }
+  f().next();
+`);
+assertDeepEq(runOffThreadScript(id), {value: "pass", done: true});
+
+id = offThreadCompileScript(`
+  async function f() { return "pass"; }
+  f();
+`);
+assertEventuallyEq(runOffThreadScript(id), "pass");
+
+id = offThreadCompileScript(`
+  async function* f() { return "pass"; }
+  f().next();
+`);
+assertEventuallyDeepEq(runOffThreadScript(id), {value: "pass", done: true});
+
+// Copied from js/src/tests/shell.js
+function getPromiseResult(promise) {
+  var result, error, caught = false;
+  promise.then(r => { result = r; },
+               e => { caught = true; error = e; });
+  drainJobQueue();
+  if (caught)
+    throw error;
+  return result;
+}
+
+function assertEventuallyEq(promise, expected) {
+  assertEq(getPromiseResult(promise), expected);
+}
+
+function assertEventuallyDeepEq(promise, expected) {
+  assertDeepEq(getPromiseResult(promise), expected);
+}
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-05.js
@@ -13,26 +13,20 @@ 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!
+        // If we force-return a generator object here, the caller will never
+        // receive an async generator object.
         resumption = frame.eval(`(function* f2() { hit2 = true; })()`);
         assertEq(resumption.return.class, "Generator");
         return resumption;
     }
 };
 
-let error;
-try {
-    g.f(0);
-} catch (e) {
-    error = e;
-}
+let it = g.f(0);
 assertEq(hits, 1);
-assertEq(error instanceof g.Error, true);
+assertEq(gw.makeDebuggeeValue(it), resumption.return);
 assertEq(g.hit2, false);
--- a/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js
+++ b/js/src/jit-test/tests/debug/onEnterFrame-async-resumption-07.js
@@ -39,16 +39,16 @@ 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("value").return, "moar ponies");
 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/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -4634,38 +4634,16 @@ bool BaselineCodeGen<Handler>::emit_JSOP
   }
 
   masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
   frame.pop();
   frame.push(R0);
   return true;
 }
 
-typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
-static const VMFunction ToAsyncGenInfo =
-    FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
-
-template <typename Handler>
-bool BaselineCodeGen<Handler>::emit_JSOP_TOASYNCGEN() {
-  frame.syncStack(0);
-  masm.unboxObject(frame.addressOfStackValue(-1), R0.scratchReg());
-
-  prepareVMCall();
-  pushArg(R0.scratchReg());
-
-  if (!callVM(ToAsyncGenInfo)) {
-    return false;
-  }
-
-  masm.tagValue(JSVAL_TYPE_OBJECT, ReturnReg, R0);
-  frame.pop();
-  frame.push(R0);
-  return true;
-}
-
 typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue);
 static const VMFunction ToAsyncIterInfo =
     FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_TOASYNCITER() {
   frame.syncStack(0);
   masm.unboxObject(frame.addressOfStackValue(-2), R0.scratchReg());
@@ -6070,16 +6048,17 @@ MethodStatus BaselineCompiler::emitBody(
     }
 
     switch (op) {
       // ===== NOT Yet Implemented =====
       case JSOP_FORCEINTERPRETER:
         // Intentionally not implemented.
       case JSOP_UNUSED71:
       case JSOP_UNUSED151:
+      case JSOP_UNUSED192:
       case JSOP_LIMIT:
         // === !! WARNING WARNING WARNING !! ===
         // DO NOT add new ops to this list! All bytecode ops MUST have Baseline
         // support. Follow-up bugs are not acceptable.
         JitSpew(JitSpew_BaselineAbort, "Unhandled op: %s", CodeName[op]);
         return Method_CantCompile;
 
 #define EMIT_OP(OP)                                            \
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -187,17 +187,16 @@ namespace jit {
   _(JSOP_DEBUGLEAVELEXICALENV)  \
   _(JSOP_PUSHVARENV)            \
   _(JSOP_POPVARENV)             \
   _(JSOP_EXCEPTION)             \
   _(JSOP_DEBUGGER)              \
   _(JSOP_ARGUMENTS)             \
   _(JSOP_REST)                  \
   _(JSOP_TOASYNC)               \
-  _(JSOP_TOASYNCGEN)            \
   _(JSOP_TOASYNCITER)           \
   _(JSOP_TOID)                  \
   _(JSOP_TOSTRING)              \
   _(JSOP_TABLESWITCH)           \
   _(JSOP_ITER)                  \
   _(JSOP_MOREITER)              \
   _(JSOP_ISNOITER)              \
   _(JSOP_ENDITER)               \
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11373,25 +11373,16 @@ typedef JSObject* (*ToAsyncFn)(JSContext
 static const VMFunction ToAsyncInfo =
     FunctionInfo<ToAsyncFn>(js::WrapAsyncFunction, "ToAsync");
 
 void CodeGenerator::visitToAsync(LToAsync* lir) {
   pushArg(ToRegister(lir->unwrapped()));
   callVM(ToAsyncInfo, lir);
 }
 
-typedef JSObject* (*ToAsyncGenFn)(JSContext*, HandleFunction);
-static const VMFunction ToAsyncGenInfo =
-    FunctionInfo<ToAsyncGenFn>(js::WrapAsyncGenerator, "ToAsyncGen");
-
-void CodeGenerator::visitToAsyncGen(LToAsyncGen* lir) {
-  pushArg(ToRegister(lir->unwrapped()));
-  callVM(ToAsyncGenInfo, lir);
-}
-
 typedef JSObject* (*ToAsyncIterFn)(JSContext*, HandleObject, HandleValue);
 static const VMFunction ToAsyncIterInfo =
     FunctionInfo<ToAsyncIterFn>(js::CreateAsyncFromSyncIterator, "ToAsyncIter");
 
 void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) {
   pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex));
   pushArg(ToRegister(lir->iterator()));
   callVM(ToAsyncIterInfo, lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2330,19 +2330,16 @@ AbortReasonOr<Ok> IonBuilder::inspectOpc
 
     case JSOP_TYPEOF:
     case JSOP_TYPEOFEXPR:
       return jsop_typeof();
 
     case JSOP_TOASYNC:
       return jsop_toasync();
 
-    case JSOP_TOASYNCGEN:
-      return jsop_toasyncgen();
-
     case JSOP_TOASYNCITER:
       return jsop_toasynciter();
 
     case JSOP_TOID:
       return jsop_toid();
 
     case JSOP_ITERNEXT:
       return jsop_iternext();
@@ -2528,16 +2525,17 @@ AbortReasonOr<Ok> IonBuilder::inspectOpc
       break;
 
     case JSOP_FORCEINTERPRETER:
       // Intentionally not implemented.
       break;
 
     case JSOP_UNUSED71:
     case JSOP_UNUSED151:
+    case JSOP_UNUSED192:
     case JSOP_LIMIT:
       break;
   }
 
   // Track a simpler message, since the actionable abort message is a
   // static string, and the internal opcode name isn't an actionable
   // thing anyways.
   trackActionableAbort("Unsupported bytecode");
@@ -13132,28 +13130,16 @@ AbortReasonOr<Ok> IonBuilder::jsop_toasy
   MToAsync* ins = MToAsync::New(alloc(), unwrapped);
 
   current->add(ins);
   current->push(ins);
 
   return resumeAfter(ins);
 }
 
-AbortReasonOr<Ok> IonBuilder::jsop_toasyncgen() {
-  MDefinition* unwrapped = current->pop();
-  MOZ_ASSERT(unwrapped->type() == MIRType::Object);
-
-  MToAsyncGen* ins = MToAsyncGen::New(alloc(), unwrapped);
-
-  current->add(ins);
-  current->push(ins);
-
-  return resumeAfter(ins);
-}
-
 AbortReasonOr<Ok> IonBuilder::jsop_toasynciter() {
   MDefinition* nextMethod = current->pop();
   MDefinition* iterator = current->pop();
   MOZ_ASSERT(iterator->type() == MIRType::Object);
 
   MToAsyncIter* ins = MToAsyncIter::New(alloc(), iterator, nextMethod);
 
   current->add(ins);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -636,17 +636,16 @@ class IonBuilder : public MIRGenerator,
   AbortReasonOr<Ok> jsop_lambda_arrow(JSFunction* fun);
   AbortReasonOr<Ok> jsop_setfunname(uint8_t prefixKind);
   AbortReasonOr<Ok> jsop_pushlexicalenv(uint32_t index);
   AbortReasonOr<Ok> jsop_copylexicalenv(bool copySlots);
   AbortReasonOr<Ok> jsop_functionthis();
   AbortReasonOr<Ok> jsop_globalthis();
   AbortReasonOr<Ok> jsop_typeof();
   AbortReasonOr<Ok> jsop_toasync();
-  AbortReasonOr<Ok> jsop_toasyncgen();
   AbortReasonOr<Ok> jsop_toasynciter();
   AbortReasonOr<Ok> jsop_toid();
   AbortReasonOr<Ok> jsop_iter();
   AbortReasonOr<Ok> jsop_itermore();
   AbortReasonOr<Ok> jsop_isnoiter();
   AbortReasonOr<Ok> jsop_iterend();
   AbortReasonOr<Ok> jsop_iternext();
   AbortReasonOr<Ok> jsop_in();
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1126,23 +1126,16 @@ void LIRGenerator::visitTypeOf(MTypeOf* 
 }
 
 void LIRGenerator::visitToAsync(MToAsync* ins) {
   LToAsync* lir = new (alloc()) LToAsync(useRegisterAtStart(ins->input()));
   defineReturn(lir, ins);
   assignSafepoint(lir, ins);
 }
 
-void LIRGenerator::visitToAsyncGen(MToAsyncGen* ins) {
-  LToAsyncGen* lir =
-      new (alloc()) LToAsyncGen(useRegisterAtStart(ins->input()));
-  defineReturn(lir, ins);
-  assignSafepoint(lir, ins);
-}
-
 void LIRGenerator::visitToAsyncIter(MToAsyncIter* ins) {
   LToAsyncIter* lir =
       new (alloc()) LToAsyncIter(useRegisterAtStart(ins->getIterator()),
                                  useBoxAtStart(ins->getNextMethod()));
   defineReturn(lir, ins);
   assignSafepoint(lir, ins);
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4377,27 +4377,16 @@ class MToAsync : public MUnaryInstructio
     setResultType(MIRType::Object);
   }
 
  public:
   INSTRUCTION_HEADER(ToAsync)
   TRIVIAL_NEW_WRAPPERS
 };
 
-class MToAsyncGen : public MUnaryInstruction, public SingleObjectPolicy::Data {
-  explicit MToAsyncGen(MDefinition* unwrapped)
-      : MUnaryInstruction(classOpcode, unwrapped) {
-    setResultType(MIRType::Object);
-  }
-
- public:
-  INSTRUCTION_HEADER(ToAsyncGen)
-  TRIVIAL_NEW_WRAPPERS
-};
-
 class MToAsyncIter : public MBinaryInstruction,
                      public MixPolicy<ObjectPolicy<0>, BoxPolicy<1>>::Data {
   explicit MToAsyncIter(MDefinition* iterator, MDefinition* nextMethod)
       : MBinaryInstruction(classOpcode, iterator, nextMethod) {
     setResultType(MIRType::Object);
   }
 
  public:
@@ -6799,17 +6788,18 @@ struct LambdaFunctionInfo {
         singletonType(fun->isSingleton()),
         useSingletonForClone(ObjectGroup::useSingletonForClone(fun)) {
     // If this assert fails, make sure CodeGenerator::visitLambda does the
     // right thing. We can't assert this off-thread in CodeGenerator,
     // because fun->isAsync() accesses the script/lazyScript and can race
     // with delazification on the main thread.
     MOZ_ASSERT_IF(flags & JSFunction::EXTENDED,
                   fun->isArrow() || fun->allowSuperProperty() ||
-                      fun->isSelfHostedBuiltin() || fun->isAsync());
+                      fun->isSelfHostedBuiltin() ||
+                      (fun->isAsync() && !fun->isGenerator()));
   }
 
   // Be careful when calling this off-thread. Don't call any JSFunction*
   // methods that depend on script/lazyScript - this can race with
   // delazification on the main thread.
   JSFunction* funUnsafe() const { return fun_; }
 
   bool appendRoots(MRootList& roots) const {
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -787,28 +787,16 @@ class LToAsync : public LCallInstruction
   explicit LToAsync(const LAllocation& input)
       : LCallInstructionHelper(classOpcode) {
     setOperand(0, input);
   }
 
   const LAllocation* unwrapped() { return getOperand(0); }
 };
 
-class LToAsyncGen : public LCallInstructionHelper<1, 1, 0> {
- public:
-  LIR_HEADER(ToAsyncGen)
-
-  explicit LToAsyncGen(const LAllocation& input)
-      : LCallInstructionHelper(classOpcode) {
-    setOperand(0, input);
-  }
-
-  const LAllocation* unwrapped() { return getOperand(0); }
-};
-
 class LToAsyncIter : public LCallInstructionHelper<1, 1 + BOX_PIECES, 0> {
  public:
   LIR_HEADER(ToAsyncIter)
 
   explicit LToAsyncIter(const LAllocation& iterator,
                         const LBoxAllocation& nextMethod)
       : LCallInstructionHelper(classOpcode) {
     setOperand(0, iterator);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -470,17 +470,17 @@ static bool MappedArgGetter(JSContext* c
   } else if (JSID_IS_ATOM(id, cx->names().length)) {
     if (!argsobj.hasOverriddenLength()) {
       vp.setInt32(argsobj.initialLength());
     }
   } else {
     MOZ_ASSERT(JSID_IS_ATOM(id, cx->names().callee));
     if (!argsobj.hasOverriddenCallee()) {
       RootedFunction callee(cx, &argsobj.callee());
-      if (callee->isAsync()) {
+      if (callee->isAsync() && !callee->isGenerator()) {
         vp.setObject(*GetWrappedAsyncFunction(callee));
       } else {
         vp.setObject(*callee);
       }
     }
   }
   return true;
 }
--- a/js/src/vm/AsyncIteration.cpp
+++ b/js/src/vm/AsyncIteration.cpp
@@ -17,131 +17,16 @@
 #include "vm/SelfHosting.h"
 
 #include "vm/JSContext-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/List-inl.h"
 
 using namespace js;
 
-#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));
-
-  // Step 1.
-  InvokeArgs args2(cx);
-  if (!FillArgumentsFromArraylike(cx, args2, args)) {
-    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).
-  // 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;
-  }
-
-  JSFunction* wrapped = NewFunctionWithProto(
-      cx, WrappedAsyncGenerator, length, JSFunction::NATIVE_FUN, nullptr,
-      funName, proto, gc::AllocKind::FUNCTION_EXTENDED);
-  if (!wrapped) {
-    return nullptr;
-  }
-
-  if (unwrapped->hasInferredName()) {
-    wrapped->setInferredName(unwrapped->inferredName());
-  }
-
-  // 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.
@@ -273,29 +158,27 @@ 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<AsyncGeneratorGeneratorObject>());
+    JSContext* cx, HandleFunction asyncGen) {
+  MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator());
 
   AsyncGeneratorObject* asyncGenObj =
       OrdinaryCreateFromConstructorAsynGen(cx, asyncGen);
   if (!asyncGenObj) {
     return nullptr;
   }
 
-  // Async Iteration proposal 6.4.3.2 AsyncGeneratorStart.
-  // Step 6.
-  asyncGenObj->setGenerator(generatorVal);
+  // ES2019 draft rev c2aad21fee7f5ddc89fdf7d3d305618ca3a13242
+  // 25.5.3.2 AsyncGeneratorStart.
 
   // Step 7.
   asyncGenObj->setSuspendedStart();
 
   // Step 8.
   asyncGenObj->clearSingleQueueRequest();
 
   asyncGenObj->clearCachedRequest();
@@ -441,67 +324,62 @@ static MOZ_MUST_USE bool AsyncGeneratorY
 // 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());
-
-  // 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);
-  }
+  MOZ_ASSERT(!asyncGenObj->isClosed(),
+             "closed generator when resuming async generator");
+  MOZ_ASSERT(asyncGenObj->isSuspended(),
+             "non-suspended generator when resuming async generator");
 
   // 11.4.3.5 steps 12-14, 16-20.
   HandlePropertyName funName = completionKind == CompletionKind::Normal
                                    ? cx->names().AsyncGeneratorNext
                                    : completionKind == CompletionKind::Throw
                                          ? cx->names().AsyncGeneratorThrow
                                          : cx->names().AsyncGeneratorReturn;
   FixedInvokeArgs<1> args(cx);
   args[0].set(argument);
-  RootedValue result(cx);
-  if (!CallSelfHostedFunction(cx, funName, generatorVal, args, &result)) {
+  RootedValue thisOrRval(cx, ObjectValue(*asyncGenObj));
+  if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) {
     // 11.4.3.2 step 5.d, f.
-    if (!generatorVal.toObject().as<AbstractGeneratorObject>().isClosed()) {
-      generatorVal.toObject().as<AbstractGeneratorObject>().setClosed();
+    if (!asyncGenObj->isClosed()) {
+      asyncGenObj->setClosed();
     }
     return AsyncGeneratorThrown(cx, asyncGenObj);
   }
 
   // 4.1 steps 2-9.
-  if (asyncGenObj->generatorObj()->isAfterAwait()) {
-    return AsyncGeneratorAwait(cx, asyncGenObj, result);
+  if (asyncGenObj->isAfterAwait()) {
+    return AsyncGeneratorAwait(cx, asyncGenObj, thisOrRval);
   }
 
   // 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());
+  RootedObject resultObj(cx, &thisOrRval.toObject());
   RootedValue value(cx);
   if (!GetProperty(cx, resultObj, resultObj, cx->names().value, &value)) {
     return false;
   }
 
-  if (asyncGenObj->generatorObj()->isAfterYield()) {
+  if (asyncGenObj->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[] = {
--- a/js/src/vm/AsyncIteration.h
+++ b/js/src/vm/AsyncIteration.h
@@ -3,53 +3,25 @@
  * 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 "builtin/Promise.h"
+#include "js/Class.h"
 #include "vm/GeneratorObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/List.h"
 
 namespace js {
 
-// An async generator is implemented using two function objects, which are
-// referred to as the "unwrapped" and the "wrapped" async generator object.
-// The unwrapped function is a generator function compiled from the async
-// generator's script. |await| expressions within the async generator 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,
-// hence its name, and is the publicly exposed object of the async generator.
-//
-// The unwrapped async generator is created while compiling the async generator,
-// and the wrapped async generator is created while executing the async
-// generator declaration or expression.
-
-// Create a wrapped async generator from an unwrapped async generator with given
-// prototype object.
-JSObject* WrapAsyncGeneratorWithProto(JSContext* cx, HandleFunction unwrapped,
-                                      HandleObject proto);
-
-// Create a wrapped async generator from an unwrapped async generator
-// with default prototype object.
-JSObject* WrapAsyncGenerator(JSContext* cx, HandleFunction unwrapped);
-
-// Returns true if the given function is a wrapped async generator.
-bool IsWrappedAsyncGenerator(JSFunction* fun);
-
-// Returns a wrapped async generator from an unwrapped async generator.
-JSFunction* GetWrappedAsyncGenerator(JSFunction* unwrapped);
-
-// Return an unwrapped async generator from a wrapped async generator.
-JSFunction* GetUnwrappedAsyncGenerator(JSFunction* wrapped);
+class AsyncGeneratorObject;
 
 // Resume the async generator when the `await` operand fulfills to `value`.
 MOZ_MUST_USE bool AsyncGeneratorAwaitedFulfilled(
     JSContext* cx, Handle<AsyncGeneratorObject*> asyncGenObj,
     HandleValue value);
 
 // Resume the async generator when the `await` operand rejects with `reason`.
 MOZ_MUST_USE bool AsyncGeneratorAwaitedRejected(
@@ -62,18 +34,16 @@ MOZ_MUST_USE bool AsyncGeneratorAwaitedR
 // fulfilled or rejected.
 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 AsyncGeneratorObject;
-
 // AsyncGeneratorRequest record in the spec.
 // Stores the info from AsyncGenerator#{next,return,throw}.
 //
 // This object is reused across multiple requests as an optimization, and
 // stored in the Slot_CachedRequest slot.
 class AsyncGeneratorRequest : public NativeObject {
  private:
   enum AsyncGeneratorRequestSlots {
@@ -123,24 +93,21 @@ class AsyncGeneratorRequest : public Nat
   JS::Value completionValue() const {
     return getFixedSlot(Slot_CompletionValue);
   }
   PromiseObject* promise() const {
     return &getFixedSlot(Slot_Promise).toObject().as<PromiseObject>();
   }
 };
 
-class AsyncGeneratorObject : public NativeObject {
+class AsyncGeneratorObject : public AbstractGeneratorObject {
  private:
   enum AsyncGeneratorObjectSlots {
     // Int32 value containing one of the |State| fields from below.
-    Slot_State = 0,
-
-    // Generator object for the unwrapped async generator.
-    Slot_Generator,
+    Slot_State = AbstractGeneratorObject::RESERVED_SLOTS,
 
     // * null value if this async generator has no requests
     // * AsyncGeneratorRequest if this async generator has only one request
     // * list object if this async generator has 2 or more requests
     Slot_QueueOrRequest,
 
     // Cached AsyncGeneratorRequest for later use.
     // undefined if there's no cache.
@@ -178,18 +145,16 @@ class AsyncGeneratorObject : public Nati
     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, a
   // list is created and requests are appended into it, and the list is
   // stored to the slot.
 
   bool isSingleQueue() const {
     return getFixedSlot(Slot_QueueOrRequest).isNull() ||
            getFixedSlot(Slot_QueueOrRequest)
@@ -216,18 +181,17 @@ class AsyncGeneratorObject : public Nati
   }
   void setQueue(ListObject* queue_) {
     setFixedSlot(Slot_QueueOrRequest, ObjectValue(*queue_));
   }
 
  public:
   static const Class class_;
 
-  static AsyncGeneratorObject* create(JSContext* cx, HandleFunction asyncGen,
-                                      HandleValue generatorVal);
+  static AsyncGeneratorObject* create(JSContext* cx, HandleFunction asyncGen);
 
   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; }
@@ -235,23 +199,16 @@ 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); }
-  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);
 
   static AsyncGeneratorRequest* peekRequest(
--- a/js/src/vm/BytecodeUtil.cpp
+++ b/js/src/vm/BytecodeUtil.cpp
@@ -2059,17 +2059,16 @@ bool ExpressionDecompiler::decompilePC(j
         return write("JS_IS_CONSTRUCTING");
 
       case JSOP_ITER:
         return write("ITER");
 
       case JSOP_LAMBDA:
       case JSOP_LAMBDA_ARROW:
       case JSOP_TOASYNC:
-      case JSOP_TOASYNCGEN:
         return write("FUN");
 
       case JSOP_TOASYNCITER:
         return write("ASYNCITER");
 
       case JSOP_MOREITER:
         // For stack dump, defIndex == 0 is not used.
         MOZ_ASSERT(defIndex == 1);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -217,18 +217,16 @@ static const Class DebuggerSource_class 
  * even though their actual code is indeed JavaScript. Debugger should treat
  * async functions and generators like any other scripted function, so we must
  * carefully check for them whenever we want inspect a function.
  */
 
 static JSFunction* RemoveAsyncWrapper(JSFunction* fun) {
   if (js::IsWrappedAsyncFunction(fun)) {
     fun = js::GetUnwrappedAsyncFunction(fun);
-  } else if (js::IsWrappedAsyncGenerator(fun)) {
-    fun = js::GetUnwrappedAsyncGenerator(fun);
   }
 
   return fun;
 }
 
 static inline bool EnsureFunctionHasScript(JSContext* cx, HandleFunction fun) {
   if (fun->isInterpretedLazy()) {
     AutoRealm ar(cx, fun);
@@ -11197,18 +11195,17 @@ bool DebuggerObject::isAsyncFunction() c
   MOZ_ASSERT(isDebuggeeFunction());
 
   return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isAsync();
 }
 
 bool DebuggerObject::isGeneratorFunction() const {
   MOZ_ASSERT(isDebuggeeFunction());
 
-  JSFunction* fun = RemoveAsyncWrapper(&referent()->as<JSFunction>());
-  return fun->isGenerator();
+  return RemoveAsyncWrapper(&referent()->as<JSFunction>())->isGenerator();
 }
 
 bool DebuggerObject::isGlobal() const { return referent()->is<GlobalObject>(); }
 
 bool DebuggerObject::isScriptedProxy() const {
   return js::IsScriptedProxy(referent());
 }
 
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -3638,17 +3638,17 @@ bool js::InitFunctionEnvironmentObjects(
   MOZ_ASSERT(frame.isFunctionFrame());
   MOZ_ASSERT(frame.callee()->needsSomeEnvironmentObject());
 
   RootedFunction callee(cx, frame.callee());
 
   // Named lambdas may have an environment that holds itself for recursion.
   if (callee->needsNamedLambdaEnvironment()) {
     NamedLambdaObject* declEnv;
-    if (callee->isAsync()) {
+    if (callee->isAsync() && !callee->isGenerator()) {
       // Named async function needs special environment to return
       // wrapped function for the binding.
       RootedFunction fun(cx, GetWrappedAsyncFunction(callee));
       declEnv = NamedLambdaObject::create(cx, frame, fun);
     } else {
       declEnv = NamedLambdaObject::create(cx, frame);
     }
     if (!declEnv) {
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * 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/GeneratorObject.h"
 
 #include "js/PropertySpec.h"
+#include "vm/AsyncIteration.h"
 #include "vm/JSObject.h"
 
 #include "vm/ArrayObject-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/JSAtom-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
@@ -19,54 +20,29 @@
 using namespace js;
 
 JSObject* AbstractGeneratorObject::create(JSContext* cx,
                                           AbstractFramePtr frame) {
   MOZ_ASSERT(frame.isGeneratorFrame());
   MOZ_ASSERT(frame.script()->nfixed() == 0);
   MOZ_ASSERT(!frame.isConstructing());
 
-  Rooted<GlobalObject*> global(cx, cx->global());
-
-  RootedValue pval(cx);
   RootedFunction fun(cx, frame.callee());
 
   Rooted<AbstractGeneratorObject*> genObj(cx);
   if (!fun->isAsync()) {
-    MOZ_ASSERT(fun->isGenerator());
-
-    // FIXME: This would be faster if we could avoid doing a lookup to get
-    // the prototype for the instance.  Bug 906600.
-    if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) {
-      return nullptr;
-    }
-    RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
-    if (!proto) {
-      proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global);
-      if (!proto) {
-        return nullptr;
-      }
-    }
-
-    genObj = NewObjectWithGivenProto<GeneratorObject>(cx, proto);
-    if (!genObj) {
-      return nullptr;
-    }
+    genObj = GeneratorObject::create(cx, fun);
   } else if (fun->isGenerator()) {
-    RootedObject proto(cx, nullptr);
-    genObj = NewObjectWithGivenProto<AsyncGeneratorGeneratorObject>(cx, proto);
-    if (!genObj) {
-      return nullptr;
-    }
+    genObj = AsyncGeneratorObject::create(cx, fun);
   } else {
     RootedObject proto(cx, nullptr);
     genObj = NewObjectWithGivenProto<AsyncFunctionGeneratorObject>(cx, proto);
-    if (!genObj) {
-      return nullptr;
-    }
+  }
+  if (!genObj) {
+    return nullptr;
   }
 
   genObj->setCallee(*frame.callee());
   genObj->setEnvironmentChain(*frame.environmentChain());
   if (frame.script()->needsArgsObj()) {
     genObj->setArgsObj(frame.argsObj());
   }
   genObj->clearExpressionStack();
@@ -213,32 +189,47 @@ bool AbstractGeneratorObject::resume(JSC
   activation.regs().sp++;
   MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
   activation.regs().sp[-1] = arg;
 
   genObj->setRunning();
   return true;
 }
 
+GeneratorObject* GeneratorObject::create(JSContext* cx, HandleFunction fun) {
+  MOZ_ASSERT(fun->isGenerator() && !fun->isAsync());
+
+  // FIXME: This would be faster if we could avoid doing a lookup to get
+  // the prototype for the instance.  Bug 906600.
+  RootedValue pval(cx);
+  if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) {
+    return nullptr;
+  }
+  RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
+  if (!proto) {
+    proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global());
+    if (!proto) {
+      return nullptr;
+    }
+  }
+  return NewObjectWithGivenProto<GeneratorObject>(cx, proto);
+}
+
 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);
@@ -329,10 +320,10 @@ bool AbstractGeneratorObject::isAfterYie
              code[offset] == JSOP_AWAIT);
 
   return code[offset] == op;
 }
 
 template <>
 bool JSObject::is<js::AbstractGeneratorObject>() const {
   return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() ||
-         is<AsyncGeneratorGeneratorObject>();
+         is<AsyncGeneratorObject>();
 }
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -201,32 +201,27 @@ class AbstractGeneratorObject : public N
   }
 };
 
 class GeneratorObject : public AbstractGeneratorObject {
  public:
   enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS };
 
   static const Class class_;
+
+  static GeneratorObject* create(JSContext* cx, HandleFunction fun);
 };
 
 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/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -407,19 +407,19 @@ inline int32_t GlobalObject::OffThreadPl
 /* static */ JSObject* GlobalObject::createOffThreadObject(
     JSContext* cx, Handle<GlobalObject*> global, unsigned slot) {
   // Don't create prototype objects for off-thread parse globals. Instead
   // create a placeholder object which we can use to find the real prototype
   // when the off-thread compartment is merged back into the target
   // compartment.
 
   MOZ_ASSERT(global->zone()->createdForHelperThread());
-  MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO || slot == MODULE_PROTO ||
-             slot == IMPORT_ENTRY_PROTO || slot == EXPORT_ENTRY_PROTO ||
-             slot == REQUESTED_MODULE_PROTO);
+  MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO || slot == ASYNC_GENERATOR ||
+             slot == MODULE_PROTO || slot == IMPORT_ENTRY_PROTO ||
+             slot == EXPORT_ENTRY_PROTO || slot == REQUESTED_MODULE_PROTO);
 
   auto placeholder = OffThreadPlaceholderObject::New(cx, slot);
   if (!placeholder) {
     return nullptr;
   }
 
   global->setSlot(slot, ObjectValue(*placeholder));
   return placeholder;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -741,16 +741,20 @@ static bool EnsureParserCreatedClasses(J
   if (!EnsureConstructor(cx, global, JSProto_RegExp)) {
     return false;  // needed by regular expression literals
   }
 
   if (!GlobalObject::initGenerators(cx, global)) {
     return false;  // needed by function*() {}
   }
 
+  if (!GlobalObject::initAsyncGenerators(cx, global)) {
+    return false;  // needed by async function*() {}
+  }
+
   if (kind == ParseTaskKind::Module &&
       !GlobalObject::ensureModulePrototypesCreated(cx, global)) {
     return false;
   }
 
   return true;
 }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1942,16 +1942,17 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
     }
 
     /* Various 1-byte no-ops. */
     CASE(JSOP_NOP)
     CASE(JSOP_NOP_DESTRUCTURING)
     CASE(JSOP_TRY_DESTRUCTURING)
     CASE(JSOP_UNUSED71)
     CASE(JSOP_UNUSED151)
+    CASE(JSOP_UNUSED192)
     CASE(JSOP_TRY)
     CASE(JSOP_CONDSWITCH) {
       MOZ_ASSERT(CodeSpec[*REGS.pc].length == 1);
       ADVANCE_AND_DISPATCH(1);
     }
 
     CASE(JSOP_JUMPTARGET)
     CASE(JSOP_LOOPHEAD) {
@@ -3588,28 +3589,16 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
       if (!wrapped) {
         goto error;
       }
 
       REGS.sp[-1].setObject(*wrapped);
     }
     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<Value> nextMethod(&rootValue0, REGS.sp[-1]);
       ReservedRooted<JSObject*> iter(&rootObject1, &REGS.sp[-2].toObject());
       JSObject* asyncIter = CreateAsyncFromSyncIterator(cx, iter, nextMethod);
       if (!asyncIter) {
         goto error;
       }
 
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -285,20 +285,21 @@ bool CallerGetterImpl(JSContext* cx, con
     ++iter;
   }
 
   if (iter.done() || !iter.isFunctionFrame()) {
     args.rval().setNull();
     return true;
   }
 
-  RootedObject caller(cx, iter.callee(cx));
-  if (caller->is<JSFunction>() && caller->as<JSFunction>().isAsync()) {
-    caller = GetWrappedAsyncFunction(&caller->as<JSFunction>());
+  JSFunction* callerFun = iter.callee(cx);
+  if (callerFun->isAsync() && !callerFun->isGenerator()) {
+    callerFun = GetWrappedAsyncFunction(callerFun);
   }
+  RootedObject caller(cx, callerFun);
   if (!cx->compartment()->wrap(cx, &caller)) {
     return false;
   }
 
   // Censor the caller if we don't have full access to it.  If we do, but the
   // caller is a function with strict mode code, throw a TypeError per ES5.
   // If we pass these checks, we can return the computed caller.
   {
@@ -312,18 +313,16 @@ bool CallerGetterImpl(JSContext* cx, con
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_DEAD_OBJECT);
       return false;
     }
 
     JSFunction* callerFun = &callerObj->as<JSFunction>();
     if (IsWrappedAsyncFunction(callerFun)) {
       callerFun = GetUnwrappedAsyncFunction(callerFun);
-    } else if (IsWrappedAsyncGenerator(callerFun)) {
-      callerFun = GetUnwrappedAsyncGenerator(callerFun);
     }
     MOZ_ASSERT(!callerFun->isBuiltin(),
                "non-builtin iterator returned a builtin?");
 
     if (callerFun->strict()) {
       JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                 JSMSG_CALLER_IS_STRICT);
       return false;
@@ -360,36 +359,33 @@ static bool CallerSetter(JSContext* cx, 
 
 static const JSPropertySpec function_properties[] = {
     JS_PSGS("arguments", ArgumentsGetter, ArgumentsSetter, 0),
     JS_PSGS("caller", CallerGetter, CallerSetter, 0), JS_PS_END};
 
 static bool ResolveInterpretedFunctionPrototype(JSContext* cx,
                                                 HandleFunction fun,
                                                 HandleId id) {
-  bool isAsyncGenerator = IsWrappedAsyncGenerator(fun);
-
-  MOZ_ASSERT_IF(!isAsyncGenerator,
-                fun->isInterpreted() || fun->isAsmJSNative());
+  MOZ_ASSERT(fun->isInterpreted() || fun->isAsmJSNative());
   MOZ_ASSERT(id == NameToId(cx->names().prototype));
 
   // Assert that fun is not a compiler-created function object, which
   // must never leak to script or embedding code and then be mutated.
   // Also assert that fun is not bound, per the ES5 15.3.4.5 ref above.
-  MOZ_ASSERT_IF(!isAsyncGenerator, !IsInternalFunctionObject(*fun));
+  MOZ_ASSERT(!IsInternalFunctionObject(*fun));
   MOZ_ASSERT(!fun->isBoundFunction());
 
   // Make the prototype object an instance of Object with the same parent as
   // the function object itself, unless the function is an ES6 generator.  In
   // that case, per the 15 July 2013 ES6 draft, section 15.19.3, its parent is
   // the GeneratorObjectPrototype singleton.
   bool isGenerator = fun->isGenerator();
   Rooted<GlobalObject*> global(cx, &fun->global());
   RootedObject objProto(cx);
-  if (isAsyncGenerator) {
+  if (isGenerator && fun->isAsync()) {
     objProto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, global);
   } else if (isGenerator) {
     objProto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, global);
   } else {
     objProto = GlobalObject::getOrCreateObjectPrototype(cx, global);
   }
   if (!objProto) {
     return false;
@@ -400,17 +396,17 @@ static bool ResolveInterpretedFunctionPr
   if (!proto) {
     return false;
   }
 
   // Per ES5 13.2 the prototype's .constructor property is configurable,
   // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
   // section 15.19.3, the .prototype of a generator function does not link
   // back with a .constructor.
-  if (!isGenerator && !isAsyncGenerator) {
+  if (!isGenerator) {
     RootedValue objVal(cx, ObjectValue(*fun));
     if (!DefineDataProperty(cx, proto, cx->names().constructor, objVal, 0)) {
       return false;
     }
   }
 
   // Per ES5 15.3.5.2 a user-defined function's .prototype property is
   // initially non-configurable, non-enumerable, and writable.
@@ -432,22 +428,19 @@ bool JSFunction::needsPrototypeProperty(
    * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors.
    * Generators are not constructors, but they have a .prototype property
    * anyway, according to errata to ES6. See bug 1191486.
    *
    * Thus all of the following don't get a .prototype property:
    * - Methods (that are not class-constructors or generators)
    * - Arrow functions
    * - Function.prototype
+   * - Async functions
    */
-  if (isBuiltin()) {
-    return IsWrappedAsyncGenerator(this);
-  }
-
-  return isConstructor() || isGenerator() || isAsync();
+  return !isBuiltin() && (isConstructor() || isGenerator());
 }
 
 static bool fun_mayResolve(const JSAtomState& names, jsid id, JSObject*) {
   if (!JSID_IS_ATOM(id)) {
     return false;
   }
 
   JSAtom* atom = JSID_TO_ATOM(id);
@@ -541,19 +534,20 @@ static bool fun_resolve(JSContext* cx, H
 
 template <XDRMode mode>
 XDRResult js::XDRInterpretedFunction(XDRState<mode>* xdr,
                                      HandleScope enclosingScope,
                                      HandleScriptSourceObject sourceObject,
                                      MutableHandleFunction objp) {
   enum FirstWordFlag {
     HasAtom = 0x1,
-    HasGeneratorProto = 0x2,
-    IsLazy = 0x4,
-    HasSingletonType = 0x8
+    IsGenerator = 0x2,
+    IsAsync = 0x4,
+    IsLazy = 0x8,
+    HasSingletonType = 0x10
   };
 
   /* NB: Keep this in sync with CloneInnerInterpretedFunction. */
   RootedAtom atom(xdr->cx());
   uint32_t firstword = 0; /* bitmask of FirstWordFlag */
   uint32_t flagsword = 0; /* word for argument count and fun->flags */
 
   JSContext* cx = xdr->cx();
@@ -567,18 +561,22 @@ XDRResult js::XDRInterpretedFunction(XDR
       return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
     }
 
     if (fun->explicitName() || fun->hasInferredName() ||
         fun->hasGuessedAtom()) {
       firstword |= HasAtom;
     }
 
-    if (fun->isGenerator() || fun->isAsync()) {
-      firstword |= HasGeneratorProto;
+    if (fun->isGenerator()) {
+      firstword |= IsGenerator;
+    }
+
+    if (fun->isAsync()) {
+      firstword |= IsAsync;
     }
 
     if (fun->isInterpretedLazy()) {
       // Encode a lazy script.
       firstword |= IsLazy;
       lazy = fun->lazyScript();
     } else {
       // Encode the script.
@@ -610,19 +608,23 @@ XDRResult js::XDRInterpretedFunction(XDR
 
   if (firstword & HasAtom) {
     MOZ_TRY(XDRAtom(xdr, &atom));
   }
   MOZ_TRY(xdr->codeUint32(&flagsword));
 
   if (mode == XDR_DECODE) {
     RootedObject proto(cx);
-    if (firstword & HasGeneratorProto) {
-      proto =
-          GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+    if ((firstword & IsGenerator) || (firstword & IsAsync)) {
+      if ((firstword & IsGenerator) && (firstword & IsAsync)) {
+        proto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
+      } else {
+        proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(
+            cx, cx->global());
+      }
       if (!proto) {
         return xdr->fail(JS::TranscodeResult_Throw);
       }
     }
 
     gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
     if (uint16_t(flagsword) & JSFunction::EXTENDED) {
       allocKind = gc::AllocKind::FUNCTION_EXTENDED;
@@ -878,20 +880,16 @@ JSString* js::FunctionToString(JSContext
   if (IsAsmJSFunction(fun)) {
     return AsmJSFunctionToString(cx, fun);
   }
 
   if (IsWrappedAsyncFunction(fun)) {
     RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
     return FunctionToString(cx, unwrapped, isToSource);
   }
-  if (IsWrappedAsyncGenerator(fun)) {
-    RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
-    return FunctionToString(cx, unwrapped, isToSource);
-  }
 
   RootedScript script(cx);
   if (fun->hasScript()) {
     script = fun->nonLazyScript();
   }
 
   // Default class constructors are self-hosted, but have their source
   // objects overridden to refer to the span of the class statement or
@@ -1910,34 +1908,39 @@ static bool CreateDynamicFunction(JSCont
    * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
    * and so would a call to f from another top-level's script or function.
    */
   RootedAtom anonymousAtom(cx, cx->names().anonymous);
 
   // Initialize the function with the default prototype:
   // Leave as nullptr to get the default from clasp for normal functions.
   // Use %Generator% for generators and the unwrapped function of async
-  // functions and async generators.
+  // functions. Use %AsyncGenerator% for async generator functions.
   RootedObject defaultProto(cx);
   if (isGenerator || isAsync) {
-    defaultProto =
-        GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global);
+    if (isGenerator && isAsync) {
+      defaultProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
+    } else {
+      defaultProto =
+          GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, global);
+    }
     if (!defaultProto) {
       return false;
     }
   }
 
   // Step 30-37 (reordered).
   RootedObject globalLexical(cx, &global->lexicalEnvironment());
   JSFunction::Flags flags =
       (isGenerator || isAsync)
           ? JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC
           : JSFunction::INTERPRETED_LAMBDA;
-  gc::AllocKind allocKind =
-      isAsync ? gc::AllocKind::FUNCTION_EXTENDED : gc::AllocKind::FUNCTION;
+  gc::AllocKind allocKind = (isAsync && !isGenerator)
+                                ? gc::AllocKind::FUNCTION_EXTENDED
+                                : gc::AllocKind::FUNCTION;
   RootedFunction fun(
       cx,
       NewFunctionWithProto(cx, nullptr, 0, flags, globalLexical, anonymousAtom,
                            defaultProto, allocKind, TenuredObject));
   if (!fun) {
     return false;
   }
 
@@ -1989,27 +1992,22 @@ static bool CreateDynamicFunction(JSCont
   }
 
   // Steps 6, 29.
   RootedObject proto(cx);
   if (!GetPrototypeFromBuiltinConstructor(cx, args, protoKey, &proto)) {
     return false;
   }
 
-  if (isAsync) {
+  if (isAsync && !isGenerator) {
     // Create the async function wrapper.
-    JSObject* wrapped;
-    if (isGenerator) {
-      wrapped = proto ? WrapAsyncGeneratorWithProto(cx, fun, proto)
-                      : WrapAsyncGenerator(cx, fun);
-    } else {
-      // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype.
-      wrapped = proto ? WrapAsyncFunctionWithProto(cx, fun, proto)
-                      : WrapAsyncFunction(cx, fun);
-    }
+
+    // Step 9.d, use %AsyncFunctionPrototype% as the fallback prototype.
+    JSObject* wrapped = proto ? WrapAsyncFunctionWithProto(cx, fun, proto)
+                              : WrapAsyncFunction(cx, fun);
     if (!wrapped) {
       return false;
     }
 
     fun = &wrapped->as<JSFunction>();
   } else {
     // Steps 7.d, 8.d (implicit).
     // Call SetPrototype if an explicit prototype was given.
@@ -2194,18 +2192,22 @@ bool js::CanReuseScriptForClone(JS::Real
 }
 
 static inline JSFunction* NewFunctionClone(JSContext* cx, HandleFunction fun,
                                            NewObjectKind newKind,
                                            gc::AllocKind allocKind,
                                            HandleObject proto) {
   RootedObject cloneProto(cx, proto);
   if (!proto && (fun->isGenerator() || fun->isAsync())) {
-    cloneProto =
-        GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+    if (fun->isGenerator() && fun->isAsync()) {
+      cloneProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
+    } else {
+      cloneProto =
+          GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+    }
     if (!cloneProto) {
       return nullptr;
     }
   }
 
   RootedFunction clone(cx);
   clone =
       NewObjectWithClassProto<JSFunction>(cx, cloneProto, allocKind, newKind);
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -3951,18 +3951,22 @@ void js::DescribeScriptedCallerForCompil
 }
 
 static JSObject* CloneInnerInterpretedFunction(
     JSContext* cx, HandleScope enclosingScope, HandleFunction srcFun,
     Handle<ScriptSourceObject*> sourceObject) {
   /* NB: Keep this in sync with XDRInterpretedFunction. */
   RootedObject cloneProto(cx);
   if (srcFun->isGenerator() || srcFun->isAsync()) {
-    cloneProto =
-        GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+    if (srcFun->isGenerator() && srcFun->isAsync()) {
+      cloneProto = GlobalObject::getOrCreateAsyncGenerator(cx, cx->global());
+    } else {
+      cloneProto =
+          GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+    }
     if (!cloneProto) {
       return nullptr;
     }
   }
 
   gc::AllocKind allocKind = srcFun->getAllocKind();
   uint16_t flags = srcFun->flags();
   if (srcFun->isSelfHostedBuiltin()) {
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2062,25 +2062,18 @@
      *
      *   Category: Variables and Scopes
      *   Type: This
      *   Operands:
      *   Stack: this => this
      */ \
     MACRO(JSOP_CHECKTHISREINIT, 191, "checkthisreinit", NULL, 1, 1, 1, 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) \
+    MACRO(JSOP_UNUSED192, 192, "unused192", NULL, 1, 0, 0, JOF_BYTE) \
     /*
      * Pops the top two values on the stack as 'propval' and 'obj', pushes
      * 'propval' property of 'obj' onto the stack.
      *
      * Like JSOP_GETELEM but for call context.
      *
      *   Category: Literals
      *   Type: Object
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -38,16 +38,17 @@
 #include "js/CharacterEncoding.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/Date.h"
 #include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/AsyncIteration.h"
 #include "vm/Compression.h"
 #include "vm/GeneratorObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 #include "vm/JSObject.h"
 #include "vm/PIC.h"
@@ -2555,18 +2556,18 @@ static const JSFunctionSpec intrinsic_fu
     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_FN("IsAsyncGeneratorObject",
+          intrinsic_IsInstanceOfBuiltin<AsyncGeneratorObject>, 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",