Bug 636296 - Change meaning of JSStackFrame::hasCallObj to be more sane (r=waldo)

This commit is contained in:
Luke Wagner 2011-03-14 11:30:36 -07:00
parent 658e6134f9
commit 1117713217
16 changed files with 210 additions and 249 deletions

View File

@ -1494,14 +1494,13 @@ JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp)
if (!ac.enter())
return NULL;
/* Force creation of argument object if not yet created */
(void) js_GetArgsObject(cx, fp);
/*
* XXX ill-defined: null return here means error was reported, unlike a
* null returned above or in the #else
*/
return js_GetCallObject(cx, fp);
if (!fp->hasCallObj() && fp->isNonEvalFunctionFrame())
return CreateFunCallObject(cx, fp);
return &fp->callObj();
}
JS_PUBLIC_API(JSBool)

View File

@ -295,7 +295,7 @@ InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
stackDepth = 0;
valueCount = 0;
for (fp = js_GetTopStackFrame(cx); fp; fp = fp->prev()) {
if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
if (fp->isNonEvalFunctionFrame()) {
Value v = NullValue();
if (checkAccess &&
!checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) {

View File

@ -946,16 +946,17 @@ CalleeGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
return CheckForEscapingClosure(cx, obj, vp);
}
namespace js {
/*
* Construct a call object for the given bindings. The callee is the function
* on behalf of which the call object is being created.
* 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.
*/
JSObject *
NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject *callee)
static JSObject *
NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
{
size_t argsVars = bindings->countArgsAndVars();
Bindings &bindings = script->bindings;
size_t argsVars = bindings.countArgsAndVars();
size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
gc::FinalizeKind kind = gc::GetGCObjectKind(slots);
@ -984,8 +985,6 @@ NewCallObject(JSContext *cx, Bindings *bindings, JSObject &scopeChain, JSObject
return callobj;
}
} // namespace js
static inline JSObject *
NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
{
@ -1002,39 +1001,28 @@ NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
return envobj;
}
JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp)
{
/* Create a call object for fp only if it lacks one. */
JS_ASSERT(fp->isFunctionFrame());
if (fp->hasCallObj())
return &fp->callObj();
namespace js {
#ifdef DEBUG
/* A call object should be a frame's outermost scope chain element. */
Class *clasp = fp->scopeChain().getClass();
if (clasp == &js_WithClass || clasp == &js_BlockClass)
JS_ASSERT(fp->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
else if (clasp == &js_CallClass)
JS_ASSERT(fp->scopeChain().getPrivate() != fp);
#endif
JSObject *
CreateFunCallObject(JSContext *cx, JSStackFrame *fp)
{
JS_ASSERT(fp->isNonEvalFunctionFrame());
JS_ASSERT(!fp->hasCallObj());
JSObject *scopeChain = &fp->scopeChain();
JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
scopeChain->getPrivate() != fp);
/*
* Create the call object, using the frame's enclosing scope as its
* parent, and link the call to its stack frame. For a named function
* expression Call's parent points to an environment object holding
* function's name.
* For a named function expression Call's parent points to an environment
* object holding function's name.
*/
JSAtom *lambdaName =
(fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL;
if (lambdaName) {
JSObject *envobj = NewDeclEnvObject(cx, fp);
if (!envobj)
if (JSAtom *lambdaName = (fp->fun()->flags & JSFUN_LAMBDA) ? fp->fun()->atom : NULL) {
scopeChain = NewDeclEnvObject(cx, fp);
if (!scopeChain)
return NULL;
/* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
fp->setScopeChainNoCallObj(*envobj);
if (!js_DefineNativeProperty(cx, &fp->scopeChain(), ATOM_TO_JSID(lambdaName),
if (!js_DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
ObjectValue(fp->callee()),
CalleeGetter, NULL,
JSPROP_PERMANENT | JSPROP_READONLY,
@ -1043,29 +1031,36 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
}
}
JSObject *callobj =
NewCallObject(cx, &fp->fun()->script()->bindings, fp->scopeChain(), &fp->callee());
JSObject *callobj = NewCallObject(cx, fp->script(), *scopeChain, &fp->callee());
if (!callobj)
return NULL;
callobj->setPrivate(fp);
JS_ASSERT(fp->fun() == fp->callee().getFunctionPrivate());
/*
* Push callobj on the top of the scope chain, and make it the
* variables object.
*/
fp->setScopeChainAndCallObj(*callobj);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
JSObject *
CreateEvalCallObject(JSContext *cx, JSStackFrame *fp)
{
JSObject *callobj = NewCallObject(cx, fp->script(), fp->scopeChain(), NULL);
if (!callobj)
return false;
callobj->setPrivate(fp);
fp->setScopeChainWithOwnCallObj(*callobj);
return callobj;
}
} // namespace js
JSObject * JS_FASTCALL
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
{
JS_ASSERT(!js_IsNamedLambda(fun));
JS_ASSERT(scopeChain);
JS_ASSERT(callee);
return NewCallObject(cx, &fun->script()->bindings, *scopeChain, callee);
return NewCallObject(cx, fun->script(), *scopeChain, callee);
}
JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
@ -1084,11 +1079,8 @@ void
js_PutCallObject(JSContext *cx, JSStackFrame *fp)
{
JSObject &callobj = fp->callObj();
/*
* Strict mode eval frames have Call objects to put. Normal eval frames
* never put a Call object.
*/
JS_ASSERT(callobj.getPrivate() == fp);
JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
/* Get the arguments object to snapshot fp's actual argument values. */

View File

@ -321,15 +321,6 @@ JSObject::getFunctionPrivate() const
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.
*/
@ -515,9 +506,6 @@ js_ValueToCallableObject(JSContext *cx, js::Value *vp, uintN flags);
extern void
js_ReportIsNotFunction(JSContext *cx, const js::Value *vp, uintN flags);
extern JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp);
extern JSObject * JS_FASTCALL
js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain);
@ -530,6 +518,12 @@ js_PutCallObjectOnTrace(JSContext *cx, JSObject *scopeChain, uint32 nargs,
namespace js {
JSObject *
CreateFunCallObject(JSContext *cx, JSStackFrame *fp);
JSObject *
CreateEvalCallObject(JSContext *cx, JSStackFrame *fp);
extern JSBool
GetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);

View File

@ -1494,16 +1494,10 @@ js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp)
MarkObject(trc, fp->scopeChain(), "scope chain");
if (fp->isDummyFrame())
return;
if (fp->hasCallObj())
MarkObject(trc, fp->callObj(), "call");
if (fp->hasArgsObj())
MarkObject(trc, fp->argsObj(), "arguments");
if (fp->isScriptFrame()) {
js_TraceScript(trc, fp->script());
fp->script()->compartment->active = true;
}
js_TraceScript(trc, fp->script());
fp->script()->compartment->active = true;
MarkValue(trc, fp->returnValue(), "rval");
}

View File

@ -277,7 +277,7 @@ GetScopeChainFull(JSContext *cx, JSStackFrame *fp, JSObject *blockChain)
* Don't force a call object for a lightweight function call, but do
* insist that there is a call object for a heavyweight function call.
*/
JS_ASSERT_IF(fp->isFunctionFrame() && fp->fun()->isHeavyweight(),
JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(),
fp->hasCallObj());
return &fp->scopeChain();
}
@ -293,10 +293,9 @@ GetScopeChainFull(JSContext *cx, JSStackFrame *fp, JSObject *blockChain)
* Also, identify the innermost compiler-allocated block we needn't clone.
*/
JSObject *limitBlock, *limitClone;
if (fp->isFunctionFrame() && !fp->hasCallObj()) {
JS_ASSERT_IF(fp->scopeChain().isClonedBlock(),
fp->scopeChain().getPrivate() != js_FloatingFrameIfGenerator(cx, fp));
if (!js_GetCallObject(cx, fp))
if (fp->isNonEvalFunctionFrame() && !fp->hasCallObj()) {
JS_ASSERT_IF(fp->scopeChain().isClonedBlock(), fp->scopeChain().getPrivate() != fp);
if (!CreateFunCallObject(cx, fp))
return NULL;
/* We know we must clone everything on blockChain. */
@ -635,15 +634,13 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
if (status == mjit::Compile_Okay) {
ok = mjit::JaegerShot(cx);
JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
!fp->hasCallObj() && !fp->hasArgsObj());
JS_ASSERT_IF(!fp->isYielding(), !fp->hasCallObj() && !fp->hasArgsObj());
return ok;
}
#endif
ok = Interpret(cx, fp);
JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
!fp->hasCallObj() && !fp->hasArgsObj());
JS_ASSERT_IF(!fp->isYielding(), !fp->hasCallObj() && !fp->hasArgsObj());
return ok;
error:
@ -721,7 +718,7 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
cx->stack().pushInvokeFrame(cx, args, &frame);
/* Now that the new frame is rooted, maybe create a call object. */
if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
if (fun->isHeavyweight() && !CreateFunCallObject(cx, fp))
return false;
/* Run function until JSOP_STOP, JSOP_RETURN or error. */
@ -894,6 +891,34 @@ ExternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, const Value &fval,
return ExternalInvoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
}
#if JS_HAS_SHARP_VARS
JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
static JS_NEVER_INLINE bool
InitSharpSlots(JSContext *cx, JSStackFrame *fp)
{
JSStackFrame *prev = fp->prev();
JSScript *script = fp->script();
JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
Value *sharps = &fp->slots()[script->nfixed - SHARP_NSLOTS];
if (prev && prev->script()->hasSharps) {
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
? prev->fun()->script()->bindings.sharpSlotBase(cx)
: prev->numFixed() - SHARP_NSLOTS;
if (base < 0)
return false;
sharps[0] = prev->slots()[base];
sharps[1] = prev->slots()[base + 1];
} else {
sharps[0].setUndefined();
sharps[1].setUndefined();
}
return true;
}
#endif
bool
Execute(JSContext *cx, JSObject *chain, JSScript *script,
JSStackFrame *prev, uintN flags, Value *result)
@ -908,10 +933,12 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
}
LeaveTrace(cx);
AutoScriptRooter root(cx, script);
/*
* Get a pointer to new frame/slots. This memory is not "claimed", so the
* code before pushExecuteFrame must not reenter the interpreter.
* code before pushExecuteFrame must not reenter the interpreter or assume
* the frame is rooted.
*/
ExecuteFrameGuard frame;
if (!cx->stack().getExecuteFrame(cx, script, &frame))
@ -926,13 +953,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
JS_ASSERT(chain == &prev->scopeChain());
frame.fp()->initEvalFrame(cx, script, prev, flags);
/*
* We want to call |prev->varobj()|, but this requires knowing the
* CallStackSegment of |prev|. If |prev == cx->fp()|, the callstack is
* simply the context's active callstack, so we can use
* |prev->varobj(cx)|. When |prev != cx->fp()|, we need to do a slow
* linear search. Luckily, this only happens with EvaluateInFrame.
*/
/* NB: prev may not be in cx->currentSegment. */
initialVarObj = (prev == cx->maybefp())
? &prev->varobj(cx)
: &prev->varobj(cx->containingSegment(prev));
@ -950,67 +971,35 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
return false;
}
/* Initialize frame. */
frame.fp()->initGlobalFrame(script, *innerizedChain, flags);
/* Compute 'this'. */
/* If scope chain is an inner window, outerize for 'this'. */
JSObject *thisp = chain->thisObject(cx);
if (!thisp)
return false;
frame.fp()->globalThis().setObject(*thisp);
initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX) ? 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 ((flags & JSFRAME_EVAL) && script->strictModeCode) {
AutoScriptRooter root(cx, script);
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);
initialVarObj = cx->hasRunOption(JSOPTION_VAROBJFIX)
? chain->getGlobal()
: chain;
}
JS_ASSERT(!initialVarObj->getOps()->defineProperty);
#if JS_HAS_SHARP_VARS
JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
if (script->hasSharps) {
JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
Value *sharps = &frame.fp()->slots()[script->nfixed - SHARP_NSLOTS];
if (prev && prev->script()->hasSharps) {
JS_ASSERT(prev->numFixed() >= SHARP_NSLOTS);
int base = (prev->isFunctionFrame() && !prev->isEvalOrDebuggerFrame())
? prev->fun()->script()->bindings.sharpSlotBase(cx)
: prev->numFixed() - SHARP_NSLOTS;
if (base < 0)
return false;
sharps[0] = prev->slots()[base];
sharps[1] = prev->slots()[base + 1];
} else {
sharps[0].setUndefined();
sharps[1].setUndefined();
}
if (frame.fp()->isStrictEvalFrame()) {
/* Give strict mode eval its own fresh lexical environment. */
initialVarObj = CreateEvalCallObject(cx, frame.fp());
if (!initialVarObj)
return false;
JS_ASSERT(initialVarObj == &frame.fp()->scopeChain());
}
#endif
/* Officially push |fp|. |frame|'s destructor pops. */
/* Officially push the frame. ~FrameGuard pops. */
cx->stack().pushExecuteFrame(cx, initialVarObj, &frame);
/* Now that the frame has been pushed, we can call the thisObject hook. */
if (!prev) {
JSObject *thisp = chain->thisObject(cx);
if (!thisp)
return false;
frame.fp()->globalThis().setObject(*thisp);
}
#if JS_HAS_SHARP_VARS
if (script->hasSharps && !InitSharpSlots(cx, frame.fp()))
return false;
#endif
Probes::startExecution(cx, script);
@ -4718,7 +4707,7 @@ BEGIN_CASE(JSOP_FUNCALL)
atoms = script->atomMap.vector;
/* Now that the new frame is rooted, maybe create a call object. */
if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
if (newfun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp))
goto error;
RESET_USE_METHODJIT();
@ -5265,7 +5254,7 @@ END_CASE(JSOP_GETUPVAR_DBG)
BEGIN_CASE(JSOP_GETFCSLOT)
BEGIN_CASE(JSOP_CALLFCSLOT)
{
JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
JS_ASSERT(regs.fp->isNonEvalFunctionFrame());
uintN index = GET_UINT16(regs.pc);
JSObject *obj = &argv[-2].toObject();
@ -5702,7 +5691,7 @@ BEGIN_CASE(JSOP_LAMBDA_DBGFC)
END_CASE(JSOP_LAMBDA_DBGFC)
BEGIN_CASE(JSOP_CALLEE)
JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
JS_ASSERT(regs.fp->isNonEvalFunctionFrame());
PUSH_COPY(argv[-2]);
END_CASE(JSOP_CALLEE)
@ -6615,7 +6604,7 @@ BEGIN_CASE(JSOP_GENERATOR)
BEGIN_CASE(JSOP_YIELD)
JS_ASSERT(!cx->isExceptionPending());
JS_ASSERT(regs.fp->isFunctionFrame() && !regs.fp->isEvalFrame());
JS_ASSERT(regs.fp->isNonEvalFunctionFrame());
if (cx->generatorFor(regs.fp)->state == JSGEN_CLOSING) {
js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
JSDVG_SEARCH_STACK, argv[-2], NULL);

View File

@ -150,10 +150,6 @@ struct JSStackFrame
* function frame: execution of function code or an eval in a function
* dummy frame: bookkeeping frame (read: hack)
*
* As noted, global and function frames may optionally be 'eval frames', which
* further restricts the stack frame members which may be used. Namely, the
* argument-related members of function eval frames are not valid, since an eval
* shares its containing function's arguments rather than having its own.
*/
bool isFunctionFrame() const {
@ -174,11 +170,35 @@ struct JSStackFrame
return retval;
}
/*
* Eval frames
*
* As noted above, global and function frames may optionally be 'eval
* frames'. Eval code shares its parent's arguments which means that the
* arg-access members of JSStackFrame may not be used for eval frames.
* Search for 'hasArgs' below for more details.
*
* A further sub-classification of eval frames is whether the frame was
* pushed for an ES5 strict-mode eval().
*/
bool isEvalFrame() const {
JS_ASSERT_IF(flags_ & JSFRAME_EVAL, isScriptFrame());
return flags_ & JSFRAME_EVAL;
}
bool isNonEvalFunctionFrame() const {
return (flags_ & (JSFRAME_FUNCTION | JSFRAME_EVAL)) == JSFRAME_FUNCTION;
}
bool isStrictEvalFrame() const {
return isEvalFrame() && script()->strictModeCode;
}
bool isNonStrictEvalFrame() const {
return isEvalFrame() && !script()->strictModeCode;
}
/*
* Frame initialization
*
@ -342,7 +362,7 @@ struct JSStackFrame
/* True if this frame has arguments. Contrast with hasArgsObj. */
bool hasArgs() const {
return isFunctionFrame() && !isEvalFrame();
return isNonEvalFunctionFrame();
}
uintN numFormalArgs() const {
@ -484,6 +504,12 @@ struct JSStackFrame
* up the scope chain until the first call object. Thus, it is important,
* when setting the scope chain, to indicate whether the new scope chain
* contains a new call object and thus changes the 'hasCallObj' state.
*
* NB: 'fp->hasCallObj()' implies that fp->callObj() needs to be 'put' when
* the frame is popped. Since the scope chain of a non-strict eval frame
* contains the call object of the parent (function) frame, it is possible
* to have:
* !fp->hasCall() && fp->scopeChain().isCall()
*/
JSObject &scopeChain() const {
@ -496,13 +522,14 @@ struct JSStackFrame
}
bool hasCallObj() const {
return !!(flags_ & JSFRAME_HAS_CALL_OBJ);
bool ret = !!(flags_ & JSFRAME_HAS_CALL_OBJ);
JS_ASSERT_IF(ret, !isNonStrictEvalFrame());
return ret;
}
inline JSObject &callObj() const;
inline JSObject *maybeCallObj() const;
inline void setScopeChainNoCallObj(JSObject &obj);
inline void setScopeChainAndCallObj(JSObject &obj);
inline void setScopeChainWithOwnCallObj(JSObject &obj);
inline void clearCallObj();
/*

View File

@ -94,7 +94,6 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
JS_ASSERT(!hasImacropc());
JS_ASSERT(!hasHookData());
JS_ASSERT(annotation() == NULL);
JS_ASSERT(!hasCallObj());
}
@ -163,21 +162,21 @@ JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev,
{
JS_ASSERT(flagsArg & JSFRAME_EVAL);
JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0);
JS_ASSERT(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
JS_ASSERT(prev->isScriptFrame());
/* Copy (callee, thisv). */
js::Value *dstvp = (js::Value *)this - 2;
js::Value *srcvp = prev->flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL)
? (js::Value *)prev - 2
: prev->formalArgs() - 2;
js::Value *srcvp = prev->hasArgs()
? prev->formalArgs() - 2
: (js::Value *)prev - 2;
dstvp[0] = srcvp[0];
dstvp[1] = srcvp[1];
JS_ASSERT_IF(prev->flags_ & JSFRAME_FUNCTION,
JS_ASSERT_IF(prev->isFunctionFrame(),
dstvp[0].toObject().isFunction());
/* Initialize stack frame members. */
flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN |
(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL | JSFRAME_HAS_CALL_OBJ));
(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
if (isFunctionFrame()) {
exec = prev->exec;
args.script = script;
@ -186,8 +185,6 @@ JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev,
}
scopeChain_ = &prev->scopeChain();
JS_ASSERT_IF(isFunctionFrame(), &callObj() == &prev->callObj());
prev_ = prev;
prevpc_ = prev->pc(cx);
JS_ASSERT(!hasImacropc());
@ -249,10 +246,11 @@ JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
* js_LiveFrameToFloating comment in jsiter.h.
*/
if (hasCallObj()) {
callObj().setPrivate(this);
JSObject &obj = callObj();
obj.setPrivate(this);
otherfp->flags_ &= ~JSFRAME_HAS_CALL_OBJ;
if (js_IsNamedLambda(fun())) {
JSObject *env = callObj().getParent();
JSObject *env = obj.getParent();
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
env->setPrivate(this);
}
@ -435,19 +433,24 @@ JSStackFrame::setScopeChainNoCallObj(JSObject &obj)
{
#ifdef DEBUG
JS_ASSERT(&obj != NULL);
JSObject *callObjBefore = maybeCallObj();
if (!hasCallObj() && &scopeChain() != sInvalidScopeChain) {
for (JSObject *pobj = &scopeChain(); pobj; pobj = pobj->getParent())
JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
if (&obj != sInvalidScopeChain) {
if (hasCallObj()) {
JSObject *pobj = &obj;
while (pobj && pobj->getPrivate() != this)
pobj = pobj->getParent();
JS_ASSERT(pobj);
} else {
for (JSObject *pobj = &obj; pobj; pobj = pobj->getParent())
JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
}
}
#endif
scopeChain_ = &obj;
flags_ |= JSFRAME_HAS_SCOPECHAIN;
JS_ASSERT(callObjBefore == maybeCallObj());
}
inline void
JSStackFrame::setScopeChainAndCallObj(JSObject &obj)
JSStackFrame::setScopeChainWithOwnCallObj(JSObject &obj)
{
JS_ASSERT(&obj != NULL);
JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this);
@ -465,7 +468,8 @@ JSStackFrame::clearCallObj()
inline JSObject &
JSStackFrame::callObj() const
{
JS_ASSERT(hasCallObj());
JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj());
JSObject *pobj = &scopeChain();
while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith());
@ -474,12 +478,6 @@ JSStackFrame::callObj() const
return *pobj;
}
inline JSObject *
JSStackFrame::maybeCallObj() const
{
return hasCallObj() ? &callObj() : NULL;
}
namespace js {
class AutoPreserveEnumerators {
@ -516,15 +514,11 @@ struct AutoInterpPreparer {
inline void
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
{
JS_ASSERT(!fp->isYielding());
JS_ASSERT(!fp->isEvalFrame() || fp->script()->strictModeCode);
/* The order is important as js_PutCallObject needs to access argsObj. */
if (fp->hasCallObj()) {
if (fp->hasCallObj())
js_PutCallObject(cx, fp);
} else if (fp->hasArgsObj()) {
else if (fp->hasArgsObj())
js_PutArgsObject(cx, fp);
}
}
/*
@ -804,7 +798,7 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
inline bool
ScriptPrologue(JSContext *cx, JSStackFrame *fp)
{
JS_ASSERT_IF(fp->isFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
if (fp->isConstructing()) {
JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
@ -824,31 +818,12 @@ ScriptEpilogue(JSContext *cx, JSStackFrame *fp, bool ok)
if (cx->compartment->debugMode)
ok = ScriptDebugEpilogue(cx, fp, ok);
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->hasArgsObj());
JS_ASSERT(fp->hasCallObj());
JS_ASSERT(fp->callObj().callIsForEval());
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()) {
JS_ASSERT_IF(fp->hasCallObj(), !fp->callObj().callIsForEval());
PutActivationObjects(cx, fp);
}
}
/*
* A yielding frame's activation objects are transferred to the floating
* frame, stored in the generator, and thus need not be synced.
*/
if (!fp->isYielding())
PutActivationObjects(cx, fp);
/*
* If inline-constructing, replace primitive rval with the new object

View File

@ -1201,7 +1201,7 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
#ifdef DEBUG
jsbytecode *callerPC = caller->pc(cx);
JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
JS_ASSERT_IF(caller->isFunctionFrame(), caller->fun()->isHeavyweight());
JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
#endif
} else {
@ -1249,7 +1249,7 @@ EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame
JSScript *script = NULL;
JSScript **bucket = EvalCacheHash(cx, linearStr);
if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame()) {
if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame()) {
script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket);
/*
@ -6869,11 +6869,15 @@ js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
fputc('\n', stderr);
}
}
if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
if (fp->hasArgs()) {
fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
}
MaybeDumpObject("callobj", fp->maybeCallObj());
if (fp->hasCallObj()) {
fprintf(stderr, " has call obj: ");
dumpValue(ObjectValue(fp->callObj()));
fprintf(stderr, "\n");
}
MaybeDumpObject("argsobj", fp->maybeArgsObj());
if (!fp->isDummyFrame()) {
MaybeDumpValue("this", fp->thisValue());

View File

@ -516,7 +516,7 @@ struct JSObject : js::gc::Cell {
}
/* Functions for setting up scope chain object maps and shapes. */
void initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent);
void initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent);
void initClonedBlock(JSContext *cx, JSObject *proto, JSStackFrame *priv);
void setBlockOwnShape(JSContext *cx);

View File

@ -172,16 +172,16 @@ JSObject::finalize(JSContext *cx)
* parent, map, and shape, and allocate slots.
*/
inline void
JSObject::initCall(JSContext *cx, const js::Bindings *bindings, JSObject *parent)
JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent)
{
init(cx, &js_CallClass, NULL, parent, NULL, false);
map = bindings->lastShape();
map = bindings.lastShape();
/*
* If |bindings| is for a function that has extensible parents, that means
* its Call should have its own shape; see js::Bindings::extensibleParents.
*/
if (bindings->extensibleParents())
if (bindings.extensibleParents())
setOwnShape(js_GenerateShape(cx));
else
objShape = map->shape;

View File

@ -1735,7 +1735,7 @@ static inline uintN
entryFrameArgc(JSContext *cx)
{
JSStackFrame *fp = cx->fp();
return fp->isGlobalFrame() || fp->isEvalFrame() ? 0 : fp->numActualArgs();
return fp->hasArgs() ? fp->numActualArgs() : 0;
}
template <typename Visitor>
@ -2000,7 +2000,7 @@ NativeStackSlots(JSContext *cx, unsigned callDepth)
unsigned depth = callDepth;
for (; depth > 0; --depth, next = fp, fp = fp->prev()) {
JS_ASSERT(fp->isFunctionFrame() && !fp->isEvalFrame());
JS_ASSERT(fp->isNonEvalFunctionFrame());
slots += SPECIAL_FRAME_SLOTS;
if (next)
slots += CountStackAndArgs(next, fp->slots());
@ -3157,7 +3157,7 @@ public:
{
JS_ASSERT(&fp->scopeChain() == JSStackFrame::sInvalidScopeChain);
frameobj->setPrivate(fp);
fp->setScopeChainAndCallObj(*frameobj);
fp->setScopeChainWithOwnCallObj(*frameobj);
} else {
fp->setScopeChainNoCallObj(*frameobj);
}
@ -13759,7 +13759,7 @@ TraceRecorder::interpretedFunctionCall(Value& fval, JSFunction* fun, uintN argc,
fi->spdist = cx->regs->sp - fp->slots();
fi->set_argc(uint16(argc), constructing);
fi->callerHeight = stackSlots - (2 + argc);
fi->callerArgc = fp->isGlobalFrame() || fp->isEvalFrame() ? 0 : fp->numActualArgs();
fi->callerArgc = fp->hasArgs() ? fp->numActualArgs() : 0;
if (callDepth >= tree->maxCallDepth)
tree->maxCallDepth = callDepth + 1;

View File

@ -351,7 +351,7 @@ mjit::Compiler::generatePrologue()
/* Create the call object. */
if (fun->isHeavyweight()) {
prepareStubCall(Uses(0));
INLINE_STUBCALL(stubs::GetCallObject);
INLINE_STUBCALL(stubs::CreateFunCallObject);
}
j.linkTo(masm.label(), &masm);
@ -360,7 +360,7 @@ mjit::Compiler::generatePrologue()
/*
* Load the scope chain into the frame if necessary. The scope chain
* is always set for global and eval frames, and will have been set by
* GetCallObject for heavyweight function frames.
* CreateFunCallObject for heavyweight function frames.
*/
RegisterID t0 = Registers::ReturnReg;
Jump hasScope = masm.branchTest32(Assembler::NonZero,
@ -2248,7 +2248,7 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutActivationObjects);
} else {
/* if (hasCallObj() || hasArgsObj()) stubs::PutActivationObjects() */
/* if (hasCallObj() || hasArgsObj()) */
Jump putObjs = masm.branchTest32(Assembler::NonZero,
Address(JSFrameReg, JSStackFrame::offsetOfFlags()),
Imm32(JSFRAME_HAS_CALL_OBJ | JSFRAME_HAS_ARGS_OBJ));
@ -2261,10 +2261,10 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
emitFinalReturn(stubcc.masm);
}
} else {
if (fp->isEvalFrame() && script->strictModeCode) {
if (fp->isStrictEvalFrame()) {
/* There will always be a call object. */
prepareStubCall(Uses(fe ? 1 : 0));
INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
INLINE_STUBCALL(stubs::PutActivationObjects);
}
}

View File

@ -325,7 +325,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
f.regs.sp = fp->base();
f.regs.pc = script->code;
if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
if (fun->isHeavyweight() && !js::CreateFunCallObject(cx, fp))
THROWV(NULL);
CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
@ -367,6 +367,10 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint
stack.pushInlineFrame(cx, newscript, newfp, &f.regs);
JS_ASSERT(newfp == f.regs.fp);
/* Scope with a call object parented by callee's parent. */
if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, newfp))
return false;
/* Try to compile if not already compiled. */
if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter);
@ -379,10 +383,6 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint
*unjittable = true;
}
/* Create call object now that we can't fail entering callee. */
if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp))
return false;
/* If newscript was successfully compiled, run it. */
if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) {
*pret = jit->invokeEntry;
@ -479,15 +479,6 @@ stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
return;
}
void JS_FASTCALL
stubs::PutStrictEvalCallObject(VMFrame &f)
{
JS_ASSERT(f.fp()->isEvalFrame());
JS_ASSERT(f.fp()->script()->strictModeCode);
JS_ASSERT(f.fp()->hasCallObj());
js_PutCallObject(f.cx, f.fp());
}
void JS_FASTCALL
stubs::PutActivationObjects(VMFrame &f)
{
@ -580,10 +571,10 @@ js_InternalThrow(VMFrame &f)
}
void JS_FASTCALL
stubs::GetCallObject(VMFrame &f)
stubs::CreateFunCallObject(VMFrame &f)
{
JS_ASSERT(f.fp()->fun()->isHeavyweight());
if (!js_GetCallObject(f.cx, f.fp()))
if (!js::CreateFunCallObject(f.cx, f.fp()))
THROW();
}
@ -807,9 +798,7 @@ HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame)
returnOK = ScriptEpilogue(cx, cx->fp(), true);
}
JS_ASSERT_IF(cx->fp()->isFunctionFrame() &&
!cx->fp()->isEvalFrame(),
!cx->fp()->hasCallObj());
JS_ASSERT_IF(cx->fp()->isNonEvalFunctionFrame(), !cx->fp()->hasCallObj());
if (cx->fp() != entryFrame) {
InlineReturn(f);

View File

@ -770,8 +770,7 @@ CheckStackAndEnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
goto error;
ok = EnterMethodJIT(cx, fp, code, stackLimit);
JS_ASSERT_IF(!fp->isYielding() && !(fp->isEvalFrame() && !fp->script()->strictModeCode),
!fp->hasCallObj() && !fp->hasArgsObj());
JS_ASSERT_IF(!fp->isYielding(), !fp->hasCallObj() && !fp->hasArgsObj());
return ok;
error:

View File

@ -109,9 +109,8 @@ 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 PutStrictEvalCallObject(VMFrame &f);
void JS_FASTCALL PutActivationObjects(VMFrame &f);
void JS_FASTCALL GetCallObject(VMFrame &f);
void JS_FASTCALL CreateFunCallObject(VMFrame &f);
#if JS_MONOIC
void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
#else