Get rid of JSOP_{GET,CALL}UPVAR and simplify code greatly (592202, r=jorendorff).

This commit is contained in:
Brendan Eich 2010-12-29 23:46:50 -08:00
Родитель cd3e22e469
Коммит 3a7ffc111f
23 изменённых файлов: 160 добавлений и 398 удалений

Просмотреть файл

@ -896,7 +896,7 @@ private:
#ifdef DEBUG
# define FUNCTION_KIND_METER_LIST(_) \
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
_(display), _(flat), _(setupvar), _(badfunarg), \
_(flat), _(badfunarg), \
_(joinedsetmethod), _(joinedinitmethod), \
_(joinedreplace), _(joinedsort), _(joinedmodulepat), \
_(mreadbarrier), _(mwritebarrier), _(mwslotbarrier), \

Просмотреть файл

@ -1974,99 +1974,6 @@ EmitLeaveBlock(JSContext *cx, JSCodeGenerator *cg, JSOp op,
return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
}
/*
* When eval is called from a function, the eval code or function code it
* compiles may reference upvars that live in the eval-calling function. The
* eval-invoked compiler does not have explicit definitions for these upvars
* and we do not attempt to create them a-priori (by inspecting the function's
* args and vars) -- we could, but we'd take an avoidable penalty for each
* function local not referenced by any upvar. Instead, we map such upvars
* lazily, growing upvarMap.vector by powers of two.
*
* This function knows that it is called with pn pointing to a PN_NAME-arity
* node, and cg->parser->callerFrame having a non-null fun member, and the
* static level of cg at least one greater than the eval-calling function's
* static level.
*/
static bool
MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
{
JSContext *cx = cg->parser->context;
JSFunction *fun = cg->parser->callerFrame->fun();
uintN upvarLevel = fun->u.i.script->staticLevel;
JSFunctionBox *funbox = cg->funbox;
if (funbox) {
/*
* Treat top-level function definitions as escaping (i.e., as funargs),
* required since we compile each such top level function or statement
* and throw away the AST, so we can't yet see all funarg uses of this
* function being compiled (cg->funbox->object). See bug 493177.
*/
if (funbox->level == fun->u.i.script->staticLevel + 1U &&
!(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) {
JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
((JSFunction *) funbox->object)->atom);
return true;
}
while (funbox->level >= upvarLevel) {
if (funbox->node->pn_dflags & PND_FUNARG)
return true;
funbox = funbox->parent;
if (!funbox)
break;
}
}
JSAtom *atom = pn->pn_atom;
uintN index;
BindingKind kind = fun->script()->bindings.lookup(cx, atom, &index);
if (kind == NONE)
return true;
JS_ASSERT(cg->staticLevel > upvarLevel);
if (cg->staticLevel >= UpvarCookie::UPVAR_LEVEL_LIMIT)
return true;
JSAtomListElement *ale = cg->upvarList.lookup(atom);
if (!ale) {
if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
return false;
ale = cg->upvarList.add(cg->parser, atom);
if (!ale)
return false;
JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
UpvarCookie *vector = cg->upvarMap.vector;
uint32 length = cg->upvarMap.length;
JS_ASSERT(ALE_INDEX(ale) <= length);
if (ALE_INDEX(ale) == length) {
length = 2 * JS_MAX(2, length);
vector = reinterpret_cast<UpvarCookie *>(cx->realloc(vector, length * sizeof *vector));
if (!vector)
return false;
cg->upvarMap.vector = vector;
cg->upvarMap.length = length;
}
if (kind != ARGUMENT)
index += fun->nargs;
JS_ASSERT(index < JS_BIT(16));
uintN skip = cg->staticLevel - upvarLevel;
vector[ALE_INDEX(ale)].set(skip, index);
}
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie.set(cg->staticLevel, uint16(ALE_INDEX(ale)));
pn->pn_dflags |= PND_BOUND;
return true;
}
/*
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
* undeclared globals. Return true if a conversion was made.
@ -2232,63 +2139,11 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_TRUE;
}
if (!caller->isFunctionFrame())
return JS_TRUE;
/*
* Make sure the variable object used by the compiler to initialize
* parent links matches the caller's varobj. Compile-n-go compiler-
* created function objects have the top-level cg's scopeChain set
* as their parent by Parser::newFunction.
* Out of tricks, so we must rely on PICs to optimize named
* accesses from direct eval called from function code.
*/
JSObject *scopeobj = cg->inFunction()
? FUN_OBJECT(cg->fun())->getParent()
: cg->scopeChain();
if (scopeobj != cg->parser->callerVarObj)
return JS_TRUE;
/*
* We are compiling eval or debug script inside a function frame
* and the scope chain matches the function's variable object.
* Optimize access to function's arguments and variable and the
* arguments object.
*/
if (op != JSOP_NAME)
return JS_TRUE;
/*
* It is illegal to add upvars to heavyweight functions (and
* unnecessary, since the optimization avoids creating call
* objects). Take the following code as an eval string:
*
* (function () {
* $(init);
* function init() {
* $();
* }
* })();
*
* The first instance of "$" cannot be an upvar, because the
* outermost lambda is on "init"'s scope chain, which escapes.
*
* A similar restriction exists for upvars which do not cross
* eval (see the end of BindNameToSlot and bug 616762).
*/
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
JSFunction *fun = cg->parser->callerFrame->fun();
JS_ASSERT(cg->staticLevel >= fun->u.i.script->staticLevel);
unsigned skip = cg->staticLevel - fun->u.i.script->staticLevel;
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
return MakeUpvarForEval(pn, cg);
return JS_TRUE;
}
/* Optimize accesses to undeclared globals. */
@ -2348,50 +2203,6 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
uint16 level = cookie.level();
JS_ASSERT(cg->staticLevel >= level);
/*
* A JSDefinition witnessed as a declaration by the parser cannot be an
* upvar, unless it is the degenerate kind of upvar selected above (in the
* code before the PND_GVAR test) for the special case of compile-and-go
* code generated from eval called from a function, where the eval code
* uses local vars defined in the function. We detect this upvar-for-eval
* case by checking dn's op.
*/
if (PN_OP(dn) == JSOP_GETUPVAR) {
JS_ASSERT(cg->staticLevel >= level);
if (op != JSOP_NAME)
return JS_TRUE;
#ifdef DEBUG
JSStackFrame *caller = cg->parser->callerFrame;
#endif
JS_ASSERT(caller->isScriptFrame());
JSTreeContext *tc = cg;
while (tc->staticLevel != level)
tc = tc->parent;
JSCodeGenerator *evalcg = tc->asCodeGenerator();
JS_ASSERT(evalcg->compileAndGo());
JS_ASSERT(caller->isFunctionFrame());
JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
/*
* Don't generate upvars on the left side of a for loop. See
* bug 470758 and bug 520513.
*/
if (evalcg->flags & TCF_IN_FOR_INIT)
return JS_TRUE;
if (cg->staticLevel == level) {
pn->pn_op = JSOP_GETUPVAR;
pn->pn_cookie = cookie;
pn->pn_dflags |= PND_BOUND;
return JS_TRUE;
}
return MakeUpvarForEval(pn, cg);
}
const uintN skip = cg->staticLevel - level;
if (skip != 0) {
JS_ASSERT(cg->inFunction());
@ -2410,29 +2221,8 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
return JS_TRUE;
if (cg->fun()->isFlatClosure()) {
op = JSOP_GETFCSLOT;
} else {
/*
* The function we're compiling may not be heavyweight, but if it
* escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR.
* Parser::analyzeFunctions has arranged for this function's
* enclosing functions to be heavyweight, so we can safely stick
* with JSOP_NAME/JSOP_CALLNAME.
*/
if (cg->funbox->node->pn_dflags & PND_FUNARG)
return JS_TRUE;
/*
* Generator functions may be resumed from any call stack, which
* defeats the display optimization to static link searching used
* by JSOP_{GET,CALL}UPVAR.
*/
if (cg->skipSpansGenerator(skip))
return JS_TRUE;
op = JSOP_GETUPVAR;
}
if (!cg->fun()->isFlatClosure())
return JS_TRUE;
ale = cg->upvarList.lookup(atom);
if (ale) {
@ -2473,7 +2263,7 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
vector[index].set(skip, slot);
}
pn->pn_op = op;
pn->pn_op = JSOP_GETFCSLOT;
JS_ASSERT((index & JS_BITMASK(16)) == index);
pn->pn_cookie.set(0, index);
pn->pn_dflags |= PND_BOUND;
@ -2852,9 +2642,6 @@ EmitNameOp(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
case JSOP_GETLOCAL:
op = JSOP_CALLLOCAL;
break;
case JSOP_GETUPVAR:
op = JSOP_CALLUPVAR;
break;
case JSOP_GETFCSLOT:
op = JSOP_CALLFCSLOT;
break;
@ -6267,7 +6054,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
} else {
JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
? JSOP_GETGNAME
: (PN_OP(pn2) == JSOP_SETGLOBAL)

Просмотреть файл

@ -234,9 +234,9 @@ struct JSStmtInfo {
/*
* 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, thanks to JSOP_{GET,CALL}UPVAR), because this function contains
* a closure that needs one or more scope objects surrounding it (i.e., Call
* object for a heavyweight outer function). See bug 560234.
* 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

Просмотреть файл

@ -455,8 +455,6 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
* immediate operand.
*/
switch (op) {
case JSOP_GETUPVAR: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLUPVAR: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_GETFCSLOT: *pc = JSOP_GETUPVAR_DBG; break;
case JSOP_CALLFCSLOT: *pc = JSOP_CALLUPVAR_DBG; break;
case JSOP_DEFFUN_FC: *pc = JSOP_DEFFUN_DBGFC; break;
@ -1226,30 +1224,25 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
}
JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee();
JS_ASSERT(callee);
*vp = callee->getFlatClosureUpvar(i);
*vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
return true;
}
JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
uintN i = (uint16) JSID_TO_INT(id);
JSObject *callee = obj->getCallObjCallee();
JS_ASSERT(callee);
Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
Value *upvarp = &callee->getFlatClosureUpvar(i);
GC_POKE(cx, *upvarp);
*upvarp = *vp;
GC_POKE(cx, *up);
*up = *vp;
return true;
}
@ -2794,16 +2787,20 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
JS_REQUIRES_STACK JSObject *
JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
{
/*
* Flat closures can be partial, they may need to search enclosing scope
* objects via JSOP_NAME, etc.
* Flat closures cannot yet be partial, that is, all upvars must be copied,
* or the closure won't be flattened. Therefore they do not need to search
* enclosing scope objects via JSOP_NAME, etc.
*
* FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
* bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
* annotations on this function's prototype and definition.
*/
JSObject *scopeChain = GetScopeChainFast(cx, cx->fp(), op, oplen);
if (!scopeChain)
return NULL;
VOUCH_DOES_NOT_REQUIRE_STACK();
JSObject *scopeChain = &cx->fp()->scopeChain();
JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
if (!closure || !fun->script()->bindings.hasUpvars())

Просмотреть файл

@ -467,7 +467,7 @@ CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
extern JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
extern JS_REQUIRES_STACK JSObject *
extern JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
extern JS_REQUIRES_STACK JSObject *
@ -526,7 +526,7 @@ extern JSBool
GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
@ -535,7 +535,7 @@ extern JSBool
SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
extern JSBool
SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
} // namespace js

Просмотреть файл

@ -2131,9 +2131,8 @@ AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
* same way as non-call bytecodes.
*/
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
@ -5289,22 +5288,6 @@ BEGIN_CASE(JSOP_SETLOCAL)
}
END_SET_CASE(JSOP_SETLOCAL)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
JSUpvarArray *uva = script->upvars();
uintN index = GET_UINT16(regs.pc);
JS_ASSERT(index < uva->length);
const Value &rval = GetUpvar(cx, script->staticLevel, uva->vector[index]);
PUSH_COPY(rval);
if (op == JSOP_CALLUPVAR)
PUSH_UNDEFINED();
}
END_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_GETUPVAR_DBG)
BEGIN_CASE(JSOP_CALLUPVAR_DBG)
{

Просмотреть файл

@ -2883,8 +2883,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
}
case JSOP_GETUPVAR:
case JSOP_CALLUPVAR:
case JSOP_GETUPVAR_DBG:
case JSOP_CALLUPVAR_DBG:
case JSOP_GETFCSLOT:

