Bug 608473 - |var eval = otherWindow.eval; eval(...)| should behave like indirectly calling that eval from a script in that other window. r=jorendorff

--HG--
extra : rebase_source : 9accca7e9a2b8304c2c823852cfd71b13c595afe
This commit is contained in:
Jeff Walden 2011-03-02 20:56:37 -08:00
parent 5c723ace4f
commit fc5075d1f7
9 changed files with 99 additions and 29 deletions

View File

@ -603,7 +603,7 @@ JS_SameValue(JSContext *cx, jsval v1, jsval v2, JSBool *same)
JS_PUBLIC_API(JSBool)
JS_IsBuiltinEvalFunction(JSFunction *fun)
{
return IsBuiltinEvalFunction(fun);
return IsAnyBuiltinEval(fun);
}
JS_PUBLIC_API(JSBool)

View File

@ -1980,13 +1980,14 @@ struct JSClass {
#define JSCLASS_FREEZE_CTOR (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
/* Additional global reserved slots, beyond those for standard prototypes. */
#define JSRESERVED_GLOBAL_SLOTS_COUNT 6
#define JSRESERVED_GLOBAL_SLOTS_COUNT 7
#define JSRESERVED_GLOBAL_THIS (JSProto_LIMIT * 3)
#define JSRESERVED_GLOBAL_THROWTYPEERROR (JSRESERVED_GLOBAL_THIS + 1)
#define JSRESERVED_GLOBAL_REGEXP_STATICS (JSRESERVED_GLOBAL_THROWTYPEERROR + 1)
#define JSRESERVED_GLOBAL_FUNCTION_NS (JSRESERVED_GLOBAL_REGEXP_STATICS + 1)
#define JSRESERVED_GLOBAL_EVAL_ALLOWED (JSRESERVED_GLOBAL_FUNCTION_NS + 1)
#define JSRESERVED_GLOBAL_FLAGS (JSRESERVED_GLOBAL_EVAL_ALLOWED + 1)
#define JSRESERVED_GLOBAL_EVAL (JSRESERVED_GLOBAL_EVAL_ALLOWED + 1)
#define JSRESERVED_GLOBAL_FLAGS (JSRESERVED_GLOBAL_EVAL + 1)
/* Global flags. */
#define JSGLOBAL_FLAGS_CLEARED 0x1

View File

@ -1315,17 +1315,16 @@ InvokeConstructorWithGivenThis(JSContext *cx, JSObject *thisobj, const Value &fv
}
bool
DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp)
DirectEval(JSContext *cx, uint32 argc, Value *vp)
{
JS_ASSERT(vp == cx->regs->sp - argc - 2);
JS_ASSERT(vp[0].isObject());
JS_ASSERT(vp[0].toObject().isFunction());
JS_ASSERT(vp[0].toObject().getFunctionPrivate() == evalfun);
JS_ASSERT(IsBuiltinEvalFunction(evalfun));
JSStackFrame *caller = cx->fp();
JS_ASSERT(caller->isScriptFrame());
AutoFunctionCallProbe callProbe(cx, evalfun, caller->script());
JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), vp[0]));
AutoFunctionCallProbe callProbe(cx, vp[0].toObject().getFunctionPrivate(), caller->script());
JSObject *scopeChain =
GetScopeChainFast(cx, caller, JSOP_EVAL, JSOP_EVAL_LENGTH + JSOP_LINENO_LENGTH);
@ -4698,14 +4697,10 @@ BEGIN_CASE(JSOP_EVAL)
argc = GET_ARGC(regs.pc);
vp = regs.sp - (argc + 2);
if (!IsFunctionObject(*vp, &callee))
if (!IsBuiltinEvalForScope(&regs.fp->scopeChain(), *vp))
goto call_using_invoke;
newfun = callee->getFunctionPrivate();
if (!IsBuiltinEvalFunction(newfun))
goto call_using_invoke;
if (!DirectEval(cx, newfun, argc, vp))
if (!DirectEval(cx, argc, vp))
goto error;
}
END_CASE(JSOP_EVAL)

View File

