Bug 795721 - Inherit FunctionBox from ObjectBox; r=njn
authorEddy Bruel <ejpbruel@mozilla.com>
Tue, 02 Oct 2012 14:56:26 +0200
changeset 109009 ef321673c843dd0e570770d4e3134376ad871591
parent 109008 5a3024f9a7447752e3d248b2e032e6ad8b39a604
child 109010 74a06c56da4eade326fb40c808b72d4adbeccf28
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersnjn
bugs795721
milestone18.0a1
Bug 795721 - Inherit FunctionBox from ObjectBox; r=njn
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/NameFunctions.cpp
js/src/frontend/ParseNode-inl.h
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/jsprvtd.h
js/src/jsreflect.cpp
js/src/jsscript.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -696,17 +696,17 @@ EnclosingStaticScope(BytecodeEmitter *bc
     if (bce->blockChain)
         return bce->blockChain;
 
     if (!bce->sc->isFunction) {
         JS_ASSERT(!bce->parent);
         return NULL;
     }
 
-    return bce->sc->asFunbox()->fun();
+    return bce->sc->asFunbox()->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);
@@ -912,17 +912,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()->fun();
+            JSFunction *funOfDef = bceOfDef->sc->asFunbox()->function();
             if (funOfDef->isHeavyweight()) {
                 skippedScopes++;
                 if (funOfDef->isNamedLambda())
                     skippedScopes++;
             }
             bceOfDef = bceOfDef->parent;
         }
     } else {
@@ -1373,17 +1373,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()->fun());
+        RootedFunction fun(cx, bce->sc->asFunbox()->function());
         JS_ASSERT(fun->flags & JSFUN_LAMBDA);
         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.
          *
@@ -2687,17 +2687,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()->fun()->isHeavyweight()))
+        (!bce->sc->isFunction || bce->sc->asFunbox()->function()->isHeavyweight()))
     {
         bce->switchToProlog();
         if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
             return false;
         if (!EmitIndexOp(cx, prologOp, atomIndex, bce))
             return false;
         bce->switchToMain();
     }
@@ -4830,17 +4830,17 @@ EmitFor(JSContext *cx, BytecodeEmitter *
     return pn->pn_left->isKind(PNK_FORIN)
            ? EmitForIn(cx, bce, pn, top)
            : EmitNormalFor(cx, bce, pn, top);
 }
 
 static JS_NEVER_INLINE bool
 EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
-    RootedFunction fun(cx, pn->pn_funbox->fun());
+    RootedFunction fun(cx, pn->pn_funbox->function());
     JS_ASSERT(fun->isInterpreted());
     if (fun->script()) {
         /*
          * 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());
@@ -4886,17 +4886,17 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
             return false;
 
         /* We measured the max scope depth when we parsed the function. */
         if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
             return false;
     }
 
     /* Make the function object a literal in the outer script's pool. */
-    unsigned index = bce->objectList.add(&pn->pn_funbox->objbox);
+    unsigned index = bce->objectList.add(pn->pn_funbox);
 
     /* Non-hoisted functions simply emit their respective op. */
     if (!pn->functionIsHoisted()) {
         if (pn->pn_funbox->inGenexpLambda && NewSrcNote(cx, bce, SRC_GENEXP) < 0)
             return false;
 
         return EmitIndex32(cx, pn->getOp(), index, bce);
     }
