Bug 1530324 - Part 2: Add abstract super class for GeneratorObject. r=arai
authorAndré Bargull <andre.bargull@gmail.com>
Mon, 25 Feb 2019 05:03:57 -0800
changeset 519554 90d0e91224a9f061e631f88a4aed499108349b5d
parent 519553 1dd26ddfc3734772ec7531d34792ee2c80402fe8
child 519555 e125746cec9579271e48e5570ab9fec66222633d
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 2: Add abstract super class for GeneratorObject. r=arai
js/src/frontend/BytecodeEmitter.cpp
js/src/jit/BaselineCompiler.cpp
js/src/jit/CacheIRCompiler.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/vm/Debugger-inl.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GeneratorObject.cpp
js/src/vm/GeneratorObject.h
js/src/vm/Interpreter.cpp
js/src/vm/Iteration.cpp
js/src/vm/Opcodes.h
js/src/vm/Stack-inl.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2206,19 +2206,19 @@ bool BytecodeEmitter::isRunOnceLambda() 
          !funbox->isAsync() && !funbox->function()->explicitName();
 }
 
 bool BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset,
                                           uint32_t* resumeIndex) {
   static constexpr uint32_t MaxResumeIndex = JS_BITMASK(24);
 
   static_assert(
-      MaxResumeIndex < uint32_t(GeneratorObject::RESUME_INDEX_CLOSING),
-      "resumeIndex should not include magic GeneratorObject resumeIndex "
-      "values");
+      MaxResumeIndex < uint32_t(AbstractGeneratorObject::RESUME_INDEX_CLOSING),
+      "resumeIndex should not include magic AbstractGeneratorObject "
+      "resumeIndex values");
   static_assert(
       MaxResumeIndex <= INT32_MAX / sizeof(uintptr_t),
       "resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies "
       "on this when loading resume entries from BaselineScript");
 
   *resumeIndex = resumeOffsetList.length();
   if (*resumeIndex > MaxResumeIndex) {
     reportError(nullptr, JSMSG_TOO_MANY_RESUME_INDEXES);
@@ -7167,18 +7167,18 @@ bool BytecodeEmitter::emitSelfHostedResu
 
   ParseNode* valNode = genNode->pn_next;
   if (!emitTree(valNode)) {
     return false;
   }
 
   ParseNode* kindNode = valNode->pn_next;
   MOZ_ASSERT(kindNode->isKind(ParseNodeKind::StringExpr));
-  uint16_t operand =
-      GeneratorObject::getResumeKind(cx, kindNode->as<NameNode>().atom());
+  uint16_t operand = AbstractGeneratorObject::getResumeKind(
+      cx, kindNode->as<NameNode>().atom());
   MOZ_ASSERT(!kindNode->pn_next);
 
   if (!emitCall(JSOP_RESUME, operand)) {
     return false;
   }
 
   return true;
 }
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -5153,22 +5153,24 @@ template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_JSOP_INITIALYIELD() {
   frame.syncStack(0);
   frame.assertStackDepth(1);
 
   Register genObj = R2.scratchReg();
   masm.unboxObject(frame.addressOfStackValue(-1), genObj);
 
   MOZ_ASSERT_IF(handler.maybePC(), GET_RESUMEINDEX(handler.maybePC()) == 0);
-  masm.storeValue(Int32Value(0),
-                  Address(genObj, GeneratorObject::offsetOfResumeIndexSlot()));
+  masm.storeValue(
+      Int32Value(0),
+      Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()));
 
   Register envObj = R0.scratchReg();
   Register temp = R1.scratchReg();
-  Address envChainSlot(genObj, GeneratorObject::offsetOfEnvironmentChainSlot());
+  Address envChainSlot(genObj,
+                       AbstractGeneratorObject::offsetOfEnvironmentChainSlot());
   masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
   masm.guardedCallPreBarrierAnyZone(envChainSlot, MIRType::Value, temp);
   masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
 
   Label skipBarrier;
   masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
   masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp, &skipBarrier);
   masm.push(genObj);
@@ -5193,23 +5195,24 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 
   Register genObj = R2.scratchReg();
   masm.unboxObject(R0, genObj);
 
   if (frame.hasKnownStackDepth(1)) {
     // If the expression stack is empty, we can inline the YIELD.
 
     Register temp = R1.scratchReg();
-    Address resumeIndexSlot(genObj, GeneratorObject::offsetOfResumeIndexSlot());
+    Address resumeIndexSlot(genObj,
+                            AbstractGeneratorObject::offsetOfResumeIndexSlot());
     loadResumeIndexBytecodeOperand(temp);
     masm.storeValue(JSVAL_TYPE_INT32, temp, resumeIndexSlot);
 
     Register envObj = R0.scratchReg();
-    Address envChainSlot(genObj,
-                         GeneratorObject::offsetOfEnvironmentChainSlot());
+    Address envChainSlot(
+        genObj, AbstractGeneratorObject::offsetOfEnvironmentChainSlot());
     masm.loadPtr(frame.addressOfEnvironmentChain(), envObj);
     masm.guardedCallPreBarrier(envChainSlot, MIRType::Value);
     masm.storeValue(JSVAL_TYPE_OBJECT, envObj, envChainSlot);
 
     Label skipBarrier;
     masm.branchPtrInNurseryChunk(Assembler::Equal, genObj, temp, &skipBarrier);
     masm.branchPtrInNurseryChunk(Assembler::NotEqual, envObj, temp,
                                  &skipBarrier);
@@ -5292,41 +5295,40 @@ bool BaselineCodeGen<Handler>::emit_JSOP
 }
 
 typedef bool (*InterpretResumeFn)(JSContext*, HandleObject, HandleValue,
                                   HandlePropertyName, MutableHandleValue);
 static const VMFunction InterpretResumeInfo =
     FunctionInfo<InterpretResumeFn>(jit::InterpretResume, "InterpretResume");
 
 typedef bool (*GeneratorThrowFn)(JSContext*, BaselineFrame*,
-                                 Handle<GeneratorObject*>, HandleValue,
+                                 Handle<AbstractGeneratorObject*>, HandleValue,
                                  uint32_t);
 static const VMFunction GeneratorThrowOrReturnInfo =
     FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrReturn,
                                    "GeneratorThrowOrReturn", TailCall);
 
 template <>
 bool BaselineCompilerCodeGen::emit_JSOP_RESUME() {
-  GeneratorObject::ResumeKind resumeKind =
-      GeneratorObject::getResumeKind(handler.pc());
+  auto resumeKind = AbstractGeneratorObject::getResumeKind(handler.pc());
 
   frame.syncStack(0);
   masm.assertStackAlignment(sizeof(Value), 0);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   regs.take(BaselineFrameReg);
 
   // Load generator object.
   Register genObj = regs.takeAny();
   masm.unboxObject(frame.addressOfStackValue(-2), genObj);
 
   // Load callee.
   Register callee = regs.takeAny();
-  masm.unboxObject(Address(genObj, GeneratorObject::offsetOfCalleeSlot()),
-                   callee);
+  masm.unboxObject(
+      Address(genObj, AbstractGeneratorObject::offsetOfCalleeSlot()), callee);
 
   // Load the script. Note that we don't relazify generator scripts, so it's
   // guaranteed to be non-lazy.
   Register scratch1 = regs.takeAny();
   masm.loadPtr(Address(callee, JSFunction::offsetOfScript()), scratch1);
 
   // Load the BaselineScript or call a stub if we don't have one.
   Label interpret;
@@ -5420,34 +5422,35 @@ bool BaselineCompilerCodeGen::emit_JSOP_
   masm.push(BaselineFrameReg);
   masm.moveStackPtrTo(BaselineFrameReg);
   masm.subFromStackPtr(Imm32(BaselineFrame::Size()));
   masm.assertStackAlignment(sizeof(Value), 0);
 
   // Store flags and env chain.
   masm.store32(Imm32(BaselineFrame::HAS_INITIAL_ENV), frame.addressOfFlags());
   masm.unboxObject(
-      Address(genObj, GeneratorObject::offsetOfEnvironmentChainSlot()),
+      Address(genObj, AbstractGeneratorObject::offsetOfEnvironmentChainSlot()),
       scratch2);
   masm.storePtr(scratch2, frame.addressOfEnvironmentChain());
 
   // Store the arguments object if there is one.
   Label noArgsObj;
-  Address argsObjSlot(genObj, GeneratorObject::offsetOfArgsObjSlot());
+  Address argsObjSlot(genObj, AbstractGeneratorObject::offsetOfArgsObjSlot());
   masm.branchTestUndefined(Assembler::Equal, argsObjSlot, &noArgsObj);
   masm.unboxObject(argsObjSlot, scratch2);
   {
     masm.storePtr(scratch2, frame.addressOfArgsObj());
     masm.or32(Imm32(BaselineFrame::HAS_ARGS_OBJ), frame.addressOfFlags());
   }
   masm.bind(&noArgsObj);
 
   // Push expression slots if needed.
   Label noExprStack;
-  Address exprStackSlot(genObj, GeneratorObject::offsetOfExpressionStackSlot());
+  Address exprStackSlot(genObj,
+                        AbstractGeneratorObject::offsetOfExpressionStackSlot());
   masm.branchTestNull(Assembler::Equal, exprStackSlot, &noExprStack);
   {
     masm.unboxObject(exprStackSlot, scratch2);
 
     Register initLength = regs.takeAny();
     masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
     masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()),
                 initLength);
