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:
brendan%mozilla.org
2007-02-10 00:45:44 +00:00
parent cf950e51fe
commit 6d67d8b830
6 changed files with 64 additions and 44 deletions

View File

@@ -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

View File

@@ -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))

View File

@@ -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, \

View File

@@ -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)

View File

@@ -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);

View File

@@ -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. */