diff --git a/js/src/imacros.c.out b/js/src/imacros.c.out index 6d936c1afc54..cd101262fd42 100644 --- a/js/src/imacros.c.out +++ b/js/src/imacros.c.out @@ -692,6 +692,15 @@ static struct { /* 4*/ JSOP_STOP, }, }; +static struct { + jsbytecode scriptsetter[5]; +} setprop_imacros = { + { +/* 0*/ JSOP_CALL, 0, 1, +/* 3*/ JSOP_POP, +/* 4*/ JSOP_STOP, + }, +}; static struct { jsbytecode scriptgetter[4]; } getthisprop_imacros = { @@ -755,7 +764,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { 0, /* JSOP_PROPDEC */ 0, /* JSOP_ELEMDEC */ 1, /* JSOP_GETPROP */ - 0, /* JSOP_SETPROP */ + 2, /* JSOP_SETPROP */ 0, /* JSOP_GETELEM */ 0, /* JSOP_SETELEM */ 0, /* JSOP_CALLNAME */ @@ -810,7 +819,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { 0, /* JSOP_FORELEM */ 0, /* JSOP_POPN */ 0, /* JSOP_BINDNAME */ - 0, /* JSOP_SETNAME */ + 2, /* JSOP_SETNAME */ 0, /* JSOP_THROW */ 0, /* JSOP_IN */ 0, /* JSOP_INSTANCEOF */ @@ -936,7 +945,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { 0, /* JSOP_DEFLOCALFUN_DBGFC */ 0, /* JSOP_LAMBDA_DBGFC */ 0, /* JSOP_CONCATN */ - 0, /* JSOP_SETMETHOD */ + 2, /* JSOP_SETMETHOD */ 0, /* JSOP_INITMETHOD */ 0, /* JSOP_UNBRAND */ 0, /* JSOP_UNBRANDTHIS */ @@ -964,15 +973,18 @@ uint8 js_opcode2extra[JSOP_LIMIT] = { || x == JSOP_NEG \ || x == JSOP_POS \ || x == JSOP_GETPROP \ + || x == JSOP_SETPROP \ || x == JSOP_CALL \ || x == JSOP_ITER \ || x == JSOP_NEXTITER \ || x == JSOP_APPLY \ || x == JSOP_NEW \ + || x == JSOP_SETNAME \ || x == JSOP_CALLPROP \ || x == JSOP_GETTHISPROP \ || x == JSOP_GETARGPROP \ || x == JSOP_GETLOCALPROP \ + || x == JSOP_SETMETHOD \ || x == JSOP_OBJTOSTR \ ) jsbytecode* @@ -1015,6 +1027,7 @@ js_GetImacroStart(jsbytecode* pc) { if (size_t(pc - objtostr_imacros.toString) < 35) return objtostr_imacros.toString; if (size_t(pc - getprop_imacros.scriptgetter) < 4) return getprop_imacros.scriptgetter; if (size_t(pc - callprop_imacros.scriptgetter) < 5) return callprop_imacros.scriptgetter; + if (size_t(pc - setprop_imacros.scriptsetter) < 5) return setprop_imacros.scriptsetter; if (size_t(pc - getthisprop_imacros.scriptgetter) < 4) return getthisprop_imacros.scriptgetter; return NULL; } diff --git a/js/src/imacros.jsasm b/js/src/imacros.jsasm index 989996e3ddc5..7ce2b622d796 100644 --- a/js/src/imacros.jsasm +++ b/js/src/imacros.jsasm @@ -740,6 +740,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 6e7befe3a650..06a43218c583 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4510,7 +4510,7 @@ class DefaultSlotMap : public SlotMap DefaultSlotMap(TraceRecorder& tr) : SlotMap(tr) { } - + virtual ~DefaultSlotMap() { } @@ -11323,6 +11323,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->fp->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, JSScopeProperty* sprop, JSObject* funobj) { @@ -11388,7 +11405,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); } @@ -12039,6 +12056,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->fp->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->fp->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->fp->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) { @@ -12053,28 +12097,22 @@ TraceRecorder::getPropertyWithScriptGetter(JSObject *obj, LIns* obj_ins, JSScope switch (*cx->fp->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: @@ -12403,9 +12441,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_ult, @@ -12473,7 +12511,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) { @@ -12502,7 +12540,7 @@ TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex) lir->insStore(LIR_stfi, 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 47e767000508..340e62e19cc6 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1300,6 +1300,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); @@ -1317,6 +1320,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 000000000000..00dbab031750 --- /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 000000000000..21d34a6d3c6f --- /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 000000000000..bcccd910adaf --- /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});