mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 514568 - Use a fresh variable environment for strict mode code run by eval, and give strict mode eval code frames a Call object backed by those variables. r=igor
This commit is contained in:
parent
8144b311b1
commit
de8c94c378
@ -2071,8 +2071,22 @@ MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
|
|||||||
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
* Try to convert a *NAME op to a *GNAME op, which optimizes access to
|
||||||
* undeclared globals. Return true if a conversion was made.
|
* undeclared globals. Return true if a conversion was made.
|
||||||
*
|
*
|
||||||
* This conversion is not made if we are in strict mode, because the
|
* This conversion is not made if we are in strict mode. In eval code nested
|
||||||
* access to an undeclared global would be an error.
|
* within (strict mode) eval code, access to an undeclared "global" might
|
||||||
|
* merely be to a binding local to that outer eval:
|
||||||
|
*
|
||||||
|
* "use strict";
|
||||||
|
* var x = "global";
|
||||||
|
* eval('var x = "eval"; eval("x");'); // 'eval', not 'global'
|
||||||
|
*
|
||||||
|
* Outside eval code, access to an undeclared global is a strict mode error:
|
||||||
|
*
|
||||||
|
* "use strict";
|
||||||
|
* function foo()
|
||||||
|
* {
|
||||||
|
* undeclared = 17; // throws ReferenceError
|
||||||
|
* }
|
||||||
|
* foo();
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
TryConvertToGname(JSCodeGenerator *cg, JSParseNode *pn, JSOp *op)
|
TryConvertToGname(JSCodeGenerator *cg, JSParseNode *pn, JSOp *op)
|
||||||
|
@ -941,12 +941,16 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
return CheckForEscapingClosure(cx, obj, vp);
|
return CheckForEscapingClosure(cx, obj, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSObject *
|
namespace js {
|
||||||
NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &callee)
|
|
||||||
{
|
|
||||||
Bindings &bindings = fun->script()->bindings;
|
|
||||||
|
|
||||||
size_t argsVars = bindings.countArgsAndVars();
|
/*
|
||||||
|
* Construct a call object for the given bindings. The callee is the function
|
||||||
|
* on behalf of which the call object is being created.
|
||||||
|
*/
|
||||||
|
JSObject *
|
||||||
|
NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject *callee)
|
||||||
|
{
|
||||||
|
size_t argsVars = bindings->countArgsAndVars();
|
||||||
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
|
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
|
||||||
gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
|
gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
|
||||||
|
|
||||||
@ -956,7 +960,7 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca
|
|||||||
|
|
||||||
/* Init immediately to avoid GC seeing a half-init'ed object. */
|
/* Init immediately to avoid GC seeing a half-init'ed object. */
|
||||||
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
|
callobj->init(cx, &js_CallClass, NULL, &scopeChain, NULL, false);
|
||||||
callobj->setMap(bindings.lastShape());
|
callobj->setMap(bindings->lastShape());
|
||||||
|
|
||||||
/* This must come after callobj->lastProp has been set. */
|
/* This must come after callobj->lastProp has been set. */
|
||||||
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
|
if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
|
||||||
@ -976,6 +980,8 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject &scopeChain, JSObject &ca
|
|||||||
return callobj;
|
return callobj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
static inline JSObject *
|
static inline JSObject *
|
||||||
NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
|
NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
|
||||||
{
|
{
|
||||||
@ -1029,7 +1035,8 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject *callobj = NewCallObject(cx, fp->fun(), fp->scopeChain(), fp->callee());
|
JSObject *callobj =
|
||||||
|
NewCallObject(cx, &fp->fun()->script()->bindings, fp->scopeChain(), &fp->callee());
|
||||||
if (!callobj)
|
if (!callobj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -1049,7 +1056,8 @@ js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSO
|
|||||||
{
|
{
|
||||||
JS_ASSERT(!js_IsNamedLambda(fun));
|
JS_ASSERT(!js_IsNamedLambda(fun));
|
||||||
JS_ASSERT(scopeChain);
|
JS_ASSERT(scopeChain);
|
||||||
return NewCallObject(cx, fun, *scopeChain, *callee);
|
JS_ASSERT(callee);
|
||||||
|
return NewCallObject(cx, &fun->script()->bindings, *scopeChain, callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
|
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
|
||||||
@ -1208,7 +1216,10 @@ GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||||
uintN i = (uint16) JSID_TO_INT(id);
|
uintN i = (uint16) JSID_TO_INT(id);
|
||||||
|
|
||||||
*vp = obj->getCallObjCallee().getFlatClosureUpvar(i);
|
JSObject *callee = obj->getCallObjCallee();
|
||||||
|
JS_ASSERT(callee);
|
||||||
|
|
||||||
|
*vp = callee->getFlatClosureUpvar(i);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1218,7 +1229,10 @@ SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
|
||||||
uintN i = (uint16) JSID_TO_INT(id);
|
uintN i = (uint16) JSID_TO_INT(id);
|
||||||
|
|
||||||
Value *upvarp = &obj->getCallObjCallee().getFlatClosureUpvar(i);
|
JSObject *callee = obj->getCallObjCallee();
|
||||||
|
JS_ASSERT(callee);
|
||||||
|
|
||||||
|
Value *upvarp = &callee->getFlatClosureUpvar(i);
|
||||||
GC_POKE(cx, *upvarp);
|
GC_POKE(cx, *upvarp);
|
||||||
*upvarp = *vp;
|
*upvarp = *vp;
|
||||||
return true;
|
return true;
|
||||||
@ -1296,9 +1310,15 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||||||
JS_ASSERT(!obj->getProto());
|
JS_ASSERT(!obj->getProto());
|
||||||
|
|
||||||
if (!JSID_IS_ATOM(id))
|
if (!JSID_IS_ATOM(id))
|
||||||
return JS_TRUE;
|
return true;
|
||||||
|
|
||||||
JS_ASSERT(!obj->getCallObjCalleeFunction()->script()->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
|
JSObject *callee = obj->getCallObjCallee();
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (callee) {
|
||||||
|
JSScript *script = callee->getFunctionPrivate()->script();
|
||||||
|
JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Resolve arguments so that we never store a particular Call object's
|
* Resolve arguments so that we never store a particular Call object's
|
||||||
@ -1308,19 +1328,19 @@ call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
|||||||
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
|
* properties; see js::Bindings::add and js::Interpret's JSOP_DEFFUN
|
||||||
* rebinding-Call-property logic.
|
* rebinding-Call-property logic.
|
||||||
*/
|
*/
|
||||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.argumentsAtom)) {
|
if (callee && id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
|
||||||
if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
|
if (!js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
|
||||||
GetCallArguments, SetCallArguments,
|
GetCallArguments, SetCallArguments,
|
||||||
JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
|
JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE,
|
||||||
0, 0, NULL, JSDNP_DONT_PURGE)) {
|
0, 0, NULL, JSDNP_DONT_PURGE)) {
|
||||||
return JS_FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
*objp = obj;
|
*objp = obj;
|
||||||
return JS_TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Control flow reaches here only if id was not resolved. */
|
/* Control flow reaches here only if id was not resolved. */
|
||||||
return JS_TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -321,6 +321,15 @@ JSObject::getFunctionPrivate() const
|
|||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct a call object for the given bindings. If this is a call object
|
||||||
|
* for a function invocation, callee should be the function being called.
|
||||||
|
* Otherwise it must be a call object for eval of strict mode code, and callee
|
||||||
|
* must be null.
|
||||||
|
*/
|
||||||
|
extern JSObject *
|
||||||
|
NewCallObject(JSContext *cx, js::Bindings *bindings, JSObject &scopeChain, JSObject *callee);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NB: jsapi.h and jsobj.h must be included before any call to this macro.
|
* NB: jsapi.h and jsobj.h must be included before any call to this macro.
|
||||||
*/
|
*/
|
||||||
|
@ -921,6 +921,9 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||||||
if (!cx->stack().getExecuteFrame(cx, script, &frame))
|
if (!cx->stack().getExecuteFrame(cx, script, &frame))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* Initialize fixed slots (GVAR ops expect NULL). */
|
||||||
|
SetValueRangeToNull(frame.fp()->slots(), script->nfixed);
|
||||||
|
|
||||||
/* Initialize frame and locals. */
|
/* Initialize frame and locals. */
|
||||||
JSObject *initialVarObj;
|
JSObject *initialVarObj;
|
||||||
if (prev) {
|
if (prev) {
|
||||||
@ -953,15 +956,26 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||||||
return false;
|
return false;
|
||||||
frame.fp()->globalThis().setObject(*thisp);
|
frame.fp()->globalThis().setObject(*thisp);
|
||||||
|
|
||||||
initialVarObj = (cx->options & JSOPTION_VAROBJFIX)
|
initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
|
||||||
? chain->getGlobal()
|
}
|
||||||
: chain;
|
|
||||||
|
/*
|
||||||
|
* Strict mode eval code receives its own, fresh lexical environment; thus
|
||||||
|
* strict mode eval can't mutate its calling frame's binding set.
|
||||||
|
*/
|
||||||
|
if (script->strictModeCode) {
|
||||||
|
initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
|
||||||
|
if (!initialVarObj)
|
||||||
|
return false;
|
||||||
|
initialVarObj->setPrivate(frame.fp());
|
||||||
|
|
||||||
|
/* Clear the Call object propagated from the previous frame, if any. */
|
||||||
|
if (frame.fp()->hasCallObj())
|
||||||
|
frame.fp()->clearCallObj();
|
||||||
|
frame.fp()->setScopeChainAndCallObj(*initialVarObj);
|
||||||
}
|
}
|
||||||
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
|
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
|
||||||
|
|
||||||
/* Initialize fixed slots (GVAR ops expect NULL). */
|
|
||||||
SetValueRangeToNull(frame.fp()->slots(), script->nfixed);
|
|
||||||
|
|
||||||
#if JS_HAS_SHARP_VARS
|
#if JS_HAS_SHARP_VARS
|
||||||
JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
|
JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
|
||||||
if (script->hasSharps) {
|
if (script->hasSharps) {
|
||||||
|
@ -188,9 +188,7 @@ JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev,
|
|||||||
|
|
||||||
/* Initialize stack frame members. */
|
/* Initialize stack frame members. */
|
||||||
flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN |
|
flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN |
|
||||||
(prev->flags_ & (JSFRAME_FUNCTION |
|
(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL | JSFRAME_HAS_CALL_OBJ));
|
||||||
JSFRAME_GLOBAL |
|
|
||||||
JSFRAME_HAS_CALL_OBJ));
|
|
||||||
if (isFunctionFrame()) {
|
if (isFunctionFrame()) {
|
||||||
exec = prev->exec;
|
exec = prev->exec;
|
||||||
args.script = script;
|
args.script = script;
|
||||||
|
@ -880,7 +880,15 @@ struct JSObject : js::gc::Cell {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* Reserved slot structure for Arguments objects:
|
* Reserved slot structure for Call objects:
|
||||||
|
*
|
||||||
|
* private - the stack frame corresponding to the Call object
|
||||||
|
* until js_PutCallObject or its on-trace analog
|
||||||
|
* is called, null thereafter
|
||||||
|
* JSSLOT_CALL_CALLEE - callee function for the stack frame, or null if
|
||||||
|
* the stack frame is for strict mode eval code
|
||||||
|
* JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
|
||||||
|
* frames (not valid for strict mode eval frames)
|
||||||
*/
|
*/
|
||||||
static const uint32 JSSLOT_CALL_CALLEE = 0;
|
static const uint32 JSSLOT_CALL_CALLEE = 0;
|
||||||
static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
|
static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
|
||||||
@ -892,9 +900,13 @@ struct JSObject : js::gc::Cell {
|
|||||||
/* The stack frame for this Call object, if the frame is still active. */
|
/* The stack frame for this Call object, if the frame is still active. */
|
||||||
inline JSStackFrame *maybeCallObjStackFrame() const;
|
inline JSStackFrame *maybeCallObjStackFrame() const;
|
||||||
|
|
||||||
inline JSObject &getCallObjCallee() const;
|
/*
|
||||||
|
* The callee function if this Call object was created for a function
|
||||||
|
* invocation, or null if it was created for a strict mode eval frame.
|
||||||
|
*/
|
||||||
|
inline JSObject *getCallObjCallee() const;
|
||||||
inline JSFunction *getCallObjCalleeFunction() const;
|
inline JSFunction *getCallObjCalleeFunction() const;
|
||||||
inline void setCallObjCallee(JSObject &callee);
|
inline void setCallObjCallee(JSObject *callee);
|
||||||
|
|
||||||
inline const js::Value &getCallObjArguments() const;
|
inline const js::Value &getCallObjArguments() const;
|
||||||
inline void setCallObjArguments(const js::Value &v);
|
inline void setCallObjArguments(const js::Value &v);
|
||||||
|
@ -440,18 +440,18 @@ JSObject::maybeCallObjStackFrame() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
JSObject::setCallObjCallee(JSObject &callee)
|
JSObject::setCallObjCallee(JSObject *callee)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
JS_ASSERT(callee.isFunction());
|
JS_ASSERT_IF(callee, callee->isFunction());
|
||||||
return getSlotRef(JSSLOT_CALL_CALLEE).setObject(callee);
|
return getSlotRef(JSSLOT_CALL_CALLEE).setObjectOrNull(callee);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JSObject &
|
inline JSObject *
|
||||||
JSObject::getCallObjCallee() const
|
JSObject::getCallObjCallee() const
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
return getSlot(JSSLOT_CALL_CALLEE).toObject();
|
return getSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JSFunction *
|
inline JSFunction *
|
||||||
@ -465,6 +465,7 @@ inline const js::Value &
|
|||||||
JSObject::getCallObjArguments() const
|
JSObject::getCallObjArguments() const
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
|
JS_ASSERT(getCallObjCallee() != NULL);
|
||||||
return getSlot(JSSLOT_CALL_ARGUMENTS);
|
return getSlot(JSSLOT_CALL_ARGUMENTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,6 +473,7 @@ inline void
|
|||||||
JSObject::setCallObjArguments(const js::Value &v)
|
JSObject::setCallObjArguments(const js::Value &v)
|
||||||
{
|
{
|
||||||
JS_ASSERT(isCall());
|
JS_ASSERT(isCall());
|
||||||
|
JS_ASSERT(getCallObjCallee() != NULL);
|
||||||
setSlot(JSSLOT_CALL_ARGUMENTS, v);
|
setSlot(JSSLOT_CALL_ARGUMENTS, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3081,7 +3081,7 @@ public:
|
|||||||
JS_ASSERT(p == fp->addressOfScopeChain());
|
JS_ASSERT(p == fp->addressOfScopeChain());
|
||||||
if (frameobj->isCall() &&
|
if (frameobj->isCall() &&
|
||||||
!frameobj->getPrivate() &&
|
!frameobj->getPrivate() &&
|
||||||
&fp->callee() == &frameobj->getCallObjCallee())
|
fp->maybeCallee() == frameobj->getCallObjCallee())
|
||||||
{
|
{
|
||||||
JS_ASSERT(&fp->scopeChain() == JSStackFrame::sInvalidScopeChain);
|
JS_ASSERT(&fp->scopeChain() == JSStackFrame::sInvalidScopeChain);
|
||||||
frameobj->setPrivate(fp);
|
frameobj->setPrivate(fp);
|
||||||
|
Loading…
Reference in New Issue
Block a user