Просмотреть файл

@ -443,15 +443,19 @@ OPDEF(JSOP_NOTRACE, 182,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 186,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
OPDEF(JSOP_DELDESC, 186,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_CALLPROP, 187,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
/*
* Get a display (free) variable from the closure's reserved slots.
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_GETUPVAR, 187,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR, 188,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DELDESC, 189,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_BLOCKCHAIN, 188,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Opcode to hold 24-bit immediate integer operands.
@ -586,7 +590,7 @@ OPDEF(JSOP_OBJTOP, 228,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16
OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16)
/*
* Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops.
* Debugger versions of the flat closure (_FC) ops.
*/
OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
@ -614,15 +618,5 @@ OPDEF(JSOP_GLOBALDEC, 245,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL
OPDEF(JSOP_CALLGLOBAL, 246,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 247,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
/*
* These opcodes contain a reference to the current blockChain object.
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_BLOCKCHAIN, 248,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,249,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
OPDEF(JSOP_FUNCALL, 250,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_FUNCALL, 248,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)

Просмотреть файл

@ -1909,8 +1909,8 @@ Parser::analyzeFunctions(JSFunctionBox *funbox, uint32& tcflags)
* 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 use JSOP_GETUPVAR to reach p. In contast function h neither escapes
* nor uses an upvar "above" o_m's level.
* 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
@ -2050,9 +2050,8 @@ Parser::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
PN_OP(lexdep) == JSOP_CALLEE)) {
/*
* Mark this formerly-Algol-like function as an escaping
* function (i.e., as a funarg), because it is used from a
* funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
* access upvars.
* 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
@ -2372,26 +2371,8 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!fn->isFunArg()) {
/*
* This function is Algol-like, it never escapes. So long as it
* does not assign to outer variables, it needs only an upvars
* array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
* bytecode to reach up the frame stack at runtime based on
* those upvars' cookies.
* This function is Algol-like, it never escapes.
*
* Any assignments to upvars from functions called by this one
* will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
* which load from stack homes when interpreting or from native
* stack slots when executing a trace.
*
* We could add JSOP_SETUPVAR, etc., but it is uncommon for a
* nested function to assign to an outer lexical variable, so
* we defer adding yet more code footprint in the absence of
* evidence motivating these opcodes.
*/
bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
uintN nupvars = 0;
/*
* Check that at least one outer lexical binding was assigned
* to (global variables don't count). This is conservative: we
* could limit assignments to those in the current function,
@ -2404,30 +2385,13 @@ Parser::setFunctionKinds(JSFunctionBox *funbox, uint32& tcflags)
if (!lexdep->isFreeVar()) {
JS_ASSERT(lexdep->frameLevel() <= funbox->level);
++nupvars;
if (lexdep->isAssigned())
break;
break;
}
}
if (!ale)
mutation = false;
if (nupvars == 0) {
if (!ale) {
FUN_METER(onlyfreevar);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else if (!mutation &&
!(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
/*
* Algol-like functions can read upvars using the dynamic
* link (cx->fp/fp->down), optimized using the cx->display
* lookup table indexed by static level. They do not need
* to entrain and search their environment objects.
*/
FUN_METER(display);
FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
} else {
if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
FUN_METER(setupvar);
}
} else {
uintN nupvars = 0, nflattened = 0;
@ -2902,8 +2866,8 @@ Parser::functionDef(JSAtom *funAtom, FunctionType type, uintN lambda)
pn->pn_cookie.makeFree();
/*
* If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
* is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
* If a lambda, 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 (non-lambda, non-body-level functions) as
* escaping funargs, since we can't statically analyze their definitions

Просмотреть файл

@ -832,12 +832,8 @@ struct LexicalScopeNode : public JSParseNode {
* and because all uses are contained in the same block as the definition.
*
* We also analyze function uses to flag upward/downward funargs, optimizing
* Algol-like (not passed as funargs, only ever called) lightweight functions
* using cx->display. See JSOP_{GET,CALL}UPVAR.
*
* This means that closure optimizations may be frustrated by with, eval, or
* assignment to an outer var. Such hard cases require heavyweight functions
* and JSOP_NAME, etc.
* those lambdas that post-dominate their upvars inevitable only assignments or
* initializations as flat closures (after Chez Scheme's display closures).
*/
#define dn_uses pn_link

Просмотреть файл

@ -485,10 +485,10 @@ Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
/*
* Beware duplicate formal parameters, allowed by ECMA-262 in
* non-strict mode. Otherwise we know that JSFunction::addLocal
* (our caller) won't pass an id already in the table to us. In
* the case of duplicate formals, the last one wins, so while
* we must not overcount entries, we must store newShape.
* non-strict mode. Otherwise we know that Bindings::add (our
* caller) won't pass an id already in the table to us. In the
* case of duplicate formals, the last one wins, so while we
* must not overcount entries, we must store newShape.
*/
if (!SHAPE_FETCH(spp))
++table->entryCount;

Просмотреть файл

@ -91,7 +91,7 @@ Bindings::lookup(JSContext *cx, JSAtom *name, uintN *indexp) const
if (shape->getter() == GetCallArg)
return ARGUMENT;
if (shape->getter() == GetFlatUpvar)
if (shape->getter() == GetCallUpvar)
return UPVAR;
return shape->writable() ? VARIABLE : CONSTANT;
@ -122,8 +122,8 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
slot += nargs;
} else if (kind == UPVAR) {
indexp = &nupvars;
getter = GetFlatUpvar;
setter = SetFlatUpvar;
getter = GetCallUpvar;
setter = SetCallUpvar;
slot = SHAPE_INVALID_SLOT;
} else {
JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
@ -218,7 +218,7 @@ Bindings::getLocalNameArray(JSContext *cx, JSArenaPool *pool)
if (shape.getter() == GetCallArg) {
JS_ASSERT(index < nargs);
} else if (shape.getter() == GetFlatUpvar) {
} else if (shape.getter() == GetCallUpvar) {
JS_ASSERT(index < nupvars);
index += nargs + nvars;
} else {
@ -267,7 +267,7 @@ Bindings::lastVariable() const
const js::Shape *shape = lastUpvar();
if (nupvars > 0) {
while (shape->getter() == GetFlatUpvar)
while (shape->getter() == GetCallUpvar)
shape = shape->previous();
}
return shape;

Просмотреть файл

@ -73,7 +73,7 @@ namespace js {
*
* TODO: consider giving more bits to the slot value and takings ome from the level.
*/
class UpvarCookie
class UpvarCookie
{
uint32 value;
@ -206,11 +206,11 @@ class Bindings {
inline const js::Shape *lastShape() const;
enum {
/*
* A script may have no more than this many arguments, variables, or
* upvars.
*/
BINDING_COUNT_LIMIT = 0xFFFF
/*
* A script may have no more than this many arguments, variables, or
* upvars.
*/
BINDING_COUNT_LIMIT = 0xFFFF
};
/*
@ -224,11 +224,10 @@ class Bindings {
* which must deal with such cases.) Pass null for name when indicating a
* destructuring argument. Return true on success.
*
*
* The parser builds shape paths for functions, usable by Call objects at
* runtime, by calling addLocal. All ARGUMENT bindings must be added before
* before any VARIABLE or CONSTANT bindings, which themselves must be added
* before all UPVAR bindings.
* runtime, by calling an "add" method. All ARGUMENT bindings must be added
* before before any VARIABLE or CONSTANT bindings, which themselves must
* be added before all UPVAR bindings.
*/
bool add(JSContext *cx, JSAtom *name, BindingKind kind);
@ -431,7 +430,7 @@ struct JSScript {
* JS_CompileFile, etc.) have these objects.
* - Function scripts never have script objects; such scripts are owned
* by their function objects.
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* - Temporary scripts created by obj_eval, JS_EvaluateScript, and
* similar functions never have these objects; such scripts are
* explicitly destroyed by the code that created them.
* Debugging API functions (JSDebugHooks::newScriptHook;

Просмотреть файл

@ -13256,30 +13256,6 @@ TraceRecorder::stackLoad(Address addr, uint8 type)
}
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETUPVAR()
{
uintN index = GET_UINT16(cx->regs->pc);
JSScript *script = cx->fp()->script();
JSUpvarArray* uva = script->upvars();
JS_ASSERT(index < uva->length);
Value v;
LIns* upvar_ins = upvar(script, uva, index, v);
if (!upvar_ins)
return ARECORD_STOP;
stack(0, upvar_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_CALLUPVAR()
{
CHECK_STATUS_A(record_JSOP_GETUPVAR());
stack(1, w.immiUndefined());
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_GETFCSLOT()
{

Просмотреть файл

@ -206,7 +206,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 80)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 81)
/*
* Library-private functions.

Просмотреть файл

@ -1954,22 +1954,6 @@ mjit::Compiler::generateMethod()
return Compile_Error;
END_CASE(JSOP_CALLPROP)
BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR)
{
uint32 index = GET_UINT16(PC);
JSUpvarArray *uva = script->upvars();
JS_ASSERT(index < uva->length);
prepareStubCall(Uses(0));
masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
INLINE_STUBCALL(stubs::GetUpvar);
frame.pushSynced();
if (op == JSOP_CALLUPVAR)
frame.push(UndefinedValue());
}
END_CASE(JSOP_CALLUPVAR)
BEGIN_CASE(JSOP_UINT24)
frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
END_CASE(JSOP_UINT24)

Просмотреть файл

@ -65,7 +65,7 @@ function test()
(function (){
var x;
eval("const x; (function ()x)");
eval("var x; (function ()x)");
}
)();

Просмотреть файл

@ -78,7 +78,7 @@ function test()
function f() {
var x;
(function(){})();
eval("if(x|=[]) {const x; }");
eval("if(x|=[]) {var x; }");
}
f();

Просмотреть файл

@ -40,6 +40,10 @@ script regress-586482-4.js
script regress-586482-5.js
script regress-588339.js
script regress-yarr-regexp.js
script regress-592202-1.js
script regress-592202-2.js
script regress-592202-3.js
script regress-592202-4.js
script regress-592217.js
script regress-592556-c35.js
script regress-593256.js

Просмотреть файл

@ -0,0 +1,7 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
i = 42
eval("let(y){(function(){let({}=y){(function(){let({}=y=[])(i)})()}})()}")
reportCompare(0, 0, "ok");

Просмотреть файл

@ -0,0 +1,15 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
eval("\
let(b)((\
function(){\
let(d=b)\
((function(){\
b=b\
})())\
}\
)())\
")
reportCompare(0, 0, "ok");

Просмотреть файл

@ -0,0 +1,28 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
test();
function test()
{
var counter = 0;
function f(x,y) {
try
{
throw 42;
}
catch(e2)
{
foo(function(){ return x; }| "9.2" && 5 || counter && e);
++counter;
}
}
f(2, 1);
}
function foo(bar) { return ""+bar; }
reportCompare(0, 0, "ok");

Просмотреть файл

@ -0,0 +1,31 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
function p() { }
function test()
{
var counter = 0;
function f(x) {
try
{
throw 42;
}
catch(e)
{
assertEq(counter, 0);
p(function(){x;});
counter = 1;
}
}
f(2);
assertEq(counter, 1);
}
test();
reportCompare(0, 0, "ok");