Bug 514568 - Trace strict eval frame Call objects correctly, keep the eval script safe from GC, and properly put strict eval Call objects when the script completes. r=igor

This commit is contained in:
Jeff Walden 2011-01-05 13:55:18 -06:00
parent 3bcc7a15d3
commit 9bfd43b2d9
7 changed files with 92 additions and 75 deletions

View File

@ -1079,58 +1079,70 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
/* Get the arguments object to snapshot fp's actual argument values. */
if (fp->hasArgsObj()) {
JS_ASSERT(!fp->isEvalFrame());
if (!fp->hasOverriddenArgs())
callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
js_PutArgsObject(cx, fp);
}
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
JSScript *script = fp->script();
Bindings &bindings = script->bindings;
Bindings &bindings = fun->script()->bindings;
uintN n = bindings.countArgsAndVars();
JSObject *callee = callobj.getCallObjCallee();
if (callee) {
JSFunction *fun = fp->fun();
JS_ASSERT(fun == callee->getFunctionPrivate());
JS_ASSERT(script == fun->script());
if (n != 0) {
JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
if (uintN n = bindings.countArgsAndVars()) {
JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
uint32 nvars = bindings.countVars();
uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
uint32 nvars = bindings.countVars();
uint32 nargs = bindings.countArgs();
JS_ASSERT(fun->nargs == nargs);
JS_ASSERT(nvars + nargs == n);
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
#endif
) {
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object.
*/
uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
}
JSScript *script = fun->script();
if (script->usesEval
#ifdef JS_METHODJIT
|| script->debugMode
#endif
) {
CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
} else {
/*
* For each arg & var that is closed over, copy it from the stack
* into the call object.
*/
uint32 nclosed = script->nClosedArgs;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedArg(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
}
nclosed = script->nClosedVars;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedVar(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
nclosed = script->nClosedVars;
for (uint32 i = 0; i < nclosed; i++) {
uint32 e = script->getClosedVar(i);
callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
}
}
}
}
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent();
/* Clear private pointers to fp, which is about to go away (js_Invoke). */
if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
JS_ASSERT(env->getPrivate() == fp);
env->setPrivate(NULL);
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
JS_ASSERT(env->getPrivate() == fp);
env->setPrivate(NULL);
}
} else {
JS_ASSERT(fp->isEvalFrame());
JS_ASSERT(script->strictModeCode);
JS_ASSERT(bindings.countArgs() == 0);
/* This could be optimized as above. But for now, keep it simple. */
CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
}
callobj.setPrivate(NULL);
@ -1360,8 +1372,7 @@ static void
call_trace(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isCall());
JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
if (fp) {
if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
/*
* FIXME: Hide copies of stack values rooted by fp from the Cycle
* Collector, which currently lacks a non-stub Unlink implementation
@ -1370,7 +1381,7 @@ call_trace(JSTracer *trc, JSObject *obj)
* hiding hack.
*/
uintN first = JSObject::CALL_RESERVED_SLOTS;
uintN count = fp->fun()->script()->bindings.countArgsAndVars();
uintN count = fp->script()->bindings.countArgsAndVars();
JS_ASSERT(obj->numSlots() >= first + count);
SetValueRangeToUndefined(obj->getSlots() + first, count);

View File

@ -966,12 +966,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
}
#if 0 /* to be reenabled shortly when this works */
/*
* 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) {
AutoScriptRooter root(cx, script);
initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
if (!initialVarObj)
return false;
@ -982,7 +982,6 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.fp()->clearCallObj();
frame.fp()->setScopeChainAndCallObj(*initialVarObj);
}
#endif
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
#if JS_HAS_SHARP_VARS

View File

@ -722,13 +722,28 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
hook(cx, fp, JS_FALSE, &ok, hookData);
/*
* An eval frame's parent owns its activation objects. A yielding frame's
* activation objects are transferred to the floating frame, stored in the
* generator.
*/
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
PutActivationObjects(cx, fp);
if (fp->isEvalFrame()) {
/*
* The parent (ancestor for nested eval) of a non-strict eval frame
* owns its activation objects. Strict mode eval frames own their own
* Call objects but never have an arguments object (the first non-eval
* parent frame has it).
*/
if (fp->script()->strictModeCode) {
JS_ASSERT(!fp->isYielding());
JS_ASSERT(fp->hasCallObj());
JS_ASSERT(!fp->hasArgsObj());
js_PutCallObject(cx, fp);
}
} else {
/*
* Otherwise only function frames have activation objects. A yielding
* frame's activation objects are transferred to the floating frame,
* stored in the generator, and thus need not be synced.
*/
if (fp->isFunctionFrame() && !fp->isYielding())
PutActivationObjects(cx, fp);
}
/*
* If inline-constructing, replace primitive rval with the new object

View File

@ -480,13 +480,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
return;
}
void JS_FASTCALL
stubs::PutCallObject(VMFrame &f)
{
JS_ASSERT(f.fp()->hasCallObj());
js_PutCallObject(f.cx, f.fp());
}
void JS_FASTCALL
stubs::PutActivationObjects(VMFrame &f)
{

View File

@ -107,7 +107,6 @@ void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
void JS_FASTCALL Throw(VMFrame &f);
void JS_FASTCALL PutCallObject(VMFrame &f);
void JS_FASTCALL PutActivationObjects(VMFrame &f);
void JS_FASTCALL GetCallObject(VMFrame &f);
#if JS_MONOIC

View File

@ -16,22 +16,22 @@ url-prefix ../../jsreftest.html?test=ecma_5/eval/
# optimizations we might perform -- add new tests for such changes as needed.
#
# script exhaustive-fun-normalcaller-direct-normalcode.js
# script exhaustive-fun-normalcaller-direct-strictcode.js
# script exhaustive-fun-normalcaller-indirect-normalcode.js
# script exhaustive-fun-normalcaller-indirect-strictcode.js
# script exhaustive-fun-strictcaller-direct-normalcode.js
# script exhaustive-fun-strictcaller-direct-strictcode.js
# script exhaustive-fun-strictcaller-indirect-normalcode.js
# script exhaustive-fun-strictcaller-indirect-strictcode.js
# script exhaustive-global-normalcaller-direct-normalcode.js
# script exhaustive-global-normalcaller-direct-strictcode.js
# script exhaustive-global-normalcaller-indirect-normalcode.js
# script exhaustive-global-normalcaller-indirect-strictcode.js
# script exhaustive-global-strictcaller-direct-normalcode.js
# script exhaustive-global-strictcaller-direct-strictcode.js
# script exhaustive-global-strictcaller-indirect-normalcode.js
# script exhaustive-global-strictcaller-indirect-strictcode.js
script exhaustive-fun-normalcaller-direct-normalcode.js
script exhaustive-fun-normalcaller-direct-strictcode.js
script exhaustive-fun-normalcaller-indirect-normalcode.js
script exhaustive-fun-normalcaller-indirect-strictcode.js
script exhaustive-fun-strictcaller-direct-normalcode.js
script exhaustive-fun-strictcaller-direct-strictcode.js
script exhaustive-fun-strictcaller-indirect-normalcode.js
script exhaustive-fun-strictcaller-indirect-strictcode.js
script exhaustive-global-normalcaller-direct-normalcode.js
script exhaustive-global-normalcaller-direct-strictcode.js
script exhaustive-global-normalcaller-indirect-normalcode.js
script exhaustive-global-normalcaller-indirect-strictcode.js
script exhaustive-global-strictcaller-direct-normalcode.js
script exhaustive-global-strictcaller-direct-strictcode.js
script exhaustive-global-strictcaller-indirect-normalcode.js
script exhaustive-global-strictcaller-indirect-strictcode.js
# Not written yet! These require a new shell primitive to work there, though
# browser could probably use setTimeout for this. Moreover, we haven't

View File

@ -39,4 +39,4 @@ script unbrand-this.js
script this-for-function-expression-recursion.js
script assign-to-callee-name.js
script directive-prologue-01.js
# script eval-variable-environment.js
script eval-variable-environment.js