From eb16f9a84d8a6dc4cae2677fdf3925de6002e042 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Mon, 17 Aug 2009 18:08:20 -0700 Subject: [PATCH] Bug 495325 - Follow ES about indirect eval being global eval. r=brendan/igor --HG-- extra : rebase_source : 397bd79f787fc6d079fea1b2433a8e262626ca46 --- js/src/jsinterp.cpp | 5 +-- js/src/jsobj.cpp | 100 +++++++++++++++++++++----------------------- 2 files changed, 50 insertions(+), 55 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 1920c9203b1..4512650b6c1 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1488,7 +1488,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, frame.callobj = down->callobj; frame.argsobj = down->argsobj; frame.varobj = down->varobj; - frame.fun = down->fun; + frame.fun = (script->staticLevel > 0) ? down->fun : NULL; frame.thisv = down->thisv; if (down->flags & JSFRAME_COMPUTED_THIS) flags |= JSFRAME_COMPUTED_THIS; @@ -1541,9 +1541,8 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, } else { sharps[0] = sharps[1] = JSVAL_VOID; } - } else + } #endif - JS_ASSERT_IF(down, script->nfixed == 0); } else { frame.slots = NULL; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 61231326c97..f89299c3fca 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -1253,13 +1253,18 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */ #if JS_HAS_EVAL_THIS_SCOPE JSObject *callerScopeChain = NULL, *callerVarObj = NULL; - JSObject *setCallerScopeChain = NULL; - JSBool setCallerVarObj = JS_FALSE; + JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE; #endif fp = js_GetTopStackFrame(cx); caller = js_GetScriptedCaller(cx, fp); - indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); + if (!caller) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_INDIRECT_CALL, js_eval_str); + return JS_FALSE; + } + + indirectCall = (caller->regs && *caller->regs->pc != JSOP_EVAL); /* * This call to js_GetWrappedObject is safe because of the security checks @@ -1301,7 +1306,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * object, then we need to provide one for the compiler to stick any * declared (var) variables into. */ - if (caller && !caller->varobj && !js_GetCallObject(cx, caller)) + if (!caller->varobj && !js_GetCallObject(cx, caller)) return JS_FALSE; /* Accept an optional trailing argument that overrides the scope object. */ @@ -1314,39 +1319,45 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) /* From here on, control must exit through label out with ok set. */ MUST_FLOW_THROUGH("out"); + uintN staticLevel = caller->script->staticLevel + 1; if (!scopeobj) { #if JS_HAS_EVAL_THIS_SCOPE - /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ + /* + * If we see an indirect call, then run eval in the global scope. We do + * this so the compiler can make assumptions about what bindings may or + * may not exist in the current frame if it doesn't see 'eval'. + */ if (indirectCall) { + /* Pretend that we're top level. */ + staticLevel = 0; + callerScopeChain = js_GetScopeChain(cx, caller); if (!callerScopeChain) { ok = JS_FALSE; goto out; } + OBJ_TO_INNER_OBJECT(cx, obj); if (!obj) { ok = JS_FALSE; goto out; } - if (obj != callerScopeChain) { - ok = js_CheckPrincipalsAccess(cx, obj, - JS_StackFramePrincipals(cx, caller), - cx->runtime->atomState.evalAtom); - if (!ok) - goto out; - scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } + ok = js_CheckPrincipalsAccess(cx, obj, + JS_StackFramePrincipals(cx, caller), + cx->runtime->atomState.evalAtom); + if (!ok) + goto out; - /* Set fp->scopeChain too, for the compiler. */ - caller->scopeChain = fp->scopeChain = scopeobj; + /* NB: We know obj is a global object here. */ + JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); + scopeobj = obj; - /* Remember scopeobj so we can null its private when done. */ - setCallerScopeChain = scopeobj; - } + /* Set fp->scopeChain too, for the compiler. */ + caller->scopeChain = fp->scopeChain = scopeobj; + + /* Remember scopeobj so we can null its private when done. */ + setCallerScopeChain = JS_TRUE; callerVarObj = caller->varobj; if (obj != callerVarObj) { @@ -1363,12 +1374,10 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * NB: This means that native callers (who reach this point through * the C API) must use the two parameter form. */ - if (caller) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } + scopeobj = js_GetScopeChain(cx, caller); + if (!scopeobj) { + ok = JS_FALSE; + goto out; } } else { scopeobj = js_GetWrappedObject(cx, scopeobj); @@ -1377,19 +1386,15 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) ok = JS_FALSE; goto out; } + ok = js_CheckPrincipalsAccess(cx, scopeobj, JS_StackFramePrincipals(cx, caller), cx->runtime->atomState.evalAtom); if (!ok) goto out; - scopeobj = js_NewWithObject(cx, scopeobj, - JS_GetGlobalForObject(cx, scopeobj), -1); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } - argv[1] = OBJECT_TO_JSVAL(scopeobj); + /* We're pretending that we're in global code. */ + staticLevel = 0; } /* Ensure we compile this eval with the right object in the scope chain. */ @@ -1399,23 +1404,16 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto out; } - tcflags = TCF_COMPILE_N_GO; - if (caller) { - tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1); - principals = JS_EvalFramePrincipals(cx, fp, caller); - file = js_ComputeFilename(cx, caller, principals, &line); - } else { - principals = NULL; - file = NULL; - line = 0; - } + tcflags = TCF_COMPILE_N_GO | TCF_PUT_STATIC_LEVEL(staticLevel); + principals = JS_EvalFramePrincipals(cx, fp, caller); + file = js_ComputeFilename(cx, caller, principals, &line); str = JSVAL_TO_STRING(argv[0]); script = NULL; /* Cache local eval scripts indexed by source qualified by scope. */ bucket = EvalCacheHash(cx, str); - if (caller->fun) { + if (!indirectCall && caller->fun) { uintN count = 0; JSScript **scriptp = bucket; @@ -1480,7 +1478,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } if (!script) { - script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, + JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL; + script = JSCompiler::compileScript(cx, scopeobj, callerFrame, + principals, tcflags, str->chars(), str->length(), NULL, file, line, str); if (!script) { @@ -1491,8 +1491,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) if (argc < 2) { /* Execute using caller's new scope object (might be a Call object). */ - if (caller) - scopeobj = caller->scopeChain; + scopeobj = caller->scopeChain; } /* @@ -1513,11 +1512,8 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) out: #if JS_HAS_EVAL_THIS_SCOPE /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ - if (setCallerScopeChain) { + if (setCallerScopeChain) caller->scopeChain = callerScopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); - setCallerScopeChain->setPrivate(NULL); - } if (setCallerVarObj) caller->varobj = callerVarObj; #endif