зеркало из https://github.com/mozilla/pjs.git
Bug 592644 - ES5 indirect eval. Part 1, add support and tests. r=brendan.
--HG-- extra : rebase_source : 5d07f3e847b2adfb46f30f343af2e06d494012c3
This commit is contained in:
Родитель
c4ead6da5c
Коммит
332c179f26
101
js/src/jsobj.cpp
101
js/src/jsobj.cpp
|
@ -1008,56 +1008,21 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
|
|||
|
||||
JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
|
||||
if (!caller) {
|
||||
/* Eval code needs to inherit principals from the 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);
|
||||
bool directCall = (callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
|
||||
|
||||
/*
|
||||
* If the callee was originally a cross-compartment wrapper, this should
|
||||
* be an indirect call.
|
||||
* If the callee was originally a cross-compartment wrapper, this is an
|
||||
* indirect call.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (directCall && caller->scopeChain().compartment() != vp[0].toObject().compartment())
|
||||
directCall = false;
|
||||
|
||||
Value *argv = JS_ARGV(cx, vp);
|
||||
if (!argv[0].isString()) {
|
||||
|
@ -1083,52 +1048,38 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
|
|||
MUST_FLOW_THROUGH("out");
|
||||
uintN staticLevel = caller->script()->staticLevel + 1;
|
||||
|
||||
JSObject *scopeobj;
|
||||
|
||||
/*
|
||||
* Bring fp->scopeChain up to date. We're either going to use
|
||||
* it (direct call) or save it and restore it (indirect call).
|
||||
* 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'.)
|
||||
*/
|
||||
JSObject *callerScopeChain;
|
||||
if (directCall) {
|
||||
/* Compile using the caller's current scope object. */
|
||||
scopeobj = js_GetScopeChainFast(cx, caller, JSOP_EVAL,
|
||||
JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
|
||||
if (!scopeobj)
|
||||
return JS_FALSE;
|
||||
|
||||
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) {
|
||||
JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
|
||||
} else {
|
||||
/* 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.
|
||||
*/
|
||||
JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
|
||||
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 JS_FALSE;
|
||||
JS_ASSERT(result == scopeobj);
|
||||
|
||||
// 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 JS_FALSE;
|
||||
|
@ -1174,7 +1125,7 @@ obj_eval(JSContext *cx, uintN argc, Value *vp)
|
|||
* calls to eval from global code are not cached.
|
||||
*/
|
||||
JSScript **bucket = EvalCacheHash(cx, str);
|
||||
if (!indirectCall && caller->isFunctionFrame()) {
|
||||
if (directCall && caller->isFunctionFrame()) {
|
||||
uintN count = 0;
|
||||
JSScript **scriptp = bucket;
|
||||
|
||||
|
|
|
@ -86,7 +86,6 @@
|
|||
#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 */
|
||||
|
@ -115,7 +114,6 @@
|
|||
#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 */
|
||||
|
@ -140,7 +138,6 @@
|
|||
#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 */
|
||||
|
@ -165,7 +162,6 @@
|
|||
#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 */
|
||||
|
@ -190,7 +186,6 @@
|
|||
#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 */
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// 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);
|
|
@ -0,0 +1,38 @@
|
|||
// 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);
|
|
@ -1,2 +1,4 @@
|
|||
url-prefix ../../jsreftest.html?test=ecma_5/Global/
|
||||
script parseInt-01.js
|
||||
script eval-01.js
|
||||
script eval-02.js
|
||||
|
|
|
@ -146,7 +146,6 @@ 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
|
||||
|
|
|
@ -1,117 +0,0 @@
|
|||
/* -*- 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');
|
||||
}
|
|
@ -11,6 +11,5 @@ 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
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/* -*- 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');
|
||||
}
|
Загрузка…
Ссылка в новой задаче