From b152e9656d2067d57d6c339479a9331028d16f5f Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Thu, 17 Sep 2009 18:13:20 -0700 Subject: [PATCH] Bug 517117: don't trace JSOP_ARGCNT if arguments.length has been overridden, r=dvander --HG-- extra : rebase_source : a53403064c533bdc3270408f0d29eeec12970040 --- js/src/jsfun.cpp | 45 ++----------------- js/src/jsfun.h | 37 +++++++++++++++ js/src/jstracer.cpp | 10 +++-- .../tests/basic/setArgumentsLength2.js | 12 +++++ 4 files changed, 60 insertions(+), 44 deletions(-) create mode 100644 js/src/trace-test/tests/basic/setArgumentsLength2.js diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 18ced2d33de..db11ed43993 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -81,43 +81,6 @@ #include "jsatominlines.h" -/* - * Reserved slot structure for Arguments objects: - * - * JSSLOT_PRIVATE - the corresponding frame until the frame exits. - * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating - * whether arguments.length was overwritten. - * JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was - * overwritten. - * JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after - * the frame exists. The slot's value will be JSVAL_HOLE - * if arguments[i] was deleted or overwritten. - */ -const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; -const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2; -const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3; - -/* Number of extra fixed slots besides JSSLOT_PRIVATE. */ -const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START - - JSSLOT_ARGS_LENGTH; - -/* - * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval. - * Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that - * we check first that the shift does not overflow uint32. - */ -JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30)); -JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX); - -static inline bool -IsOverriddenArgsLength(JSObject *obj) -{ - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); - - jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; - return (JSVAL_TO_INT(v) & 1) != 0; -} - static inline void SetOverriddenArgsLength(JSObject *obj) { @@ -136,7 +99,7 @@ InitArgsLengthSlot(JSObject *obj, uint32 argc) JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); JS_ASSERT(obj->fslots[JSSLOT_ARGS_LENGTH] == JSVAL_VOID); obj->fslots[JSSLOT_ARGS_LENGTH] = INT_TO_JSVAL(argc << 1); - JS_ASSERT(!IsOverriddenArgsLength(obj)); + JS_ASSERT(!js_IsOverriddenArgsLength(obj)); } static inline uint32 @@ -225,7 +188,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp) } } else if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); - if (argsobj && IsOverriddenArgsLength(argsobj)) + if (argsobj && js_IsOverriddenArgsLength(argsobj)) return argsobj->getProperty(cx, id, vp); *vp = INT_TO_JSVAL(jsint(fp->argc)); } @@ -561,7 +524,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) } } } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { - if (!IsOverriddenArgsLength(obj)) + if (!js_IsOverriddenArgsLength(obj)) *vp = INT_TO_JSVAL(GetArgsLength(obj)); } else { JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)); @@ -641,7 +604,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, id = INT_JSVAL_TO_JSID(idval); } } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { - if (!IsOverriddenArgsLength(obj)) + if (!js_IsOverriddenArgsLength(obj)) id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) { diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 65ed767bee0..069b7f4c2b0 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -334,6 +334,43 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp); extern void js_PutArgsObject(JSContext *cx, JSStackFrame *fp); +/* + * Reserved slot structure for Arguments objects: + * + * JSSLOT_PRIVATE - the corresponding frame until the frame exits. + * JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag indicating + * whether arguments.length was overwritten. + * JSSLOT_ARGS_CALLEE - the arguments.callee value or JSVAL_HOLE if that was + * overwritten. + * JSSLOT_ARGS_COPY_START .. - room to store the corresponding arguments after + * the frame exists. The slot's value will be JSVAL_HOLE + * if arguments[i] was deleted or overwritten. + */ +const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2; +const uint32 JSSLOT_ARGS_COPY_START = JSSLOT_PRIVATE + 3; + +/* Number of extra fixed slots besides JSSLOT_PRIVATE. */ +const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START - + JSSLOT_ARGS_LENGTH; + +/* + * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval. + * Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that + * we check first that the shift does not overflow uint32. + */ +JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30)); +JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX); + +JS_INLINE bool +js_IsOverriddenArgsLength(JSObject *obj) +{ + JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + + jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; + return (JSVAL_TO_INT(v) & 1) != 0; +} + extern JSBool js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 1b792555b00..6f673144519 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -12710,13 +12710,17 @@ TraceRecorder::record_JSOP_ARGCNT() // argc is fixed on trace, so ideally we would simply generate LIR for // constant argc. But the user can mutate arguments.length in the // interpreter, so we have to check for that in the trace entry frame. + // We also have to check that arguments.length has not been mutated + // at record time, because if so we will generate incorrect constant + // LIR, which will assert in alu(). + if (cx->fp->argsobj && js_IsOverriddenArgsLength(JSVAL_TO_OBJECT(cx->fp->argsobj))) + ABORT_TRACE("can't trace JSOP_ARGCNT if arguments.length has been modified"); LIns *a_ins = get(&cx->fp->argsobj); if (callDepth == 0) { LIns *br = lir->insBranch(LIR_jt, lir->ins_peq0(a_ins), NULL); - // The following implements IsOverriddenArgsLength on trace. - // The '2' bit is set set if length was overridden. - const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; + // The following implements js_IsOverriddenArgsLength on trace. + // The '2' bit is set if length was overridden. LIns *len_ins = stobj_get_fslot(a_ins, JSSLOT_ARGS_LENGTH); LIns *ovr_ins = lir->ins2(LIR_piand, len_ins, INS_CONSTWORD(2)); diff --git a/js/src/trace-test/tests/basic/setArgumentsLength2.js b/js/src/trace-test/tests/basic/setArgumentsLength2.js new file mode 100644 index 00000000000..2dcb1742425 --- /dev/null +++ b/js/src/trace-test/tests/basic/setArgumentsLength2.js @@ -0,0 +1,12 @@ +// don't crash + +var q; + +function f() { + while (arguments.length > 0) { + q = arguments[arguments.length-1]; + arguments.length--; + } +} + +f(1, 2, 3, 4, 5);