зеркало из https://github.com/mozilla/pjs.git
Get rid of JSOP_{GET,CALL}UPVAR and simplify code greatly (592202, r=jorendorff).
This commit is contained in:
Родитель
cd3e22e469
Коммит
3a7ffc111f
|
@ -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");
|
Загрузка…
Ссылка в новой задаче