@@ -5469,37 +5472,38 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     regs.add(initLength);
   }
 
   masm.bind(&noExprStack);
   masm.pushValue(retVal);
 
   masm.switchToObjectRealm(genObj, scratch2);
 
-  if (resumeKind == GeneratorObject::NEXT) {
+  if (resumeKind == AbstractGeneratorObject::NEXT) {
     // Determine the resume address based on the resumeIndex and the
     // resumeIndex -> native table in the BaselineScript.
     masm.load32(
         Address(scratch1, BaselineScript::offsetOfResumeEntriesOffset()),
         scratch2);
     masm.addPtr(scratch2, scratch1);
-    masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfResumeIndexSlot()),
-                    scratch2);
+    masm.unboxInt32(
+        Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()),
+        scratch2);
     masm.loadPtr(
         BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))),
         scratch1);
 
     // Mark as running and jump to the generator's JIT code.
     masm.storeValue(
-        Int32Value(GeneratorObject::RESUME_INDEX_RUNNING),
-        Address(genObj, GeneratorObject::offsetOfResumeIndexSlot()));
+        Int32Value(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
+        Address(genObj, AbstractGeneratorObject::offsetOfResumeIndexSlot()));
     masm.jump(scratch1);
   } else {
-    MOZ_ASSERT(resumeKind == GeneratorObject::THROW ||
-               resumeKind == GeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::THROW ||
+               resumeKind == AbstractGeneratorObject::RETURN);
 
     // Update the frame's frameSize field.
     masm.computeEffectiveAddress(
         Address(BaselineFrameReg, BaselineFrame::FramePointerOffset), scratch2);
     masm.movePtr(scratch2, scratch1);
     masm.subStackPtrFrom(scratch2);
     masm.store32(scratch2, Address(BaselineFrameReg,
                                    BaselineFrame::reverseOffsetOfFrameSize()));
@@ -5541,22 +5545,22 @@ bool BaselineCompilerCodeGen::emit_JSOP_
     // framePushed.
     masm.implicitPop((fun.explicitStackSlots() + 1) * sizeof(void*));
   }
 
   // If the generator script has no JIT code, call into the VM.
   masm.bind(&interpret);
 
   prepareVMCall();
-  if (resumeKind == GeneratorObject::NEXT) {
+  if (resumeKind == AbstractGeneratorObject::NEXT) {
     pushArg(ImmGCPtr(cx->names().next));
-  } else if (resumeKind == GeneratorObject::THROW) {
+  } else if (resumeKind == AbstractGeneratorObject::THROW) {
     pushArg(ImmGCPtr(cx->names().throw_));
   } else {
-    MOZ_ASSERT(resumeKind == GeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::RETURN);
     pushArg(ImmGCPtr(cx->names().return_));
   }
 
   masm.loadValue(frame.addressOfStackValue(-1), retVal);
   pushArg(retVal);
   pushArg(genObj);
 
   if (!callVM(InterpretResumeInfo)) {
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -4270,21 +4270,22 @@ bool CacheIRCompiler::emitCallIsSuspende
   // Test if it's a GeneratorObject.
   masm.unboxObject(input, scratch);
   masm.branchTestObjClass(Assembler::NotEqual, scratch,
                           &GeneratorObject::class_, scratch2, scratch,
                           &returnFalse);
 
   // If the resumeIndex slot holds an int32 value < RESUME_INDEX_CLOSING,
   // the generator is suspended.
-  Address addr(scratch, GeneratorObject::offsetOfResumeIndexSlot());
+  Address addr(scratch, AbstractGeneratorObject::offsetOfResumeIndexSlot());
   masm.branchTestInt32(Assembler::NotEqual, addr, &returnFalse);
   masm.unboxInt32(addr, scratch);
   masm.branch32(Assembler::AboveOrEqual, scratch,
-                Imm32(GeneratorObject::RESUME_INDEX_CLOSING), &returnFalse);
+                Imm32(AbstractGeneratorObject::RESUME_INDEX_CLOSING),
+                &returnFalse);
 
   masm.moveValue(BooleanValue(true), output.valueReg());
   masm.jump(&done);
 
   masm.bind(&returnFalse);
   masm.moveValue(BooleanValue(false), output.valueReg());
 
   masm.bind(&done);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -787,17 +787,17 @@ bool DebugEpilogue(JSContext* cx, Baseli
 void FrameIsDebuggeeCheck(BaselineFrame* frame) {
   AutoUnsafeCallWithABI unsafe;
   if (frame->script()->isDebuggee()) {
     frame->setIsDebuggee();
   }
 }
 
 JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame) {
-  return GeneratorObject::create(cx, frame);
+  return AbstractGeneratorObject::create(cx, frame);
 }
 
 bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
                    jsbytecode* pc) {
   MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT);
 
   MOZ_ASSERT(frame->numValueSlots() > frame->script()->nfixed());
   uint32_t stackDepth = frame->numValueSlots() - frame->script()->nfixed();
