diff --git a/js/src/jsapi-tests/testLookup.cpp b/js/src/jsapi-tests/testLookup.cpp index 4d45f8d791eb..82c6eb1374e8 100644 --- a/js/src/jsapi-tests/testLookup.cpp +++ b/js/src/jsapi-tests/testLookup.cpp @@ -1,5 +1,5 @@ #include "tests.h" -#include "jsfun.h" // for js_IsInternalFunctionObject +#include "jsfun.h" // for js::IsInternalFunctionObject BEGIN_TEST(testLookup_bug522590) { @@ -21,7 +21,7 @@ BEGIN_TEST(testLookup_bug522590) CHECK(JSVAL_IS_OBJECT(r)); JSObject *funobj = JSVAL_TO_OBJECT(r); CHECK(HAS_FUNCTION_CLASS(funobj)); - CHECK(!js_IsInternalFunctionObject(funobj)); + CHECK(!js::IsInternalFunctionObject(funobj)); CHECK(GET_FUNCTION_PRIVATE(cx, funobj) != (JSFunction *) funobj); return true; diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 7d0fa07616dc..8d9ae59dc3e8 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -228,16 +228,21 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) { if (obj->isArray()) { *lengthp = obj->fslots[JSSLOT_ARRAY_LENGTH]; - return JS_TRUE; + return true; + } + + if (obj->isArguments() && !IsOverriddenArgsLength(obj)) { + *lengthp = GetArgsLength(obj); + return true; } AutoValueRooter tvr(cx, JSVAL_NULL); if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr())) - return JS_FALSE; + return false; if (JSVAL_IS_INT(tvr.value())) { *lengthp = jsuint(jsint(JSVAL_TO_INT(tvr.value()))); /* jsuint cast does ToUint32 */ - return JS_TRUE; + return true; } *lengthp = js_ValueToECMAUint32(cx, tvr.addr()); @@ -587,11 +592,9 @@ js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) JSBool js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) { - JSClass *clasp; + JSObject *wrappedObj = js_GetWrappedObject(cx, obj); - clasp = OBJ_GET_CLASS(cx, js_GetWrappedObject(cx, obj)); - *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass || - clasp == &js_SlowArrayClass); + *answerp = wrappedObj->isArguments() || wrappedObj->isArray(); if (!*answerp) { *lengthp = 0; return JS_TRUE; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index ae40b2b98650..f013b4ccaf67 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -979,6 +979,7 @@ struct JSRuntime { JSBackgroundThread *deallocatorThread; #endif + JSEmptyScope *emptyArgumentsScope; JSEmptyScope *emptyBlockScope; /* diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 51ef81748883..e11a5f60d726 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -86,7 +86,7 @@ using namespace js; static inline void SetOverriddenArgsLength(JSObject *obj) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; v = INT_TO_JSVAL(JSVAL_TO_INT(v) | 1); @@ -97,27 +97,17 @@ SetOverriddenArgsLength(JSObject *obj) static inline void InitArgsLengthSlot(JSObject *obj, uint32 argc) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); 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(!js_IsOverriddenArgsLength(obj)); -} - -static inline uint32 -GetArgsLength(JSObject *obj) -{ - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); - - uint32 argc = uint32(JSVAL_TO_INT(obj->fslots[JSSLOT_ARGS_LENGTH])) >> 1; - JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); - return argc; + JS_ASSERT(!IsOverriddenArgsLength(obj)); } static inline void SetArgsPrivateNative(JSObject *argsobj, ArgsPrivateNative *apn) { - JS_ASSERT(STOBJ_GET_CLASS(argsobj) == &js_ArgumentsClass); + JS_ASSERT(argsobj->isArguments()); uintptr_t p = (uintptr_t) apn; argsobj->setPrivate((void*) (p | 2)); } @@ -167,7 +157,7 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp) JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj); if (arg < fp->argc) { if (argsobj) { - jsval v = OBJ_GET_SLOT(cx, argsobj, JSSLOT_ARGS_COPY_START+arg); + jsval v = GetArgsSlot(argsobj, arg); if (v == JSVAL_HOLE) return argsobj->getProperty(cx, id, vp); } @@ -190,7 +180,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 && js_IsOverriddenArgsLength(argsobj)) + if (argsobj && IsOverriddenArgsLength(argsobj)) return argsobj->getProperty(cx, id, vp); *vp = INT_TO_JSVAL(jsint(fp->argc)); } @@ -200,12 +190,25 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp) static JSObject * NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee) { - JSObject *argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, parent, 0); - if (!argsobj || !js_EnsureReservedSlots(cx, argsobj, argc)) + JSObject *proto; + if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) return NULL; + JSObject *argsobj = js_NewGCObject(cx); + if (!argsobj) + return NULL; + + /* Init immediately to avoid GC seeing a half-init'ed object. */ + argsobj->init(&js_ArgumentsClass, proto, parent, JSVAL_NULL); argsobj->fslots[JSSLOT_ARGS_CALLEE] = OBJECT_TO_JSVAL(callee); InitArgsLengthSlot(argsobj, argc); + + argsobj->map = cx->runtime->emptyArgumentsScope; + cx->runtime->emptyArgumentsScope->hold(); + + /* This must come after argsobj->map has been set. */ + if (!js_EnsureReservedSlots(cx, argsobj, argc)) + return NULL; return argsobj; } @@ -213,13 +216,11 @@ static void PutArguments(JSContext *cx, JSObject *argsobj, jsval *args) { uint32 argc = GetArgsLength(argsobj); - JS_LOCK_OBJ(cx, argsobj); for (uint32 i = 0; i != argc; ++i) { - jsval v = STOBJ_GET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i); + jsval v = argsobj->dslots[i]; if (v != JSVAL_HOLE) - STOBJ_SET_SLOT(argsobj, JSSLOT_ARGS_COPY_START + i, args[i]); + argsobj->dslots[i] = args[i]; } - JS_UNLOCK_OBJ(cx, argsobj); } JSObject * @@ -303,7 +304,7 @@ JS_DEFINE_CALLINFO_6(extern, OBJECT, js_Arguments, CONTEXT, OBJECT, UINT32, OBJE JSBool JS_FASTCALL js_PutArguments(JSContext *cx, JSObject *argsobj, jsval *args) { - JS_ASSERT(js_GetArgsPrivateNative(argsobj)); + JS_ASSERT(GetArgsPrivateNative(argsobj)); PutArguments(cx, argsobj, args); argsobj->setPrivate(NULL); return true; @@ -315,12 +316,12 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArguments, CONTEXT, OBJECT, JSVALPTR, 0 static JSBool args_delProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); if (JSVAL_IS_INT(idval)) { uintN arg = uintN(JSVAL_TO_INT(idval)); if (arg < GetArgsLength(obj)) - OBJ_SET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg, JSVAL_HOLE); + SetArgsSlot(obj, arg, JSVAL_HOLE); } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { SetOverriddenArgsLength(obj); } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) { @@ -514,7 +515,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) uintN arg = uintN(JSVAL_TO_INT(idval)); if (arg < GetArgsLength(obj)) { #ifdef JS_TRACER - ArgsPrivateNative *argp = js_GetArgsPrivateNative(obj); + ArgsPrivateNative *argp = GetArgsPrivateNative(obj); if (argp) { if (NativeToValue(cx, *vp, argp->typemap()[arg], &argp->argv[arg])) return true; @@ -527,13 +528,13 @@ ArgGetter(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) if (fp) { *vp = fp->argv[arg]; } else { - jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg); + jsval v = GetArgsSlot(obj, arg); if (v != JSVAL_HOLE) *vp = v; } } } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { - if (!js_IsOverriddenArgsLength(obj)) + if (!IsOverriddenArgsLength(obj)) *vp = INT_TO_JSVAL(GetArgsLength(obj)); } else { JS_ASSERT(idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)); @@ -608,20 +609,17 @@ static JSBool args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, JSObject **objp) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); *objp = NULL; jsid id = 0; if (JSVAL_IS_INT(idval)) { uint32 arg = uint32(JSVAL_TO_INT(idval)); - if (arg < GetArgsLength(obj) && - OBJ_GET_SLOT(cx, obj, JSSLOT_ARGS_COPY_START + arg) != JSVAL_HOLE) { + if (arg < GetArgsLength(obj) && GetArgsSlot(obj, arg) != JSVAL_HOLE) id = INT_JSVAL_TO_JSID(idval); - } } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) { - if (!js_IsOverriddenArgsLength(obj)) + if (!IsOverriddenArgsLength(obj)) id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) { if (obj->fslots[JSSLOT_ARGS_CALLEE] != JSVAL_HOLE) id = ATOM_TO_JSID(cx->runtime->atomState.calleeAtom); @@ -642,7 +640,7 @@ args_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, static JSBool args_enumerate(JSContext *cx, JSObject *obj) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); /* * Trigger reflection in args_resolve using a series of js_LookupProperty @@ -676,10 +674,12 @@ args_enumerate(JSContext *cx, JSObject *obj) static void args_or_call_trace(JSTracer *trc, JSObject *obj) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass || - STOBJ_GET_CLASS(obj) == &js_CallClass); - if (STOBJ_GET_CLASS(obj) == &js_ArgumentsClass && js_GetArgsPrivateNative(obj)) - return; + if (obj->isArguments()) { + if (GetArgsPrivateNative(obj)) + return; + } else { + JS_ASSERT(obj->getClass() == &js_CallClass); + } JSStackFrame *fp = (JSStackFrame *) obj->getPrivate(); if (fp && (fp->flags & JSFRAME_GENERATOR)) { @@ -694,7 +694,7 @@ args_or_call_trace(JSTracer *trc, JSObject *obj) static uint32 args_reserveSlots(JSContext *cx, JSObject *obj) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); return GetArgsLength(obj); } @@ -712,7 +712,7 @@ args_reserveSlots(JSContext *cx, JSObject *obj) JSClass js_ArgumentsClass = { js_Object_str, JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_RESERVED_SLOTS(ARGS_CLASS_FIXED_RESERVED_SLOTS) | + JSCLASS_HAS_RESERVED_SLOTS(ARGS_FIXED_RESERVED_SLOTS) | JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JS_PropertyStub, args_delProperty, JS_PropertyStub, JS_PropertyStub, @@ -1494,7 +1494,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, * script or embedding code and then be mutated. */ if (flags & JSRESOLVE_ASSIGNING) { - JS_ASSERT(!js_IsInternalFunctionObject(obj)); + JS_ASSERT(!IsInternalFunctionObject(obj)); return JS_TRUE; } @@ -1504,7 +1504,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, */ atom = cx->runtime->atomState.classPrototypeAtom; if (id == ATOM_KEY(atom)) { - JS_ASSERT(!js_IsInternalFunctionObject(obj)); + JS_ASSERT(!IsInternalFunctionObject(obj)); /* * Beware of the wacky case of a user function named Object -- trying @@ -1541,7 +1541,7 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset); if (id == ATOM_KEY(atom)) { - JS_ASSERT(!js_IsInternalFunctionObject(obj)); + JS_ASSERT(!IsInternalFunctionObject(obj)); if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, @@ -2099,11 +2099,34 @@ js_fun_apply(JSContext *cx, uintN argc, jsval *vp) sp = invokevp; *sp++ = fval; *sp++ = OBJECT_TO_JSVAL(obj); - for (i = 0; i < argc; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; + if (aobj && aobj->isArguments()) { + /* + * Two cases, two loops: note how in the case of an active stack frame + * backing aobj, even though we copy from fp->argv, we still must check + * aobj->dslots[i] for a hole, to handle a delete on the corresponding + * arguments element. See args_delProperty. + */ + JSStackFrame *fp = (JSStackFrame *) aobj->getPrivate(); + if (fp) { + memcpy(sp, fp->argv, argc * sizeof(jsval)); + for (i = 0; i < argc; i++) { + if (aobj->dslots[i] == JSVAL_HOLE) // suppress deleted element + sp[i] = JSVAL_VOID; + } + } else { + memcpy(sp, aobj->dslots, argc * sizeof(jsval)); + for (i = 0; i < argc; i++) { + if (sp[i] == JSVAL_HOLE) + sp[i] = JSVAL_VOID; + } + } + } else { + for (i = 0; i < argc; i++) { + ok = aobj->getProperty(cx, INT_TO_JSID(jsint(i)), sp); + if (!ok) + goto out; + sp++; + } } ok = js_Invoke(cx, argc, invokevp, 0); @@ -2125,8 +2148,8 @@ fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp) if (JSVAL_IS_PRIMITIVE(vp[2]) || (aobj = JSVAL_TO_OBJECT(vp[2]), - OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && - OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { + !aobj->isArray() && + !aobj->isArguments())) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); return JS_FALSE; @@ -2145,7 +2168,7 @@ fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp) *sp++ = vp[1]; *sp++ = JSVAL_NULL; /* this is filled automagically */ for (i = 0; i < length; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); + ok = aobj->getProperty(cx, INT_TO_JSID(jsint(i)), sp); if (!ok) goto out; sp++; diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 37f7d1f825b9..e1c69d321fd1 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -214,7 +214,27 @@ struct JSFunction : public JSObject JS_FN(name, fastcall, nargs, flags) #endif +/* + * NB: the Arguments class is an uninitialized internal class that masquerades + * (according to Object.prototype.toString.call(argsobj)) as "Object". + * + * WARNING (to alert embedders reading this private .h file): arguments objects + * are *not* thread-safe and should not be used concurrently -- they should be + * used by only one thread at a time, preferably by only one thread over their + * lifetime (a JS worker that migrates from one OS thread to another but shares + * nothing is ok). + * + * Yes, this is an incompatible change, which prefigures the impending move to + * single-threaded objects and GC heaps. + */ extern JSClass js_ArgumentsClass; + +inline bool +JSObject::isArguments() const +{ + return getClass() == &js_ArgumentsClass; +} + extern JS_FRIEND_DATA(JSClass) js_CallClass; extern JSClass js_DeclEnvClass; extern const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS; @@ -244,29 +264,33 @@ JSObject::isFunction() const (JS_ASSERT(HAS_FUNCTION_CLASS(funobj)), \ (JSFunction *) (funobj)->getPrivate()) +namespace js { + /* * Return true if this is a compiler-created internal function accessed by * its own object. Such a function object must not be accessible to script * or embedding code. */ inline bool -js_IsInternalFunctionObject(JSObject *funobj) +IsInternalFunctionObject(JSObject *funobj) { JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); JSFunction *fun = (JSFunction *) funobj->getPrivate(); return funobj == fun && (fun->flags & JSFUN_LAMBDA) && !funobj->getParent(); } + +struct ArgsPrivateNative; -namespace js { struct ArgsPrivateNative; } - -inline js::ArgsPrivateNative * -js_GetArgsPrivateNative(JSObject *argsobj) +inline ArgsPrivateNative * +GetArgsPrivateNative(JSObject *argsobj) { - JS_ASSERT(STOBJ_GET_CLASS(argsobj) == &js_ArgumentsClass); + JS_ASSERT(argsobj->isArguments()); uintptr_t p = (uintptr_t) argsobj->getPrivate(); - return (js::ArgsPrivateNative *) (p & 2 ? p & ~2 : NULL); + return (ArgsPrivateNative *) (p & 2 ? p & ~2 : NULL); } +} /* namespace js */ + extern JSObject * js_InitFunctionClass(JSContext *cx, JSObject *obj); @@ -399,17 +423,30 @@ js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->a * 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. + * JSSLOT_ARGS_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. + * + * The static assertion checks that hand-optimized code can fetch and store the + * argument value at argsobj->dslots[i] for argument index i. But future-proof + * your code by using {Get,Set}ArgsSlot instead of naked dslots references. */ -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; +const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1; +const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2; +const uint32 JSSLOT_ARGS_START = JSSLOT_PRIVATE + 3; + +JS_STATIC_ASSERT(JSSLOT_ARGS_START == JS_INITIAL_NSLOTS); /* Number of extra fixed slots besides JSSLOT_PRIVATE. */ -const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START - - JSSLOT_ARGS_LENGTH; +const uint32 ARGS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_START - JSSLOT_ARGS_LENGTH; + +/* + * Maximum supported value of arguments.length. It bounds the maximum number of + * arguments that can be supplied via the second (so-called |argArray|) param + * to Function.prototype.apply. This value also bounds the number of elements + * parsed in an array initialiser. + */ +const uint32 JS_ARGS_LENGTH_MAX = JS_BIT(24) - 1; /* * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval. @@ -419,15 +456,41 @@ const uint32 ARGS_CLASS_FIXED_RESERVED_SLOTS = JSSLOT_ARGS_COPY_START - 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) +namespace js { + +inline jsval +GetArgsSlot(JSObject *argsobj, uint32 arg) { - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_ArgumentsClass); + return argsobj->dslots[arg]; +} + +inline void +SetArgsSlot(JSObject *argsobj, uint32 arg, jsval v) +{ + argsobj->dslots[arg] = v; +} + +inline bool +IsOverriddenArgsLength(JSObject *obj) +{ + JS_ASSERT(obj->isArguments()); jsval v = obj->fslots[JSSLOT_ARGS_LENGTH]; return (JSVAL_TO_INT(v) & 1) != 0; } +inline uint32 +GetArgsLength(JSObject *obj) +{ + JS_ASSERT(obj->isArguments()); + + uint32 argc = uint32(JSVAL_TO_INT(obj->fslots[JSSLOT_ARGS_LENGTH])) >> 1; + JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX); + return argc; +} + +} /* namespace js */ + extern JSBool js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 884389b7063b..f1bb63815ecb 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -436,6 +436,7 @@ struct JSObject { map->ops->dropProperty(cx, this, prop); } + inline bool isArguments() const; inline bool isArray() const; inline bool isDenseArray() const; inline bool isFunction() const; diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 699502041561..84825f207713 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -1520,21 +1520,28 @@ BEGIN_CASE(JSOP_LENGTH) if (JSVAL_IS_STRING(lval)) { str = JSVAL_TO_STRING(lval); regs.sp[-1] = INT_TO_JSVAL(str->length()); - } else if (!JSVAL_IS_PRIMITIVE(lval) && - (obj = JSVAL_TO_OBJECT(lval), obj->isArray())) { - jsuint length; + } else if (!JSVAL_IS_PRIMITIVE(lval)) { + obj = JSVAL_TO_OBJECT(lval); + if (obj->isArray()) { + /* + * We know that the array is created with its 'length' private data + * in a fixed slot at JSSLOT_ARRAY_LENGTH. See also JSOP_ARRAYPUSH, + * far below. + */ + jsuint length = obj->fslots[JSSLOT_ARRAY_LENGTH]; - /* - * We know that the array is created with only its 'length' private - * data in a fixed slot at JSSLOT_ARRAY_LENGTH. See also - * JSOP_ARRAYPUSH, far below. - */ - length = obj->fslots[JSSLOT_ARRAY_LENGTH]; - if (length <= JSVAL_INT_MAX) { + if (length <= JSVAL_INT_MAX) + regs.sp[-1] = INT_TO_JSVAL(length); + else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length, ®s.sp[-1])) + goto error; + } else if (obj->isArguments() && !IsOverriddenArgsLength(obj)) { + uint32 length = GetArgsLength(obj); + + JS_ASSERT(INT_FITS_IN_JSVAL(length)); regs.sp[-1] = INT_TO_JSVAL(length); - } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length, - ®s.sp[-1])) { - goto error; + } else { + i = -2; + goto do_getprop_with_lval; } } else { i = -2; @@ -1867,19 +1874,36 @@ BEGIN_CASE(JSOP_GETELEM) VALUE_TO_OBJECT(cx, -2, lval, obj); if (JSVAL_IS_INT(rval)) { if (obj->isDenseArray()) { - jsuint length; + jsuint idx = jsuint(JSVAL_TO_INT(rval)); - length = js_DenseArrayCapacity(obj); - i = JSVAL_TO_INT(rval); - if ((jsuint)i < length && - i < obj->fslots[JSSLOT_ARRAY_LENGTH]) { - rval = obj->dslots[i]; + if (idx < jsuint(obj->fslots[JSSLOT_ARRAY_LENGTH]) && + idx < js_DenseArrayCapacity(obj)) { + rval = obj->dslots[idx]; if (rval != JSVAL_HOLE) goto end_getelem; /* Reload rval from the stack in the rare hole case. */ rval = FETCH_OPND(-1); } + } else if (obj->isArguments() +#ifdef JS_TRACER + && !GetArgsPrivateNative(obj) +#endif + ) { + uint32 arg = uint32(JSVAL_TO_INT(rval)); + + if (arg < GetArgsLength(obj)) { + JSStackFrame *afp = (JSStackFrame *) obj->getPrivate(); + if (afp) { + rval = afp->argv[arg]; + goto end_getelem; + } + + rval = GetArgsSlot(obj, arg); + if (rval != JSVAL_HOLE) + goto end_getelem; + rval = FETCH_OPND(-1); + } } id = INT_JSVAL_TO_JSID(rval); } else { diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 1f9a4af30526..a54210a6a986 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -390,12 +390,4 @@ typedef void extern JSBool js_CStringsAreUTF8; #endif -/* - * Maximum supported value of Arguments.length. It bounds the maximum number - * of arguments that can be supplied to the function call using - * Function.prototype.apply. This value also gives the maximum number of - * elements in the array initializer. - */ -#define JS_ARGS_LENGTH_MAX (JS_BIT(24) - 1) - #endif /* jsprvtd_h___ */ diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 31ce1330b642..7298d825c961 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -55,8 +55,10 @@ #include "jsatom.h" #include "jscntxt.h" #include "jsdbgapi.h" +#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */ #include "jslock.h" #include "jsnum.h" +#include "jsobj.h" #include "jsscope.h" #include "jsstr.h" #include "jstracer.h" @@ -279,11 +281,43 @@ JSScope::destroy(JSContext *cx) bool JSScope::initRuntimeState(JSContext *cx) { - cx->runtime->emptyBlockScope = cx->create(cx, &js_ObjectOps, - &js_BlockClass); - JS_ASSERT(cx->runtime->emptyBlockScope->nrefs == 2); - cx->runtime->emptyBlockScope->nrefs = 1; - return !!cx->runtime->emptyBlockScope; + JSRuntime *rt = cx->runtime; + + rt->emptyArgumentsScope = cx->create(cx, &js_ObjectOps, &js_ArgumentsClass); + if (!rt->emptyArgumentsScope) + return false; + JS_ASSERT(rt->emptyArgumentsScope->nrefs == 2); + rt->emptyArgumentsScope->nrefs = 1; + + /* + * NewArguments allocates dslots to have enough room for the argc of the + * particular arguments object being created. + * + * Thus we fake freeslot in the shared empty scope for the many unmutated + * arguments objects so that, until and unless a scope property is defined + * on a particular arguments object, it can share the runtime-wide empty + * scope with other arguments objects, whatever their initial argc values. + * + * This allows assertions that the arg slot being got or set by a fast path + * is less than freeslot to succeed. As the shared emptyArgumentsScope is + * never mutated, it's safe to pretend to have all the slots possible. + * + * Note how the fast paths in jsops.cpp for JSOP_LENGTH and JSOP_GETELEM + * bypass resolution of scope properties for length and element indices on + * arguments objects. This helps ensure that any arguments object needing + * its own mutable scope (with unique shape) is a rare event. + */ + rt->emptyArgumentsScope->freeslot = JS_INITIAL_NSLOTS + JS_ARGS_LENGTH_MAX; + + rt->emptyBlockScope = cx->create(cx, &js_ObjectOps, &js_BlockClass); + if (!rt->emptyBlockScope) { + rt->emptyArgumentsScope->drop(cx); + rt->emptyArgumentsScope = NULL; + return false; + } + JS_ASSERT(rt->emptyBlockScope->nrefs == 2); + rt->emptyBlockScope->nrefs = 1; + return true; } /* static */ @@ -291,6 +325,10 @@ void JSScope::finishRuntimeState(JSContext *cx) { JSRuntime *rt = cx->runtime; + if (rt->emptyArgumentsScope) { + rt->emptyArgumentsScope->drop(cx); + rt->emptyArgumentsScope = NULL; + } if (rt->emptyBlockScope) { rt->emptyBlockScope->drop(cx); rt->emptyBlockScope = NULL; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 04bc452a6438..88284a327eb7 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -63,7 +63,7 @@ #include "jsbool.h" #include "jsbuiltins.h" #include "jscntxt.h" -#include "jsversion.h" +#include "jsfun.h" /* for JS_ARGS_LENGTH_MAX */ #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" @@ -76,6 +76,7 @@ #include "jsstr.h" #include "jsbit.h" #include "jsvector.h" +#include "jsversion.h" #include "jsstrinlines.h" using namespace js; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 0207e418dd4e..5a1a65c23cc2 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -3294,10 +3294,8 @@ FlushNativeStackFrame(JSContext* cx, unsigned callDepth, const TraceType* mp, do for (; n != 0; fp = fp->down) { --n; if (fp->argv) { - if (fp->argsobj && - js_GetArgsPrivateNative(JSVAL_TO_OBJECT(fp->argsobj))) { + if (fp->argsobj && GetArgsPrivateNative(JSVAL_TO_OBJECT(fp->argsobj))) JSVAL_TO_OBJECT(fp->argsobj)->setPrivate(fp); - } JS_ASSERT(JSVAL_IS_OBJECT(fp->argv[-1])); JS_ASSERT(HAS_FUNCTION_CLASS(fp->calleeObject())); @@ -11731,7 +11729,7 @@ TraceRecorder::record_JSOP_GETELEM() return InjectStatus(getPropertyByName(obj_ins, &idx, &lval)); } - if (obj->getClass() == &js_ArgumentsClass) { + if (obj->isArguments()) { unsigned depth; JSStackFrame *afp = guardArguments(obj, obj_ins, &depth); if (afp) { @@ -12395,7 +12393,7 @@ TraceRecorder::guardCallee(jsval& callee) JS_REQUIRES_STACK JSStackFrame * TraceRecorder::guardArguments(JSObject *obj, LIns* obj_ins, unsigned *depthp) { - JS_ASSERT(obj->getClass() == &js_ArgumentsClass); + JS_ASSERT(obj->isArguments()); JSStackFrame *afp = frameIfInRange(obj, depthp); if (!afp) @@ -12577,7 +12575,7 @@ TraceRecorder::record_JSOP_APPLY() p2i(stobj_get_fslot(aobj_ins, JSSLOT_ARRAY_LENGTH)), length), BRANCH_EXIT); - } else if (OBJ_GET_CLASS(cx, aobj) == &js_ArgumentsClass) { + } else if (aobj->isArguments()) { unsigned depth; JSStackFrame *afp = guardArguments(aobj, aobj_ins, &depth); if (!afp) @@ -14089,7 +14087,7 @@ TraceRecorder::record_JSOP_ARGSUB() JS_REQUIRES_STACK LIns* TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) { - // The following implements js_IsOverriddenArgsLength on trace. + // The following implements IsOverriddenArgsLength on trace. // The '2' bit is set if length was overridden. LIns *len_ins = stobj_get_fslot(argsobj_ins, JSSLOT_ARGS_LENGTH); LIns *ovr_ins = lir->ins2(LIR_piand, len_ins, INS_CONSTWORD(2)); @@ -14109,7 +14107,7 @@ TraceRecorder::record_JSOP_ARGCNT() // 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))) + if (cx->fp->argsobj && IsOverriddenArgsLength(JSVAL_TO_OBJECT(cx->fp->argsobj))) RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); LIns *a_ins = get(&cx->fp->argsobj); if (callDepth == 0) { @@ -14983,7 +14981,7 @@ TraceRecorder::record_JSOP_LENGTH() JSObject* obj = JSVAL_TO_OBJECT(l); LIns* obj_ins = get(&l); - if (obj->getClass() == &js_ArgumentsClass) { + if (obj->isArguments()) { unsigned depth; JSStackFrame *afp = guardArguments(obj, obj_ins, &depth); if (!afp) @@ -14991,7 +14989,7 @@ TraceRecorder::record_JSOP_LENGTH() // We must both check at record time and guard at run time that // arguments.length has not been reassigned, redefined or deleted. - if (js_IsOverriddenArgsLength(obj)) + if (IsOverriddenArgsLength(obj)) RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins); diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 7aec65f1461d..fc01d124cb32 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -1015,7 +1015,7 @@ class TypedArrayTemplate jsval v; for (uintN i = 0; i < len; ++i) { - if (!JS_GetElement(cx, ar, i, &v)) + if (!ar->getProperty(cx, INT_TO_JSID(i), &v)) return false; *dest++ = nativeFromValue(cx, v); }