Fix 366966, block scope vs. local functions (r=mrbkap).
git-svn-id: svn://10.0.0.236/trunk@219921 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -1116,7 +1116,6 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
SHOW_FLAG(THISP_STRING);
|
||||
SHOW_FLAG(THISP_NUMBER);
|
||||
SHOW_FLAG(THISP_BOOLEAN);
|
||||
SHOW_FLAG(BLOCKLOCALFUN);
|
||||
SHOW_FLAG(INTERPRETED);
|
||||
|
||||
#undef SHOW_FLAG
|
||||
|
||||
@@ -1322,7 +1322,9 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
||||
JS_ASSERT(*returnop == JSOP_RETURN);
|
||||
for (stmt = cg->treeContext.topStmt; stmt != toStmt;
|
||||
stmt = stmt->down) {
|
||||
if (stmt->type == STMT_FINALLY) {
|
||||
if (stmt->type == STMT_FINALLY ||
|
||||
((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
|
||||
STMT_MAYBE_SCOPE(stmt))) {
|
||||
if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
|
||||
return JS_FALSE;
|
||||
*returnop = JSOP_RETRVAL;
|
||||
@@ -4017,16 +4019,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
|
||||
/*
|
||||
* If this local function is in a body block, flag cg so that any
|
||||
* outer function will be flagged with JSFUN_BLOCKLOCALFUN, which
|
||||
* helps JSOP_DEFLOCALFUN capture the body block including any let
|
||||
* variables in the local function's scope chain.
|
||||
* If this local function is declared in a body block induced by
|
||||
* let declarations, reparent fun->object to the compiler-created
|
||||
* body block object so that JSOP_DEFLOCALFUN can clone that block
|
||||
* into the runtime scope chain.
|
||||
*/
|
||||
stmt = cg->treeContext.topStmt;
|
||||
if (stmt && stmt->type == STMT_BLOCK &&
|
||||
stmt->down && stmt->down->type == STMT_BLOCK &&
|
||||
(stmt->down->flags & SIF_SCOPE)) {
|
||||
cg->treeContext.flags |= TCF_HAS_BLOCKLOCALFUN;
|
||||
obj = ATOM_TO_OBJECT(stmt->down->atom);
|
||||
JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
|
||||
OBJ_SET_PARENT(cx, fun->object, obj);
|
||||
}
|
||||
|
||||
if (!EmitIndexConstOp(cx, JSOP_DEFLOCALFUN, slot, atomIndex, cg))
|
||||
|
||||
@@ -183,8 +183,6 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */
|
||||
#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */
|
||||
#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */
|
||||
#define TCF_HAS_BLOCKLOCALFUN 0x800 /* inner function declared in let-bearing
|
||||
outer function body */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
|
||||
@@ -68,8 +68,6 @@ struct JSFunction {
|
||||
};
|
||||
|
||||
#define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */
|
||||
#define JSFUN_BLOCKLOCALFUN 0x4000 /* has body block containing both let vars
|
||||
and local function declarations */
|
||||
|
||||
#define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED)
|
||||
#define FUN_NATIVE(fun) (FUN_INTERPRETED(fun) ? NULL : (fun)->u.n.native)
|
||||
|
||||
@@ -4863,41 +4863,53 @@ interrupt:
|
||||
slot = GET_VARNO(pc);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
|
||||
/*
|
||||
* Handle the hard case of a local function defined in a body that
|
||||
* contains direct let declarations. In this case, we must search
|
||||
* the script's constants for the first block object, and clone it
|
||||
* onto the scope chain, so that the local function being defined
|
||||
* here captures the let variables.
|
||||
*/
|
||||
JS_ASSERT(!fp->blockChain);
|
||||
if (fp->fun && (fp->fun->flags & JSFUN_BLOCKLOCALFUN)) {
|
||||
jsatomid aid;
|
||||
|
||||
obj2 = NULL;
|
||||
for (aid = 0; aid < script->atomMap.length; aid++) {
|
||||
if (ATOM_IS_OBJECT(script->atomMap.vector[aid])) {
|
||||
obj2 = ATOM_TO_OBJECT(script->atomMap.vector[aid]);
|
||||
if (OBJ_GET_CLASS(cx, obj2) == &js_BlockClass)
|
||||
break;
|
||||
}
|
||||
}
|
||||
fp->blockChain = obj2;
|
||||
if (!(fp->flags & JSFRAME_POP_BLOCKS)) {
|
||||
/*
|
||||
* If the compiler-created function object (obj) is scoped by a
|
||||
* let-induced body block, temporarily update fp->blockChain so
|
||||
* that js_GetScopeChain will clone the block into the runtime
|
||||
* scope needed to parent the function object's clone.
|
||||
*/
|
||||
parent = OBJ_GET_PARENT(cx, obj);
|
||||
if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass)
|
||||
fp->blockChain = parent;
|
||||
parent = js_GetScopeChain(cx, fp);
|
||||
} else {
|
||||
/*
|
||||
* We have already emulated JSOP_ENTERBLOCK for the enclosing
|
||||
* body block, for a prior JSOP_DEFLOCALFUN in the prolog, so
|
||||
* we just load fp->scopeChain into parent.
|
||||
*
|
||||
* In typical execution scenarios, the prolog bytecodes that
|
||||
* include this JSOP_DEFLOCALFUN run, then come main bytecodes
|
||||
* including JSOP_ENTERBLOCK for the outermost (body) block.
|
||||
* JSOP_ENTERBLOCK will detect that it need not do anything if
|
||||
* the body block was entered above due to a local function.
|
||||
* Finally the matching JSOP_LEAVEBLOCK runs.
|
||||
*
|
||||
* If the matching JSOP_LEAVEBLOCK for the body block does not
|
||||
* run for some reason, the body block will be properly "put"
|
||||
* (via js_PutBlockObject) by the PutBlockObjects call at the
|
||||
* bottom of js_Interpret.
|
||||
*/
|
||||
parent = fp->scopeChain;
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
|
||||
JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj));
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent))
|
||||
== &js_CallClass);
|
||||
}
|
||||
|
||||
/* If re-parenting, store a clone of the function object. */
|
||||
obj2 = fp->scopeChain;
|
||||
parent = js_GetScopeChain(cx, fp);
|
||||
if (OBJ_GET_PARENT(cx, obj) != parent) {
|
||||
SAVE_SP_AND_PC(fp);
|
||||
obj = js_CloneFunctionObject(cx, obj, parent);
|
||||
if (!obj)
|
||||
if (!obj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
fp->blockChain = NULL;
|
||||
fp->scopeChain = obj2;
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
fp->vars[slot] = OBJECT_TO_JSVAL(obj);
|
||||
END_CASE(JSOP_DEFLOCALFUN)
|
||||
|
||||
@@ -5839,12 +5851,23 @@ interrupt:
|
||||
*/
|
||||
if (fp->flags & JSFRAME_POP_BLOCKS) {
|
||||
JS_ASSERT(!fp->blockChain);
|
||||
obj = js_CloneBlockObject(cx, obj, fp->scopeChain, fp);
|
||||
if (!obj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for
|
||||
* the body block in order to correctly scope the local cloned
|
||||
* function object it creates.
|
||||
*/
|
||||
parent = fp->scopeChain;
|
||||
if (OBJ_GET_PROTO(cx, parent) == obj) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass);
|
||||
} else {
|
||||
obj = js_CloneBlockObject(cx, obj, parent, fp);
|
||||
if (!obj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
fp->scopeChain = obj;
|
||||
}
|
||||
fp->scopeChain = obj;
|
||||
} else {
|
||||
JS_ASSERT(!fp->blockChain ||
|
||||
OBJ_GET_PARENT(cx, obj) == fp->blockChain);
|
||||
|
||||
@@ -1356,8 +1356,6 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
|
||||
fun->u.i.script = script;
|
||||
if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
if (cg->treeContext.flags & TCF_HAS_BLOCKLOCALFUN)
|
||||
fun->flags |= JSFUN_BLOCKLOCALFUN;
|
||||
}
|
||||
|
||||
/* Tell the debugger about this compiled script. */
|
||||
|
||||
Reference in New Issue
Block a user