Add Ion support for global scripts (bug 707423, r=cdleary).
authorDavid Anderson <danderson@mozilla.com>
Wed, 11 Jan 2012 15:36:34 -0800
changeset 105557 f3fef5d48874a02afb1f385730a445411f5f1056
parent 105556 aeb8431afb8ddea852ce205aae587cea2fcd45f2
child 105558 0a8aee9639cf79a14803c31cd29cff3305366e50
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerscdleary
bugs707423
milestone12.0a1
Add Ion support for global scripts (bug 707423, r=cdleary).
js/src/ion/Bailouts.cpp
js/src/ion/CompileInfo.h
js/src/ion/Ion.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonFrames.cpp
js/src/ion/IonFrames.h
js/src/jit-test/tests/v8-v5/check-splay.js
js/src/vm/Stack.cpp
js/src/vm/StackSpace.h
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -156,41 +156,41 @@ class IonBailoutIterator
 
 static void
 RestoreOneFrame(JSContext *cx, StackFrame *fp, IonBailoutIterator &iter)
 {
     uint32 exprStackSlots = iter.slots() - fp->script()->nfixed;
 
     IonSpew(IonSpew_Bailouts, "expr stack slots %u, is function frame %u",
             exprStackSlots, fp->isFunctionFrame());
+
+    // The scope chain value will be undefined if the function never
+    // accesses its scope chain (via a NAME opcode) or modifies the
+    // scope chain via BLOCK opcodes. In such cases keep the default
+    // environment-of-callee scope.
+    Value scopeChainv = iter.read();
+    if (scopeChainv.isObject())
+        fp->setScopeChainNoCallObj(scopeChainv.toObject());
+    else
+        JS_ASSERT(scopeChainv.isUndefined());
+
     if (fp->isFunctionFrame()) {
+        Value thisv = iter.read();
+        fp->formalArgs()[-1] = thisv;
+
         JS_ASSERT(iter.slots() >= CountArgSlots(fp->fun()));
         IonSpew(IonSpew_Bailouts, "frame slots %u, nargs %u, nfixed %u",
                 iter.slots(), fp->fun()->nargs, fp->script()->nfixed);
 
-        // The scope chain value will be undefined if the function never
-        // accesses its scope chain (via a NAME opcode) or modifies the
-        // scope chain via BLOCK opcodes. In such cases keep the default
-        // environment-of-callee scope.
-        Value scopeChainv = iter.read();
-        if (scopeChainv.isObject())
-            fp->setScopeChainNoCallObj(scopeChainv.toObject());
-        else
-            JS_ASSERT(scopeChainv.isUndefined());
-
-        Value thisv = iter.read();
-        fp->formalArgs()[-1] = thisv;
-
         for (uint32 i = 0; i < fp->fun()->nargs; i++) {
             Value arg = iter.read();
             fp->formalArgs()[i] = arg;
         }
-
-        exprStackSlots -= CountArgSlots(fp->fun());
     }
+    exprStackSlots -= CountArgSlots(fp->maybeFun());
 
     for (uint32 i = 0; i < fp->script()->nfixed; i++) {
         Value slot = iter.read();
         fp->slots()[i] = slot;
     }
 
     IonSpew(IonSpew_Bailouts, " pushing %u expression stack slots", exprStackSlots);
     FrameRegs &regs = cx->regs();
@@ -254,21 +254,30 @@ ConvertFrames(JSContext *cx, IonActivati
     // Forbid OSR in the future: bailouts are now expected.
     in.ionScript()->forbidOsr();
 
     BailoutClosure *br = cx->new_<BailoutClosure>();
     if (!br)
         return BAILOUT_RETURN_FATAL_ERROR;
     activation->setBailout(br);
 
-    // Non-function frames are not supported yet. We don't compile or enter
-    // global scripts so this assert should not fire yet.
-    JS_ASSERT(in.callee());
+    StackFrame *fp;
+    if (in.callee()) {
+        // This is a normal function frame.
+        fp = cx->stack.pushBailoutFrame(cx, *in.callee(), in.script(), br->frameGuard());
+    } else {
+        // The scope chain will be updated, if necessary, in RestoreOneFrame().
+        // The |this| value for global scripts is always an object, and is
+        // precomputed in the original frame, so it's safe to re-use that
+        // value (it is not included in snapshots or resume points).
+        JSObject *prevScopeChain = &cx->fp()->scopeChain();
+        Value thisv = cx->fp()->thisValue();
+        fp = cx->stack.pushBailoutFrame(cx, in.script(), *prevScopeChain, thisv, br->frameGuard());
+    }
 
-    StackFrame *fp = cx->stack.pushBailoutFrame(cx, *in.callee(), in.script(), br->frameGuard());
     if (!fp)
         return BAILOUT_RETURN_FATAL_ERROR;
 
     br->setEntryFrame(fp);
 
     if (in.callee())
         fp->formalArgs()[-2].setObject(*in.callee());
 
--- a/js/src/ion/CompileInfo.h
+++ b/js/src/ion/CompileInfo.h
@@ -42,17 +42,17 @@
 #define jsion_compileinfo_h__
 
 namespace js {
 namespace ion {
 
 inline uintN
 CountArgSlots(JSFunction *fun)
 {
-    return fun ? fun->nargs + 2 : 0; // +2 for |scopeChain| and |this|
+    return fun ? fun->nargs + 2 : 1; // +2 for |scopeChain| and |this|, or +1 for |scopeChain|
 }
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
     CompileInfo(JSScript *script, JSFunction *fun, jsbytecode *osrPc)
       : script_(script), fun_(fun), osrPc_(osrPc)
@@ -109,17 +109,16 @@ class CompileInfo
     uintN nlocals() const {
         return script()->nfixed;
     }
     uintN ninvoke() const {
         return nlocals() + CountArgSlots(fun());
     }
 
     uint32 scopeChainSlot() const {
-        JS_ASSERT(fun());
         return 0;
     }
     uint32 thisSlot() const {
         JS_ASSERT(fun());
         return 1;
     }
     uint32 firstArgSlot() const {
         JS_ASSERT(fun());
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -689,44 +689,35 @@ IonCompile(JSContext *cx, JSScript *scri
         types::AutoEnterTypeInference enter(cx, true);
         TypeInferenceOracle oracle;
 
         if (!oracle.init(cx, script))
             return false;
 
         types::AutoEnterCompilation enterCompiler(cx, script);
 
-        IonBuilder builder(cx, temp, graph, &oracle, *info);
+        IonBuilder builder(cx, &fp->scopeChain(), temp, graph, &oracle, *info);
         if (!TestCompiler(builder, graph)) {
             IonSpew(IonSpew_Abort, "IM Compilation failed.");
             return false;
         }
     } else {
         DummyOracle oracle;
-        IonBuilder builder(cx, temp, graph, &oracle, *info);
+        IonBuilder builder(cx, &fp->scopeChain(), temp, graph, &oracle, *info);
         if (!TestCompiler(builder, graph)) {
             IonSpew(IonSpew_Abort, "IM Compilation failed.");
             return false;
         }
-   }
-
+    }
     return true;
 }
 
 static bool
 CheckFrame(StackFrame *fp)
 {
-    if (!fp->isFunctionFrame()) {
-        // Support for this is almost there - we would need a new
-        // pushBailoutFrame. For the most part we just don't support
-        // the opcodes in a global script yet.
-        IonSpew(IonSpew_Abort, "global frame");
-        return false;
-    }
-
     if (fp->isEvalFrame()) {
         // Eval frames are not yet supported. Supporting this will require new
         // logic in pushBailoutFrame to deal with linking prev.
         IonSpew(IonSpew_Abort, "eval frame");
         return false;
     }
 
     if (fp->isConstructing()) {
@@ -759,22 +750,22 @@ CheckFrame(StackFrame *fp)
 
     if (fp->isDebuggerFrame()) {
         IonSpew(IonSpew_Abort, "debugger frame");
         return false;
     }
 
     // This check is to not overrun the stack. Eventually, we will want to
     // handle this when we support JSOP_ARGUMENTS or function calls.
-    if (fp->numActualArgs() >= SNAPSHOT_MAX_NARGS) {
+    if (fp->isFunctionFrame() && fp->numActualArgs() >= SNAPSHOT_MAX_NARGS) {
         IonSpew(IonSpew_Abort, "too many actual args");
         return false;
     }
 
-    JS_ASSERT_IF(fp->fun(), !fp->fun()->isHeavyweight());
+    JS_ASSERT_IF(fp->maybeFun(), !fp->fun()->isHeavyweight());
     return true;
 }
 
 // Decide if a transition from interpreter execution to Ion code should occur.
 // May compile or recompile the target JSScript.
 MethodStatus
 ion::CanEnterAtBranch(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecode *pc)
 {
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -50,33 +50,33 @@
 
 #ifdef JS_THREADSAFE
 # include "prthread.h"
 #endif
 
 using namespace js;
 using namespace js::ion;
 
-IonBuilder::IonBuilder(JSContext *cx, TempAllocator &temp, MIRGraph &graph, TypeOracle *oracle,
-                       CompileInfo &info, size_t inliningDepth)
+IonBuilder::IonBuilder(JSContext *cx, JSObject *scopeChain, TempAllocator &temp, MIRGraph &graph,
+                       TypeOracle *oracle, CompileInfo &info, size_t inliningDepth)
   : MIRGenerator(cx, temp, graph, info),
     script(info.script()),
+    initialScopeChain_(scopeChain),
     lastResumePoint_(NULL),
     callerResumePoint_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth)
 {
     pc = info.startPC();
 }
 
 static inline int32
 GetJumpOffset(jsbytecode *pc)
 {
-    JSOp op = JSOp(*pc);
-    JS_ASSERT(js_CodeSpec[op].type() == JOF_JUMP);
+    JS_ASSERT(js_CodeSpec[JSOp(*pc)].type() == JOF_JUMP);
     return GET_JUMP_OFFSET(pc);
 }
 
 static inline jsbytecode *
 GetNextPc(jsbytecode *pc)
 {
     return pc + js_CodeSpec[JSOp(*pc)].length;
 }
@@ -219,17 +219,18 @@ IonBuilder::build()
 {
     current = newBlock(pc);
     if (!current)
         return false;
 
     IonSpew(IonSpew_MIR, "Analyzing script %s:%d (%p)",
             script->filename, script->lineno, (void *) script);
 
-    initParameters();
+    if (!initParameters())
+        return false;
 
     // Initialize local variables.
     for (uint32 i = 0; i < info().nlocals(); i++) {
         MConstant *undef = MConstant::New(UndefinedValue());
         current->add(undef);
         current->initSlot(info().localSlot(i), undef);
     }
 
@@ -381,49 +382,58 @@ IonBuilder::rewriteParameters()
         // 
         // As usual, it would be invalid for v1 to be captured in the initial
         // resume point, rather than v0.
         current->add(actual);
         current->rewriteSlot(i, actual);
     }
 }
 
-void
+bool
 IonBuilder::initParameters()
 {
+    // The scope chain is only tracked in scripts that have NAME opcodes which
+    // will try to access the scope. For other scripts, the scope instructions
+    // will be held live by resume points and code will still be generated for
+    // them, so just use a constant undefined value.
+    MInstruction *scope;
+    if (script->analysis()->usesScopeChain()) {
+        if (info().fun()) {
+            MCallee *callee = MCallee::New();
+            current->add(callee);
+
+            scope = MFunctionEnvironment::New(callee);
+        } else {
+            if (!script->compileAndGo)
+                return abort("non-CNG global scripts are not supported");
+
+            scope = MConstant::New(ObjectValue(*initialScopeChain_));
+        }
+    } else {
+        scope = MConstant::New(UndefinedValue());
+    }
+
+    current->add(scope);
+    current->initSlot(info().scopeChainSlot(), scope);
+
     if (!info().fun())
-        return;
+        return true;
 
     MParameter *param = MParameter::New(MParameter::THIS_SLOT,
                                         oracle->thisTypeSet(script));
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32 i = 0; i < info().nargs(); i++) {
         param = MParameter::New(i, oracle->parameterTypeSet(script, i));
         current->add(param);
         current->initSlot(info().argSlot(i), param);
     }
 
-    // The scope chain is only tracked in scripts that have NAME opcodes which
-    // will try to access the scope. For other scripts, the scope instructions
-    // will be held live by resume points and code will still be generated for
-    // them, so just use a constant undefined value.
-    MInstruction *scope;
-    if (script->analysis()->usesScopeChain()) {
-        MCallee *callee = MCallee::New();
-        current->add(callee);
-
-        scope = MFunctionEnvironment::New(callee);
-    } else {
-        scope = MConstant::New(UndefinedValue());
-    }
-
-    current->add(scope);
-    current->initSlot(info().scopeChainSlot(), scope);
+    return true;
 }
 
 // We try to build a control-flow graph in the order that it would be built as
 // if traversing the AST. This leads to a nice ordering and lets us build SSA
 // in one pass, since the bytecode is structured.
 //
 // We traverse the bytecode iteratively, maintaining a current basic block.
 // Each basic block has a mapping of local slots to instructions, as well as a
@@ -2241,24 +2251,24 @@ IonBuilder::maybeInline(uint32 argc)
 
     MIRGraphExits exits;
     AutoAccumulateExits aae(graph(), exits);
 
     if (cx->typeInferenceEnabled()) {
         TypeInferenceOracle oracle;
         if (!oracle.init(cx, data.callee->script()))
             return InliningStatus_Error;
-        IonBuilder inlineBuilder(cx, temp(), graph(), &oracle, *info, inliningDepth + 1);
+        IonBuilder inlineBuilder(cx, NULL, temp(), graph(), &oracle, *info, inliningDepth + 1);
         return jsop_call_inline(argc, inlineBuilder, &data)
              ? InliningStatus_Inlined
              : InliningStatus_Error;
     }
 
     DummyOracle oracle;
-    IonBuilder inlineBuilder(cx, temp(), graph(), &oracle, *info, inliningDepth + 1);
+    IonBuilder inlineBuilder(cx, NULL, temp(), graph(), &oracle, *info, inliningDepth + 1);
     return jsop_call_inline(argc, inlineBuilder, &data)
          ? InliningStatus_Inlined
          : InliningStatus_Error;
 }
 
 bool
 IonBuilder::jsop_call(uint32 argc)
 {
@@ -2431,34 +2441,34 @@ IonBuilder::newOsrPreheader(MBasicBlock 
     {
         uint32 slot = info().scopeChainSlot();
 
         MOsrScopeChain *scopev = MOsrScopeChain::New(entry);
         osrBlock->add(scopev);
         osrBlock->initSlot(slot, scopev);
     }
 
-    // Initialize |this| parameter.
-    {
+    if (info().fun()) {
+        // Initialize |this| parameter.
         uint32 slot = info().thisSlot();
         ptrdiff_t offset = StackFrame::offsetOfThis(info().fun());
 
         MOsrValue *thisv = MOsrValue::New(entry, offset);
         osrBlock->add(thisv);
         osrBlock->initSlot(slot, thisv);
-    }
-
-    // Initialize arguments.
-    for (uint32 i = 0; i < info().nargs(); i++) {
-        uint32 slot = info().argSlot(i);
-        ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i);
-
-        MOsrValue *osrv = MOsrValue::New(entry, offset);
-        osrBlock->add(osrv);
-        osrBlock->initSlot(slot, osrv);
+
+        // Initialize arguments.
+        for (uint32 i = 0; i < info().nargs(); i++) {
+            uint32 slot = info().argSlot(i);
+            ptrdiff_t offset = StackFrame::offsetOfFormalArg(info().fun(), i);
+
+            MOsrValue *osrv = MOsrValue::New(entry, offset);
+            osrBlock->add(osrv);
+            osrBlock->initSlot(slot, osrv);
+        }
     }
 
     // Initialize locals.
     for (uint32 i = 0; i < info().nlocals(); i++) {
         uint32 slot = info().localSlot(i);
         ptrdiff_t offset = StackFrame::offsetOfFixed(i);
 
         MOsrValue *osrv = MOsrValue::New(entry, offset);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -179,18 +179,18 @@ class IonBuilder : public MIRGenerator
         JSFunction *callee;
 
         InliningData()
           : shouldInline(false), callee(NULL)
         { }
     };
 
   public:
-    IonBuilder(JSContext *cx, TempAllocator &temp, MIRGraph &graph, TypeOracle *oracle,
-               CompileInfo &info, size_t inliningDepth = 0);
+    IonBuilder(JSContext *cx, JSObject *scopeChain, TempAllocator &temp, MIRGraph &graph,
+               TypeOracle *oracle, CompileInfo &info, size_t inliningDepth = 0);
 
     bool build();
     bool buildInline(MResumePoint *callerResumePoint, MDefinition *thisDefn,
                      MDefinitionVector &args);
 
   private:
     bool traverseBytecode();
     ControlStatus snoopControlFlow(JSOp op);
@@ -264,17 +264,17 @@ class IonBuilder : public MIRGenerator
 
     // Please see the Big Honkin' Comment about how resume points work in
     // IonBuilder.cpp, near the definition for this function.
     bool resumeAt(MInstruction *ins, jsbytecode *pc);
     bool resumeAfter(MInstruction *ins);
 
     void insertRecompileCheck();
 
-    void initParameters();
+    bool initParameters();
     void rewriteParameters();
     bool pushConstant(const Value &v);
     bool pushTypeBarrier(MInstruction *ins, types::TypeSet *actual, types::TypeSet *observed);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
     bool jsop_pos();
@@ -316,16 +316,17 @@ class IonBuilder : public MIRGenerator
     bool makeInliningDecision(uint32 argc, InliningData *data);
 
   public:
     // A builder is inextricably tied to a particular script.
     JSScript * const script;
 
   private:
     jsbytecode *pc;
+    JSObject *initialScopeChain_;
     MBasicBlock *current;
     MResumePoint *lastResumePoint_;
 
     /* Information used for inline-call builders. */
     MResumePoint *callerResumePoint_;
     jsbytecode *callerPC() {
         return callerResumePoint_ ? callerResumePoint_->pc() : NULL;
     }
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -97,20 +97,21 @@ FrameRecovery::FrameRecovery(uint8 *fp, 
     unpackCalleeToken(fp_->calleeToken());
 }
 
 void
 FrameRecovery::unpackCalleeToken(CalleeToken token)
 {
     switch (GetCalleeTokenTag(token)) {
       case CalleeToken_Function:
-         callee_ = CalleeTokenToFunction(token);
-         script_ = callee_->script();
-         break;
+        callee_ = CalleeTokenToFunction(token);
+        script_ = callee_->script();
+        break;
       case CalleeToken_Script:
+        callee_ = NULL;
         script_ = CalleeTokenToScript(token);
         break;
       case CalleeToken_InvalidationRecord:
       {
         InvalidationRecord *record = CalleeTokenToInvalidationRecord(token);
         unpackCalleeToken(record->calleeToken);
         break;
       }
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -396,17 +396,26 @@ IonFrameIterator::more() const
 static inline IonScript *
 GetTopIonFrame(JSContext *cx)
 {
     IonFrameIterator iter(JS_THREAD_DATA(cx)->ionTop);
     JS_ASSERT(iter.type() == IonFrame_Exit);
     ++iter;
     JS_ASSERT(iter.type() == IonFrame_JS);
     IonJSFrameLayout *frame = static_cast<IonJSFrameLayout*>(iter.current());
-    JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
-    return fun->script()->ion;
+    switch (GetCalleeTokenTag(frame->calleeToken())) {
+      case CalleeToken_Function: {
+        JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
+        return fun->script()->ion;
+      }
+      case CalleeToken_Script:
+        return CalleeTokenToScript(frame->calleeToken())->ion;
+      default:
+        JS_NOT_REACHED("unexpected callee token kind");
+        return NULL;
+    }
 }
 
 } /* namespace ion */
 } /* namespace js */
 
 #endif // jsion_frames_h__
 
--- a/js/src/jit-test/tests/v8-v5/check-splay.js
+++ b/js/src/jit-test/tests/v8-v5/check-splay.js
@@ -89,17 +89,16 @@ function InsertNewNode() {
   do {
     key = GenerateKey();
   } while (splayTree.find(key) != null);
   splayTree.insert(key, GeneratePayloadTree(kSplayTreePayloadDepth, key));
   return key;
 }
 
 
-
 function SplaySetup() {
   splayTree = new SplayTree();
   for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
 }
 
 
 function SplayTearDown() {
   // Allow the garbage collector to reclaim the memory
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -767,16 +767,37 @@ ContextStack::pushBailoutFrame(JSContext
     bfg->regs_.prepareToRun(*fp, script);
 
     bfg->prevRegs_ = seg_->pushRegs(bfg->regs_);
     JS_ASSERT(space().firstUnused() == bfg->regs_.sp);
     bfg->setPushed(*this);
     return fp;
 }
 
+StackFrame *
+ContextStack::pushBailoutFrame(JSContext *cx, JSScript *script, JSObject &scopeChain,
+                               const Value &thisv, BailoutFrameGuard *bfg)
+{
+    StackFrame *prev = maybefp();
+    uintN nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
+    Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &bfg->pushedSeg_);
+    if (!firstUnused)
+        return NULL;
+
+    StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
+    fp->initExecuteFrame(script, prev, seg_->maybeRegs(), thisv, scopeChain, EXECUTE_GLOBAL);
+    SetValueRangeToUndefined(fp->slots(), script->nfixed);
+    bfg->regs_.prepareToRun(*fp, script);
+
+    bfg->prevRegs_ = seg_->pushRegs(bfg->regs_);
+    JS_ASSERT(space().firstUnused() == bfg->regs_.sp);
+    bfg->setPushed(*this);
+    return fp;
+}
+
 bool
 ContextStack::pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg)
 {
     JS_ASSERT(dest == scopeChain.compartment());
 
     uintN nvars = VALUES_PER_STACK_FRAME;
     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
     if (!firstUnused)
--- a/js/src/vm/StackSpace.h
+++ b/js/src/vm/StackSpace.h
@@ -282,19 +282,23 @@ class ContextStack
     bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
                          InitialFrameFlags initial, InvokeFrameGuard *ifg);
 
     /* Called by Execute for execution of eval or global code. */
     bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                           JSObject &scopeChain, ExecuteType type,
                           StackFrame *evalInFrame, ExecuteFrameGuard *efg);
 
+    // Bailout for normal functions.
     StackFrame *pushBailoutFrame(JSContext *cx, JSFunction &fun, JSScript *script,
                                  BailoutFrameGuard *bfg);
 
+    // Bailout for global scripts.
+    StackFrame *pushBailoutFrame(JSContext *cx, JSScript *script, JSObject &scopeChain, const Value &thisv, BailoutFrameGuard *efg);
+
     /*
      * Called by SendToGenerator to resume a yielded generator. In addition to
      * pushing a frame onto the VM stack, this function copies over the
      * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
      * will copy the frame back to the floating frame.
      */
     bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);