@@ -815,29 +815,29 @@ bool NormalSuspend(JSContext* cx, Handle
 
   size_t firstSlot = frame->numValueSlots() - stackDepth;
   for (size_t i = 0; i < stackDepth - 1; i++) {
     exprStack.infallibleAppend(*frame->valueSlot(firstSlot + i));
   }
 
   MOZ_ASSERT(exprStack.length() == stackDepth - 1);
 
-  return GeneratorObject::normalSuspend(cx, obj, frame, pc, exprStack.begin(),
-                                        stackDepth - 1);
+  return AbstractGeneratorObject::normalSuspend(
+      cx, obj, frame, pc, exprStack.begin(), stackDepth - 1);
 }
 
 bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc) {
   MOZ_ASSERT(*pc == JSOP_FINALYIELDRVAL);
-  GeneratorObject::finalSuspend(obj);
+  AbstractGeneratorObject::finalSuspend(obj);
   return true;
 }
 
 bool InterpretResume(JSContext* cx, HandleObject obj, HandleValue val,
                      HandlePropertyName kind, MutableHandleValue rval) {
-  MOZ_ASSERT(obj->is<GeneratorObject>());
+  MOZ_ASSERT(obj->is<AbstractGeneratorObject>());
 
   FixedInvokeArgs<3> args(cx);
 
   args[0].setObject(*obj);
   args[1].set(val);
   args[2].setString(kind);
 
   return CallSelfHostedFunction(cx, cx->names().InterpretGeneratorResume,
@@ -857,36 +857,36 @@ bool DebugAfterYield(JSContext* cx, Base
     return HandlePrologueResumeMode(cx, frame, pc, mustReturn, resumeMode);
   }
 
   *mustReturn = false;
   return true;
 }
 
 bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
-                            Handle<GeneratorObject*> genObj, HandleValue arg,
-                            uint32_t resumeKind) {
+                            Handle<AbstractGeneratorObject*> genObj,
+                            HandleValue arg, uint32_t resumeKind) {
   // Set the frame's pc to the current resume pc, so that frame iterators
   // work. This function always returns false, so we're guaranteed to enter
   // the exception handler where we will clear the pc.
   JSScript* script = frame->script();
   uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()];
   jsbytecode* pc = script->offsetToPC(offset);
   frame->setOverridePc(pc);
 
