Back out changeset 89006937466d (crashing tests on tinderbox).

This commit is contained in:
Jason Orendorff 2010-10-06 15:51:14 -05:00
parent 447d62bbdc
commit 1344bee2a5
9 changed files with 367 additions and 203 deletions

View File

@ -998,112 +998,71 @@ EvalCacheHash(JSContext *cx, JSString *str)
return &JS_SCRIPTS_TO_GC(cx)[h];
}
static JS_ALWAYS_INLINE JSScript *
EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel,
JSPrincipals *principals, JSObject *scopeobj, JSScript ***bucketp)
{
/*
* Cache local eval scripts indexed by source qualified by scope.
*
* An eval cache entry should never be considered a hit unless its
* strictness matches that of the new eval code. The existing code takes
* care of this, because hits are qualified by the function from which
* eval was called, whose strictness doesn't change. Scripts produced by
* calls to eval from global code are not cached.
*/
JSScript **bucket = EvalCacheHash(cx, str);
*bucketp = bucket;
uintN count = 0;
JSScript **scriptp = bucket;
EVAL_CACHE_METER(probe);
JSVersion version = cx->findVersion();
JSScript *script;
while ((script = *scriptp) != NULL) {
if (script->savedCallerFun &&
script->staticLevel == staticLevel &&
script->version == version &&
(script->principals == principals ||
(principals->subsume(principals, script->principals) &&
script->principals->subsume(script->principals, principals)))) {
/*
* Get the prior (cache-filling) eval's saved caller function.
* See Compiler::compileScript in jsparse.cpp.
*/
JSFunction *fun = script->getFunction(0);
if (fun == caller->fun()) {
/*
* Get the source string passed for safekeeping in the
* atom map by the prior eval to Compiler::compileScript.
*/
JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
if (src == str || js_EqualStrings(src, str)) {
/*
* Source matches, qualify by comparing scopeobj to the
* COMPILE_N_GO-memoized parent of the first literal
* function or regexp object if any. If none, then this
* script has no compiled-in dependencies on the prior
* eval's scopeobj.
*/
JSObjectArray *objarray = script->objects();
int i = 1;
if (objarray->length == 1) {
if (script->regexpsOffset != 0) {
objarray = script->regexps();
i = 0;
} else {
EVAL_CACHE_METER(noscope);
i = -1;
}
}
if (i < 0 ||
objarray->vector[i]->getParent() == scopeobj) {
JS_ASSERT(staticLevel == script->staticLevel);
EVAL_CACHE_METER(hit);
*scriptp = script->u.nextToGC;
script->u.nextToGC = NULL;
return script;
}
}
}
}
if (++count == EVAL_CACHE_CHAIN_LIMIT)
return NULL;
EVAL_CACHE_METER(step);
scriptp = &script->u.nextToGC;
}
return NULL;
}
static JSBool
obj_eval(JSContext *cx, uintN argc, Value *vp)
{
if (argc < 1) {
vp->setUndefined();
return true;
return JS_TRUE;
}
JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
jsbytecode *callerPC;
bool directCall = caller &&
(callerPC = caller->pc(cx)) != NULL &&
js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL;
if (!caller) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_INDIRECT_CALL, js_eval_str);
return JS_FALSE;
}
jsbytecode *callerPC = caller->pc(cx);
bool indirectCall = (callerPC && *callerPC != JSOP_EVAL);
/*
* If the callee was originally a cross-compartment wrapper, this is an
* indirect call.
* If the callee was originally a cross-compartment wrapper, this should
* be an indirect call.
*/
if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment())
directCall = false;
if (caller->scopeChain().compartment() != vp[0].toObject().compartment())
indirectCall = true;
/*
* Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....))
* that attempt to use a non-global object as the scope object.
*
* This ban is a bit silly, since we could just disregard the this-argument
* entirely and comply with ES5, which supports indirect eval. See bug
* 592664.
*/
{
JSObject *obj = ComputeThisFromVp(cx, vp);
if (!obj)
return JS_FALSE;
/*
* This call to JSObject::wrappedObject is safe because the result is
* only used for this check.
*/
obj = obj->wrappedObject(cx);
OBJ_TO_INNER_OBJECT(cx, obj);
if (!obj)
return JS_FALSE;
JSObject *parent = obj->getParent();
if (indirectCall || parent) {
uintN flags = parent
? JSREPORT_ERROR
: JSREPORT_STRICT | JSREPORT_WARNING;
if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
JSMSG_BAD_INDIRECT_CALL,
js_eval_str)) {
return JS_FALSE;
}
}
}
Value *argv = JS_ARGV(cx, vp);
if (!argv[0].isString()) {
*vp = argv[0];
return true;
return JS_TRUE;
}
/*
@ -1116,7 +1075,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
"Support for eval(code, scopeObject) has been removed. "
"Use |with (scopeObject) eval(code);| instead.";
if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
return false;
return JS_FALSE;
caller->script()->warnedAboutTwoArgumentEval = true;
}
@ -1124,45 +1083,55 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
MUST_FLOW_THROUGH("out");
uintN staticLevel = caller->script()->staticLevel + 1;
JSObject *scopeobj;
/*
* Per ES5, if we see an indirect call, then run in the global scope.
* (eval is specified this way so that the compiler can make assumptions
* about what bindings may or may not exist in the current frame if it
* doesn't see 'eval'.)
* Bring fp->scopeChain up to date. We're either going to use
* it (direct call) or save it and restore it (indirect call).
*/
if (directCall) {
JSObject *callerScopeChain;
if (callerPC && *callerPC == JSOP_EVAL)
callerScopeChain = js_GetScopeChainFast(cx, caller, JSOP_EVAL,
JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
else
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;
scopeobj = vp[0].toObject().getGlobal();
} else {
/*
* Compile using the caller's current scope object.
*
* NB: This means that the C API must not be used to call eval.
*/
scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL,
JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
if (!scopeobj)
return false;
JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
} else {
/* Pretend that we're top level. */
staticLevel = 0;
scopeobj = vp[0].toObject().getGlobal();
scopeobj = callerScopeChain;
}
#endif
/* Ensure we compile this eval with the right object in the scope chain. */
JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str);
JS_ASSERT_IF(result, result == scopeobj);
if (!result)
return false;
JS_ASSERT(result == scopeobj);
return JS_FALSE;
/*
* CSP check: Is eval() allowed at all?
* Report errors via CSP is done in the script security mgr.
*/
// CSP check: is eval() allowed at all?
// report errors via CSP is done in the script security mgr.
if (!js_CheckContentSecurityPolicy(cx)) {
JS_ReportError(cx, "call to eval() blocked by CSP");
return false;
return JS_FALSE;
}
JSObject *callee = &vp[0].toObject();
@ -1171,6 +1140,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
const char *file = js_ComputeFilename(cx, caller, principals, &line);
JSString *str = argv[0].toString();
JSScript *script = NULL;
const jschar *chars;
size_t length;
str->getCharsAndLength(chars, length);
@ -1189,14 +1160,86 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
ok = js_ConsumeJSONText(cx, jp, chars+1, length-2);
ok &= js_FinishJSONParse(cx, jp, NullValue());
if (ok)
return true;
return JS_TRUE;
}
}
JSScript *script = NULL;
JSScript **bucket = NULL;
if (directCall && caller->isFunctionFrame())
script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, &bucket);
/*
* Cache local eval scripts indexed by source qualified by scope.
*
* An eval cache entry should never be considered a hit unless its
* strictness matches that of the new eval code. The existing code takes
* care of this, because hits are qualified by the function from which
* eval was called, whose strictness doesn't change. Scripts produced by
* calls to eval from global code are not cached.
*/
JSScript **bucket = EvalCacheHash(cx, str);
if (!indirectCall && caller->isFunctionFrame()) {
uintN count = 0;
JSScript **scriptp = bucket;
EVAL_CACHE_METER(probe);
JSVersion version = cx->findVersion();
while ((script = *scriptp) != NULL) {
if (script->savedCallerFun &&
script->staticLevel == staticLevel &&
script->version == version &&
(script->principals == principals ||
(principals->subsume(principals, script->principals) &&
script->principals->subsume(script->principals, principals)))) {
/*
* Get the prior (cache-filling) eval's saved caller function.
* See Compiler::compileScript in jsparse.cpp.
*/
JSFunction *fun = script->getFunction(0);
if (fun == caller->fun()) {
/*
* Get the source string passed for safekeeping in the
* atom map by the prior eval to Compiler::compileScript.
*/
JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
if (src == str || js_EqualStrings(src, str)) {
/*
* Source matches, qualify by comparing scopeobj to the
* COMPILE_N_GO-memoized parent of the first literal
* function or regexp object if any. If none, then this
* script has no compiled-in dependencies on the prior
* eval's scopeobj.
*/
JSObjectArray *objarray = script->objects();
int i = 1;
if (objarray->length == 1) {
if (script->regexpsOffset != 0) {
objarray = script->regexps();
i = 0;
} else {
EVAL_CACHE_METER(noscope);
i = -1;
}
}
if (i < 0 ||
objarray->vector[i]->getParent() == scopeobj) {
JS_ASSERT(staticLevel == script->staticLevel);
EVAL_CACHE_METER(hit);
*scriptp = script->u.nextToGC;
script->u.nextToGC = NULL;
break;
}
}
}
}
if (++count == EVAL_CACHE_CHAIN_LIMIT) {
script = NULL;
break;
}
EVAL_CACHE_METER(step);
scriptp = &script->u.nextToGC;
}
}
/*
* We can't have a callerFrame (down in js_Execute's terms) if we're in
@ -1211,7 +1254,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
chars, length,
NULL, file, line, str, staticLevel);
if (!script)
return false;
return JS_FALSE;
}
assertSameCompartment(cx, scopeobj, script);
@ -1224,10 +1267,8 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
cx->runtime->atomState.evalAtom) &&
Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
if (bucket) {
script->u.nextToGC = *bucket;
*bucket = script;
}
script->u.nextToGC = *bucket;
*bucket = script;
#ifdef CHECK_SCRIPT_OWNER
script->owner = NULL;
#endif

View File

@ -86,6 +86,7 @@
#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */
#endif
#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */
#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */
#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */
#define JS_HAS_XDR 0 /* has XDR API and internal support */
#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */
@ -114,6 +115,7 @@
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
#define JS_HAS_XDR 1 /* has XDR API and internal support */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
@ -138,6 +140,7 @@
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
#define JS_HAS_XDR 1 /* has XDR API and internal support */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
@ -162,6 +165,7 @@
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
#define JS_HAS_XDR 1 /* has XDR API and internal support */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
@ -186,6 +190,7 @@
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
#define JS_HAS_XDR 1 /* has XDR API and internal support */
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */

