Merge backout.

This commit is contained in:
Robert Sayre 2010-06-05 11:43:27 -04:00
commit 37d6a810f7
11 changed files with 172 additions and 206 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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___ */

View File

@ -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)

View File

@ -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.
*/

View File

@ -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)

View File

@ -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))

View File

@ -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;
}

View File

@ -1,5 +0,0 @@
var x = this;
var y;
for (var i = 0; i < 9; i++)
y = this;
assertEq(x, y);

View File

@ -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);