@ -964,13 +964,11 @@ ExternalInvokeConstructor(JSContext *cx, const Value &fval, uintN argc, Value *a
/*
* Performs a direct eval for the given arguments, which must correspond to the
* currently-executing stack frame, which must be a script frame. evalfun must
* be the built-in eval function and must correspond to the callee in vp[0].
* When this function succeeds it returns the result in *vp, adjusts the JS
* stack pointer, and returns true.
* currently-executing stack frame, which must be a script frame. On completion
* the result is returned in *vp and the JS stack pointer is adjusted.
*/
extern JS_REQUIRES_STACK bool
DirectEval(JSContext *cx, JSFunction *evalfun, uint32 argc, Value *vp);
DirectEval(JSContext *cx, uint32 argc, Value *vp);
/*
* Performs a direct eval for the given arguments, which must correspond to the

View File

@ -1187,7 +1187,7 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
/* ES5 15.1.2.1 steps 2-8. */
JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, Jsvalify(vp)));
JS_ASSERT(IsBuiltinEvalFunction(callee->getFunctionPrivate()));
JS_ASSERT(IsAnyBuiltinEval(callee->getFunctionPrivate()));
JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
/*
@ -1307,7 +1307,15 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
}
bool
IsBuiltinEvalFunction(JSFunction *fun)
IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
{
JSObject *global = scopeChain->getGlobal();
JS_ASSERT((global->getClass()->flags & JSCLASS_GLOBAL_FLAGS) == JSCLASS_GLOBAL_FLAGS);
return global->getReservedSlot(JSRESERVED_GLOBAL_EVAL) == v;
}
bool
IsAnyBuiltinEval(JSFunction *fun)
{
return fun->maybeNative() == eval;
}
@ -3777,8 +3785,13 @@ js_InitObjectClass(JSContext *cx, JSObject *obj)
/* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
if (!js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS))
JSObject *evalobj = js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS);
if (!evalobj)
return NULL;
if (obj->isGlobal()) {
if (!js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_EVAL, ObjectValue(*evalobj)))
return NULL;
}
return proto;
}

View File

@ -1940,8 +1940,16 @@ extern bool
EvalKernel(JSContext *cx, uintN argc, js::Value *vp, EvalType evalType, JSStackFrame *caller,
JSObject *scopeobj);
/*
* True iff |v| is the built-in eval function for the global object that
* corresponds to |scopeChain|.
*/
extern bool
IsBuiltinEvalFunction(JSFunction *fun);
IsBuiltinEvalForScope(JSObject *scopeChain, const js::Value &v);
/* True iff fun is a built-in eval function. */
extern bool
IsAnyBuiltinEval(JSFunction *fun);
}

View File

@ -437,19 +437,14 @@ stubs::Eval(VMFrame &f, uint32 argc)
{
Value *vp = f.regs.sp - (argc + 2);
JSObject *callee;
JSFunction *fun;
if (!IsFunctionObject(*vp, &callee) ||
!IsBuiltinEvalFunction((fun = callee->getFunctionPrivate())))
{
if (!IsBuiltinEvalForScope(&f.regs.fp->scopeChain(), *vp)) {
if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
THROW();
return;
}
JS_ASSERT(f.regs.fp == f.cx->fp());
if (!DirectEval(f.cx, fun, argc, vp))
if (!DirectEval(f.cx, argc, vp))
THROW();
}

View File

@ -0,0 +1,59 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
//-----------------------------------------------------------------------------
var BUGNUMBER = 608473;
var summary =
'|var eval = otherWindow.eval; eval(...)| should behave like indirectly ' +
'calling that eval from a script in that other window';
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var originalEval = eval;
var res;
function f()
{
return [this, eval("this")];
}
var otherGlobalSameCompartment = newGlobal("same-compartment");
eval = otherGlobalSameCompartment.eval;
res = new f();
assertEq(res[0] !== res[1], true);
assertEq(res[0] !== this, true);
assertEq(res[0] instanceof f, true);
assertEq(res[1], otherGlobalSameCompartment);
res = f();
assertEq(res[0] !== res[1], true);
assertEq(res[0], this);
assertEq(res[1], otherGlobalSameCompartment);
var otherGlobalDifferentCompartment = newGlobal("new-compartment");
eval = otherGlobalDifferentCompartment.eval;
res = new f();
assertEq(res[0] !== res[1], true);
assertEq(res[0] !== this, true);
assertEq(res[0] instanceof f, true);
assertEq(res[1], otherGlobalDifferentCompartment);
res = f();
assertEq(res[0] !== res[1], true);
assertEq(res[0], this);
assertEq(res[1], otherGlobalDifferentCompartment);
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

View File

@ -9,6 +9,7 @@ script bug352085.js
script bug472534.js
script bug496985.js
script bug566661.js
skip-if(!xulRuntime.shell) script cross-global-eval-is-indirect.js # needs newGlobal()
script eval-native-callback-is-indirect.js
script extension-methods-reject-null-undefined-this.js
skip-if(!xulRuntime.shell) script function-definition-with.js # needs evaluate()