Bug 531675 - Ignore the second argument of eval, except to warn once per JSScript (per function, or per global script for uses not in functions) that it's not supported. (Again.) r=mrbkap

--HG--
extra : rebase_source : 2a0b9670e7c2c90ab0c41d37e3ab99c3bbc4c819
This commit is contained in:
Jeff Walden 2010-07-23 11:27:07 -05:00
parent b1284554d5
commit 173c4d1adf
6 changed files with 55 additions and 156 deletions

View File

@ -1072,109 +1072,67 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
return JS_TRUE;
}
/* Accept an optional trailing argument that overrides the scope object. */
JSObject *scopeobj = NULL;
if (argc >= 2) {
if (!js_ValueToObjectOrNull(cx, argv[1], &scopeobj))
/*
* We once supported a second argument to eval to use as the scope chain
* when evaluating the code string. Warn when such uses are seen so that
* authors will know that support for eval(s, o) has been removed.
*/
if (argc > 1 && !caller->script->warnedAboutTwoArgumentEval) {
static const char TWO_ARGUMENT_WARNING[] =
"Support for eval(code, scopeObject) has been removed. "
"Use |with (scopeObject) eval(code);| instead.";
if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
return JS_FALSE;
argv[1].setObjectOrNull(scopeobj);
JSObject *obj = scopeobj;
while (obj) {
if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
return false;
JSObject *parent = obj->getParent();
if (!obj->isNative() ||
(!parent && !(obj->getClass()->flags & JSCLASS_IS_GLOBAL))) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_INVALID_EVAL_SCOPE_ARG);
return false;
}
obj = parent;
}
caller->script->warnedAboutTwoArgumentEval = true;
}
/* Guard for conditionally-created with object below. */
struct WithGuard {
JSObject *obj;
WithGuard() : obj(NULL) {}
~WithGuard() { if (obj) obj->setPrivate(NULL); }
} withGuard;
/* From here on, control must exit through label out with ok set. */
MUST_FLOW_THROUGH("out");
uintN staticLevel = caller->script->staticLevel + 1;
if (!scopeobj) {
/*
* Bring fp->scopeChain up to date. We're either going to use
* it (direct call) or save it and restore it (indirect call).
*/
JSObject *callerScopeChain = js_GetScopeChain(cx, caller);
if (!callerScopeChain)
return JS_FALSE;
/*
* Bring fp->scopeChain up to date. We're either going to use
* it (direct call) or save it and restore it (indirect call).
*/
JSObject *callerScopeChain = js_GetScopeChain(cx, caller);
if (!callerScopeChain)
return JS_FALSE;
JSObject *scopeobj = NULL;
#if JS_HAS_EVAL_THIS_SCOPE
/*
* 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;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
if (!js_CheckPrincipalsAccess(cx, obj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom)) {
return JS_FALSE;
}
/* NB: We know inner is a global object here. */
JS_ASSERT(!obj->getParent());
scopeobj = obj;
} else {
/*
* Compile using the caller's current scope object.
*
* NB: This means that native callers (who reach this point through
* the C API) must use the two parameter form.
*/
JS_ASSERT_IF(caller->argv, caller->callobj);
scopeobj = callerScopeChain;
}
#endif
} else {
scopeobj = scopeobj->wrappedObject(cx);
OBJ_TO_INNER_OBJECT(cx, scopeobj);
if (!scopeobj)
return JS_FALSE;
if (!js_CheckPrincipalsAccess(cx, scopeobj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom))
return JS_FALSE;
/*
* If scopeobj is not a global object, then we need to wrap it in a
* with object to maintain invariants in the engine (see bug 520164).
*/
if (scopeobj->getParent()) {
JSObject *global = scopeobj->getGlobal();
withGuard.obj = js_NewWithObject(cx, scopeobj, global, 0);
if (!withGuard.obj)
return JS_FALSE;
scopeobj = withGuard.obj;
JS_ASSERT(argc >= 2);
argv[1].setObject(*withGuard.obj);
}
/* We're pretending that we're in global code. */
/*
* 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;
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
if (!js_CheckPrincipalsAccess(cx, obj,
JS_StackFramePrincipals(cx, caller),
cx->runtime->atomState.evalAtom)) {
return JS_FALSE;
}
/* NB: We know inner is a global object here. */
JS_ASSERT(!obj->getParent());
scopeobj = obj;
} else {
/*
* Compile using the caller's current scope object.
*
* NB: This means that the C API must not be used to call eval.
*/
JS_ASSERT_IF(caller->argv, caller->callobj);
scopeobj = callerScopeChain;
}
#endif
/* Ensure we compile this eval with the right object in the scope chain. */
JSObject *result = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);

View File

@ -74,7 +74,7 @@ static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
/* static */ const JSScript JSScript::emptyScriptConst = {
const_cast<jsbytecode*>(emptyScriptCode),
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, true, false, false, false,
1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, true, false, false, false, true,
const_cast<jsbytecode*>(emptyScriptCode),
{0, NULL}, NULL, 0, 0, 0, NULL
};

View File

@ -172,6 +172,9 @@ struct JSScript {
bool savedCallerFun:1; /* object 0 is caller function */
bool hasSharps:1; /* script uses sharp variables */
bool strictModeCode:1; /* code is in strict mode */
bool warnedAboutTwoArgumentEval:1; /* have warned about use of
obsolete eval(s, o) in
this script */
jsbytecode *main; /* main entry point, after predef'ing prolog */
JSAtomMap atomMap; /* maps immediate index to literal struct */

View File

@ -2,5 +2,4 @@ url-prefix ../../jsreftest.html?test=js1_4/Eval/
script eval-001.js
script eval-002.js
script eval-003.js
script regress-531037.js
script regress-531682.js

View File

@ -1,39 +0,0 @@
/* -*- Mode: java; tab-width:8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'regress-531037.js';
//-----------------------------------------------------------------------------
var BUGNUMBER = 531037;
var summary = 'Checking corner cases of eval(source, scope) form';
var actual;
var expect = "No crash";
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test() {
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
var b = 10;
var fff = function() { return --b >= 0; };
var src = "while (fff());";
eval(src, null);
b = 10;
try {
eval(src, {fff: function() {throw 0;}});
throw new Error("Unexpected success of eval");
} catch (e) {
if (e !== 0)
throw e;
}
actual = "No crash";
reportCompare(expect, actual, summary);
exitFunc ('test');
}

View File

@ -1,22 +0,0 @@
/* -*- Mode: java; tab-width:8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
(function() {
var b = 10;
var fff = function() { return --b >= 0; };
var src = "while (fff());";
eval(src, null);
b = 10;
try {
eval(src, {fff: function() {throw 0;}});
throw new Error("Unexpected success of eval");
} catch (e) {
if (e !== 0)
throw e;
}
})();
// We expect this to finish without crashes or exceptions