diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index d68edfbd86c..df9944f796e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4602,8 +4602,11 @@ JS_PUBLIC_API(JSBool) JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; + CHECK_REQUEST(cx); - JSBool ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(FUN_OBJECT(fun)), argc, argv, rval); + ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(FUN_OBJECT(fun)), argc, argv, + rval); LAST_FRAME_CHECKS(cx, ok); return ok; } @@ -4627,8 +4630,10 @@ JS_PUBLIC_API(JSBool) JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, jsval *argv, jsval *rval) { + JSBool ok; + CHECK_REQUEST(cx); - JSBool ok = js_InternalCall(cx, obj, fval, argc, argv, rval); + ok = js_InternalCall(cx, obj, fval, argc, argv, rval); LAST_FRAME_CHECKS(cx, ok); return ok; } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index f7e80bce28b..c21035a1b4b 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -864,7 +864,8 @@ JS_InitCTypesClass(JSContext *cx, JSObject *global); */ #define JS_CALLEE(cx,vp) ((vp)[0]) #define JS_ARGV_CALLEE(argv) ((argv)[-2]) -#define JS_THIS_OBJECT(cx,vp) JSVAL_TO_OBJECT(JS_THIS(cx,vp)) +#define JS_THIS(cx,vp) JS_ComputeThis(cx, vp) +#define JS_THIS_OBJECT(cx,vp) ((JSObject *) JS_THIS(cx,vp)) #define JS_ARGV(cx,vp) ((vp) + 2) #define JS_RVAL(cx,vp) (*(vp)) #define JS_SET_RVAL(cx,vp,v) (*(vp) = (v)) @@ -872,12 +873,6 @@ JS_InitCTypesClass(JSContext *cx, JSObject *global); extern JS_PUBLIC_API(jsval) JS_ComputeThis(JSContext *cx, jsval *vp); -static JS_ALWAYS_INLINE jsval -JS_THIS(JSContext *cx, jsval *vp) -{ - return JSVAL_IS_PRIMITIVE(vp[1]) ? JS_ComputeThis(cx, vp) : vp[1]; -} - extern JS_PUBLIC_API(void *) JS_malloc(JSContext *cx, size_t nbytes); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index a024db097c7..94eba049e92 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -240,6 +240,17 @@ js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp) return JS_TRUE; } +/* Some objects (e.g., With) delegate 'this' to another object. */ +static inline JSObject * +CallThisObjectHook(JSContext *cx, JSObject *obj, jsval *argv) +{ + JSObject *thisp = obj->thisObject(cx); + if (!thisp) + return NULL; + argv[-1] = OBJECT_TO_JSVAL(thisp); + return thisp; +} + /* * ECMA requires "the global object", but in embeddings such as the browser, * which have multiple top-level objects (windows, frames, etc. in the DOM), @@ -258,10 +269,36 @@ js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp) JS_STATIC_INTERPRET JSObject * js_ComputeGlobalThis(JSContext *cx, jsval *argv) { - JSObject *thisp = JSVAL_TO_OBJECT(argv[-2])->getGlobal()->thisObject(cx); - if (thisp) - argv[-1] = OBJECT_TO_JSVAL(thisp); - return thisp; + JSObject *thisp; + + if (JSVAL_IS_PRIMITIVE(argv[-2]) || + !JSVAL_TO_OBJECT(argv[-2])->getParent()) { + thisp = cx->globalObject; + } else { + thisp = JSVAL_TO_OBJECT(argv[-2])->getGlobal(); + } + + return CallThisObjectHook(cx, thisp, argv); +} + +static JSObject * +ComputeThis(JSContext *cx, jsval *argv) +{ + JSObject *thisp; + + JS_ASSERT(!JSVAL_IS_NULL(argv[-1])); + if (!JSVAL_IS_OBJECT(argv[-1])) { + if (!js_PrimitiveToObject(cx, &argv[-1])) + return NULL; + thisp = JSVAL_TO_OBJECT(argv[-1]); + return thisp; + } + + thisp = JSVAL_TO_OBJECT(argv[-1]); + if (thisp->getClass() == &js_CallClass || thisp->getClass() == &js_BlockClass) + return js_ComputeGlobalThis(cx, argv); + + return CallThisObjectHook(cx, thisp, argv); } JSObject * @@ -270,28 +307,7 @@ js_ComputeThis(JSContext *cx, jsval *argv) JS_ASSERT(argv[-1] != JSVAL_HOLE); // check for SynthesizeFrame poisoning if (JSVAL_IS_NULL(argv[-1])) return js_ComputeGlobalThis(cx, argv); - if (!JSVAL_IS_OBJECT(argv[-1])) { - if (!js_PrimitiveToObject(cx, &argv[-1])) - return NULL; - return JSVAL_TO_OBJECT(argv[-1]); - } - - JSObject *obj = JSVAL_TO_OBJECT(argv[-1]); - JS_ASSERT(js_IsSaneThisObject(obj)); - return obj; -} - -JSObject * -JSStackFrame::computeThisObject(JSContext *cx) -{ - JS_ASSERT(JSVAL_IS_PRIMITIVE(thisv)); - JS_ASSERT(fun); - - JSObject *obj = js_ComputeThis(cx, argv); - if (!obj) - return NULL; - thisv = OBJECT_TO_JSVAL(obj); - return obj; + return ComputeThis(cx, argv); } #if JS_HAS_NO_SUCH_METHOD @@ -502,6 +518,26 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags) } } + if (flags & JSINVOKE_CONSTRUCT) { + JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1])); + } else { + /* + * We must call js_ComputeThis in case we are not called from the + * interpreter, where a prior bytecode has computed an appropriate + * |this| already. + * + * But we need to compute |this| eagerly only for so-called "slow" + * (i.e., not fast) native functions. Fast natives must use either + * JS_THIS or JS_THIS_OBJECT, and scripted functions will go through + * the appropriate this-computing bytecode, e.g., JSOP_THIS. + */ + if (native && (!fun || !(fun->flags & JSFUN_FAST_NATIVE))) { + if (!js_ComputeThis(cx, vp + 2)) + return false; + flags |= JSFRAME_COMPUTED_THIS; + } + } + start_call: if (native && fun && fun->isFastNative()) { #ifdef DEBUG_NOT_THROWING @@ -531,6 +567,7 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags) nmissing = (minargs > argc ? minargs - argc : 0) + fun->u.n.extra; nvars = 0; } + } else { nvars = nmissing = 0; } @@ -598,27 +635,6 @@ js_Invoke(JSContext *cx, const InvokeArgsGuard &args, uintN flags) return false; } - /* - * Compute |this|. Currently, this must happen after the frame is pushed - * and fp->scopeChain is correct because the thisObject hook may call - * JS_GetScopeChain. - */ - JS_ASSERT_IF(flags & JSINVOKE_CONSTRUCT, !JSVAL_IS_PRIMITIVE(vp[1])); - if (JSVAL_IS_OBJECT(vp[1]) && !(flags & JSINVOKE_CONSTRUCT)) { - /* - * We must call the thisObject hook in case we are not called from the - * interpreter, where a prior bytecode has computed an appropriate - * |this| already. - */ - JSObject *thisp = JSVAL_TO_OBJECT(vp[1]); - thisp = thisp ? thisp : funobj->getGlobal(); - thisp = thisp->thisObject(cx); - if (!thisp) - return false; - fp->thisv = vp[1] = OBJECT_TO_JSVAL(thisp); - } - JS_ASSERT_IF(!JSVAL_IS_PRIMITIVE(vp[1]), js_IsSaneThisObject(JSVAL_TO_OBJECT(vp[1]))); - /* Call the hook if present after we fully initialized the frame. */ JSInterpreterHook hook = cx->debugHooks->callHook; void *hookData = NULL; @@ -772,7 +788,7 @@ js_Execute(JSContext *cx, JSObject *const chain, JSScript *script, fp->argsobj = down->argsobj; fp->fun = (script->staticLevel > 0) ? down->fun : NULL; fp->thisv = down->thisv; - fp->flags = flags; + fp->flags = flags | (down->flags & JSFRAME_COMPUTED_THIS); fp->argc = down->argc; fp->argv = down->argv; fp->annotation = down->annotation; @@ -793,7 +809,7 @@ js_Execute(JSContext *cx, JSObject *const chain, JSScript *script, fp->argsobj = NULL; fp->fun = NULL; /* Ininitialize fp->thisv after pushExecuteFrame. */ - fp->flags = flags; + fp->flags = flags | JSFRAME_COMPUTED_THIS; fp->argc = 0; fp->argv = NULL; fp->annotation = NULL; @@ -2176,8 +2192,6 @@ js_Interpret(JSContext *cx) script = fp->script; JS_ASSERT(!script->isEmpty()); JS_ASSERT(script->length > 1); - JS_ASSERT(JSVAL_IS_OBJECT(fp->thisv)); - JS_ASSERT_IF(!fp->fun, !JSVAL_IS_NULL(fp->thisv)); /* Count of JS function calls that nest in this C js_Interpret frame. */ inlineCallCount = 0; diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 261f143faf8..760122e53f4 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -59,7 +59,8 @@ typedef struct JSFrameRegs { /* JS stack frame flags. */ enum JSFrameFlags { JSFRAME_CONSTRUCTING = 0x01, /* frame is for a constructor invocation */ - JSFRAME_OVERRIDE_ARGS = 0x02, /* overridden arguments local variable */ + JSFRAME_COMPUTED_THIS = 0x02, /* frame.thisv was computed already and + JSVAL_IS_OBJECT(thisv) */ JSFRAME_ASSIGNING = 0x04, /* a complex (not simplex JOF_ASSIGNING) op is currently assigning to a property */ JSFRAME_DEBUGGER = 0x08, /* frame for JS_EvaluateInStackFrame */ @@ -67,6 +68,7 @@ enum JSFrameFlags { JSFRAME_FLOATING_GENERATOR = 0x20, /* frame copy stored in a generator obj */ JSFRAME_YIELDING = 0x40, /* js_Interpret dispatched JSOP_YIELD */ JSFRAME_GENERATOR = 0x80, /* frame belongs to generator-iterator */ + JSFRAME_OVERRIDE_ARGS = 0x100, /* overridden arguments local variable */ JSFRAME_SPECIAL = JSFRAME_DEBUGGER | JSFRAME_EVAL }; @@ -87,22 +89,7 @@ struct JSStackFrame JSVAL_OBJECT */ JSScript *script; /* script being interpreted */ JSFunction *fun; /* function being called or null */ - - /* - * The value of |this| in this stack frame, or JSVAL_NULL if |this| is to - * be computed lazily on demand. - * - * thisv is eagerly initialized for non-function-call frames and qualified - * method calls, but lazily initialized in most unqualified function calls. - * See getThisObject(). - * - * Usually if argv != NULL then thisv == argv[-1], but natives may assign - * to argv[-1]. Also, obj_eval can trigger a special case where two stack - * frames have the same argv. If one of the frames fills in both argv[-1] - * and thisv, the other frame's thisv is left null. - */ - jsval thisv; - + jsval thisv; /* "this" pointer if in method */ uintN argc; /* actual argument count */ jsval *argv; /* base of argument stack slots */ jsval rval; /* function return value */ @@ -224,9 +211,6 @@ struct JSStackFrame } return false; } - -private: - JSObject *computeThisObject(JSContext *cx); }; namespace js { @@ -273,9 +257,10 @@ js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp); /* * For a call with arguments argv including argv[-1] (nominal |this|) and - * argv[-2] (callee) replace null |this| with callee's parent and replace - * primitive values with the equivalent wrapper objects. argv[-1] must not be - * JSVAL_VOID or an activation object. + * argv[-2] (callee) replace null |this| with callee's parent, replace + * primitive values with the equivalent wrapper objects and censor activation + * objects as, per ECMA-262, they may not be referred to by |this|. argv[-1] + * must not be a JSVAL_VOID. */ extern JSObject * js_ComputeThis(JSContext *cx, jsval *argv); @@ -464,7 +449,14 @@ JS_END_EXTERN_C inline JSObject * JSStackFrame::getThisObject(JSContext *cx) { - return JSVAL_IS_PRIMITIVE(thisv) ? computeThisObject(cx) : JSVAL_TO_OBJECT(thisv); + if (flags & JSFRAME_COMPUTED_THIS) + return JSVAL_TO_OBJECT(thisv); /* JSVAL_COMPUTED_THIS invariant */ + JSObject* obj = js_ComputeThis(cx, argv); + if (!obj) + return NULL; + thisv = OBJECT_TO_JSVAL(obj); + flags |= JSFRAME_COMPUTED_THIS; + return obj; } #endif /* jsinterp_h___ */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 1474e9f6454..0b949edb41c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6415,9 +6415,7 @@ js_DumpAtom(JSAtom *atom) void dumpValue(jsval val) { - if ((val & 0xfffffff0) == 0xdadadad0) { - fprintf(stderr, "**uninitialized** %p", (void *) val); - } else if (JSVAL_IS_NULL(val)) { + if (JSVAL_IS_NULL(val)) { fprintf(stderr, "null"); } else if (JSVAL_IS_VOID(val)) { fprintf(stderr, "undefined"); @@ -6651,6 +6649,8 @@ js_DumpStackFrame(JSContext *cx, JSStackFrame *start) fprintf(stderr, " none"); if (fp->flags & JSFRAME_CONSTRUCTING) fprintf(stderr, " constructing"); + if (fp->flags & JSFRAME_COMPUTED_THIS) + fprintf(stderr, " computed_this"); if (fp->flags & JSFRAME_ASSIGNING) fprintf(stderr, " assigning"); if (fp->flags & JSFRAME_DEBUGGER) diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 15e845dff75..0aad4e3d7e4 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1096,25 +1096,6 @@ js_IsCacheableNonGlobalScope(JSObject *obj) return cacheable; } -#ifdef DEBUG -/* - * Used in assertions only. False if obj is a special object which must be - * censored and thus can't be the value of 'this'. - */ -inline bool -js_IsSaneThisObject(JSObject *obj) -{ - extern JS_FRIEND_DATA(JSClass) js_CallClass; - extern JS_FRIEND_DATA(JSClass) js_DeclEnvClass; - - JSClass *clasp = obj->getClass(); - return clasp != &js_CallClass && - clasp != &js_BlockClass && - clasp != &js_DeclEnvClass && - clasp != &js_WithClass; -} -#endif - /* * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success. */ diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 15f39002afe..18660e434a7 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -216,10 +216,8 @@ BEGIN_CASE(JSOP_STOP) } JS_ASSERT(regs.sp == StackBase(fp)); - if ((fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(fp->rval)) { - JS_ASSERT(!JSVAL_IS_PRIMITIVE(fp->thisv)); + if ((fp->flags & JSFRAME_CONSTRUCTING) && JSVAL_IS_PRIMITIVE(fp->rval)) fp->rval = fp->thisv; - } ok = JS_TRUE; if (inlineCallCount) inline_return: @@ -270,10 +268,8 @@ BEGIN_CASE(JSOP_STOP) * passed in via |this|, and instrument this constructor invocation. */ if (fp->flags & JSFRAME_CONSTRUCTING) { - if (JSVAL_IS_PRIMITIVE(fp->rval)) { - JS_ASSERT(!JSVAL_IS_PRIMITIVE(fp->thisv)); + if (JSVAL_IS_PRIMITIVE(fp->rval)) fp->rval = fp->thisv; - } JS_RUNTIME_METER(cx->runtime, constructs); } @@ -2070,8 +2066,6 @@ BEGIN_CASE(JSOP_APPLY) *disp = newfp; } JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags)); - JS_ASSERT_IF(!JSVAL_IS_PRIMITIVE(vp[1]), - js_IsSaneThisObject(JSVAL_TO_OBJECT(vp[1]))); newfp->thisv = vp[1]; newfp->imacpc = NULL; @@ -2178,29 +2172,6 @@ BEGIN_CASE(JSOP_SETCALL) goto error; END_CASE(JSOP_SETCALL) -#define SLOW_PUSH_THISV(cx, obj) \ - JS_BEGIN_MACRO \ - JSClass *clasp; \ - JSObject *thisp = obj; \ - if (!thisp->getParent() || \ - (clasp = thisp->getClass()) == &js_CallClass || \ - clasp == &js_BlockClass || \ - clasp == &js_DeclEnvClass) { \ - /* Normal case: thisp is global or an activation record. */ \ - /* Callee determines 'this'. */ \ - thisp = NULL; \ - } else { \ - /* thisp is a With object or, even stranger, a plain nonglobal */ \ - /* object on the scope chain. Call the thisObject hook now: */ \ - /* since we are pushing a non-null thisv, the callee will */ \ - /* assume 'this' was computed. */ \ - thisp = thisp->thisObject(cx); \ - if (!thisp) \ - goto error; \ - } \ - PUSH_OPND(OBJECT_TO_JSVAL(thisp)); \ - JS_END_MACRO - BEGIN_CASE(JSOP_NAME) BEGIN_CASE(JSOP_CALLNAME) { @@ -2213,31 +2184,19 @@ BEGIN_CASE(JSOP_CALLNAME) ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); if (entry->vword.isObject()) { rval = entry->vword.toJsval(); - } else if (entry->vword.isSlot()) { + goto do_push_rval; + } + + if (entry->vword.isSlot()) { slot = entry->vword.toSlot(); JS_ASSERT(slot < obj2->scope()->freeslot); rval = obj2->lockedGetSlot(slot); - } else { - JS_ASSERT(entry->vword.isSprop()); - sprop = entry->vword.toSprop(); - NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval); + goto do_push_rval; } - // Push results, the same as below, but with a property cache hit there - // is no need to test for the unusual and uncacheable case where the - // caller determines 'this'. -#ifdef DEBUG - JSClass *clasp; - JS_ASSERT(!obj->getParent() || - (clasp = obj->getClass()) == &js_CallClass || - clasp == &js_BlockClass || - clasp == &js_DeclEnvClass); -#endif - PUSH_OPND(rval); - if (op == JSOP_CALLNAME) - PUSH_OPND(JSVAL_NULL); - len = JSOP_NAME_LENGTH; - DO_NEXT_OP(len); + JS_ASSERT(entry->vword.isSprop()); + sprop = entry->vword.toSprop(); + goto do_native_get; } id = ATOM_TO_JSID(atom); @@ -2262,13 +2221,15 @@ BEGIN_CASE(JSOP_CALLNAME) goto error; } else { sprop = (JSScopeProperty *)prop; + do_native_get: NATIVE_GET(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, &rval); obj2->dropProperty(cx, (JSProperty *) sprop); } + do_push_rval: PUSH_OPND(rval); if (op == JSOP_CALLNAME) - SLOW_PUSH_THISV(cx, obj); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); } END_CASE(JSOP_NAME) @@ -3733,7 +3694,7 @@ BEGIN_CASE(JSOP_XMLNAME) goto error; STORE_OPND(-1, rval); if (op == JSOP_CALLXMLNAME) - SLOW_PUSH_THISV(cx, obj); + PUSH_OPND(OBJECT_TO_JSVAL(obj)); END_CASE(JSOP_XMLNAME) BEGIN_CASE(JSOP_DESCENDANTS) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index cd578eba017..ba55886ef27 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1820,7 +1820,7 @@ FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep) /* Push lambda and its 'this' parameter. */ jsval *sp = rdata.args.getvp(); *sp++ = OBJECT_TO_JSVAL(lambda); - *sp++ = JSVAL_NULL; + *sp++ = OBJECT_TO_JSVAL(lambda->getParent()); /* Push $&, $1, $2, ... */ if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp)) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 55cd9e6b79a..b857954f80b 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -3343,6 +3343,8 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const TraceType* mp, do fp->scopeChain->setPrivate(fp); } fp->thisv = fp->argv[-1]; + if (fp->flags & JSFRAME_CONSTRUCTING) // constructors always compute 'this' + fp->flags |= JSFRAME_COMPUTED_THIS; } } } @@ -9461,54 +9463,78 @@ TraceRecorder::unbox_jsval(jsval v, LIns* v_ins, VMSideExit* exit) JS_REQUIRES_STACK RecordingStatus TraceRecorder::getThis(LIns*& this_ins) { - JSStackFrame *fp = cx->fp; - JS_ASSERT_IF(fp->argv, fp->argv[-1] == fp->thisv); + /* + * JSStackFrame::getThisObject updates cx->fp->argv[-1], so sample it into 'original' first. + */ + jsval original = JSVAL_NULL; + if (cx->fp->argv) { + original = cx->fp->argv[-1]; + if (!JSVAL_IS_PRIMITIVE(original)) { + if (JSVAL_TO_OBJECT(original)->hasClass(&js_WithClass)) + RETURN_STOP("can't trace getThis on With object"); + guardNotClass(get(&cx->fp->argv[-1]), &js_WithClass, snapshot(MISMATCH_EXIT), + ACC_OTHER); + } + } - if (!fp->fun) { - // Top-level code. It is an invariant of the interpreter that fp->thisv - // is non-null. Furthermore, we would not be recording if globalObj - // were not at the end of the scope chain, so `this` can only be one - // object, which we can burn into the trace. + JSObject* thisObj = cx->fp->getThisObject(cx); + if (!thisObj) + RETURN_ERROR("fp->getThisObject failed"); - JS_ASSERT(!fp->argv); - JS_ASSERT(!JSVAL_IS_PRIMITIVE(fp->thisv)); + /* In global code, bake in the global object as 'this' object. */ + if (!cx->fp->callee()) { + JS_ASSERT(callDepth == 0); + this_ins = INS_CONSTOBJ(thisObj); -#ifdef DEBUG - JSObject *obj = globalObj->thisObject(cx); - if (!obj) - RETURN_ERROR("thisObject hook failed"); - JS_ASSERT(JSVAL_TO_OBJECT(fp->thisv) == obj); -#endif - - this_ins = INS_CONSTOBJ(JSVAL_TO_OBJECT(fp->thisv)); + /* + * We don't have argv[-1] in global code, so we don't update the + * tracker here. + */ return RECORD_CONTINUE; } - jsval thisv = fp->argv[-1]; - JS_ASSERT(thisv == fp->thisv || JSVAL_IS_NULL(fp->thisv)); + jsval& thisv = cx->fp->argv[-1]; JS_ASSERT(JSVAL_IS_OBJECT(thisv)); - JS_ASSERT(fp->callee()->getGlobal() == globalObj); - if (!JSVAL_IS_NULL(thisv)) { - // fp->argv[-1] has already been computed. Since the type- - // specialization of traces distinguishes between null and objects, the - // same will be true at run time (or we won't get this far). - this_ins = get(&fp->argv[-1]); + /* + * Traces type-specialize between null and objects, so if we currently see + * a null value in argv[-1], this trace will only match if we see null at + * runtime as well. Bake in the global object as 'this' object, updating + * the tracker as well. We can only detect this condition prior to calling + * JSStackFrame::getThisObject, since it updates the interpreter's copy of + * argv[-1]. + */ + JSClass* clasp = NULL; + if (JSVAL_IS_NULL(original) || + (((clasp = JSVAL_TO_OBJECT(original)->getClass()) == &js_CallClass) || + (clasp == &js_BlockClass))) { + if (clasp) + guardClass(get(&thisv), clasp, snapshot(BRANCH_EXIT), ACC_OTHER); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(thisv)); + if (thisObj != globalObj) + RETURN_STOP("global object was wrapped while recording"); + this_ins = INS_CONSTOBJ(thisObj); + set(&thisv, this_ins); return RECORD_CONTINUE; } - // Compute fp->argv[-1] now. The result is globalObj->thisObject(), which - // is trace-constant. getThisObject writes back to fp->argv[-1], so do the - // same on trace. - JSObject *obj = fp->getThisObject(cx); - if (!obj) - RETURN_ERROR("getThisObject failed"); - JS_ASSERT(fp->argv[-1] == OBJECT_TO_JSVAL(obj)); - this_ins = INS_CONSTOBJ(obj); - set(&fp->argv[-1], this_ins); + this_ins = get(&thisv); + + JSObject* wrappedGlobal = globalObj->thisObject(cx); + if (!wrappedGlobal) + RETURN_ERROR("globalObj->thisObject hook threw in getThis"); + + /* + * The only unwrapped object that needs to be wrapped that we can get here + * is the global object obtained throught the scope chain. + */ + this_ins = lir->insChoose(lir->insEqP_0(stobj_get_parent(this_ins)), + INS_CONSTOBJ(wrappedGlobal), + this_ins, avmplus::AvmCore::use_cmov()); return RECORD_CONTINUE; } + JS_REQUIRES_STACK void TraceRecorder::guardClassHelper(bool cond, LIns* obj_ins, JSClass* clasp, VMSideExit* exit, AccSet accSet) @@ -12366,7 +12392,7 @@ TraceRecorder::record_JSOP_CALLNAME() NameResult nr; CHECK_STATUS_A(scopeChainProp(obj, vp, ins, nr)); stack(0, ins); - stack(1, INS_NULL()); + stack(1, INS_CONSTOBJ(globalObj)); return ARECORD_CONTINUE; } @@ -12382,7 +12408,7 @@ TraceRecorder::record_JSOP_CALLNAME() JS_ASSERT(pcval.toObject()->isFunction()); stack(0, INS_CONSTOBJ(pcval.toObject())); - stack(1, INS_NULL()); + stack(1, obj_ins); return ARECORD_CONTINUE; } @@ -15111,8 +15137,9 @@ TraceRecorder::record_JSOP_GETTHISPROP() /* * It's safe to just use cx->fp->thisv here because getThis() returns - * ARECORD_STOP or ARECORD_ERROR if thisv is not available. + * ARECORD_STOP if thisv is not available. */ + JS_ASSERT(cx->fp->flags & JSFRAME_COMPUTED_THIS); CHECK_STATUS_A(getProp(JSVAL_TO_OBJECT(cx->fp->thisv), this_ins)); return ARECORD_CONTINUE; } diff --git a/js/src/trace-test/tests/basic/testThis1.js b/js/src/trace-test/tests/basic/testThis1.js deleted file mode 100644 index 5f660d8cb5b..00000000000 --- a/js/src/trace-test/tests/basic/testThis1.js +++ /dev/null @@ -1,5 +0,0 @@ -var x = this; -var y; -for (var i = 0; i < 9; i++) - y = this; -assertEq(x, y); diff --git a/js/src/trace-test/tests/basic/testThis2.js b/js/src/trace-test/tests/basic/testThis2.js deleted file mode 100644 index bef3db4aa88..00000000000 --- a/js/src/trace-test/tests/basic/testThis2.js +++ /dev/null @@ -1,4 +0,0 @@ -var cx = evalcx(""); -evalcx("function f() {var x; for (var i=0;i<9;i++) x=this; return x;}", cx); -var f = cx.f; -assertEq(f(), cx);