View File

@ -1,38 +0,0 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var a = 9;
var global = this;
function test() {
var a = 0;
// direct eval sees local a
assertEq(eval('a+1'), 1);
assertEq(eval('eval("a+1")'), 1);
// indirect: using a name other than 'eval'
var foo = eval;
assertEq(foo('a+1'), 10);
assertEq(eval('foo("a+1")'), 10); // outer eval is direct, inner foo("a+1") is indirect
// indirect: qualified method call
assertEq(this.eval("a+1"), 10);
assertEq(global.eval("a+1"), 10);
var obj = {foo: eval, eval: eval};
assertEq(obj.foo('a+1'), 10);
assertEq(obj.eval('a+1'), 10);
var name = "eval";
assertEq(obj[name]('a+1'), 10);
assertEq([eval][0]('a+1'), 10);
// indirect: not called from a CallExpression at all
assertEq(eval.call(undefined, 'a+1'), 10);
assertEq(eval.call(global, 'a+1'), 10);
assertEq(eval.apply(undefined, ['a+1']), 10);
assertEq(eval.apply(global, ['a+1']), 10);
assertEq(['a+1'].map(eval)[0], 10);
}
test();
reportCompare(0, 0);

View File

@ -1,38 +0,0 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var a = 9;
function directArg(eval, s) {
var a = 1;
return eval(s);
}
function directVar(f, s) {
var eval = f;
var a = 1;
return eval(s);
}
function directWith(obj, s) {
var f;
with (obj) {
f = function () {
var a = 1;
return eval(s);
};
}
return f();
}
// direct eval, even though 'eval' is an argument
assertEq(directArg(eval, 'a+1'), 2);
// direct eval, even though 'eval' is a var
assertEq(directVar(eval, 'a+1'), 2);
// direct eval, even though 'eval' is found via a with block
assertEq(directWith(this, 'a+1'), 2);
assertEq(directWith({eval: eval, a: -1000}, 'a+1'), 2);
reportCompare(0, 0);