@@ -6014,17 +6014,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()->fun());
+        RootedFunction fun(cx, bce->sc->asFunbox()->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) {
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -170,17 +170,17 @@ class NameResolver
 
     /*
      * Resolve the name of a function. If the function already has a name
      * listed, then it is skipped. Otherwise an intelligent name is guessed to
      * assign to the function's displayAtom field
      */
     JSAtom *resolveFun(ParseNode *pn, HandleAtom prefix) {
         JS_ASSERT(pn != NULL && pn->isKind(PNK_FUNCTION));
-        RootedFunction fun(cx, pn->pn_funbox->fun());
+        RootedFunction fun(cx, pn->pn_funbox->function());
         if (nparents == 0)
             return NULL;
 
         StringBuffer buf(cx);
         this->buf = &buf;
 
         /* If the function already has a name, use that */
         if (fun->displayAtom() != NULL) {
--- a/js/src/frontend/ParseNode-inl.h
+++ b/js/src/frontend/ParseNode-inl.h
@@ -27,17 +27,17 @@ UpvarCookie::set(JSContext *cx, unsigned
     slot_ = newSlot;
     return true;
 }
 
 inline PropertyName *
 ParseNode::name() const
 {
     JS_ASSERT(isKind(PNK_FUNCTION) || isKind(PNK_NAME) || isKind(PNK_INTRINSICNAME));
-    JSAtom *atom = isKind(PNK_FUNCTION) ? pn_funbox->fun()->atom() : pn_atom;
+    JSAtom *atom = isKind(PNK_FUNCTION) ? pn_funbox->function()->atom() : pn_atom;
     return atom->asPropertyName();
 }
 
 inline bool
 ParseNode::isConstant()
 {
     switch (pn_type) {
       case PNK_NUMBER:
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -429,17 +429,17 @@ CloneParseTree(ParseNode *opn, Parser *p
     pn->setDefn(opn->isDefn());
     pn->setUsed(opn->isUsed());
 
     switch (pn->getArity()) {
 #define NULLCHECK(e)    JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO
 
       case PN_FUNC:
         NULLCHECK(pn->pn_funbox =
-                  parser->newFunctionBox(opn->pn_funbox->fun(), pc, opn->pn_funbox->strictModeState));
+                  parser->newFunctionBox(opn->pn_funbox->function(), pc, opn->pn_funbox->strictModeState));
         NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, parser));
         pn->pn_cookie = opn->pn_cookie;
         pn->pn_dflags = opn->pn_dflags;
         pn->pn_blockid = opn->pn_blockid;
         break;
 
       case PN_LIST:
         pn->makeEmpty();
@@ -786,8 +786,27 @@ NameNode::dump(int indent)
     else {
         fprintf(stderr, "(%s ", name);
         indent += strlen(name) + 2;
         DumpParseTree(expr(), indent);
         fprintf(stderr, ")");
     }
 }
 #endif
+
+ObjectBox::ObjectBox(JSObject *object, ObjectBox* traceLink)
+  : object(object),
+    traceLink(traceLink),
+    emitLink(NULL)
+{
+}
+
+void
+ObjectBox::trace(JSTracer *trc)
+{
+    ObjectBox *box = this;
+    while (box) {
+        MarkObjectRoot(trc, &box->object, "parser.object");
+        if (box->isFunctionBox())
+            box->asFunctionBox()->bindings.trace(trc);
+        box = box->traceLink;
+    }
+}
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -1452,26 +1452,28 @@ LinkUseToDef(ParseNode *pn, Definition *
     JS_ASSERT(pn != dn->dn_uses);
     pn->pn_link = dn->dn_uses;
     dn->dn_uses = pn;
     dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
     pn->setUsed(true);
     pn->pn_lexdef = dn;
 }
 
-struct ObjectBox {
-    ObjectBox           *traceLink;
-    ObjectBox           *emitLink;
-    JSObject            *object;
+class ObjectBox {
+  public:
+    JSObject *object;
 
-    // An ObjectBox can hold a JSObject or a JSFunction.  In the latter case,
-    // the ObjectBox will be embedded within a FunctionBox;  |funbox| points to
-    // that FunctionBox.
-    FunctionBox         *const funbox;
+    ObjectBox(JSObject *object, ObjectBox *traceLink);
+    bool isFunctionBox() { return object->isFunction(); }
+    FunctionBox *asFunctionBox() { JS_ASSERT(isFunctionBox()); return (FunctionBox *)(this); }
+    void trace(JSTracer *trc);
 
-    ObjectBox(ObjectBox *traceLink, JSObject *obj);
-    ObjectBox(ObjectBox *traceLink, JSFunction *fun, FunctionBox *funbox);
+  protected:
+    friend struct CGObjectList;
+
+    ObjectBox *traceLink;
+    ObjectBox *emitLink;
 };
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* ParseNode_h__ */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -313,17 +313,17 @@ ParseContext::generateFunctionBindings(J
     if (!Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
                                             packedBindings))
     {
         return false;
     }
 
     FunctionBox *funbox = sc->asFunbox();
     if (bindings->hasAnyAliasedBindings() || funbox->hasExtensibleScope())
-        funbox->fun()->flags |= JSFUN_HEAVYWEIGHT;
+        funbox->function()->flags |= JSFUN_HEAVYWEIGHT;
 
     return true;
 }
 
 Parser::Parser(JSContext *cx, const CompileOptions &options,
                const jschar *chars, size_t length, bool foldConstants)
   : AutoGCRooter(cx, PARSER),
     context(cx),
@@ -354,60 +354,44 @@ Parser::init()
 
 Parser::~Parser()
 {
     JSContext *cx = context;
     cx->tempLifoAlloc().release(tempPoolMark);
     cx->activeCompilations--;
 }
 
-ObjectBox::ObjectBox(ObjectBox* traceLink, JSObject *obj)
-  : traceLink(traceLink),
-    emitLink(NULL),
-    object(obj),
-    funbox(NULL)
-{
-}
-
-ObjectBox::ObjectBox(ObjectBox* traceLink, JSFunction *fun, FunctionBox *funbox)
-  : traceLink(traceLink),
-    emitLink(NULL),
-    object(fun),
-    funbox(funbox)
-{
-}
-
 ObjectBox *
 Parser::newObjectBox(JSObject *obj)
 {
     JS_ASSERT(obj && !IsPoisonedPtr(obj));
 
     /*
      * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
      * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
      * arenas containing the entries must be alive until we are done with
      * scanning, parsing and code generation for the whole script or top-level
      * function.
      */
 
-    ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>(traceListHead, obj);
+    ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>(obj, traceListHead);
     if (!objbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
     traceListHead = objbox;
 
     return objbox;
 }
 
 FunctionBox::FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun,
                          ParseContext *outerpc, StrictMode sms)
-  : SharedContext(cx, /* isFunction = */ true, sms),
-    objbox(traceListHead, fun, this),
+  : ObjectBox(fun, traceListHead),
+    SharedContext(cx, /* isFunction = */ true, sms),
     bindings(),
     bufStart(0),
     bufEnd(0),
     ndefaults(0),
     inWith(false),                  // initialized below
     inGenexpLambda(false),
     funCxFlags()
 {
@@ -468,31 +452,25 @@ Parser::newFunctionBox(JSFunction *fun, 
      */
     FunctionBox *funbox =
         context->tempLifoAlloc().new_<FunctionBox>(context, traceListHead, fun, outerpc, sms);
     if (!funbox) {
         js_ReportOutOfMemory(context);
         return NULL;
     }
 
-    traceListHead = &funbox->objbox;
+    traceListHead = funbox;
 
     return funbox;
 }
 
 void
 Parser::trace(JSTracer *trc)
 {
-    ObjectBox *objbox = traceListHead;
-    while (objbox) {
-        MarkObjectRoot(trc, &objbox->object, "parser.object");
-        if (objbox->funbox)
-            objbox->funbox->bindings.trace(trc);
-        objbox = objbox->traceLink;
-    }
+    traceListHead->trace(trc);
 }
 
 static bool
 GenerateBlockIdForStmtNode(ParseNode *pn, ParseContext *pc)
 {
     JS_ASSERT(pc->topStmt);
     JS_ASSERT(pc->topStmt->maybeScope());
     JS_ASSERT(pn->isKind(PNK_STATEMENTLIST) || pn->isKind(PNK_LEXICALSCOPE));
@@ -659,17 +637,17 @@ 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()->fun()->atom();
+    JSAtom *atom = parser->pc->sc->asFunbox()->function()->atom();
     if (atom) {
         if (!js_AtomToPrintableString(cx, atom, &name))
             return false;
     } else {
         errnum = anonerrnum;
     }
     return (parser->*reporter)(pn, errnum, name.ptr());
 }
@@ -822,17 +800,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()->fun()->hasRest();
+    bool hasRest = pc->sc->asFunbox()->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
@@ -1173,17 +1151,17 @@ LeaveFunction(ParseNode *fn, Parser *par
                  * definitions in the ParseContext by generateFunctionBindings).
                  *
                  * If 'dn' has been assigned to, then we also flag the function
                  * scope has needing a dynamic scope so that dynamic scope
                  * setter can either ignore the set (in non-strict mode) or
                  * produce an error (in strict mode).
                  */
                 if (dn->isClosed() || dn->isAssigned())
-                    funbox->fun()->flags |= JSFUN_HEAVYWEIGHT;
+                    funbox->function()->flags |= JSFUN_HEAVYWEIGHT;
                 continue;
             }
 
             Definition *outer_dn = pc->decls().lookupFirst(atom);
 
             /*
              * Make sure to deoptimize lexical dependencies that are polluted
              * by eval and function statements (which both flag the function as
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -185,34 +185,33 @@ class GlobalSharedContext : public Share
     const RootedObject scopeChain_; /* scope chain object for the script */
 
   public:
     inline GlobalSharedContext(JSContext *cx, JSObject *scopeChain, StrictMode sms);
 
     JSObject *scopeChain() const { return scopeChain_; }
 };
 
-class FunctionBox : public SharedContext
+class FunctionBox : public ObjectBox, public SharedContext
 {
   public:
-    ObjectBox       objbox;
     Bindings        bindings;               /* bindings for this function */
     size_t          bufStart;
     size_t          bufEnd;
     uint16_t        ndefaults;
     bool            inWith:1;               /* some enclosing scope is a with-statement
                                                or E4X filter-expression */
     bool            inGenexpLambda:1;       /* lambda from generator expression */
 
     FunctionContextFlags funCxFlags;
 
     FunctionBox(JSContext *cx, ObjectBox* traceListHead, JSFunction *fun, ParseContext *pc,
                 StrictMode sms);
 
-    JSFunction *fun() const { return objbox.object->toFunction(); }
+    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; }
 
     void setIsGenerator()                  { funCxFlags.isGenerator              = true; }
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -172,17 +172,17 @@ typedef JSPropertyOp         PropertyOp;
 typedef JSStrictPropertyOp   StrictPropertyOp;
 typedef JSPropertyDescriptor PropertyDescriptor;
 
 namespace frontend {
 
 struct BytecodeEmitter;
 struct Definition;
 class FunctionBox;
-struct ObjectBox;
+class ObjectBox;
 struct Token;
 struct TokenPos;
 struct TokenPtr;
 class TokenStream;
 struct Parser;
 class ParseMapPool;
 struct ParseNode;
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -3208,17 +3208,17 @@ ASTSerializer::identifier(ParseNode *pn,
 
     RootedAtom pnAtom(cx, pn->pn_atom);
     return identifier(pnAtom, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
 {
-    RootedFunction func(cx, pn->pn_funbox->fun());
+    RootedFunction func(cx, pn->pn_funbox->function());
 
     bool isGenerator =
 #if JS_HAS_GENERATORS
         pn->pn_funbox->isGenerator();
 #else
         false;
 #endif
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1704,17 +1704,17 @@ JSScript::fullyInitFromEmitter(JSContext
         script->ndefaults = funbox->ndefaults;
     }
 
     RootedFunction fun(cx, NULL);
     if (funbox) {
         JS_ASSERT(!bce->script->noScriptRval);
         script->isGenerator = funbox->isGenerator();
         script->isGeneratorExp = funbox->inGenexpLambda;
-        script->setFunction(funbox->fun());
+        script->setFunction(funbox->function());
     }
 
     /*
      * initScriptCounts updates scriptCountsMap if necessary. The other script
      * maps in JSCompartment are populated lazily.
      */
     if (cx->hasRunOption(JSOPTION_PCCOUNT))
         (void) script->initScriptCounts(cx);