Bug 517117: don't trace JSOP_ARGCNT if arguments.length has been overridden, r=dvander

--HG--
extra : rebase_source : a53403064c533bdc3270408f0d29eeec12970040
This commit is contained in:
David Mandelin 2009-09-17 18:13:20 -07:00
parent 28f39d7f60
commit b152e9656d
4 changed files with 60 additions and 44 deletions

View File

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

View File

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

View File

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

View File

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