mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 737552 - Remove 'funargs' (r=jimb)
--HG-- extra : rebase_source : 768b5b36503747a7ab95cc745439be4c36e64168
This commit is contained in:
parent
5db503d729
commit
2679471c45
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user