View File

@ -1,4 +1,2 @@
url-prefix ../../jsreftest.html?test=ecma_5/Global/
script parseInt-01.js
script eval-01.js
script eval-02.js

View File

@ -146,6 +146,7 @@ script regress-380581.js
script regress-380889.js
script regress-381211.js
script regress-381304.js
script regress-382509.js
script regress-384680.js
script regress-385134.js
script regress-385393-02.js

View File

@ -0,0 +1,117 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is JavaScript Engine testing utilities.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//-----------------------------------------------------------------------------
var BUGNUMBER = 382509;
var summary = 'Disallow non-global indirect eval';
var actual = '';
var expect = '';
var global = typeof window == 'undefined' ? this : window;
var object = {};
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
if (options().match(/strict/))
{
options('strict');
}
if (options().match(/werror/))
{
options('werror');
}
global.foo = eval;
global.a = 'global';
expect = 'global indirect';
actual = global.foo('a+" indirect"');
reportCompare(expect, actual, summary + ': global indirect');
object.foo = eval;
object.a = 'local';
expect = 'EvalError: function eval must be called directly, and not by way of a function of another name';
try
{
actual = object.foo('a+" indirect"');
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': local indirect');
options('strict');
options('werror');
try
{
var foo = eval;
print("foo(1+1)" + foo('1+1'));
actual = 'No Error';
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': strict, rename warning');
options('strict');
options('werror');
expect = 'No Error';
try
{
var foo = eval;
foo('1+1');
actual = 'No Error';
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': not strict, no rename warning');
exitFunc ('test');
}

View File

@ -11,5 +11,6 @@ script regress-353078.js
script regress-355002.js
script regress-372565.js
script regress-378492.js
script regress-382509.js
script regress-475469.js
script regress-476655.js

View File

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is JavaScript Engine testing utilities.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//-----------------------------------------------------------------------------
var BUGNUMBER = 382509;
var summary = 'Disallow non-global indirect eval';
var actual = '';
var expect = '';
var global = typeof window == 'undefined' ? this : window;
var object = {};
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
global.foo = eval;
global.a = 'global';
expect = 'global indirect';
actual = String(['a+" indirect"'].map(global.foo));
reportCompare(expect, actual, summary + ': global indirect');
object.foo = eval;
object.a = 'local';
expect = 'EvalError: function eval must be called directly, and not by way of a function of another name';
try
{
actual = String(['a+" indirect"'].map(object.foo, object));
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': local indirect');
exitFunc ('test');
}