Properly compute 'this' object on trace and wrap if necessary (488203, r=mrbkap).

This commit is contained in:
Andreas Gal 2009-04-14 19:52:09 -07:00
parent 087d5e2840
commit 28ae2c328d
3 changed files with 53 additions and 20 deletions

View File

@ -4364,15 +4364,8 @@ js_Interpret(JSContext *cx)
#define COMPUTE_THIS(cx, fp, obj) \
JS_BEGIN_MACRO \
if (fp->flags & JSFRAME_COMPUTED_THIS) { \
obj = fp->thisp; \
} else { \
obj = js_ComputeThis(cx, JS_TRUE, fp->argv); \
if (!obj) \
goto error; \
fp->thisp = obj; \
fp->flags |= JSFRAME_COMPUTED_THIS; \
} \
if (!(obj = js_ComputeThisForFrame(cx, fp))) \
goto error; \
JS_END_MACRO
BEGIN_CASE(JSOP_THIS)

View File

@ -466,6 +466,19 @@ extern const uint16 js_PrimitiveTestFlags[];
JSFUN_THISP_TEST(JSFUN_THISP_FLAGS((fun)->flags), \
js_PrimitiveTestFlags[JSVAL_TAG(thisv) - 1]))
static inline JSObject *
js_ComputeThisForFrame(JSContext *cx, JSStackFrame *fp)
{
if (fp->flags & JSFRAME_COMPUTED_THIS)
return fp->thisp;
JSObject* obj = js_ComputeThis(cx, JS_TRUE, fp->argv);
if (!obj)
return NULL;
fp->thisp = obj;
fp->flags |= JSFRAME_COMPUTED_THIS;
return obj;
}
/*
* NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp
* is non-null), and that vp points to the callee, |this| parameter, and

View File

@ -1743,9 +1743,11 @@ skip:
}
for (; n != 0; fp = fp->down) {
--n;
if (fp->callee) { // might not have it if the entry frame is global
if (fp->callee) {
JS_ASSERT(JSVAL_IS_OBJECT(fp->argv[-1]));
fp->thisp = JSVAL_TO_OBJECT(fp->argv[-1]);
if (fp->flags & JSFRAME_CONSTRUCTING) // constructors always compute 'this'
fp->flags |= JSFRAME_COMPUTED_THIS;
}
}
}
@ -3390,7 +3392,7 @@ js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
newifp->frame.xmlNamespace = NULL;
newifp->frame.blockChain = NULL;
newifp->mark = newmark;
newifp->frame.thisp = NULL; // will be set by js_ExecuteTree -> FlushNativeStackFrame
newifp->frame.thisp = NULL; // will be updated in FlushNativeStackFrame
newifp->frame.regs = fp->regs;
newifp->frame.regs->pc = script->code;
@ -4348,8 +4350,6 @@ LeaveTree(InterpState& state, VMSideExit* lr)
// Verify that our state restoration worked.
for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) {
JS_ASSERT_IF(fp->callee, JSVAL_IS_OBJECT(fp->argv[-1]));
JS_ASSERT_IF(fp->callee && fp->thisp != JSVAL_TO_OBJECT(fp->argv[-1]),
!(fp->flags & JSFRAME_COMPUTED_THIS) && !fp->thisp);
}
#endif
#ifdef JS_JIT_SPEW
@ -6236,17 +6236,44 @@ TraceRecorder::unbox_jsval(jsval v, LIns*& v_ins, LIns* exit)
}
}
static JSObject*
ComputeThis_tn(JSContext* cx)
{
return js_ComputeThisForFrame(cx, cx->fp);
}
JS_DEFINE_CALLINFO_1(static, OBJECT, ComputeThis_tn, CONTEXT, 1, 1) /* safe to CSE */
JS_REQUIRES_STACK bool
TraceRecorder::getThis(LIns*& this_ins)
{
if (cx->fp->callee) { /* in a function */
if (JSVAL_IS_NULL(cx->fp->argv[-1]))
return false;
this_ins = get(&cx->fp->argv[-1]);
guard(false, lir->ins_eq0(this_ins), MISMATCH_EXIT);
} else { /* in global code */
this_ins = scopeChain();
JSObject* thisObj = js_ComputeThisForFrame(cx, cx->fp);
if (!thisObj)
ABORT_TRACE("js_ComputeThis failed");
if (!cx->fp->callee || JSVAL_IS_NULL(cx->fp->argv[-1])) {
JS_ASSERT(callDepth == 0);
/*
* In global code, or if this is NULL, wrap the global object and bake it directly
* into the trace.
*/
this_ins = INS_CONSTPTR(thisObj);
set(&cx->fp->argv[-1], this_ins);
return true;
}
if (callDepth == 0) {
/*
* Check that we computed the 'this' object for the entry frame. We only have to worry
* about this for callDepth == 0 since inlined function calls are not subject to
* deferred wrapping as we do not trace across global objects. Note that for nested
* tree calls the current cx->fp might not be the current entry frame of the called
* tree, however, since we don't cross global objects, we can call js_ComputeThis
* on that frame to ensure that the global object was wrapped, and that guarantee than
* inductively extends to our current 'this' object.
*/
LIns* args = { cx_ins };
guard(false, lir->ins_eq0(lir->insCall(&ComputeThis_tn_ci, &args)), MISMATCH_EXIT);
}
this_ins = get(&cx->fp->argv[-1]);
return true;
}