Bug 737552 - Remove 'funargs' (r=jimb)

--HG--
extra : rebase_source : 768b5b36503747a7ab95cc745439be4c36e64168
This commit is contained in:
Luke Wagner 2012-03-20 11:18:37 -07:00
parent 5db503d729
commit 2679471c45
6 changed files with 116 additions and 456 deletions

View File

@ -161,126 +161,127 @@ struct StmtInfo {
#define SET_STATEMENT_TOP(stmt, top) \
((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
#define TCF_COMPILING 0x01 /* TreeContext is BytecodeEmitter */
#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */
#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */
#define TCF_RETURN_VOID 0x08 /* function has 'return;' */
#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */
#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */
#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */
#define TCF_FUN_LOCAL_ARGUMENTS 0x80 /* function may contain a local named arguments */
#define TCF_FUN_USES_ARGUMENTS 0x100 /* function uses arguments except as a
parameter name */
#define TCF_FUN_HEAVYWEIGHT 0x200 /* function needs Call object per call */
#define TCF_FUN_IS_GENERATOR 0x400 /* parsed yield statement in function */
#define TCF_FUN_USES_OWN_NAME 0x800 /* named function expression that uses its
own name */
#define TCF_HAS_FUNCTION_STMT 0x1000 /* block contains a function statement */
#define TCF_GENEXP_LAMBDA 0x2000 /* flag lambda from generator expression */
#define TCF_COMPILE_N_GO 0x4000 /* compile-and-go mode of script, can
optimize name references based on scope
chain */
#define TCF_NO_SCRIPT_RVAL 0x8000 /* API caller does not want result value
from global script */
/*
* Set when parsing a declaration-like destructuring pattern. This
* flag causes PrimaryExpr to create PN_NAME parse nodes for variable
* references which are not hooked into any definition's use chain,
* added to any tree context's AtomList, etc. etc. CheckDestructuring
* will do that work later.
*
* The comments atop CheckDestructuring explain the distinction
* between assignment-like and declaration-like destructuring
* patterns, and why they need to be treated differently.
*/
#define TCF_DECL_DESTRUCTURING 0x10000
JS_ENUM_HEADER(TreeContextFlags, uint32_t)
{
/* TreeContext is BytecodeEmitter */
TCF_COMPILING = 0x1,
/*
* This function/global/eval code body contained a Use Strict Directive. Treat
* certain strict warnings as errors, and forbid the use of 'with'. See also
* TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and JSREPORT_STRICT_ERROR.
*/
#define TCF_STRICT_MODE_CODE 0x20000
/* parsing inside function body */
TCF_IN_FUNCTION = 0x2,
/* bits 0x40000 and 0x80000 are unused */
/* function has 'return expr;' */
TCF_RETURN_EXPR = 0x4,
/*
* "Module pattern", i.e., a lambda that is immediately applied and the whole
* of an expression statement.
*/
#define TCF_FUN_MODULE_PATTERN 0x200000
/* function has 'return;' */
TCF_RETURN_VOID = 0x8,
/*
* Flag to prevent a non-escaping function from being optimized into a null
* closure (i.e., a closure that needs only its global object for free variable
* resolution), because this function contains a closure that needs one or more
* scope objects surrounding it (i.e., a Call object for an outer heavyweight
* function). See bug 560234.
*/
#define TCF_FUN_ENTRAINS_SCOPES 0x400000
/* parsing init expr of for; exclude 'in' */
TCF_IN_FOR_INIT = 0x10,
/* The function calls 'eval'. */
#define TCF_FUN_CALLS_EVAL 0x800000
/* function has parameter named arguments */
TCF_FUN_PARAM_ARGUMENTS = 0x20,
/* The function mutates a positional (non-destructuring) parameter. */
#define TCF_FUN_MUTATES_PARAMETER 0x1000000
/* function may contain a local named arguments */
TCF_FUN_LOCAL_ARGUMENTS = 0x40,
/*
* Compiling an eval() script.
*/
#define TCF_COMPILE_FOR_EVAL 0x2000000
/* function uses arguments except as a parameter name */
TCF_FUN_USES_ARGUMENTS = 0x80,
/*
* The function or a function that encloses it may define new local names
* at runtime through means other than calling eval.
*/
#define TCF_FUN_MIGHT_ALIAS_LOCALS 0x4000000
/* function needs Call object per call */
TCF_FUN_HEAVYWEIGHT = 0x100,
/*
* The script contains singleton initialiser JSOP_OBJECT.
*/
#define TCF_HAS_SINGLETONS 0x8000000
/* parsed yield statement in function */
TCF_FUN_IS_GENERATOR = 0x200,
/*
* Some enclosing scope is a with-statement or E4X filter-expression.
*/
#define TCF_IN_WITH 0x10000000
/* named function expression that uses its own name */
TCF_FUN_USES_OWN_NAME = 0x400,
/*
* This function does something that can extend the set of bindings in its
* call objects --- it does a direct eval in non-strict code, or includes a
* function statement (as opposed to a function definition).
*
* This flag is *not* inherited by enclosed or enclosing functions; it
* applies only to the function in whose flags it appears.
*/
#define TCF_FUN_EXTENSIBLE_SCOPE 0x20000000
/* block contains a function statement */
TCF_HAS_FUNCTION_STMT = 0x800,
/*
* The caller is JS_Compile*Script*.
*/
#define TCF_NEED_SCRIPT_GLOBAL 0x40000000
/* flag lambda from generator expression */
TCF_GENEXP_LAMBDA = 0x1000,
/*
* Flags to check for return; vs. return expr; in a function.
*/
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID)
/* script can optimize name references based on scope chain */
TCF_COMPILE_N_GO = 0x2000,
/* API caller does not want result value from global script */
TCF_NO_SCRIPT_RVAL = 0x4000,
/*
* Set when parsing a declaration-like destructuring pattern. This flag
* causes PrimaryExpr to create PN_NAME parse nodes for variable references
* which are not hooked into any definition's use chain, added to any tree
* context's AtomList, etc. etc. CheckDestructuring will do that work
* later.
*
* The comments atop CheckDestructuring explain the distinction between
* assignment-like and declaration-like destructuring patterns, and why
* they need to be treated differently.
*/
TCF_DECL_DESTRUCTURING = 0x8000,
/*
* This function/global/eval code body contained a Use Strict Directive.
* Treat certain strict warnings as errors, and forbid the use of 'with'.
* See also TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and
* JSREPORT_STRICT_ERROR.
*/
TCF_STRICT_MODE_CODE = 0x10000,
/* The function calls 'eval'. */
TCF_FUN_CALLS_EVAL = 0x20000,
/* The function mutates a positional (non-destructuring) parameter. */
TCF_FUN_MUTATES_PARAMETER = 0x40000,
/* Compiling an eval() script. */
TCF_COMPILE_FOR_EVAL = 0x100000,
/*
* The function or a function that encloses it may define new local names
* at runtime through means other than calling eval.
*/
TCF_FUN_MIGHT_ALIAS_LOCALS = 0x200000,
/* The script contains singleton initialiser JSOP_OBJECT. */
TCF_HAS_SINGLETONS = 0x400000,
/* Some enclosing scope is a with-statement or E4X filter-expression. */
TCF_IN_WITH = 0x800000,
/*
* This function does something that can extend the set of bindings in its
* call objects --- it does a direct eval in non-strict code, or includes a
* function statement (as opposed to a function definition).
*
* This flag is *not* inherited by enclosed or enclosing functions; it
* applies only to the function in whose flags it appears.
*/
TCF_FUN_EXTENSIBLE_SCOPE = 0x1000000,
/* The caller is JS_Compile*Script*. */
TCF_NEED_SCRIPT_GLOBAL = 0x2000000
} JS_ENUM_FOOTER(TreeContextFlags);
/* Flags to check for return; vs. return expr; in a function. */
static const uint32_t TCF_RETURN_FLAGS = TCF_RETURN_EXPR | TCF_RETURN_VOID;
/*
* Sticky deoptimization flags to propagate from FunctionBody.
*/
#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \
TCF_FUN_USES_ARGUMENTS | \
TCF_FUN_PARAM_ARGUMENTS | \
TCF_FUN_LOCAL_ARGUMENTS | \
TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_IS_GENERATOR | \
TCF_FUN_USES_OWN_NAME | \
TCF_FUN_CALLS_EVAL | \
TCF_FUN_MIGHT_ALIAS_LOCALS | \
TCF_FUN_MUTATES_PARAMETER | \
TCF_STRICT_MODE_CODE | \
TCF_FUN_EXTENSIBLE_SCOPE)
static const uint32_t TCF_FUN_FLAGS = TCF_FUN_USES_ARGUMENTS |
TCF_FUN_PARAM_ARGUMENTS |
TCF_FUN_LOCAL_ARGUMENTS |
TCF_FUN_HEAVYWEIGHT |
TCF_FUN_IS_GENERATOR |
TCF_FUN_USES_OWN_NAME |
TCF_FUN_CALLS_EVAL |
TCF_FUN_MIGHT_ALIAS_LOCALS |
TCF_FUN_MUTATES_PARAMETER |
TCF_STRICT_MODE_CODE |
TCF_FUN_EXTENSIBLE_SCOPE;
struct BytecodeEmitter;
@ -453,8 +454,6 @@ struct TreeContext { /* tree context for semantic checks */
JS_ASSERT(node->pn_atom == parser->context->runtime->atomState.argumentsAtom);
countArgumentsUse(node);
flags |= TCF_FUN_USES_ARGUMENTS;
if (funbox)
funbox->node->pn_dflags |= PND_FUNARG;
}
/*

View File

@ -765,15 +765,14 @@ struct ParseNode {
#define PND_GVAR 0x40 /* gvar binding, can't close over
because it could be deleted */
#define PND_PLACEHOLDER 0x80 /* placeholder definition for lexdep */
#define PND_FUNARG 0x100 /* downward or upward funarg usage */
#define PND_BOUND 0x200 /* bound to a stack or global slot */
#define PND_DEOPTIMIZED 0x400 /* former pn_used name node, pn_lexdef
#define PND_BOUND 0x100 /* bound to a stack or global slot */
#define PND_DEOPTIMIZED 0x200 /* former pn_used name node, pn_lexdef
still valid, but this use no longer
optimizable via an upvar opcode */
#define PND_CLOSED 0x800 /* variable is closed over */
#define PND_CLOSED 0x400 /* variable is closed over */
/* Flags to propagate from uses to definition. */
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_FUNARG | PND_CLOSED)
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
/* PN_LIST pn_xflags bits. */
#define PNX_STRCAT 0x01 /* PNK_ADD list has string term */
@ -816,7 +815,6 @@ struct ParseNode {
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
bool isAssigned() const { return test(PND_ASSIGNED); }
bool isFunArg() const { return test(PND_FUNARG); }
bool isClosed() const { return test(PND_CLOSED); }
/*
@ -833,9 +831,6 @@ struct ParseNode {
*/
bool isTopLevel() const { return test(PND_TOPLEVEL); }
/* Defined below, see after struct Definition. */
void setFunArg();
void become(ParseNode *pn2);
void clear();
@ -1431,26 +1426,6 @@ void DumpParseTree(ParseNode *pn, int indent = 0);
* When the compiler unwinds from the outermost tc, tc->lexdeps contains the
* definition nodes with use chains for all free variables. These are either
* global variables or reference errors.
*
* We analyze whether a binding is initialized, whether the bound names is ever
* assigned apart from its initializer, and if the bound name definition or use
* is in a direct child of a block. These PND_* flags allow a subset dominance
* computation telling whether an initialized var dominates its uses. An inner
* function using only such outer vars (and formal parameters) can be optimized
* into a flat closure. See JSOP_{GET,CALL}DSLOT.
*
* Another important subset dominance relation: ... { var x = ...; ... x ... }
* where x is not assigned after initialization and not used outside the block.
* This style is common in the absence of 'let'. Even though the var x is not
* at top level, we can tell its initialization dominates all uses cheaply,
* because the above one-pass algorithm sees the definition before any uses,
* and because all uses are contained in the same block as the definition.
*
* We also analyze function uses to flag upward/downward funargs. If a lambda
* post-dominates each of its upvars' sole, inevitable (i.e. not hidden behind
* conditions or within loops or the like) initialization or assignment; then
* we can optimize the lambda as a flat closure (after Chez Scheme's display
* closures).
*/
#define dn_uses pn_link
@ -1528,7 +1503,7 @@ ParseNode::test(unsigned flag) const
{
JS_ASSERT(pn_defn || pn_arity == PN_FUNC || pn_arity == PN_NAME);
#ifdef DEBUG
if ((flag & (PND_ASSIGNED | PND_FUNARG)) && pn_defn && !(pn_dflags & flag)) {
if ((flag & PND_ASSIGNED) && pn_defn && !(pn_dflags & flag)) {
for (ParseNode *pn = ((Definition *) this)->dn_uses; pn; pn = pn->pn_link) {
JS_ASSERT(!pn->pn_defn);
JS_ASSERT(!(pn->pn_dflags & flag));
@ -1538,25 +1513,6 @@ ParseNode::test(unsigned flag) const
return !!(pn_dflags & flag);
}
inline void
ParseNode::setFunArg()
{
/*
* pn_defn NAND pn_used must be true, per this chart:
*
* pn_defn pn_used
* 0 0 anonymous function used implicitly, e.g. by
* hidden yield in a genexp
* 0 1 a use of a definition or placeholder
* 1 0 a definition or placeholder
* 1 1 error: this case must not be possible
*/
JS_ASSERT(!(pn_defn & pn_used));
if (pn_used)
pn_lexdef->pn_dflags |= PND_FUNARG;
pn_dflags |= PND_FUNARG;
}
inline void
LinkUseToDef(ParseNode *pn, Definition *dn, TreeContext *tc)
{

View File

@ -1070,32 +1070,11 @@ LeaveFunction(ParseNode *fn, TreeContext *funtc, PropertyName *funName = NULL,
dn->pn_cookie.set(funtc->staticLevel, UpvarCookie::CALLEE_SLOT);
dn->pn_dflags |= PND_BOUND;
/*
* If this named function expression uses its own name other
* than to call itself, flag this function specially.
*/
if (dn->isFunArg())
funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
funbox->tcflags |= TCF_FUN_USES_OWN_NAME;
foundCallee = 1;
continue;
}
if (!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
dn->isAssigned()) {
/*
* Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
* any use of dn in funtc assigns. See NoteLValue for the easy
* backward-reference case; this is the hard forward-reference
* case where we pay a higher price.
*/
for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
break;
}
}
}
Definition *outer_dn = tc->decls.lookupFirst(atom);
/*
@ -1381,23 +1360,13 @@ Parser::functionDef(PropertyName *funName, FunctionType type, FunctionSyntaxKind
return NULL;
pn->pn_body = NULL;
pn->pn_cookie.makeFree();
/*
* If this is a function expression, mark this function as escaping (as a
* "funarg") unless it is immediately applied (we clear PND_FUNARG if so --
* see memberExpr).
*
* Treat function sub-statements (those not at body level of a function or
* program) as escaping funargs, since we can't statically analyze their
* definitions and uses.
*/
bool bodyLevel = tc->atBodyLevel();
pn->pn_dflags = (kind == Expression || !bodyLevel) ? PND_FUNARG : 0;
pn->pn_dflags = 0;
/*
* Record names for function statements in tc->decls so we know when to
* avoid optimizing variable references that might name a function.
*/
bool bodyLevel = tc->atBodyLevel();
if (kind == Statement) {
if (Definition *dn = tc->decls.lookupFirst(funName)) {
Definition::Kind dn_kind = dn->kind();
@ -2411,9 +2380,6 @@ NoteLValue(JSContext *cx, ParseNode *pn, TreeContext *tc, unsigned dflag = PND_A
}
dn->pn_dflags |= dflag;
if (dn->pn_cookie.isFree() || dn->frameLevel() < tc->staticLevel)
tc->flags |= TCF_FUN_SETS_OUTER_NAME;
}
pn->pn_dflags |= dflag;
@ -3923,18 +3889,7 @@ Parser::expressionStatement()
pn->pn_pos = pn2->pn_pos;
pn->pn_kid = pn2;
switch (pn2->getKind()) {
case PNK_LP:
/*
* Flag lambdas immediately applied as statements as instances of
* the JS "module pattern". See CheckForImmediatelyAppliedLambda.
*/
if (pn2->pn_head->isKind(PNK_FUNCTION) &&
!pn2->pn_head->pn_funbox->node->isFunArg()) {
pn2->pn_head->pn_funbox->tcflags |= TCF_FUN_MODULE_PATTERN;
}
break;
case PNK_ASSIGN:
if (pn2->getKind() == PNK_ASSIGN) {
/*
* Keep track of all apparent methods created by assignments such
* as this.foo = function (...) {...} in a function that could end
@ -3951,8 +3906,6 @@ Parser::expressionStatement()
pn2->pn_right->pn_link = tc->funbox->methods;
tc->funbox->methods = pn2->pn_right;
}
break;
default:;
}
/* Check termination of this primitive statement. */
@ -5605,7 +5558,7 @@ Parser::generatorExpr(ParseNode *kid)
return NULL;
genfn->setOp(JSOP_LAMBDA);
JS_ASSERT(!genfn->pn_body);
genfn->pn_dflags = PND_FUNARG;
genfn->pn_dflags = 0;
{
TreeContext *outertc = tc;
@ -5714,21 +5667,6 @@ Parser::argumentList(ParseNode *listNode)
return JS_TRUE;
}
/* Check for an immediately-applied (new'ed) lambda and clear PND_FUNARG. */
static ParseNode *
CheckForImmediatelyAppliedLambda(ParseNode *pn)
{
if (pn->isKind(PNK_FUNCTION)) {
JS_ASSERT(pn->isArity(PN_FUNC));
FunctionBox *funbox = pn->pn_funbox;
JS_ASSERT((funbox->function())->flags & JSFUN_LAMBDA);
if (!(funbox->tcflags & (TCF_FUN_USES_ARGUMENTS | TCF_FUN_USES_OWN_NAME)))
pn->pn_dflags &= ~PND_FUNARG;
}
return pn;
}
ParseNode *
Parser::memberExpr(JSBool allowCallSyntax)
{
@ -5745,7 +5683,6 @@ Parser::memberExpr(JSBool allowCallSyntax)
ParseNode *ctorExpr = memberExpr(JS_FALSE);
if (!ctorExpr)
return NULL;
ctorExpr = CheckForImmediatelyAppliedLambda(ctorExpr);
lhs->setOp(JSOP_NEW);
lhs->initList(ctorExpr);
lhs->pn_pos.begin = ctorExpr->pn_pos.begin;
@ -5919,7 +5856,6 @@ Parser::memberExpr(JSBool allowCallSyntax)
return NULL;
nextMember->setOp(JSOP_CALL);
lhs = CheckForImmediatelyAppliedLambda(lhs);
if (lhs->isOp(JSOP_NAME)) {
if (lhs->pn_atom == context->runtime->atomState.evalAtom) {
/* Select JSOP_EVAL and flag tc as heavyweight. */
@ -6728,31 +6664,12 @@ Parser::identifierName(bool afterDoubleDot)
dn = MakePlaceholder(node, tc);
if (!dn || !tc->lexdeps->add(p, name, dn))
return NULL;
/*
* In case this is a forward reference to a function,
* we pessimistically set PND_FUNARG if the next token
* is not a left parenthesis.
*
* If the definition eventually parsed into dn is not a
* function, this flag won't hurt, and if we do parse a
* function with pn's name, then the PND_FUNARG flag is
* necessary for safe context->display-based optimiza-
* tion of the closure's static link.
*/
if (tokenStream.peekToken() != TOK_LP)
dn->pn_dflags |= PND_FUNARG;
}
}
JS_ASSERT(dn->isDefn());
LinkUseToDef(node, dn, tc);
/* Here we handle the backward function reference case. */
if (tokenStream.peekToken() != TOK_LP)
dn->pn_dflags |= PND_FUNARG;
node->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
if (stmt && stmt->type == STMT_WITH)
node->pn_dflags |= PND_DEOPTIMIZED;
}

View File

@ -126,211 +126,6 @@ CleanFunctionList(ParseNodeAllocator *allocator, FunctionBox **funboxHead)
}
}
/*
* Mark as funargs any functions that reach up to one or more upvars across an
* already-known funarg. The parser will flag the o_m lambda as a funarg in:
*
* function f(o, p) {
* o.m = function o_m(a) {
* function g() { return p; }
* function h() { return a; }
* return g() + h();
* }
* }
*
* but without this extra marking phase, function g will not be marked as a
* funarg since it is called from within its parent scope. But g reaches up to
* f's parameter p, so if o_m escapes f's activation scope, g does too and
* cannot assume that p's stack slot is still alive. In contast function h
* neither escapes nor uses an upvar "above" o_m's level.
*
* If function g itself contained lambdas that contained non-lambdas that reach
* up above its level, then those non-lambdas would have to be marked too. This
* process is potentially exponential in the number of functions, but generally
* not so complex. But it can't be done during a single recursive traversal of
* the funbox tree, so we must use a work queue.
*
* Return the minimal "skipmin" for funbox and its siblings. This is the delta
* between the static level of the bodies of funbox and its peers (which must
* be funbox->level + 1), and the static level of the nearest upvar among all
* the upvars contained by funbox and its peers. If there are no upvars, return
* FREE_STATIC_LEVEL. Thus this function never returns 0.
*/
static unsigned
FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue)
{
unsigned allskipmin = UpvarCookie::FREE_LEVEL;
do {
ParseNode *fn = funbox->node;
JS_ASSERT(fn->isArity(PN_FUNC));
int fnlevel = level;
/*
* An eval can leak funbox, functions along its ancestor line, and its
* immediate kids. Since FindFunArgs uses DFS and the parser propagates
* TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
* already been marked as funargs by this point. Therefore we have to
* flag only funbox->node and funbox->kids' nodes here.
*
* Generators need to be treated in the same way. Even if the value
* of a generator function doesn't escape, anything defined or referred
* to inside the generator can escape through a call to the generator.
* We could imagine doing static analysis to track the calls and see
* if any iterators or values returned by iterators escape, but that
* would be hard, so instead we just assume everything might escape.
*/
if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) {
fn->setFunArg();
for (FunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
kid->node->setFunArg();
}
/*
* Compute in skipmin the least distance from fun's static level up to
* an upvar, whether used directly by fun, or indirectly by a function
* nested in fun.
*/
unsigned skipmin = UpvarCookie::FREE_LEVEL;
ParseNode *pn = fn->pn_body;
if (pn->isKind(PNK_UPVARS)) {
AtomDefnMapPtr &upvars = pn->pn_names;
JS_ASSERT(upvars->count() != 0);
for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
Definition *defn = r.front().value();
Definition *lexdep = defn->resolve();
if (!lexdep->isFreeVar()) {
unsigned upvarLevel = lexdep->frameLevel();
if (int(upvarLevel) <= fnlevel)
fn->setFunArg();
unsigned skip = (funbox->level + 1) - upvarLevel;
if (skip < skipmin)
skipmin = skip;
}
}
}
/*
* If this function escapes, whether directly (the parser detects such
* escapes) or indirectly (because this non-escaping function uses an
* upvar that reaches across an outer function boundary where the outer
* function escapes), enqueue it for further analysis, and bump fnlevel
* to trap any non-escaping children.
*/
if (fn->isFunArg()) {
queue->push(funbox);
fnlevel = int(funbox->level);
}
/*
* Now process the current function's children, and recalibrate their
* cumulative skipmin to be relative to the current static level.
*/
if (funbox->kids) {
unsigned kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
JS_ASSERT(kidskipmin != 0);
if (kidskipmin != UpvarCookie::FREE_LEVEL) {
--kidskipmin;
if (kidskipmin != 0 && kidskipmin < skipmin)
skipmin = kidskipmin;
}
}
/*
* Finally, after we've traversed all of the current function's kids,
* minimize allskipmin against our accumulated skipmin. Minimize across
* funbox and all of its siblings, to compute our return value.
*/
if (skipmin != UpvarCookie::FREE_LEVEL) {
if (skipmin < allskipmin)
allskipmin = skipmin;
}
} while ((funbox = funbox->siblings) != NULL);
return allskipmin;
}
static bool
MarkFunArgs(JSContext *cx, FunctionBox *funbox, uint32_t functionCount)
{
FunctionBoxQueue queue;
if (!queue.init(functionCount)) {
js_ReportOutOfMemory(cx);
return false;
}
FindFunArgs(funbox, -1, &queue);
while ((funbox = queue.pull()) != NULL) {
ParseNode *fn = funbox->node;
JS_ASSERT(fn->isFunArg());
ParseNode *pn = fn->pn_body;
if (pn->isKind(PNK_UPVARS)) {
AtomDefnMapPtr upvars = pn->pn_names;
JS_ASSERT(!upvars->empty());
for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) {
Definition *defn = r.front().value();
Definition *lexdep = defn->resolve();
if (!lexdep->isFreeVar() &&
!lexdep->isFunArg() &&
(lexdep->kind() == Definition::FUNCTION ||
lexdep->isOp(JSOP_CALLEE))) {
/*
* Mark this formerly-Algol-like function as an escaping
* function (i.e., as a funarg), because it is used from
* another funarg.
*
* Progress is guaranteed because we set the funarg flag
* here, which suppresses revisiting this function (thanks
* to the !lexdep->isFunArg() test just above).
*/
lexdep->setFunArg();
FunctionBox *afunbox;
if (lexdep->isOp(JSOP_CALLEE)) {
/*
* A named function expression will not appear to be a
* funarg if it is immediately applied. However, if its
* name is used in an escaping function nested within
* it, then it must become flagged as a funarg again.
* See bug 545980.
*/
afunbox = funbox;
unsigned calleeLevel = lexdep->pn_cookie.level();
unsigned staticLevel = afunbox->level + 1U;
while (staticLevel != calleeLevel) {
afunbox = afunbox->parent;
--staticLevel;
}
JS_ASSERT(afunbox->level + 1U == calleeLevel);
afunbox->node->setFunArg();
} else {
afunbox = lexdep->pn_funbox;
}
queue.push(afunbox);
/*
* Walk over nested functions again, now that we have
* changed the level across which it is unsafe to access
* upvars using the runtime dynamic link (frame chain).
*/
if (afunbox->kids)
FindFunArgs(afunbox->kids, afunbox->level, &queue);
}
}
}
}
return true;
}
static void
FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
{
@ -347,7 +142,6 @@ FlagHeavyweights(Definition *dn, FunctionBox *funbox, uint32_t *tcflags)
funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
break;
}
funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
}
if (!funbox && (*tcflags & TCF_IN_FUNCTION))
@ -473,8 +267,6 @@ frontend::AnalyzeFunctions(TreeContext *tc)
CleanFunctionList(&tc->parser->allocator, &tc->functionList);
if (!tc->functionList)
return true;
if (!MarkFunArgs(tc->parser->context, tc->functionList, tc->parser->functionCount))
return false;
if (!MarkExtensibleScopeDescendants(tc->parser->context, tc->functionList, false))
return false;
bool isDirectEval = !!tc->parser->callerFrame;

View File

@ -48,8 +48,8 @@ namespace frontend {
/*
* For each function in the compilation unit given by tc, decide whether the
* function is a full closure, a null closure, or a flat closure, and set the
* heavyweight bit if necessary.
* function is a full closure or a null closure and set JSFunction flags
* accordingly.
*/
bool
AnalyzeFunctions(TreeContext *tc);

View File

@ -115,13 +115,9 @@ JSDOUBLE_IS_INT32(double d, int32_t* pi)
#if defined(_MSC_VER)
# define JS_ENUM_HEADER(id, type) enum id : type
# define JS_ENUM_MEMBER(id, type, value) id = (type)value,
# define JS_LAST_ENUM_MEMBER(id, type, value) id = (type)value
# define JS_ENUM_FOOTER(id)
#else
# define JS_ENUM_HEADER(id, type) enum id
# define JS_ENUM_MEMBER(id, type, value) id = (type)value,
# define JS_LAST_ENUM_MEMBER(id, type, value) id = (type)value
# define JS_ENUM_FOOTER(id) __attribute__((packed))
#endif