diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ee89d49c6264..98bf8884629c 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -8152,7 +8152,8 @@ JS_DEFINE_CALLINFO_4(extern, UINT32, GetClosureArg, CONTEXT, OBJECT, CVIPTR, DOU * NameResult describes how to look up name; see comment for NameResult in jstracer.h */ JS_REQUIRES_STACK AbortableRecordingStatus -TraceRecorder::scopeChainProp(JSObject* chainHead, Value*& vp, LIns*& ins, NameResult& nr) +TraceRecorder::scopeChainProp(JSObject* chainHead, Value*& vp, LIns*& ins, NameResult& nr, + JSObject** scopeObjp) { JS_ASSERT(chainHead == &cx->fp()->scopeChain()); JS_ASSERT(chainHead != globalObj); @@ -8173,6 +8174,9 @@ TraceRecorder::scopeChainProp(JSObject* chainHead, Value*& vp, LIns*& ins, NameR if (!prop) RETURN_STOP_A("failed to find name in non-global scope chain"); + if (scopeObjp) + *scopeObjp = obj; + if (obj == globalObj) { // Even if the property is on the global object, we must guard against // the creation of properties that shadow the property in the middle @@ -13513,30 +13517,66 @@ TraceRecorder::record_JSOP_SETELEM() return setElem(-3, -2, -1); } +static JSBool FASTCALL +CheckSameGlobal(JSObject *obj, JSObject *globalObj) +{ + return obj->getGlobal() == globalObj; +} +JS_DEFINE_CALLINFO_2(static, BOOL, CheckSameGlobal, OBJECT, OBJECT, 0, ACCSET_STORE_ANY) + JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_CALLNAME() { - JSObject* obj = &cx->fp()->scopeChain(); - if (obj != globalObj) { + JSObject* scopeObj = &cx->fp()->scopeChain(); + LIns *funobj_ins; + JSObject *funobj; + if (scopeObj != globalObj) { Value* vp; - LIns* ins; NameResult nr; - CHECK_STATUS_A(scopeChainProp(obj, vp, ins, nr)); - stack(0, ins); - stack(1, w.immiUndefined()); - return ARECORD_CONTINUE; + CHECK_STATUS_A(scopeChainProp(scopeObj, vp, funobj_ins, nr, &scopeObj)); + if (!nr.tracked) + vp = &nr.v; + if (!vp->isObject()) + RETURN_STOP_A("callee is not an object"); + funobj = &vp->toObject(); + if (!funobj->isFunction()) + RETURN_STOP_A("callee is not a function"); + } else { + LIns* obj_ins = w.immpObjGC(globalObj); + JSObject* obj2; + PCVal pcval; + + CHECK_STATUS_A(test_property_cache(scopeObj, obj_ins, obj2, pcval)); + + if (pcval.isNull() || !pcval.isFunObj()) + RETURN_STOP_A("callee is not a function"); + + funobj = &pcval.toFunObj(); + funobj_ins = w.immpObjGC(funobj); } - LIns* obj_ins = w.immpObjGC(globalObj); - JSObject* obj2; - PCVal pcval; + // Detect crossed globals early. The interpreter could decide to compute + // a non-Undefined |this| value, and we want to make sure that we'll (1) + // abort in this case, and (2) bail out early if a callee will need special + // |this| computation. Note that if (scopeObj != globalObj), + // scopeChainProp() guarantees that scopeObj is a cacheable scope. + if (scopeObj == globalObj) { + JSFunction *fun = funobj->getFunctionPrivate(); + if (!fun->isInterpreted() || !fun->inStrictMode()) { + if (funobj->getGlobal() != globalObj) + RETURN_STOP_A("callee crosses globals"); - CHECK_STATUS_A(test_property_cache(obj, obj_ins, obj2, pcval)); + // If the funobj is not constant, we need may a guard that the + // callee will not cross globals. This is only the case for non- + // compile-and-go trees. + if (!funobj_ins->isImmP() && !tree->script->compileAndGo) { + LIns* args[] = { w.immpObjGC(globalObj), funobj_ins }; + guard(false, w.eqi0(w.call(&CheckSameGlobal_ci, args)), MISMATCH_EXIT); + } + } + } - if (pcval.isNull() || !pcval.isFunObj()) - RETURN_STOP_A("callee is not an object"); - - stack(0, w.immpObjGC(&pcval.toFunObj())); + stack(0, funobj_ins); stack(1, w.immiUndefined()); return ARECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 7246a91da0d5..76921cc91e49 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1268,7 +1268,7 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* entryFrameIns() const; JS_REQUIRES_STACK JSStackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const; JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins); - JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, Value*& vp, nanojit::LIns*& ins, NameResult& nr); + JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, Value*& vp, nanojit::LIns*& ins, NameResult& nr, JSObject **scopeObjp = NULL); JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* shape, jsid id, Value*& vp, nanojit::LIns*& ins, NameResult& nr); JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);