diff --git a/js/src/imacros.jsasm b/js/src/imacros.jsasm index 09d13886f33..6215a9b3019 100644 --- a/js/src/imacros.jsasm +++ b/js/src/imacros.jsasm @@ -730,6 +730,15 @@ .end .end callprop +.igroup setprop JSOP_SETPROP,JSOP_SETNAME,JSOP_SETMETHOD + .imacro scriptsetter # obj val + .fixup +2 # val setter obj val + call 1 # val ret + pop # val + stop + .end +.end setprop + .igroup getthisprop JSOP_GETTHISPROP,JSOP_GETARGPROP,JSOP_GETLOCALPROP .imacro scriptgetter # .fixup +2 # getter obj diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 11aabe888be..a6cd687a615 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4515,7 +4515,7 @@ class DefaultSlotMap : public SlotMap DefaultSlotMap(TraceRecorder& tr) : SlotMap(tr) { } - + virtual ~DefaultSlotMap() { } @@ -11322,6 +11322,23 @@ TraceRecorder::lookupForSetPropertyOp(JSObject* obj, LIns* obj_ins, jsid id, return RECORD_CONTINUE; } +JS_REQUIRES_STACK RecordingStatus +TraceRecorder::setPropertyWithScriptSetter(JSScopeProperty* sprop) +{ + if (!canCallImacro()) + RETURN_STOP("cannot trace script setter, already in imacro"); + + // Rearrange the stack in preparation for the imacro. See the comment in + // getPropertyWithScriptGetter. + JSObject *setterObj = JSVAL_TO_OBJECT(sprop->setterValue()); + cx->regs->sp += 2; // obj val --- --- + stackCopy(-2, -4); // obj val obj --- + stackCopy(-4, -3); // val val obj --- + stackCopy(-1, -3); // val val obj val + stackStoreConstObject(-3, setterObj); // val setter obj val + return callImacroInfallibly(setprop_imacros.scriptsetter); +} + static JSBool FASTCALL MethodWriteBarrier(JSContext* cx, JSObject* obj, uint32 slot, jsval v) { @@ -11386,7 +11403,7 @@ TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop, // Call the setter, if any. if (!sprop->hasDefaultSetter()) { if (sprop->hasSetterValue()) - RETURN_STOP("can't trace JavaScript function setter yet"); + return setPropertyWithScriptSetter(sprop); emitNativePropertyOp(scope, sprop, obj_ins, true, boxed_ins); } @@ -12047,6 +12064,33 @@ TraceRecorder::getPropertyWithNativeGetter(LIns* obj_ins, JSScopeProperty* sprop return RECORD_CONTINUE; } +/* Set sp[dest] = sp[src], both on the real interpreter stack and in the tracker. */ +JS_REQUIRES_STACK void +TraceRecorder::stackCopy(int dest, int src) +{ + jsval* sp = cx->regs->sp; + sp[dest] = sp[src]; + set(&sp[dest], get(&sp[src])); +} + +/* Set sp[dest] = obj, both on the real interpreter stack and in the tracker. */ +JS_REQUIRES_STACK void +TraceRecorder::stackStoreConstObject(int dest, JSObject *obj) +{ + jsval* sp = cx->regs->sp; + sp[dest] = OBJECT_TO_JSVAL(obj); + set(&sp[dest], INS_CONSTOBJ(obj)); +} + +/* Set sp[dest] = obj/obj_ins, both on the real interpreter stack and in the tracker. */ +JS_REQUIRES_STACK void +TraceRecorder::stackStore(int dest, JSObject* obj, LIns* obj_ins) +{ + jsval* sp = cx->regs->sp; + sp[dest] = OBJECT_TO_JSVAL(obj); + set(&sp[dest], obj_ins); +} + JS_REQUIRES_STACK RecordingStatus TraceRecorder::getPropertyWithScriptGetter(JSObject *obj, LIns* obj_ins, JSScopeProperty* sprop) { @@ -12061,28 +12105,22 @@ TraceRecorder::getPropertyWithScriptGetter(JSObject *obj, LIns* obj_ins, JSScope switch (*cx->regs->pc) { case JSOP_GETPROP: sp++; - sp[-1] = sp[-2]; - set(&sp[-1], get(&sp[-2])); - sp[-2] = getter; - set(&sp[-2], INS_CONSTOBJ(JSVAL_TO_OBJECT(getter))); + stackCopy(-1, -2); + stackStoreConstObject(-2, JSVAL_TO_OBJECT(getter)); return callImacroInfallibly(getprop_imacros.scriptgetter); case JSOP_CALLPROP: sp += 2; - sp[-2] = getter; - set(&sp[-2], INS_CONSTOBJ(JSVAL_TO_OBJECT(getter))); - sp[-1] = sp[-3]; - set(&sp[-1], get(&sp[-3])); + stackStoreConstObject(-2, JSVAL_TO_OBJECT(getter)); + stackCopy(-1, -3); return callImacroInfallibly(callprop_imacros.scriptgetter); case JSOP_GETTHISPROP: case JSOP_GETARGPROP: case JSOP_GETLOCALPROP: sp += 2; - sp[-2] = getter; - set(&sp[-2], INS_CONSTOBJ(JSVAL_TO_OBJECT(getter))); - sp[-1] = OBJECT_TO_JSVAL(obj); - set(&sp[-1], obj_ins); + stackStoreConstObject(-2, JSVAL_TO_OBJECT(getter)); + stackStore(-1, obj, obj_ins); return callImacroInfallibly(getthisprop_imacros.scriptgetter); default: @@ -12411,9 +12449,9 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) LIns* priv_ins = stobj_get_const_fslot(obj_ins, JSSLOT_PRIVATE); // The index was on the stack and is therefore a LIR float; force it to - // be an integer. - idx_ins = makeNumberInt32(idx_ins); - + // be an integer. + idx_ins = makeNumberInt32(idx_ins); + // Ensure idx >= 0 && idx < length (by using uint32) lir->insGuard(LIR_xf, lir->ins2(LIR_ltui, @@ -12481,7 +12519,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) // Do nothing, this is already a float break; default: - JS_NOT_REACHED("Unknown typed array type in tracer"); + JS_NOT_REACHED("Unknown typed array type in tracer"); } switch (tarray->type) { @@ -12510,7 +12548,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) lir->insStore(LIR_std, typed_v_ins, addr_ins, 0, ACC_OTHER); break; default: - JS_NOT_REACHED("Unknown typed array type in tracer"); + JS_NOT_REACHED("Unknown typed array type in tracer"); } } else if (JSVAL_TO_INT(idx) < 0 || !obj->isDenseArray()) { CHECK_STATUS_A(initOrSetPropertyByIndex(obj_ins, idx_ins, &v, diff --git a/js/src/jstracer.h b/js/src/jstracer.h index e2de827253d..54bc2a53f7f 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1303,6 +1303,9 @@ class TraceRecorder JS_REQUIRES_STACK RecordingStatus getPropertyWithNativeGetter(nanojit::LIns* obj_ins, JSScopeProperty* sprop, jsval* outp); + JS_REQUIRES_STACK void stackCopy(int dest, int src); + JS_REQUIRES_STACK void stackStoreConstObject(int dest, JSObject *obj); + JS_REQUIRES_STACK void stackStore(int dest, JSObject* obj, nanojit::LIns* obj_ins); JS_REQUIRES_STACK RecordingStatus getPropertyWithScriptGetter(JSObject *obj, nanojit::LIns* obj_ins, JSScopeProperty* sprop); @@ -1320,6 +1323,7 @@ class TraceRecorder jsid id, bool* safep, JSObject** pobjp, JSScopeProperty** spropp); + JS_REQUIRES_STACK RecordingStatus setPropertyWithScriptSetter(JSScopeProperty* sprop); JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins, JSScopeProperty* sprop, jsval v, nanojit::LIns* v_ins); diff --git a/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETMETHOD.js b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETMETHOD.js new file mode 100644 index 00000000000..00dbab03175 --- /dev/null +++ b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETMETHOD.js @@ -0,0 +1,16 @@ +function s(f) { this._m = f; } + +function C(i) { + Object.defineProperty(this, "m", {set: s}); + this.m = function () { return 17; }; +} + +var arr = []; +for (var i = 0; i < 9; i++) + arr[i] = new C(i); + +checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); + +//BUG - uncomment this when bug 559912 is fixed +//for (var i = 1; i < 9; i++) +// assertEq(arr[0]._m === arr[i]._m, false); diff --git a/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETNAME.js b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETNAME.js new file mode 100644 index 00000000000..21d34a6d3c6 --- /dev/null +++ b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETNAME.js @@ -0,0 +1,8 @@ +var n = 0; +Object.defineProperty(this, "p", {get: function () { return n; }, + set: function (x) { n += x; }}); +for (var i = 0; i < 9; i++) + p = i; +assertEq(n, 36); + +checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1}); diff --git a/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETPROP.js b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETPROP.js new file mode 100644 index 00000000000..bcccd910ada --- /dev/null +++ b/js/src/trace-test/tests/basic/testScriptSetter_JSOP_SETPROP.js @@ -0,0 +1,7 @@ +var n = 0; +var a = {set p(x) { n += x; }}; +for (var i = 0; i < 9; i++) + a.p = i; +assertEq(n, 36); + +checkStats({recorderStarted: 1, recorderAborted: 0, traceCompleted: 1, traceTriggered: 1});