Bug 568593 - Refactor SharedContext; r=jorendorff
authorEddy Bruel <ejpbruel@mozilla.com>
Fri, 18 Jan 2013 14:21:32 +0100
changeset 119251 4bee0517d440f222505c2d1bc8af8841bc66338d
parent 119250 0141f43d8c5b1fc4c02aceedb6a5f9b7af8f8131
child 119252 a9676f4f869b6bab95fb65ba97b49f7eb5cc45cb
push id24195
push userMs2ger@gmail.com
push dateSat, 19 Jan 2013 16:10:11 +0000
treeherdermozilla-central@02e12a80aef9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs568593
milestone21.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 568593 - Refactor SharedContext; r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext-inl.h
js/src/frontend/SharedContext.h
js/src/jsscript.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -694,22 +694,22 @@ PushStatementBCE(BytecodeEmitter *bce, S
  * block object or compiler created function.
  */
 static JSObject *
 EnclosingStaticScope(BytecodeEmitter *bce)
 {
     if (bce->blockChain)
         return bce->blockChain;
 
-    if (!bce->sc->isFunction) {
+    if (!bce->sc->isFunctionBox()) {
         JS_ASSERT(!bce->parent);
         return NULL;
     }
 
-    return bce->sc->asFunbox()->function();
+    return bce->sc->asFunctionBox()->function();
 }
 
 // Push a block scope statement and link blockObj into bce->blockChain.
 static void
 PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StaticBlockObject &blockObj,
                   ptrdiff_t top)
 {
     PushStatementBCE(bce, stmt, STMT_BLOCK, top);
@@ -915,17 +915,17 @@ EmitAliasedVarOp(JSContext *cx, JSOp op,
     if (pn->isUsed()) {
         /*
          * As explained in BindNameToSlot, the 'level' of a use indicates how
          * many function scopes (i.e., BytecodeEmitters) to skip to find the
          * enclosing function scope of the definition being accessed.
          */
         for (unsigned i = pn->pn_cookie.level(); i; i--) {
             skippedScopes += ClonedBlockDepth(bceOfDef);
-            JSFunction *funOfDef = bceOfDef->sc->asFunbox()->function();
+            JSFunction *funOfDef = bceOfDef->sc->asFunctionBox()->function();
             if (funOfDef->isHeavyweight()) {
                 skippedScopes++;
                 if (funOfDef->isNamedLambda())
                     skippedScopes++;
             }
             bceOfDef = bceOfDef->parent;
         }
     } else {
@@ -1077,17 +1077,17 @@ BytecodeEmitter::isAliasedName(ParseNode
  * js::frontend::CompileScript; see comments there.
  *
  * The function returns -1 on failures.
  */
 static int
 AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
 {
     JS_ASSERT((unsigned) slot < bce->maxStackDepth);
-    if (bce->sc->isFunction) {
+    if (bce->sc->isFunctionBox()) {
         slot += bce->script->bindings.numVars();
         if ((unsigned) slot >= SLOTNO_LIMIT) {
             bce->reportError(NULL, JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
     return slot;
 }
@@ -1175,17 +1175,17 @@ TryConvertToGname(BytecodeEmitter *bce, 
           case JSOP_SETNAME:  *op = JSOP_SETINTRINSIC; break;
           /* Other *NAME ops aren't (yet) supported in self-hosted code. */
           default: JS_NOT_REACHED("intrinsic");
         }
         return true;
     }
     if (bce->script->compileAndGo &&
         bce->hasGlobalScope &&
-        !(bce->sc->isFunction && bce->sc->asFunbox()->mightAliasLocals()) &&
+        !(bce->sc->isFunctionBox() && bce->sc->asFunctionBox()->mightAliasLocals()) &&
         !pn->isDeoptimized() &&
         !bce->sc->strict)
     {
         // If you change anything here, you might also need to change
         // js::ReportIfUndeclaredVarAssignment.
         switch (*op) {
           case JSOP_NAME:     *op = JSOP_GETGNAME; break;
           case JSOP_SETNAME:  *op = JSOP_SETGNAME; break;
@@ -1377,17 +1377,17 @@ BindNameToSlot(JSContext *cx, BytecodeEm
 
         /*
          * Currently, the ALIASEDVAR ops do not support accessing the
          * callee of a DeclEnvObject, so use NAME.
          */
         if (dn->pn_cookie.level() != bce->script->staticLevel)
             return true;
 
-        RootedFunction fun(cx, bce->sc->asFunbox()->function());
+        RootedFunction fun(cx, bce->sc->asFunctionBox()->function());
         JS_ASSERT(fun->isLambda());
         JS_ASSERT(pn->pn_atom == fun->atom());
 
         /*
          * Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
          * address two cases: a new binding introduced by eval, and
          * assignment to the name in strict mode.
          *
@@ -1440,17 +1440,17 @@ BindNameToSlot(JSContext *cx, BytecodeEm
      * bloat where a single live function keeps its whole global script
      * alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
      * associated types::TypeSet.
      */
     if (skip) {
         BytecodeEmitter *bceSkipped = bce;
         for (unsigned i = 0; i < skip; i++)
             bceSkipped = bceSkipped->parent;
-        if (!bceSkipped->sc->isFunction)
+        if (!bceSkipped->sc->isFunctionBox())
             return true;
     }
 
     JS_ASSERT(!pn->isOp(op));
     pn->setOp(op);
     if (!pn->pn_cookie.set(bce->sc->context, skip, dn->pn_cookie.slot()))
         return false;
 
@@ -1652,37 +1652,37 @@ CheckSideEffects(JSContext *cx, Bytecode
         break;
     }
     return ok;
 }
 
 bool
 BytecodeEmitter::checkSingletonContext()
 {
-    if (!script->compileAndGo || sc->isFunction)
+    if (!script->compileAndGo || sc->isFunctionBox())
         return false;
     for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
         if (stmt->isLoop())
             return false;
     }
     hasSingletons = true;
     return true;
 }
 
 bool
 BytecodeEmitter::needsImplicitThis()
 {
     if (!script->compileAndGo)
         return true;
 
-    if (sc->isFunction) {
-        if (sc->asFunbox()->inWith)
+    if (sc->isFunctionBox()) {
+        if (sc->asFunctionBox()->inWith)
             return true;
     } else {
-        JSObject *scope = sc->asGlobal()->scopeChain();
+        JSObject *scope = sc->asGlobalSharedContext()->scopeChain();
         while (scope) {
             if (scope->isWith())
                 return true;
             scope = scope->enclosingScope();
         }
     }
 
     for (StmtInfoBCE *stmt = topStmt; stmt; stmt = stmt->down) {
@@ -2542,17 +2542,17 @@ frontend::EmitFunctionScript(JSContext *
 {
     /*
      * The decompiler has assumptions about what may occur immediately after
      * script->main (e.g., in the case of destructuring params). Thus, put the
      * following ops into the range [script->code, script->main). Note:
      * execution starts from script->code, so this has no semantic effect.
      */
 
-    FunctionBox *funbox = bce->sc->asFunbox();
+    FunctionBox *funbox = bce->sc->asFunctionBox();
     if (funbox->argumentsHasLocalBinding()) {
         JS_ASSERT(bce->next() == bce->base());  /* See JSScript::argumentsBytecode. */
         bce->switchToProlog();
         if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
             return false;
         InternalBindingsHandle bindings(bce->script, &bce->script->bindings);
         unsigned varIndex = Bindings::argumentsVarIndex(cx, bindings);
         if (bce->script->varIsAliased(varIndex)) {
@@ -2619,17 +2619,17 @@ MaybeEmitVarDecl(JSContext *cx, Bytecode
     if (!pn->pn_cookie.isFree()) {
         atomIndex = pn->pn_cookie.slot();
     } else {
         if (!bce->makeAtomIndex(pn->pn_atom, &atomIndex))
             return false;
     }
 
     if (JOF_OPTYPE(pn->getOp()) == JOF_ATOM &&
-        (!bce->sc->isFunction || bce->sc->asFunbox()->function()->isHeavyweight()))
+        (!bce->sc->isFunctionBox() || bce->sc->asFunctionBox()->function()->isHeavyweight()))
     {
         bce->switchToProlog();
         if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
             return false;
         if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
             return false;
         bce->switchToMain();
     }
@@ -4395,17 +4395,17 @@ EmitLexicalScope(JSContext *cx, Bytecode
      * shouldn't be so complicated; try to find a simpler condition.
      */
     ptrdiff_t noteIndex = -1;
     if (pn->expr()->getKind() != PNK_FOR &&
         pn->expr()->getKind() != PNK_CATCH &&
         (stmtInfo.down
          ? stmtInfo.down->type == STMT_BLOCK &&
            (!stmtInfo.down->down || stmtInfo.down->down->type != STMT_FOR_IN_LOOP)
-         : !bce->sc->isFunction))
+         : !bce->sc->isFunctionBox()))
     {
         /* There must be no source note already output for the next op. */
         JS_ASSERT(bce->noteCount() == 0 ||
                   bce->lastNoteOffset() != bce->offset() ||
                   !GettableNoteForNextOp(bce));
         noteIndex = NewSrcNote2(cx, bce, SRC_BRACE, 0);
         if (noteIndex < 0)
             return false;
@@ -4788,25 +4788,25 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
     JS_ASSERT(fun->isInterpreted());
     if (fun->hasScript()) {
         /*
          * This second pass is needed to emit JSOP_NOP with a source note
          * for the already-emitted function definition prolog opcode. See
          * comments in EmitStatementList.
          */
         JS_ASSERT(pn->functionIsHoisted());
-        JS_ASSERT(bce->sc->isFunction);
+        JS_ASSERT(bce->sc->isFunctionBox());
         return EmitFunctionDefNop(cx, bce, pn->pn_index);
     }
 
     {
         SharedContext *outersc = bce->sc;
         FunctionBox *funbox = pn->pn_funbox;
 
-        if (outersc->isFunction && outersc->asFunbox()->mightAliasLocals())
+        if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
             funbox->setMightAliasLocals();      // inherit mightAliasLocals from parent
         JS_ASSERT_IF(outersc->strict, funbox->strict);
 
         // Inherit most things (principals, version, etc) from the parent.
         Rooted<JSScript*> parent(cx, bce->script);
         Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
         CompileOptions options(cx);
         options.setPrincipals(parent->principals)
@@ -4849,17 +4849,17 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
      * For a script we emit the code as we parse. Thus the bytecode for
      * top-level functions should go in the prolog to predefine their
      * names in the variable object before the already-generated main code
      * is executed. This extra work for top-level scripts is not necessary
      * when we emit the code for a function. It is fully parsed prior to
      * invocation of the emitter and calls to EmitTree for function
      * definitions can be scheduled before generating the rest of code.
      */
-    if (!bce->sc->isFunction) {
+    if (!bce->sc->isFunctionBox()) {
         JS_ASSERT(pn->pn_cookie.isFree());
         JS_ASSERT(pn->getOp() == JSOP_NOP);
         JS_ASSERT(!bce->topStmt);
         bce->switchToProlog();
         if (!EmitIndex32(cx, JSOP_DEFFUN, index, bce))
             return false;
         if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
             return false;
@@ -5142,17 +5142,17 @@ EmitStatement(JSContext *cx, BytecodeEmi
      * expression statement as the script's result, despite the fact
      * that it appears useless to the compiler.
      *
      * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
      * calling JS_Compile* to suppress JSOP_POPV.
      */
     bool wantval = false;
     bool useful = false;
-    if (bce->sc->isFunction) {
+    if (bce->sc->isFunctionBox()) {
         JS_ASSERT(!bce->script->noScriptRval);
     } else {
         useful = wantval = !bce->script->noScriptRval;
     }
 
     /* Don't eliminate expressions with side effects. */
     if (!useful) {
         if (!CheckSideEffects(cx, bce, pn2, &useful))
@@ -5957,17 +5957,17 @@ frontend::EmitTree(JSContext *cx, Byteco
 
     switch (pn->getKind()) {
       case PNK_FUNCTION:
         ok = EmitFunc(cx, bce, pn);
         break;
 
       case PNK_ARGSBODY:
       {
-        RootedFunction fun(cx, bce->sc->asFunbox()->function());
+        RootedFunction fun(cx, bce->sc->asFunctionBox()->function());
         ParseNode *pnlast = pn->last();
 
         // Carefully emit everything in the right order:
         // 1. Destructuring
         // 2. Functions
         // 3. Defaults
         ParseNode *pnchild = pnlast->pn_head;
         if (pnlast->pn_xflags & PNX_DESTRUCT) {
@@ -5995,17 +5995,17 @@ frontend::EmitTree(JSContext *cx, Byteco
                         return false;
                 }
             }
         }
         if (fun->hasDefaults()) {
             ParseNode *rest = NULL;
             bool restIsDefn = false;
             if (fun->hasRest()) {
-                JS_ASSERT(!bce->sc->asFunbox()->argumentsHasLocalBinding());
+                JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
                 // Defaults with a rest parameter need special handling. The
                 // rest parameter needs to be undefined while defaults are being
                 // processed. To do this, we create the rest argument and let it
                 // sit on the stack while processing defaults. The rest
                 // parameter's slot is set to undefined for the course of
                 // default processing.
                 rest = pn->pn_head;
                 while (rest->pn_next != pnlast)
@@ -6040,17 +6040,17 @@ frontend::EmitTree(JSContext *cx, Byteco
             // Only bind the parameter if it's not aliased by a nested function
             // in the body.
             if (!pn2->isDefn())
                 continue;
             if (!BindNameToSlot(cx, bce, pn2))
                 return false;
             if (pn2->pn_next == pnlast && fun->hasRest() && !fun->hasDefaults()) {
                 // Fill rest parameter. We handled the case with defaults above.
-                JS_ASSERT(!bce->sc->asFunbox()->argumentsHasLocalBinding());
+                JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
                 bce->switchToProlog();
                 if (Emit1(cx, bce, JSOP_REST) < 0)
                     return false;
                 CheckTypeSet(cx, bce, JSOP_REST);
                 if (!EmitVarOp(cx, pn2, JSOP_SETARG, bce))
                     return false;
                 if (Emit1(cx, bce, JSOP_POP) < 0)
                     return false;
@@ -6110,17 +6110,17 @@ frontend::EmitTree(JSContext *cx, Byteco
         break;
 
       case PNK_RETURN:
         ok = EmitReturn(cx, bce, pn);
         break;
 
 #if JS_HAS_GENERATORS
       case PNK_YIELD:
-        JS_ASSERT(bce->sc->isFunction);
+        JS_ASSERT(bce->sc->isFunctionBox());
         if (pn->pn_kid) {
             if (!EmitTree(cx, bce, pn->pn_kid))
                 return false;
         } else {
             if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
                 return false;
         }
         if (pn->pn_hidden && NewSrcNote(cx, bce, SRC_HIDDEN) < 0)
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -800,16 +800,24 @@ ObjectBox::ObjectBox(JSObject *object, O
 }
 
 ObjectBox::ObjectBox(JSFunction *function, ObjectBox* traceLink)
   : object(function),
     traceLink(traceLink),
     emitLink(NULL)
 {
     JS_ASSERT(object->isFunction());
+    JS_ASSERT(asFunctionBox()->function() == function);
+}
+
+FunctionBox *
+ObjectBox::asFunctionBox()
+{
+    JS_ASSERT(isFunctionBox());
+    return static_cast<FunctionBox *>(this);
 }
 
 void
 ObjectBox::trace(JSTracer *trc)
 {
     ObjectBox *box = this;
     while (box) {
         MarkObjectRoot(trc, &box->object, "parser.object");
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1498,17 +1498,17 @@ LinkUseToDef(ParseNode *pn, Definition *
 }
 
 class ObjectBox {
   public:
     JSObject *object;
 
     ObjectBox(JSObject *object, ObjectBox *traceLink);
     bool isFunctionBox() { return object->isFunction(); }
-    FunctionBox *asFunctionBox() { JS_ASSERT(isFunctionBox()); return (FunctionBox *)(this); }
+    FunctionBox *asFunctionBox();
     void trace(JSTracer *trc);
 
   protected:
     friend struct CGObjectList;
 
     ObjectBox *traceLink;
     ObjectBox *emitLink;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -149,32 +149,32 @@ ParseContext::define(JSContext *cx, Prop
     pn->setDefn(true);
     pn->pn_dflags &= ~PND_PLACEHOLDER;
     if (kind == Definition::CONST)
         pn->pn_dflags |= PND_CONST;
 
     Definition *dn = (Definition *)pn;
     switch (kind) {
       case Definition::ARG:
-        JS_ASSERT(sc->isFunction);
+        JS_ASSERT(sc->isFunctionBox());
         dn->setOp(JSOP_GETARG);
         dn->pn_dflags |= PND_BOUND;
         if (!dn->pn_cookie.set(cx, staticLevel, args_.length()))
             return false;
         if (!args_.append(dn))
             return false;
         if (name == cx->names().empty)
             break;
         if (!decls_.addUnique(name, dn))
             return false;
         break;
 
       case Definition::CONST:
       case Definition::VAR:
-        if (sc->isFunction) {
+        if (sc->isFunctionBox()) {
             dn->setOp(JSOP_GETLOCAL);
             dn->pn_dflags |= PND_BOUND;
             if (!dn->pn_cookie.set(cx, staticLevel, vars_.length()))
                 return false;
             if (!vars_.append(dn))
                 return false;
         }
         if (!decls_.addUnique(name, dn))
@@ -211,17 +211,17 @@ void
 ParseContext::updateDecl(JSAtom *atom, ParseNode *pn)
 {
     Definition *oldDecl = decls_.lookupFirst(atom);
 
     pn->setDefn(true);
     Definition *newDecl = (Definition *)pn;
     decls_.updateFirst(atom, newDecl);
 
-    if (!sc->isFunction) {
+    if (!sc->isFunctionBox()) {
         JS_ASSERT(newDecl->isFreeVar());
         return;
     }
 
     JS_ASSERT(oldDecl->isBound());
     JS_ASSERT(!oldDecl->pn_cookie.isFree());
     newDecl->pn_cookie = oldDecl->pn_cookie;
     newDecl->pn_dflags |= PND_BOUND;
@@ -280,17 +280,17 @@ AppendPackedBindings(const ParseContext 
 
         *dst = Binding(name, kind, aliased);
     }
 }
 
 bool
 ParseContext::generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const
 {
-    JS_ASSERT(sc->isFunction);
+    JS_ASSERT(sc->isFunctionBox());
 
     unsigned count = args_.length() + vars_.length();
     Binding *packedBindings = cx->tempLifoAlloc().newArrayUninitialized<Binding>(count);
     if (!packedBindings) {
         js_ReportOutOfMemory(cx);
         return false;
     }
 
@@ -298,17 +298,17 @@ ParseContext::generateFunctionBindings(J
     AppendPackedBindings(this, vars_, packedBindings + args_.length());
 
     if (!Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
                                             packedBindings))
     {
         return false;
     }
 
-    FunctionBox *funbox = sc->asFunbox();
+    FunctionBox *funbox = sc->asFunctionBox();
     if (bindings->hasAnyAliasedBindings() || funbox->hasExtensibleScope())
         funbox->function()->setIsHeavyweight();
 
     return true;
 }
 
 Parser::Parser(JSContext *cx, const CompileOptions &options,
                StableCharPtr chars, size_t length, bool foldConstants)
@@ -368,17 +368,17 @@ Parser::newObjectBox(JSObject *obj)
     traceListHead = objbox;
 
     return objbox;
 }
 
 FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun,
                          ParseContext *outerpc, bool strict)
   : ObjectBox(fun, traceListHead),
-    SharedContext(cx, /* isFunction = */ true, strict),
+    SharedContext(cx, strict),
     bindings(),
     bufStart(0),
     bufEnd(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     funCxFlags()
 {
@@ -389,42 +389,42 @@ FunctionBox::FunctionBox(JSContext *cx, 
         // This covers cases that don't involve eval().  For example:
         //
         //   with (o) { (function() { g(); })(); }
         //
         // In this case, |outerpc| corresponds to global code, and
         // outerpc->parsingWith is true.
         inWith = true;
 
-    } else if (!outerpc->sc->isFunction) {
+    } else if (!outerpc->sc->isFunctionBox()) {
         // This covers the case where a function is nested within an eval()
         // within a |with| statement.
         //
         //   with (o) { eval("(function() { g(); })();"); }
         //
         // In this case, |outerpc| corresponds to the eval(),
         // outerpc->parsingWith is false because the eval() breaks the
         // ParseContext chain, and |parent| is NULL (again because of the
         // eval(), so we have to look at |outerpc|'s scopeChain.
         //
-        JSObject *scope = outerpc->sc->asGlobal()->scopeChain();
+        JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
         while (scope) {
             if (scope->isWith())
                 inWith = true;
             scope = scope->enclosingScope();
         }
     } else {
         // This is like the above case, but for more deeply nested functions.
         // For example:
         //
         //   with (o) { eval("(function() { (function() { g(); })(); })();"); } }
         //
         // In this case, the inner anonymous function needs to inherit the
         // setting of |inWith| from the outer one.
-        FunctionBox *parent = outerpc->sc->asFunbox();
+        FunctionBox *parent = outerpc->sc->asFunctionBox();
         if (parent && parent->inWith)
             inWith = true;
     }
 }
 
 FunctionBox *
 Parser::newFunctionBox(JSFunction *fun, ParseContext *outerpc, bool strict)
 {
@@ -624,30 +624,30 @@ HasFinalReturn(ParseNode *pn)
     }
 }
 
 static bool
 ReportBadReturn(JSContext *cx, Parser *parser, ParseNode *pn, Parser::Reporter reporter,
                 unsigned errnum, unsigned anonerrnum)
 {
     JSAutoByteString name;
-    JSAtom *atom = parser->pc->sc->asFunbox()->function()->atom();
+    JSAtom *atom = parser->pc->sc->asFunctionBox()->function()->atom();
     if (atom) {
         if (!js_AtomToPrintableString(cx, atom, &name))
             return false;
     } else {
         errnum = anonerrnum;
     }
     return (parser->*reporter)(pn, errnum, name.ptr());
 }
 
 static bool
 CheckFinalReturn(JSContext *cx, Parser *parser, ParseNode *pn)
 {
-    JS_ASSERT(parser->pc->sc->isFunction);
+    JS_ASSERT(parser->pc->sc->isFunctionBox());
     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
            ReportBadReturn(cx, parser, pn, &Parser::reportStrictWarning,
                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
 }
 
 /*
  * Check that it is permitted to assign to lhs.  Strict mode code may not
  * assign to 'eval' or 'arguments'.
@@ -734,33 +734,33 @@ Parser::standaloneFunctionBody(HandleFun
         return NULL;
 
     return pn;
 }
 
 ParseNode *
 Parser::functionBody(FunctionBodyType type)
 {
-    JS_ASSERT(pc->sc->isFunction);
+    JS_ASSERT(pc->sc->isFunctionBox());
     JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
 
     ParseNode *pn;
     if (type == StatementListBody) {
         pn = statements();
     } else {
         JS_ASSERT(type == ExpressionBody);
         JS_ASSERT(JS_HAS_EXPR_CLOSURES);
 
         pn = UnaryNode::create(PNK_RETURN, this);
         if (pn) {
             pn->pn_kid = assignExpr();
             if (!pn->pn_kid) {
                 pn = NULL;
             } else {
-                if (pc->sc->asFunbox()->isGenerator()) {
+                if (pc->sc->asFunctionBox()->isGenerator()) {
                     ReportBadReturn(context, this, pn, &Parser::reportError,
                                     JSMSG_BAD_GENERATOR_RETURN,
                                     JSMSG_BAD_ANON_GENERATOR_RETURN);
                     pn = NULL;
                 } else {
                     pn->setOp(JSOP_RETURN);
                     pn->pn_pos.end = pn->pn_kid->pn_pos.end;
                 }
@@ -817,17 +817,17 @@ Parser::functionBody(FunctionBodyType ty
 
     /*
      * Report error if both rest parameters and 'arguments' are used. Do this
      * check before adding artificial 'arguments' below.
      */
     Definition *maybeArgDef = pc->decls().lookupFirst(arguments);
     bool argumentsHasBinding = !!maybeArgDef;
     bool argumentsHasLocalBinding = maybeArgDef && maybeArgDef->kind() != Definition::ARG;
-    bool hasRest = pc->sc->asFunbox()->function()->hasRest();
+    bool hasRest = pc->sc->asFunctionBox()->function()->hasRest();
     if (hasRest && argumentsHasLocalBinding) {
         reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
         return NULL;
     }
 
     /*
      * Even if 'arguments' isn't explicitly mentioned, dynamic name lookup
      * forces an 'arguments' binding. The exception is that functions with rest
@@ -844,17 +844,17 @@ Parser::functionBody(FunctionBodyType ty
     }
 
     /*
      * Now that all possible 'arguments' bindings have been added, note whether
      * 'arguments' has a local binding and whether it unconditionally needs an
      * arguments object. (Also see the flags' comments in ContextFlags.)
      */
     if (argumentsHasLocalBinding) {
-        FunctionBox *funbox = pc->sc->asFunbox();
+        FunctionBox *funbox = pc->sc->asFunctionBox();
         funbox->setArgumentsHasLocalBinding();
 
         /* Dynamic scope access destroys all hope of optimization. */
         if (pc->sc->bindingsAccessedDynamically())
             funbox->setDefinitelyNeedsArgsObj();
 
         /*
          * Check whether any parameters have been assigned within this
@@ -1070,17 +1070,17 @@ Parser::newFunction(ParseContext *pc, Ha
      * function's parent slot to pc->sc->asGlobal()->scopeChain. If the global
      * context is a compile-and-go one, we leave the pre-set parent intact;
      * otherwise we clear parent and proto.
      */
     while (pc->parent)
         pc = pc->parent;
 
     RootedObject parent(context);
-    parent = pc->sc->isFunction ? NULL : pc->sc->asGlobal()->scopeChain();
+    parent = pc->sc->isFunctionBox() ? NULL : pc->sc->asGlobalSharedContext()->scopeChain();
 
     RootedFunction fun(context);
     JSFunction::Flags flags = (kind == Expression)
                               ? JSFunction::INTERPRETED_LAMBDA
                               : JSFunction::INTERPRETED;
     fun = js_NewFunction(context, NullPtr(), NULL, 0, flags, parent, atom);
     if (selfHostingMode)
         fun->setIsSelfHostedBuiltin();
@@ -1138,17 +1138,17 @@ LeaveFunction(ParseNode *fn, Parser *par
               FunctionSyntaxKind kind = Expression)
 {
     JSContext *cx = parser->context;
     ParseContext *funpc = parser->pc;
     ParseContext *pc = funpc->parent;
     pc->blockidGen = funpc->blockidGen;
 
     FunctionBox *funbox = fn->pn_funbox;
-    JS_ASSERT(funbox == funpc->sc->asFunbox());
+    JS_ASSERT(funbox == funpc->sc->asFunctionBox());
 
     if (!pc->topStmt || pc->topStmt->type == STMT_BLOCK)
         fn->pn_dflags |= PND_BLOCKCHILD;
 
     /* Propagate unresolved lexical names up to pc->lexdeps. */
     if (funpc->lexdeps->count()) {
         for (AtomDefnRange r = funpc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
@@ -1325,17 +1325,17 @@ frontend::DefineArg(Parser *parser, Pars
     return parser->pc->define(parser->context, name, argpn, Definition::ARG);
 }
 
 #if JS_HAS_DESTRUCTURING
 static bool
 BindDestructuringArg(JSContext *cx, BindData *data, HandlePropertyName name, Parser *parser)
 {
     ParseContext *pc = parser->pc;
-    JS_ASSERT(pc->sc->isFunction);
+    JS_ASSERT(pc->sc->isFunctionBox());
 
     if (pc->decls().lookupFirst(name)) {
         parser->reportError(NULL, JSMSG_BAD_DUP_ARGS);
         return false;
     }
 
     if (!CheckStrictBinding(cx, parser, name, data->pn))
         return false;
@@ -1347,17 +1347,17 @@ BindDestructuringArg(JSContext *cx, Bind
 bool
 Parser::functionArguments(ParseNode **listp, ParseNode* funcpn, bool &hasRest)
 {
     if (tokenStream.getToken() != TOK_LP) {
         reportError(NULL, JSMSG_PAREN_BEFORE_FORMAL);
         return false;
     }
 
-    FunctionBox *funbox = pc->sc->asFunbox();
+    FunctionBox *funbox = pc->sc->asFunctionBox();
     funbox->bufStart = tokenStream.offsetOfToken(tokenStream.currentToken());
 
     hasRest = false;
 
     ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, this);
     if (!argsbody)
         return false;
     argsbody->setOp(JSOP_NOP);
@@ -1578,23 +1578,23 @@ Parser::functionDef(HandlePropertyName f
          * As a SpiderMonkey-specific extension, non-body-level function
          * statements (e.g., functions in an "if" or "while" block) are
          * dynamically bound when control flow reaches the statement. The
          * emitter normally emits functions in two passes (see PNK_ARGSBODY).
          * To distinguish
          */
         if (bodyLevel) {
             JS_ASSERT(pn->functionIsHoisted());
-            JS_ASSERT_IF(pc->sc->isFunction, !pn->pn_cookie.isFree());
-            JS_ASSERT_IF(!pc->sc->isFunction, pn->pn_cookie.isFree());
+            JS_ASSERT_IF(pc->sc->isFunctionBox(), !pn->pn_cookie.isFree());
+            JS_ASSERT_IF(!pc->sc->isFunctionBox(), pn->pn_cookie.isFree());
         } else {
             JS_ASSERT(!pc->sc->strict);
             JS_ASSERT(pn->pn_cookie.isFree());
-            if (pc->sc->isFunction) {
-                FunctionBox *funbox = pc->sc->asFunbox();
+            if (pc->sc->isFunctionBox()) {
+                FunctionBox *funbox = pc->sc->asFunctionBox();
                 funbox->setMightAliasLocals();
                 funbox->setHasExtensibleScope();
             }
             pn->setOp(JSOP_DEFFUN);
 
             /*
              * Instead of setting bindingsAccessedDynamically, which would be
              * overly conservative, remember the names of all function
@@ -1871,17 +1871,17 @@ Parser::maybeParseDirective(ParseNode *p
         pn->pn_prologue = true;
 
         JSAtom *directive = string->pn_atom;
         if (directive == context->runtime->atomState.useStrict) {
             // We're going to be in strict mode. Note that this scope explicitly
             // had "use strict";
             pc->sc->setExplicitUseStrict();
             if (!pc->sc->strict) {
-                if (pc->sc->isFunction) {
+                if (pc->sc->isFunctionBox()) {
                     // Request that this function be reparsed as strict.
                     pc->funBecameStrict = true;
                     return false;
                 } else {
                     // We don't reparse global scopes, so we keep track of the
                     // one possible strict violation that could occur in the
                     // directive prologue -- octal escapes -- and complain now.
                     if (tokenStream.sawOctalEscape()) {
@@ -1951,17 +1951,17 @@ Parser::statements(bool *hasFunctionStmt
              */
             if (pc->atBodyLevel()) {
                 pn->pn_xflags |= PNX_FUNCDEFS;
             } else {
                 /*
                  * General deoptimization was done in functionDef, here we just
                  * need to tell TOK_LC in Parser::statement to add braces.
                  */
-                JS_ASSERT_IF(pc->sc->isFunction, pc->sc->asFunbox()->hasExtensibleScope());
+                JS_ASSERT_IF(pc->sc->isFunctionBox(), pc->sc->asFunctionBox()->hasExtensibleScope());
                 if (hasFunctionStmt)
                     *hasFunctionStmt = true;
             }
         }
         pn->append(next);
     }
 
     /*
@@ -2151,18 +2151,18 @@ BindVarOrConst(JSContext *cx, BindData *
 
     if (!CheckStrictBinding(cx, parser, name, pn))
         return false;
 
     StmtInfoPC *stmt = LexicalLookup(pc, name, NULL, (StmtInfoPC *)NULL);
 
     if (stmt && stmt->type == STMT_WITH) {
         pn->pn_dflags |= PND_DEOPTIMIZED;
-        if (pc->sc->isFunction)
-            pc->sc->asFunbox()->setMightAliasLocals();
+        if (pc->sc->isFunctionBox())
+            pc->sc->asFunctionBox()->setMightAliasLocals();
         return true;
     }
 
     DefinitionList::Range defs = pc->decls().lookupMulti(name);
     JS_ASSERT_IF(stmt, !defs.empty());
 
     if (defs.empty())
         return pc->define(cx, name, pn, isConstDecl ? Definition::CONST : Definition::VAR);
@@ -2523,34 +2523,34 @@ Parser::destructuringExpr(BindData *data
 }
 
 #endif /* JS_HAS_DESTRUCTURING */
 
 ParseNode *
 Parser::returnOrYield(bool useAssignExpr)
 {
     TokenKind tt = tokenStream.currentToken().type;
-    if (!pc->sc->isFunction) {
+    if (!pc->sc->isFunctionBox()) {
         reportError(NULL, JSMSG_BAD_RETURN_OR_YIELD,
                     (tt == TOK_RETURN) ? js_return_str : js_yield_str);
         return NULL;
     }
 
     ParseNode *pn = UnaryNode::create((tt == TOK_RETURN) ? PNK_RETURN : PNK_YIELD, this);
     if (!pn)
         return NULL;
 
 #if JS_HAS_GENERATORS
     if (tt == TOK_YIELD) {
         /*
          * If we're within parens, we won't know if this is a generator expression until we see
          * a |for| token, so we have to delay flagging the current function.
          */
         if (pc->parenDepth == 0) {
-            pc->sc->asFunbox()->setIsGenerator();
+            pc->sc->asFunctionBox()->setIsGenerator();
         } else {
             pc->yieldCount++;
             pc->yieldNode = pn;
         }
     }
 #endif
 
     /* This is ugly, but we don't want to require a semicolon. */
@@ -2577,17 +2577,17 @@ Parser::returnOrYield(bool useAssignExpr
         pn->pn_kid = pn2;
     } else {
 #if JS_HAS_GENERATORS
         if (tt == TOK_RETURN)
 #endif
             pc->funHasReturnVoid = true;
     }
 
-    if (pc->funHasReturnExpr && pc->sc->asFunbox()->isGenerator()) {
+    if (pc->funHasReturnExpr && pc->sc->asFunctionBox()->isGenerator()) {
         /* As in Python (see PEP-255), disallow return v; in generators. */
         ReportBadReturn(context, this, pn, &Parser::reportError, JSMSG_BAD_GENERATOR_RETURN,
                         JSMSG_BAD_ANON_GENERATOR_RETURN);
         return NULL;
     }
 
     if (context->hasStrictOption() && pc->funHasReturnExpr && pc->funHasReturnVoid &&
         !ReportBadReturn(context, this, pn, &Parser::reportStrictWarning,
@@ -4831,21 +4831,21 @@ GenexpGuard::checkValidBody(ParseNode *p
  * Call this after endBody() when determining that the body *was not* in a
  * generator expression.
  */
 bool
 GenexpGuard::maybeNoteGenerator(ParseNode *pn)
 {
     ParseContext *pc = parser->pc;
     if (pc->yieldCount > 0) {
-        if (!pc->sc->isFunction) {
+        if (!pc->sc->isFunctionBox()) {
             parser->reportError(NULL, JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
             return false;
         }
-        pc->sc->asFunbox()->setIsGenerator();
+        pc->sc->asFunctionBox()->setIsGenerator();
         if (pc->funHasReturnExpr) {
             /* At the time we saw the yield, we might not have set isGenerator yet. */
             ReportBadReturn(pc->sc->context, parser, pn, &Parser::reportError,
                             JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN);
             return false;
         }
     }
     return true;
@@ -5332,18 +5332,18 @@ Parser::generatorExpr(ParseNode *kid)
 
         /*
          * We assume conservatively that any deoptimization flags in pc->sc
          * come from the kid. So we propagate these flags into genfn. For code
          * simplicity we also do not detect if the flags were only set in the
          * kid and could be removed from pc->sc.
          */
         genFunbox->anyCxFlags = outerpc->sc->anyCxFlags;
-        if (outerpc->sc->isFunction)
-            genFunbox->funCxFlags = outerpc->sc->asFunbox()->funCxFlags;
+        if (outerpc->sc->isFunctionBox())
+            genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
 
         genFunbox->setIsGenerator();
         genFunbox->inGenexpLambda = true;
         genfn->pn_funbox = genFunbox;
         genfn->pn_blockid = genpc.bodyid;
 
         ParseNode *body = comprehensionTail(pn, outerpc->blockid(), true);
         if (!body)
@@ -5659,18 +5659,18 @@ Parser::memberExpr(bool allowCallSyntax)
                     /* Select JSOP_EVAL and flag pc as heavyweight. */
                     nextMember->setOp(JSOP_EVAL);
                     pc->sc->setBindingsAccessedDynamically();
 
                     /*
                      * In non-strict mode code, direct calls to eval can add
                      * variables to the call object.
                      */
-                    if (pc->sc->isFunction && !pc->sc->strict)
-                        pc->sc->asFunbox()->setHasExtensibleScope();
+                    if (pc->sc->isFunctionBox() && !pc->sc->strict)
+                        pc->sc->asFunctionBox()->setHasExtensibleScope();
                 }
             } else if (lhs->isOp(JSOP_GETPROP)) {
                 /* Select JSOP_FUNAPPLY given foo.apply(...). */
                 if (lhs->pn_atom == context->names().apply)
                     nextMember->setOp(JSOP_FUNAPPLY);
                 else if (lhs->pn_atom == context->names().call)
                     nextMember->setOp(JSOP_FUNCALL);
             }
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -76,22 +76,22 @@ struct ParseContext                 /* t
     DeclVector      vars_;          /* var/const definitions */
 
   public:
     const AtomDecls &decls() const {
         return decls_;
     }
 
     uint32_t numArgs() const {
-        JS_ASSERT(sc->isFunction);
+        JS_ASSERT(sc->isFunctionBox());
         return args_.length();
     }
 
     uint32_t numVars() const {
-        JS_ASSERT(sc->isFunction);
+        JS_ASSERT(sc->isFunctionBox());
         return vars_.length();
     }
 
     /*
      * This function adds a definition to the lexical scope represented by this
      * ParseContext.
      *
      * Pre-conditions:
--- a/js/src/frontend/SharedContext-inl.h
+++ b/js/src/frontend/SharedContext-inl.h
@@ -10,46 +10,45 @@
 
 #include "frontend/Parser.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
 inline
-SharedContext::SharedContext(JSContext *cx, bool isFun, bool strict)
+SharedContext::SharedContext(JSContext *cx, bool strict)
   : context(cx),
-    isFunction(isFun),
     anyCxFlags(),
     strict(strict)
 {
 }
 
 inline bool
 SharedContext::needStrictChecks()
 {
     return context->hasStrictOption() || strict;
 }
 
 inline GlobalSharedContext *
-SharedContext::asGlobal()
+SharedContext::asGlobalSharedContext()
 {
-    JS_ASSERT(!isFunction);
+    JS_ASSERT(isGlobalSharedContext());
     return static_cast<GlobalSharedContext*>(this);
 }
 
 inline FunctionBox *
-SharedContext::asFunbox()
+SharedContext::asFunctionBox()
 {
-    JS_ASSERT(isFunction);
+    JS_ASSERT(isFunctionBox());
     return static_cast<FunctionBox*>(this);
 }
 
 GlobalSharedContext::GlobalSharedContext(JSContext *cx, JSObject *scopeChain, bool strict)
-  : SharedContext(cx, /* isFunction = */ false, strict),
+  : SharedContext(cx, strict),
     scopeChain_(cx, scopeChain)
 {
 }
 
 } /* namespace frontend */
 
 template <class ContextT>
 void
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -132,30 +132,29 @@ class GlobalSharedContext;
  * The struct SharedContext is part of the current parser context (see
  * ParseContext). It stores information that is reused between the parser and
  * the bytecode emitter. Note however, that this information is not shared
  * between the two; they simply reuse the same data structure.
  */
 class SharedContext
 {
   public:
-    JSContext       *const context;
-
-    const bool isFunction;          /* true for function code, false for
-                                       global code */
+    JSContext *const context;
     AnyContextFlags anyCxFlags;
-
     bool strict;
 
     // If it's function code, funbox must be non-NULL and scopeChain must be NULL.
     // If it's global code, funbox must be NULL.
-    inline SharedContext(JSContext *cx, bool isFun, bool strict);
+    inline SharedContext(JSContext *cx, bool strict);
 
-    inline GlobalSharedContext *asGlobal();
-    inline FunctionBox *asFunbox();
+    virtual ObjectBox *toObjectBox() = 0;
+    inline bool isGlobalSharedContext() { return toObjectBox() == NULL; }
+    inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
+    inline GlobalSharedContext *asGlobalSharedContext();
+    inline FunctionBox *asFunctionBox();
 
     bool hasExplicitUseStrict()        const { return anyCxFlags.hasExplicitUseStrict; }
     bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
 
     void setExplicitUseStrict()           { anyCxFlags.hasExplicitUseStrict        = true; }
     void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
 
     // JSOPTION_STRICT warnings or strict mode errors.
@@ -165,16 +164,17 @@ class SharedContext
 class GlobalSharedContext : public SharedContext
 {
   private:
     const RootedObject scopeChain_; /* scope chain object for the script */
 
   public:
     inline GlobalSharedContext(JSContext *cx, JSObject *scopeChain, bool strict);
 
+    ObjectBox *toObjectBox() { return NULL; }
     JSObject *scopeChain() const { return scopeChain_; }
 };
 
 class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
     Bindings        bindings;               /* bindings for this function */
     size_t          bufStart;
@@ -184,16 +184,17 @@ class FunctionBox : public ObjectBox, pu
                                                or E4X filter-expression */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
 
     FunctionContextFlags funCxFlags;
 
     FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext *pc,
                 bool strict);
 
+    ObjectBox *toObjectBox() { return this; }
     JSFunction *function() const { return object->toFunction(); }
 
     bool isGenerator()              const { return funCxFlags.isGenerator; }
     bool mightAliasLocals()         const { return funCxFlags.mightAliasLocals; }
     bool hasExtensibleScope()       const { return funCxFlags.hasExtensibleScope; }
     bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
     bool definitelyNeedsArgsObj()   const { return funCxFlags.definitelyNeedsArgsObj; }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1745,17 +1745,17 @@ JSScript::fullyInitFromEmitter(JSContext
                        bce->objectList.length, bce->regexpList.length, bce->tryNoteList.length(),
                        bce->constList.length(), bce->typesetCount))
         return false;
 
     JS_ASSERT(script->mainOffset == 0);
     script->mainOffset = prologLength;
     PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
     PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
-    uint32_t nfixed = bce->sc->isFunction ? script->bindings.numVars() : 0;
+    uint32_t nfixed = bce->sc->isFunctionBox() ? script->bindings.numVars() : 0;
     JS_ASSERT(nfixed < SLOTNO_LIMIT);
     script->nfixed = uint16_t(nfixed);
     InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms);
 
     const char *filename = bce->parser->tokenStream.getFilename();
     if (filename) {
         script->filename = SaveScriptFilename(cx, filename);
         if (!script->filename)
@@ -1763,17 +1763,17 @@ JSScript::fullyInitFromEmitter(JSContext
     }
     script->lineno = bce->firstLine;
     if (script->nfixed + bce->maxStackDepth >= JS_BIT(16)) {
         bce->reportError(NULL, JSMSG_NEED_DIET, "script");
         return false;
     }
     script->nslots = script->nfixed + bce->maxStackDepth;
 
-    FunctionBox *funbox = bce->sc->isFunction ? bce->sc->asFunbox() : NULL;
+    FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : NULL;
 
     if (!FinishTakingSrcNotes(cx, bce, script->notes()))
         return false;
     if (bce->tryNoteList.length() != 0)
         bce->tryNoteList.finish(script->trynotes());
     if (bce->objectList.length != 0)
         bce->objectList.finish(script->objects());
     if (bce->regexpList.length != 0)