diff --git a/mozilla/js/src/js.c b/mozilla/js/src/js.c index 0e2affa8700..62604761ee7 100644 --- a/mozilla/js/src/js.c +++ b/mozilla/js/src/js.c @@ -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 diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index c53a490d97d..cad8777699e 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -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)) diff --git a/mozilla/js/src/jsemit.h b/mozilla/js/src/jsemit.h index 87515e9814b..90709c22ae9 100644 --- a/mozilla/js/src/jsemit.h +++ b/mozilla/js/src/jsemit.h @@ -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, \ diff --git a/mozilla/js/src/jsfun.h b/mozilla/js/src/jsfun.h index 541a26f9f38..8d5c1850a34 100644 --- a/mozilla/js/src/jsfun.h +++ b/mozilla/js/src/jsfun.h @@ -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) diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 751d4553cbd..69d796fcc8a 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -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); diff --git a/mozilla/js/src/jsscript.c b/mozilla/js/src/jsscript.c index a901162b6ca..6b663564bc1 100644 --- a/mozilla/js/src/jsscript.c +++ b/mozilla/js/src/jsscript.c @@ -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. */