-  // In the interpreter, GeneratorObject::resume marks the generator as running,
-  // so we do the same.
+  // In the interpreter, AbstractGeneratorObject::resume marks the generator as
+  // running, so we do the same.
   genObj->setRunning();
 
   bool mustReturn = false;
   if (!DebugAfterYield(cx, frame, pc, &mustReturn)) {
     return false;
   }
   if (mustReturn) {
-    resumeKind = GeneratorObject::RETURN;
+    resumeKind = AbstractGeneratorObject::RETURN;
   }
 
   MOZ_ALWAYS_FALSE(
       js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
   return false;
 }
 
 bool GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script) {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -16,17 +16,17 @@
 #include "jit/JitFrames.h"
 #include "vm/Interpreter.h"
 
 namespace js {
 
 class NamedLambdaObject;
 class WithScope;
 class InlineTypedObject;
-class GeneratorObject;
+class AbstractGeneratorObject;
 class RegExpObject;
 class TypedArrayObject;
 
 namespace gc {
 
 struct Cell;
 
 }
@@ -463,17 +463,17 @@ template <>
 struct TypeToDataType<Handle<InlineTypedObject*> > {
   static const DataType result = Type_Handle;
 };
 template <>
 struct TypeToDataType<Handle<ArrayObject*> > {
   static const DataType result = Type_Handle;
 };
 template <>
-struct TypeToDataType<Handle<GeneratorObject*> > {
+struct TypeToDataType<Handle<AbstractGeneratorObject*> > {
   static const DataType result = Type_Handle;
 };
 template <>
 struct TypeToDataType<Handle<PlainObject*> > {
   static const DataType result = Type_Handle;
 };
 template <>
 struct TypeToDataType<Handle<WithScope*> > {
@@ -546,19 +546,19 @@ struct TypeToArgProperties<Handle<Inline
       TypeToArgProperties<InlineTypedObject*>::result | VMFunction::ByRef;
 };
 template <>
 struct TypeToArgProperties<Handle<ArrayObject*> > {
   static const uint32_t result =
       TypeToArgProperties<ArrayObject*>::result | VMFunction::ByRef;
 };
 template <>
-struct TypeToArgProperties<Handle<GeneratorObject*> > {
+struct TypeToArgProperties<Handle<AbstractGeneratorObject*> > {
   static const uint32_t result =
-      TypeToArgProperties<GeneratorObject*>::result | VMFunction::ByRef;
+      TypeToArgProperties<AbstractGeneratorObject*>::result | VMFunction::ByRef;
 };
 template <>
 struct TypeToArgProperties<Handle<PlainObject*> > {
   static const uint32_t result =
       TypeToArgProperties<PlainObject*>::result | VMFunction::ByRef;
 };
 template <>
 struct TypeToArgProperties<Handle<RegExpObject*> > {
@@ -675,17 +675,17 @@ template <>
 struct TypeToRootType<Handle<InlineTypedObject*> > {
   static const uint32_t result = VMFunction::RootObject;
 };
 template <>
 struct TypeToRootType<Handle<ArrayObject*> > {
   static const uint32_t result = VMFunction::RootObject;
 };
 template <>
-struct TypeToRootType<Handle<GeneratorObject*> > {
+struct TypeToRootType<Handle<AbstractGeneratorObject*> > {
   static const uint32_t result = VMFunction::RootObject;
 };
 template <>
 struct TypeToRootType<Handle<PlainObject*> > {
   static const uint32_t result = VMFunction::RootObject;
 };
 template <>
 struct TypeToRootType<Handle<RegExpObject*> > {
@@ -1018,19 +1018,20 @@ JSObject* CreateGenerator(JSContext* cx,
 MOZ_MUST_USE bool NormalSuspend(JSContext* cx, HandleObject obj,
                                 BaselineFrame* frame, jsbytecode* pc);
 MOZ_MUST_USE bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc);
 MOZ_MUST_USE bool InterpretResume(JSContext* cx, HandleObject obj,
                                   HandleValue val, HandlePropertyName kind,
                                   MutableHandleValue rval);
 MOZ_MUST_USE bool DebugAfterYield(JSContext* cx, BaselineFrame* frame,
                                   jsbytecode* pc, bool* mustReturn);
-MOZ_MUST_USE bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
-                                         Handle<GeneratorObject*> genObj,
-                                         HandleValue arg, uint32_t resumeKind);
+MOZ_MUST_USE bool GeneratorThrowOrReturn(
+    JSContext* cx, BaselineFrame* frame,
+    Handle<AbstractGeneratorObject*> genObj, HandleValue arg,
+    uint32_t resumeKind);
 
 MOZ_MUST_USE bool GlobalNameConflictsCheckFromIon(JSContext* cx,
                                                   HandleScript script);
 MOZ_MUST_USE bool InitFunctionEnvironmentObjects(JSContext* cx,
                                                  BaselineFrame* frame);
 
 MOZ_MUST_USE bool NewArgumentsObject(JSContext* cx, BaselineFrame* frame,
                                      MutableHandleValue res);
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -4,16 +4,19 @@
  * 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_Debugger_inl_h
 #define vm_Debugger_inl_h
 
 #include "vm/Debugger.h"
 
+#include "builtin/Promise.h"
+#include "vm/GeneratorObject.h"
+
 #include "gc/WeakMap-inl.h"
 #include "vm/Stack-inl.h"
 
 /* static */ inline bool js::Debugger::onLeaveFrame(JSContext* cx,
                                                     AbstractFramePtr frame,
                                                     jsbytecode* pc, bool ok) {
   MOZ_ASSERT_IF(frame.isInterpreterFrame(),
                 frame.asInterpreterFrame() == cx->interpreterFrame());
@@ -26,17 +29,18 @@
   if (frame.isDebuggee()) {
     ok = slowPathOnLeaveFrame(cx, frame, pc, ok);
   }
   MOZ_ASSERT(!inFrameMaps(frame));
   return ok;
 }
 
 /* static */ inline bool js::Debugger::onNewGenerator(
-    JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj) {
+    JSContext* cx, AbstractFramePtr frame,
+    Handle<AbstractGeneratorObject*> genObj) {
   if (frame.isDebuggee()) {
     return slowPathOnNewGenerator(cx, frame, genObj);
   }
   return true;
 }
 
 /* static */ inline js::Debugger* js::Debugger::fromJSObject(
     const JSObject* obj) {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -728,17 +728,17 @@ bool Debugger::getFrame(JSContext* cx, c
   FrameMap::AddPtr p = frames.lookupForAdd(referent);
   if (!p) {
     RootedDebuggerFrame frame(cx);
 
     // If this is a generator frame, there may be an existing
     // Debugger.Frame object that isn't in `frames` because the generator
     // was suspended, popping the stack frame, and later resumed (and we
     // were not stepping, so did not pass through slowPathOnResumeFrame).
-    Rooted<GeneratorObject*> genObj(cx);
+    Rooted<AbstractGeneratorObject*> genObj(cx);
     GeneratorWeakMap::AddPtr gp;
     if (referent.isGeneratorFrame()) {
       {
         AutoRealm ar(cx, referent.callee());
         genObj = GetGeneratorObjectForFrame(cx, referent);
       }
       if (genObj) {
         gp = generatorFrames.lookupForAdd(genObj);
@@ -752,19 +752,19 @@ bool Debugger::getFrame(JSContext* cx, c
             return false;
           }
           if (!ensureExecutionObservabilityOfFrame(cx, referent)) {
             return false;
           }
         }
       }
 
-      // If no GeneratorObject exists yet, we create a Debugger.Frame
+      // If no AbstractGeneratorObject exists yet, we create a Debugger.Frame
       // below anyway, and Debugger::onNewGenerator() will associate it
-      // with the GeneratorObject later when we hit JSOP_GENERATOR.
+      // with the AbstractGeneratorObject later when we hit JSOP_GENERATOR.
     }
 
     if (!frame) {
       // Create and populate the Debugger.Frame object.
       RootedObject proto(
           cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
       RootedNativeObject debugger(cx, object);
 
@@ -795,17 +795,18 @@ bool Debugger::getFrame(JSContext* cx, c
       return false;
     }
   }
 
   result.set(&p->value()->as<DebuggerFrame>());
   return true;
 }
 
-bool Debugger::addGeneratorFrame(JSContext* cx, Handle<GeneratorObject*> genObj,
+bool Debugger::addGeneratorFrame(JSContext* cx,
+                                 Handle<AbstractGeneratorObject*> genObj,
                                  HandleDebuggerFrame frameObj) {
   GeneratorWeakMap::AddPtr p = generatorFrames.lookupForAdd(genObj);
   if (p) {
     MOZ_ASSERT(p->value() == frameObj);
   } else {
     if (!generatorFrames.relookupOrAdd(p, genObj, frameObj)) {
       ReportOutOfMemory(cx);
       return false;
@@ -920,17 +921,18 @@ bool Debugger::hasAnyLiveHooks(JSRuntime
 /* static */ ResumeMode Debugger::slowPathOnResumeFrame(
     JSContext* cx, AbstractFramePtr frame) {
   // Don't count on this method to be called every time a generator is
   // resumed! This is called only if the frame's debuggee bit is set,
   // i.e. the script has breakpoints or the frame is stepping.
   MOZ_ASSERT(frame.isGeneratorFrame());
   MOZ_ASSERT(frame.isDebuggee());
 
-  Rooted<GeneratorObject*> genObj(cx, GetGeneratorObjectForFrame(cx, frame));
+  Rooted<AbstractGeneratorObject*> genObj(
+      cx, GetGeneratorObjectForFrame(cx, frame));
   MOZ_ASSERT(genObj);
 
   // For each debugger, if there is an existing Debugger.Frame object for the
   // resumed `frame`, update it with the new frame pointer and make sure the
   // frame is observable.
   if (GlobalObject::DebuggerVector* debuggers =
           frame.global()->getDebuggers()) {
     for (Debugger* dbg : *debuggers) {
@@ -965,57 +967,56 @@ static void DebuggerFrame_maybeDecrement
  *
  * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
  * or awaiting, its generator is in the "suspended" state. Letting script
  * observe this state, with the generator on stack yet also reenterable, would
  * be bad, so we mark it running while we fire events.
  */
 class MOZ_RAII AutoSetGeneratorRunning {
   int32_t resumeIndex_;
-  Rooted<GeneratorObject*> genObj_;
+  Rooted<AbstractGeneratorObject*> genObj_;
 
  public:
-  AutoSetGeneratorRunning(JSContext* cx, Handle<GeneratorObject*> genObj)
+  AutoSetGeneratorRunning(JSContext* cx,
+                          Handle<AbstractGeneratorObject*> genObj)
       : resumeIndex_(0), genObj_(cx, genObj) {
     if (genObj) {
       if (!genObj->isClosed() && genObj->isSuspended()) {
         // Yielding or awaiting.
-        resumeIndex_ =
-            genObj->getFixedSlot(GeneratorObject::RESUME_INDEX_SLOT).toInt32();
+        resumeIndex_ = genObj->resumeIndex();
         genObj->setRunning();
       } else {
         // Returning or throwing. The generator is already closed, if
         // it was ever exposed at all.
         genObj_ = nullptr;
       }
     }
   }
 
   ~AutoSetGeneratorRunning() {
     if (genObj_) {
       MOZ_ASSERT(genObj_->isRunning());
-      genObj_->setFixedSlot(GeneratorObject::RESUME_INDEX_SLOT,
-                            Int32Value(resumeIndex_));
+      genObj_->setResumeIndex(resumeIndex_);
     }
   }
 };
 
 /*
  * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
  * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
  * |cx->fp()|'s return value, and return a new success value.
  */
 /* static */ bool Debugger::slowPathOnLeaveFrame(JSContext* cx,
                                                  AbstractFramePtr frame,
                                                  jsbytecode* pc, bool frameOk) {
   mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
 
   // Determine if we are suspending this frame or popping it forever.
   bool suspending = false;
-  Rooted<GeneratorObject*> genObj(cx);
+  Rooted<AbstractGeneratorObject*> genObj(cx);
   if (frame.isGeneratorFrame()) {
     // If we're leaving successfully at a yield opcode, we're probably
     // suspending; the `isClosed()` check detects a debugger forced return
     // from an `onStep` handler, which looks almost the same.
     genObj = GetGeneratorObjectForFrame(cx, frame);
     suspending =
         frameOk && pc &&
         (*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT) &&
@@ -1121,21 +1122,22 @@ class MOZ_RAII AutoSetGeneratorRunning {
       return false;
 
     default:
       MOZ_CRASH("bad final onLeaveFrame resume mode");
   }
 }
 
 /* static */ bool Debugger::slowPathOnNewGenerator(
-    JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj) {
+    JSContext* cx, AbstractFramePtr frame,
+    Handle<AbstractGeneratorObject*> genObj) {
   // This is called from JSOP_GENERATOR, after default parameter expressions
   // are evaluated and well after onEnterFrame, so Debugger.Frame objects for
   // `frame` may already have been exposed to debugger code. The
-  // GeneratorObject for this generator call, though, has just been
+  // AbstractGeneratorObject for this generator call, though, has just been
   // created. It must be associated with any existing Debugger.Frames.
   bool ok = true;
   forEachDebuggerFrame(frame, [&](DebuggerFrame* frameObjPtr) {
     if (!ok) {
       return;
     }
 
     RootedDebuggerFrame frameObj(cx, frameObjPtr);
@@ -1601,17 +1603,18 @@ static void AdjustGeneratorResumptionVal
   if (resumeMode == ResumeMode::Return && frame && frame.isFunctionFrame() &&
       frame.callee()->isGenerator()) {
     // Treat `{return: <value>}` like a `return` statement. For generators,
     // that means doing the work below. It's only what the debuggee would
     // do for an ordinary `return` statement--using a few bytecode
     // instructions--and it's simpler to do the work manually than to count
     // on that bytecode sequence existing in the debuggee, somehow jump to
     // it, and then avoid re-entering the debugger from it.
-    Rooted<GeneratorObject*> genObj(cx, GetGeneratorObjectForFrame(cx, frame));
+    Rooted<AbstractGeneratorObject*> genObj(
+        cx, GetGeneratorObjectForFrame(cx, frame));
     if (genObj) {
       // 1.  `return <value>` creates and returns a new object,
       //     `{value: <value>, done: true}`.
       if (!genObj->isBeforeInitialYield()) {
         JSObject* pair = CreateIterResultObject(cx, vp, true);
         if (!pair) {
           // Out of memory in debuggee code. Arrange for this to propagate.
           MOZ_ALWAYS_TRUE(cx->getPendingException(vp));
@@ -1963,18 +1966,17 @@ ResumeMode Debugger::fireEnterFrame(JSCo
 
   RootedValue scriptFrame(cx);
 
   FrameIter iter(cx);
 
 #if DEBUG
   // Assert that the hook won't be able to re-enter the generator.
   if (iter.hasScript() && *iter.pc() == JSOP_DEBUGAFTERYIELD) {
-    GeneratorObject* genObj =
-        GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
+    auto* genObj = GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
     MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
   }
 #endif
 
   Maybe<AutoRealm> ar;
   ar.emplace(cx, object);
 
   if (!getFrame(cx, iter, &scriptFrame)) {
@@ -4320,17 +4322,17 @@ void Debugger::removeDebuggeeGlobal(Free
   // This method can be called either from script (dbg.removeDebuggee) or
   // from an awkward time during GC sweeping. In the latter case, skip this
   // loop to avoid touching dead objects. It's correct because, when we're
   // called from GC, all `global`'s generators are guaranteed to be dying:
   // live generators would keep the global alive and we wouldn't be here. GC
   // will sweep dead keys from the weakmap.
   if (!global->zone()->isGCSweeping()) {
     generatorFrames.removeIf([global](JSObject* key) {
-      GeneratorObject& genObj = key->as<GeneratorObject>();
+      auto& genObj = key->as<AbstractGeneratorObject>();
       return genObj.isClosed() || &genObj.callee().global() == global;
     });
   }
 
   auto* globalDebuggersVector = global->getDebuggers();
   auto* zoneDebuggersVector = global->zone()->getDebuggers();
 
   // The relation must be removed from up to three places:
@@ -7554,17 +7556,17 @@ bool Debugger::observesWasm(wasm::Instan
                                                            frameobj);
     }
 
     Debugger* dbg = Debugger::fromChildJSObject(frameobj);
     dbg->frames.remove(frame);
 
     if (!suspending && frame.isGeneratorFrame()) {
       // Terminally exiting a generator.
-      GeneratorObject* genObj = GetGeneratorObjectForFrame(cx, frame);
+      auto* genObj = GetGeneratorObjectForFrame(cx, frame);
       if (GeneratorWeakMap::Ptr p = dbg->generatorFrames.lookup(genObj)) {
         dbg->generatorFrames.remove(p);
       }
     }
   });
 
   // If this is an eval frame, then from the debugger's perspective the
   // script is about to be destroyed. Remove any breakpoints in it.
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -9,27 +9,26 @@
 
 #include "mozilla/DoublyLinkedList.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Range.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Vector.h"
 
-#include "builtin/Promise.h"
 #include "ds/TraceableFifo.h"
 #include "gc/Barrier.h"
 #include "gc/WeakMap.h"
 #include "js/Debug.h"
 #include "js/GCVariant.h"
 #include "js/HashTable.h"
+#include "js/Promise.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "proxy/DeadObjectProxy.h"
-#include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 
 namespace js {
 
@@ -75,18 +74,20 @@ enum class ResumeMode {
   /**
    * Force the debuggee to return from the current frame.
    *
    * This corresponds to a resumption value of `{return: <value>}`.
    */
   Return,
 };
 
+class AbstractGeneratorObject;
 class Breakpoint;
 class DebuggerMemory;
+class PromiseObject;
 class ScriptedOnStepHandler;
 class ScriptedOnPopHandler;
 class WasmInstanceObject;
 
 typedef HashSet<ReadBarrieredGlobalObject,
                 MovableCellHasher<ReadBarrieredGlobalObject>, ZoneAllocPolicy>
     WeakGlobalObjectSet;
 
@@ -855,17 +856,18 @@ class Debugger : private mozilla::Linked
                                                   HandleScript script);
   static ResumeMode slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
   static ResumeMode slowPathOnResumeFrame(JSContext* cx,
                                           AbstractFramePtr frame);
   static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx,
                                                 AbstractFramePtr frame,
                                                 jsbytecode* pc, bool ok);
   static MOZ_MUST_USE bool slowPathOnNewGenerator(
-      JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj);
+      JSContext* cx, AbstractFramePtr frame,
+      Handle<AbstractGeneratorObject*> genObj);
   static ResumeMode slowPathOnDebuggerStatement(JSContext* cx,
                                                 AbstractFramePtr frame);
   static ResumeMode slowPathOnExceptionUnwind(JSContext* cx,
                                               AbstractFramePtr frame);
   static void slowPathOnNewScript(JSContext* cx, HandleScript script);
   static void slowPathOnNewWasmInstance(
       JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
   static void slowPathOnNewGlobalObject(JSContext* cx,
@@ -1061,17 +1063,18 @@ class Debugger : private mozilla::Linked
 
   /*
    * Announce to the debugger that a generator object has been created,
    * via JSOP_GENERATOR.
    *
    * This does not fire user hooks, but it's needed for debugger bookkeeping.
    */
   static inline MOZ_MUST_USE bool onNewGenerator(
-      JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj);
+      JSContext* cx, AbstractFramePtr frame,
+      Handle<AbstractGeneratorObject*> genObj);
 
   static inline void onNewScript(JSContext* cx, HandleScript script);
   static inline void onNewWasmInstance(
       JSContext* cx, Handle<WasmInstanceObject*> wasmInstance);
   static inline void onNewGlobalObject(JSContext* cx,
                                        Handle<GlobalObject*> global);
   static inline MOZ_MUST_USE bool onLogAllocationSite(JSContext* cx,
                                                       JSObject* obj,
@@ -1278,17 +1281,17 @@ class Debugger : private mozilla::Linked
    * The context `cx` and `frameObj` must be in the debugger realm, and
    * `genObj` must be in a debuggee realm.
    *
    * `frameObj` must be this `Debugger`'s debug wrapper for the generator or
    * async function call associated with `genObj`. This activation may
    * or may not actually be on the stack right now.
    */
   MOZ_MUST_USE bool addGeneratorFrame(JSContext* cx,
-                                      Handle<GeneratorObject*> genObj,
+                                      Handle<AbstractGeneratorObject*> genObj,
                                       HandleDebuggerFrame frameObj);
 
  private:
   Debugger(const Debugger&) = delete;
   Debugger& operator=(const Debugger&) = delete;
 };
 
 enum class DebuggerEnvironmentType { Declarative, With, Object };
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -13,64 +13,78 @@
 #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"
 
 using namespace js;
 
-JSObject* GeneratorObject::create(JSContext* cx, AbstractFramePtr frame) {
+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);
-  RootedObject fun(cx, frame.callee());
-  // 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) {
+  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;
     }
-  }
-  Rooted<GeneratorObject*> genObj(
-      cx, NewObjectWithGivenProto<GeneratorObject>(cx, proto));
-  if (!genObj) {
-    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;
+    }
+  } else {
+    // Internal generator instance.
+    RootedObject proto(cx, nullptr);
+    genObj = NewObjectWithGivenProto<GeneratorObject>(cx, proto);
+    if (!genObj) {
+      return nullptr;
+    }
   }
 
   genObj->setCallee(*frame.callee());
   genObj->setEnvironmentChain(*frame.environmentChain());
   if (frame.script()->needsArgsObj()) {
     genObj->setArgsObj(frame.argsObj());
   }
   genObj->clearExpressionStack();
 
   if (!Debugger::onNewGenerator(cx, frame, genObj)) {
     return nullptr;
   }
 
   return genObj;
 }
 
-bool GeneratorObject::suspend(JSContext* cx, HandleObject obj,
-                              AbstractFramePtr frame, jsbytecode* pc, Value* vp,
-                              unsigned nvalues) {
+bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
+                                      AbstractFramePtr frame, jsbytecode* pc,
+                                      Value* vp, unsigned nvalues) {
   MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD ||
              *pc == JSOP_AWAIT);
 
-  Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
+  auto genObj = obj.as<AbstractGeneratorObject>();
   MOZ_ASSERT(!genObj->hasExpressionStack() || genObj->isExpressionStackEmpty());
   MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
   MOZ_ASSERT_IF(*pc == JSOP_YIELD, genObj->callee().isGenerator());
 
   ArrayObject* stack = nullptr;
   if (nvalues > 0) {
     do {
       if (genObj->hasExpressionStack()) {
@@ -98,72 +112,75 @@ bool GeneratorObject::suspend(JSContext*
   genObj->setEnvironmentChain(*frame.environmentChain());
   if (stack) {
     genObj->setExpressionStack(*stack);
   }
 
   return true;
 }
 
-void GeneratorObject::finalSuspend(HandleObject obj) {
-  GeneratorObject* genObj = &obj->as<GeneratorObject>();
+void AbstractGeneratorObject::finalSuspend(HandleObject obj) {
+  auto* genObj = &obj->as<AbstractGeneratorObject>();
   MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
   genObj->setClosed();
 }
 
-GeneratorObject* js::GetGeneratorObjectForFrame(JSContext* cx,
-                                                AbstractFramePtr frame) {
+AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
+    JSContext* cx, AbstractFramePtr frame) {
   cx->check(frame);
   MOZ_ASSERT(frame.isGeneratorFrame());
 
   if (!frame.hasInitialEnvironment()) {
     return nullptr;
   }
 
   // The ".generator" binding is always present and always "aliased".
   CallObject& callObj = frame.callObj();
   Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
   Value genValue = callObj.getSlot(shape->slot());
 
   // If the `generator; setaliasedvar ".generator"; initialyield` bytecode
   // sequence has not run yet, genValue is undefined.
-  return genValue.isObject() ? &genValue.toObject().as<GeneratorObject>()
-                             : nullptr;
+  return genValue.isObject()
+             ? &genValue.toObject().as<AbstractGeneratorObject>()
+             : nullptr;
 }
 
 void js::SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame) {
   CallObject& callObj = frame.callObj();
 
   // Get the generator object stored on the scope chain and close it.
   Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
-  GeneratorObject& genObj =
-      callObj.getSlot(shape->slot()).toObject().as<GeneratorObject>();
+  auto& genObj =
+      callObj.getSlot(shape->slot()).toObject().as<AbstractGeneratorObject>();
   genObj.setClosed();
 }
 
 bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
-                                Handle<GeneratorObject*> genObj,
+                                Handle<AbstractGeneratorObject*> genObj,
                                 HandleValue arg, uint32_t resumeKind) {
-  if (resumeKind == GeneratorObject::THROW) {
+  if (resumeKind == AbstractGeneratorObject::THROW) {
     cx->setPendingException(arg);
   } else {
-    MOZ_ASSERT(resumeKind == GeneratorObject::RETURN);
+    MOZ_ASSERT(resumeKind == AbstractGeneratorObject::RETURN);
 
     MOZ_ASSERT(arg.isObject());
     frame.setReturnValue(arg);
 
     RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
     cx->setPendingException(closing);
     genObj->setClosing();
   }
   return false;
 }
 
-bool GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
-                             Handle<GeneratorObject*> genObj, HandleValue arg) {
+bool AbstractGeneratorObject::resume(JSContext* cx,
+                                     InterpreterActivation& activation,
+                                     Handle<AbstractGeneratorObject*> genObj,
+                                     HandleValue arg) {
   MOZ_ASSERT(genObj->isSuspended());
 
   RootedFunction callee(cx, &genObj->callee());
   RootedObject envChain(cx, &genObj->environmentChain());
   if (!activation.resumeGeneratorFrame(callee, envChain)) {
     return false;
   }
   activation.regs().fp()->setResumedGenerator();
@@ -269,21 +286,25 @@ JSObject* js::NewSingletonObjectWithFunc
 
   global->setReservedSlot(GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
   global->setReservedSlot(GENERATOR_FUNCTION, ObjectValue(*genFunction));
   global->setReservedSlot(GENERATOR_FUNCTION_PROTO,
                           ObjectValue(*genFunctionProto));
   return true;
 }
 
-bool GeneratorObject::isAfterYield() { return isAfterYieldOrAwait(JSOP_YIELD); }
+bool AbstractGeneratorObject::isAfterYield() {
+  return isAfterYieldOrAwait(JSOP_YIELD);
+}
 
-bool GeneratorObject::isAfterAwait() { return isAfterYieldOrAwait(JSOP_AWAIT); }
+bool AbstractGeneratorObject::isAfterAwait() {
+  return isAfterYieldOrAwait(JSOP_AWAIT);
+}
 
-bool GeneratorObject::isAfterYieldOrAwait(JSOp op) {
+bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op) {
   if (isClosed() || isClosing() || isRunning()) {
     return false;
   }
 
   JSScript* script = callee().nonLazyScript();
   jsbytecode* code = script->code();
   uint32_t nextOffset = script->resumeOffsets()[resumeIndex()];
   if (code[nextOffset] != JSOP_DEBUGAFTERYIELD) {
@@ -291,8 +312,13 @@ bool GeneratorObject::isAfterYieldOrAwai
   }
 
   uint32_t offset = nextOffset - JSOP_YIELD_LENGTH;
   MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
              code[offset] == JSOP_AWAIT);
 
   return code[offset] == op;
 }
+
+template <>
+bool JSObject::is<js::AbstractGeneratorObject>() const {
+  return is<GeneratorObject>();
+}
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -10,17 +10,17 @@
 #include "vm/ArgumentsObject.h"
 #include "vm/ArrayObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/Stack.h"
 
 namespace js {
 
-class GeneratorObject : public NativeObject {
+class AbstractGeneratorObject : public NativeObject {
  public:
   // Magic values stored in the resumeIndex slot when the generator is
   // running or closing. See the resumeIndex comment below.
   static const int32_t RESUME_INDEX_RUNNING = INT32_MAX;
   static const int32_t RESUME_INDEX_CLOSING = INT32_MAX - 1;
 
   enum {
     CALLEE_SLOT = 0,
@@ -28,18 +28,16 @@ class GeneratorObject : public NativeObj
     ARGS_OBJ_SLOT,
     EXPRESSION_STACK_SLOT,
     RESUME_INDEX_SLOT,
     RESERVED_SLOTS
   };
 
   enum ResumeKind { NEXT, THROW, RETURN };
 
-  static const Class class_;
-
  private:
   static bool suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame,
                       jsbytecode* pc, Value* vp, unsigned nvalues);
 
  public:
   static inline ResumeKind getResumeKind(jsbytecode* pc) {
     MOZ_ASSERT(*pc == JSOP_RESUME);
     unsigned arg = GET_UINT16(pc);
@@ -56,17 +54,17 @@ class GeneratorObject : public NativeObj
     }
     MOZ_ASSERT(atom == cx->names().return_);
     return RETURN;
   }
 
   static JSObject* create(JSContext* cx, AbstractFramePtr frame);
 
   static bool resume(JSContext* cx, InterpreterActivation& activation,
-                     Handle<GeneratorObject*> genObj, HandleValue arg);
+                     Handle<AbstractGeneratorObject*> genObj, HandleValue arg);
 
   static bool initialSuspend(JSContext* cx, HandleObject obj,
                              AbstractFramePtr frame, jsbytecode* pc) {
     return suspend(cx, obj, frame, pc, nullptr, 0);
   }
 
   static bool normalSuspend(JSContext* cx, HandleObject obj,
                             AbstractFramePtr frame, jsbytecode* pc, Value* vp,
@@ -160,16 +158,19 @@ class GeneratorObject : public NativeObj
     MOZ_ASSERT_IF(JSOp(*pc) != JSOP_INITIALYIELD, isRunning() || isClosing());
 
     uint32_t resumeIndex = GET_UINT24(pc);
     MOZ_ASSERT(resumeIndex < uint32_t(RESUME_INDEX_CLOSING));
 
     setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex));
     MOZ_ASSERT(isSuspended());
   }
+  void setResumeIndex(int32_t resumeIndex) {
+    setFixedSlot(RESUME_INDEX_SLOT, Int32Value(resumeIndex));
+  }
   uint32_t resumeIndex() const {
     MOZ_ASSERT(isSuspended());
     return getFixedSlot(RESUME_INDEX_SLOT).toInt32();
   }
   bool isClosed() const { return getFixedSlot(CALLEE_SLOT).isNull(); }
   void setClosed() {
     setFixedSlot(CALLEE_SLOT, NullValue());
     setFixedSlot(ENV_CHAIN_SLOT, NullValue());
@@ -195,25 +196,35 @@ class GeneratorObject : public NativeObj
   static size_t offsetOfResumeIndexSlot() {
     return getFixedSlotOffset(RESUME_INDEX_SLOT);
   }
   static size_t offsetOfExpressionStackSlot() {
     return getFixedSlotOffset(EXPRESSION_STACK_SLOT);
   }
 };
 
+class GeneratorObject : public AbstractGeneratorObject {
+ public:
+  enum { RESERVED_SLOTS = AbstractGeneratorObject::RESERVED_SLOTS };
+
+  static const Class class_;
+};
+
 bool GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
-                            Handle<GeneratorObject*> obj, HandleValue val,
-                            uint32_t resumeKind);
+                            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.
  */
-GeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
-                                            AbstractFramePtr frame);
+AbstractGeneratorObject* GetGeneratorObjectForFrame(JSContext* cx,
+                                                    AbstractFramePtr frame);
 
 void SetGeneratorClosed(JSContext* cx, AbstractFramePtr frame);
 
 }  // namespace js
 
+template <>
+bool JSObject::is<js::AbstractGeneratorObject>() const;
+
 #endif /* vm_GeneratorObject_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3997,64 +3997,64 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
 
       REGS.fp()->popOffEnvironmentChain<VarEnvironmentObject>();
     }
     END_CASE(JSOP_POPVARENV)
 
     CASE(JSOP_GENERATOR) {
       MOZ_ASSERT(!cx->isExceptionPending());
       MOZ_ASSERT(REGS.stackDepth() == 0);
-      JSObject* obj = GeneratorObject::create(cx, REGS.fp());
+      JSObject* obj = AbstractGeneratorObject::create(cx, REGS.fp());
       if (!obj) {
         goto error;
       }
       PUSH_OBJECT(*obj);
     }
     END_CASE(JSOP_GENERATOR)
 
     CASE(JSOP_INITIALYIELD) {
       MOZ_ASSERT(!cx->isExceptionPending());
       MOZ_ASSERT(REGS.fp()->isFunctionFrame());
       ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
       POP_RETURN_VALUE();
       MOZ_ASSERT(REGS.stackDepth() == 0);
-      if (!GeneratorObject::initialSuspend(cx, obj, REGS.fp(), REGS.pc)) {
+      if (!AbstractGeneratorObject::initialSuspend(cx, obj, REGS.fp(),
+                                                   REGS.pc)) {
         goto error;
       }
       goto successful_return_continuation;
     }
 
     CASE(JSOP_YIELD)
     CASE(JSOP_AWAIT) {
       MOZ_ASSERT(!cx->isExceptionPending());
       MOZ_ASSERT(REGS.fp()->isFunctionFrame());
       ReservedRooted<JSObject*> obj(&rootObject0, &REGS.sp[-1].toObject());
-      if (!GeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc,
-                                          REGS.spForStackDepth(0),
-                                          REGS.stackDepth() - 2)) {
+      if (!AbstractGeneratorObject::normalSuspend(cx, obj, REGS.fp(), REGS.pc,
+                                                  REGS.spForStackDepth(0),
+                                                  REGS.stackDepth() - 2)) {
         goto error;
       }
 
       REGS.sp--;
       POP_RETURN_VALUE();
 
       goto successful_return_continuation;
     }
 
     CASE(JSOP_RESUME) {
       {
-        Rooted<GeneratorObject*> gen(
-            cx, &REGS.sp[-2].toObject().as<GeneratorObject>());
+        Rooted<AbstractGeneratorObject*> gen(
+            cx, &REGS.sp[-2].toObject().as<AbstractGeneratorObject>());
         ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
         // popInlineFrame expects there to be an additional value on the stack
         // to pop off, so leave "gen" on the stack.
 
-        GeneratorObject::ResumeKind resumeKind =
-            GeneratorObject::getResumeKind(REGS.pc);
-        if (!GeneratorObject::resume(cx, activation, gen, val)) {
+        auto resumeKind = AbstractGeneratorObject::getResumeKind(REGS.pc);
+        if (!AbstractGeneratorObject::resume(cx, activation, gen, val)) {
           goto error;
         }
 
         JSScript* generatorScript = REGS.fp()->script();
         if (cx->realm() != generatorScript->realm()) {
           cx->enterRealmOf(generatorScript);
         }
         SET_SCRIPT(generatorScript);
@@ -4078,41 +4078,41 @@ static MOZ_NEVER_INLINE JS_HAZ_JSNATIVE_
                 gen->isClosed());
             if (!ForcedReturn(cx, REGS)) {
               goto error;
             }
             goto successful_return_continuation;
         }
 
         switch (resumeKind) {
-          case GeneratorObject::NEXT:
+          case AbstractGeneratorObject::NEXT:
             break;
-          case GeneratorObject::THROW:
-          case GeneratorObject::RETURN:
+          case AbstractGeneratorObject::THROW:
+          case AbstractGeneratorObject::RETURN:
             MOZ_ALWAYS_FALSE(GeneratorThrowOrReturn(cx, activation.regs().fp(),
                                                     gen, val, resumeKind));
             goto error;
           default:
             MOZ_CRASH("bad resumeKind");
         }
       }
       ADVANCE_AND_DISPATCH(0);
     }
 
     CASE(JSOP_DEBUGAFTERYIELD) {
-      // No-op in the interpreter, as GeneratorObject::resume takes care of
-      // fixing up InterpreterFrames.
+      // No-op in the interpreter, as AbstractGeneratorObject::resume takes care
+      // of fixing up InterpreterFrames.
       MOZ_ASSERT_IF(REGS.fp()->script()->isDebuggee(), REGS.fp()->isDebuggee());
     }
     END_CASE(JSOP_DEBUGAFTERYIELD)
 
     CASE(JSOP_FINALYIELDRVAL) {
       ReservedRooted<JSObject*> gen(&rootObject0, &REGS.sp[-1].toObject());
       REGS.sp--;
-      GeneratorObject::finalSuspend(gen);
+      AbstractGeneratorObject::finalSuspend(gen);
       goto successful_return_continuation;
     }
 
     CASE(JSOP_CHECKCLASSHERITAGE) {
       HandleValue heritage = REGS.stackHandleAt(-1);
 
       if (!CheckClassHeritageOperation(cx, heritage)) {
         goto error;
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -25,17 +25,16 @@
 #include "builtin/Array.h"
 #include "builtin/SelfHostingDefines.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "js/PropertySpec.h"
 #include "js/Proxy.h"
 #include "vm/BytecodeUtil.h"
-#include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/JSAtom.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/JSScript.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayObject.h"
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -2208,17 +2208,17 @@
     MACRO(JSOP_FINALYIELDRVAL, 204, "finalyieldrval", NULL, 1, 1, 0, JOF_BYTE) \
     /*
      * Pops the generator and argument from the stack, pushes a new generator
      * frame and resumes execution of it. Pushes the return value after the
      * generator yields.
      *
      *   Category: Statements
      *   Type: Generator
-     *   Operands: resume kind (GeneratorObject::ResumeKind)
+     *   Operands: resume kind (AbstractGeneratorObject::ResumeKind)
      *   Stack: gen, val => rval
      */ \
     MACRO(JSOP_RESUME, 205, "resume", NULL, 3, 2, 1, JOF_UINT16|JOF_INVOKE) \
     /*
      * Load the callee stored in a CallObject on the environment chain. The
      * numHops operand is the number of environment objects to skip on the
      * environment chain.
      *
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -11,17 +11,16 @@
 
 #include "mozilla/Maybe.h"
 #include "mozilla/PodOperations.h"
 
 #include "jit/BaselineFrame.h"
 #include "jit/RematerializedFrame.h"
 #include "js/Debug.h"
 #include "vm/EnvironmentObject.h"
-#include "vm/GeneratorObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
 
 #include "jit/BaselineFrame-inl.h"
 #include "vm/JSObject-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/NativeObject-inl.h"