author | David 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 id | unknown |
push user | unknown |
push date | unknown |
reviewers | brendan |
bugs | 562729 |
milestone | 1.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
|
js/src/jsemit.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsparse.cpp | file | annotate | diff | comparison | revisions | |
js/src/jsparse.h | file | annotate | diff | comparison | revisions |
--- 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 */