[JAEGER] Statically bind some global functions (bug 562729, r=brendan).
authorDavid Anderson <danderson@mozilla.com>
Thu, 10 Jun 2010 23:43:14 -0700
changeset 52818 56ac5ae1825dbea69d0d351ade5fb378616e49c6
parent 52817 14d1ceb74c70dde2f5ae28fee5da6866e33b5a36
child 52819 5e7f9a34330f5a7894fbcebee8f843990664a462
child 52820 ea1110383eb59fac773c1aee05ec8d5435f7df10
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs562729
milestone1.9.3a5pre
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
[JAEGER] Statically bind some global functions (bug 562729, r=brendan).
js/src/jsemit.cpp
js/src/jsparse.cpp
js/src/jsparse.h
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2101,17 +2101,18 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 return JS_TRUE;
 
             return MakeUpvarForEval(pn, cg);
         }
         return JS_TRUE;
     }
 
     if (dn->pn_dflags & PND_GVAR) {
-        if (js_CodeSpec[dn->pn_op].type() == JOF_GLOBAL) {
+        if (js_CodeSpec[dn->pn_op].type() == JOF_GLOBAL ||
+            dn_kind == JSDefinition::FUNCTION) {
             switch (op) {
               case JSOP_NAME:     op = JSOP_GETGLOBAL; break;
               case JSOP_SETNAME:  op = JSOP_SETGLOBAL; break;
               case JSOP_INCNAME:  op = JSOP_INCGLOBAL; break;
               case JSOP_NAMEINC:  op = JSOP_GLOBALINC; break;
               case JSOP_DECNAME:  op = JSOP_DECGLOBAL; break;
               case JSOP_NAMEDEC:  op = JSOP_GLOBALDEC; break;
               case JSOP_FORNAME:  op = JSOP_FORGLOBAL; break;
@@ -4496,20 +4497,22 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
          * 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 js_EmitTree for function
          * definitions can be scheduled before generating the rest of code.
          */
         if (!cg->inFunction()) {
             JS_ASSERT(!cg->topStmt);
-            CG_SWITCH_TO_PROLOG(cg);
-            op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
-            EMIT_INDEX_OP(op, index);
-            CG_SWITCH_TO_MAIN(cg);
+            if (pn->pn_cookie == FREE_UPVAR_COOKIE) {
+                CG_SWITCH_TO_PROLOG(cg);
+                op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
+                EMIT_INDEX_OP(op, index);
+                CG_SWITCH_TO_MAIN(cg);
+            }
 
             /* Emit NOP for the decompiler. */
             if (!EmitFunctionDefNop(cx, cg, index))
                 return JS_FALSE;
         } else {
 #ifdef DEBUG
             JSLocalKind localKind =
 #endif
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -937,21 +937,42 @@ Compiler::compileScript(JSContext *cx, J
             }
         }
     }
 
     if (globalScope.defs.length()) {
         JS_ASSERT(globalObj->scope()->freeslot == globalScope.globalFreeSlot);
         JS_ASSERT(!cg.compilingForEval());
         for (size_t i = 0; i < globalScope.defs.length(); i++) {
-            JSAtom *atom = globalScope.defs[i];
-            jsid id = ATOM_TO_JSID(atom);
+            GlobalScope::GlobalDef &def = globalScope.defs[i];
+            jsid id = ATOM_TO_JSID(def.atom);
+            Value rval;
+
+            if (def.funbox) {
+                JSFunction *fun = (JSFunction *)def.funbox->object;
+
+                /* Compile-and-go should have chosen scopeChain as the parent. */
+                JS_ASSERT(fun->getParent() == scopeChain);
+
+                /* No named statement getters or setters at global scope. */
+                JS_ASSERT(!JSFUN_GSFLAG2ATTR(fun->flags));
+
+                /*
+                 * No need to check for redeclarations or anything, global
+                 * optimizations only take place if the property is not
+                 * defined.
+                 */
+                rval.setFunObj(*fun);
+            } else {
+                rval.setUndefined();
+            }
+
             JSProperty *prop;
 
-            if (!js_DefineNativeProperty(cx, globalObj, id, Value(UndefinedTag()), PropertyStub,
+            if (!js_DefineNativeProperty(cx, globalObj, id, rval, PropertyStub,
                                          PropertyStub, JSPROP_ENUMERATE | JSPROP_PERMANENT,
                                          0, 0, &prop)) {
                 goto out;
             }
 
             JS_ASSERT(prop);
             JS_ASSERT(((JSScopeProperty*)prop)->slot == globalScope.globalFreeSlot + i);
 
@@ -2575,16 +2596,19 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
         }
 
         funtc->lexdeps.clear();
     }
 
     return true;
 }
 
+static bool
+DefineGlobal(JSParseNode *pn, JSCodeGenerator *cg, JSAtom *atom);
+
 JSParseNode *
 Parser::functionDef(uintN lambda, bool namePermitted)
 {
     JSParseNode *pn, *body, *result;
     TokenKind tt;
     JSAtomListElement *ale;
 #if JS_HAS_DESTRUCTURING
     JSParseNode *item, *list = NULL;
@@ -2989,16 +3013,23 @@ Parser::functionDef(uintN lambda, bool n
     pn->pn_op = op;
     if (pn->pn_body) {
         pn->pn_body->append(body);
         pn->pn_body->pn_pos = body->pn_pos;
     } else {
         pn->pn_body = body;
     }
 
+    if (!outertc->inFunction() && topLevel && funAtom && !lambda &&
+        outertc->compiling()) {
+        JS_ASSERT(pn->pn_cookie == FREE_UPVAR_COOKIE);
+        if (!DefineGlobal(pn, (JSCodeGenerator *)outertc, funAtom))
+            return false;
+    }
+
     pn->pn_blockid = outertc->blockid();
 
     if (!LeaveFunction(pn, &funtc, funAtom, lambda))
         return NULL;
 
     /* If the surrounding function is not strict code, reset the lexer. */
     if (!(outertc->flags & TCF_STRICT_MODE_CODE))
         tokenStream.setStrictMode(false);
@@ -3298,44 +3329,70 @@ DefineGlobal(JSParseNode *pn, JSCodeGene
          * we can. If we can't, don't bother emitting a GVAR op,
          * since it's unlikely that it will optimize either.
          */
         uint32 index;
         if (!sprop->configurable() &&
             SPROP_HAS_VALID_SLOT(sprop, globalObj->scope()) &&
             sprop->hasDefaultGetterOrIsMethod() &&
             sprop->hasDefaultSetter() &&
-            cg->addGlobalUse(atom, sprop->slot, &index) &&
-            index != FREE_UPVAR_COOKIE)
+            pn->pn_type != TOK_FUNCTION)
         {
-            pn->pn_op = JSOP_GETGLOBAL;
-            pn->pn_cookie = index;
-            pn->pn_dflags |= PND_BOUND | PND_GVAR;
+            if (!cg->addGlobalUse(atom, sprop->slot, &index)) {
+                JS_UNLOCK_SCOPE(cg->parser->context, scope);
+                return false;
+            }
+            if (index != FREE_UPVAR_COOKIE) {
+                pn->pn_op = JSOP_GETGLOBAL;
+                pn->pn_cookie = index;
+                pn->pn_dflags |= PND_BOUND | PND_GVAR;
+            }
         }
 
         JS_UNLOCK_SCOPE(cg->parser->context, scope);
         return true;
     }
     JS_UNLOCK_SCOPE(cg->parser->context, scope);
 
-    /* Definitions from |var| are not redefined, like functions. */
-    JS_ASSERT(!cg->globalMap.lookup(atom));
-
-    uint32 slot = globalScope->globalFreeSlot + globalScope->defs.length();
-    if (!globalScope->defs.append(atom))
-        return false;
+    /*
+     * Functions can be redeclared, and the last one takes effect. Check for
+     * this and make sure to rewrite the definition.
+     */
+    uint32 slot = SPROP_INVALID_SLOT;
+    JSFunctionBox *funbox = NULL;
+    if (pn->pn_type == TOK_FUNCTION) {
+        funbox = pn->pn_funbox;
+        JSAtomListElement *ale = cg->globalMap.lookup(atom);
+        if (ale) {
+            uint32 index = ALE_INDEX(ale);
+            slot = cg->globalUses[index].slot;
+            uint32 defSlot = slot - globalScope->globalFreeSlot;
+            JS_ASSERT(globalScope->defs[defSlot].funbox);
+            globalScope->defs[defSlot].funbox = funbox;
+        }
+    }
+
+    if (slot == SPROP_INVALID_SLOT) {
+        GlobalScope::GlobalDef def(atom, funbox);
+        slot = globalScope->globalFreeSlot + globalScope->defs.length();
+        if (!globalScope->defs.append(def))
+            return false;
+    }
 
     uint32 index;
     if (!cg->addGlobalUse(atom, slot, &index))
         return false;
 
     if (index != FREE_UPVAR_COOKIE) {
-        pn->pn_op = JSOP_GETGLOBAL;
         pn->pn_cookie = index;
-        pn->pn_dflags |= PND_BOUND | PND_GVAR;
+        pn->pn_dflags |= PND_GVAR;
+        if (pn->pn_type != TOK_FUNCTION) {
+            pn->pn_op = JSOP_GETGLOBAL;
+            pn->pn_dflags |= PND_BOUND;
+        }
     }
 
     return true;
 }
 
 /*
  * If compile-and-go, and a global object is present, try to bake in either
  * an already available slot or a predicted slot that will be defined after
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -289,19 +289,31 @@ struct JSDefinition;
 
 namespace js {
 
 struct GlobalScope {
     GlobalScope(JSContext *cx, JSObject *globalObj, JSCodeGenerator *cg)
       : globalObj(globalObj), cg(cg), defs(ContextAllocPolicy(cx))
     { }
 
+    struct GlobalDef {
+        JSAtom *atom;
+        JSFunctionBox *funbox;
+
+        GlobalDef() { }
+        GlobalDef(JSAtom *atom) : atom(atom), funbox(NULL)
+        { }
+        GlobalDef(JSAtom *atom, JSFunctionBox *box) :
+          atom(atom), funbox(box)
+        { }
+    };
+
     JSObject *globalObj;
     JSCodeGenerator *cg;
-    Vector<JSAtom *, 16, ContextAllocPolicy> defs;
+    Vector<GlobalDef, 16, ContextAllocPolicy> defs;
     uint32 globalFreeSlot;
 };
 
 } /* namespace js */
 
 struct JSParseNode {
     uint32              pn_type:16,     /* TOK_* type, see jsscan.h */
                         pn_op:8,        /* see JSOp enum and jsopcode.tbl */