Fix arguments objects to share a runtime-wide empty scope, instead of each getting a differently shaped own scope at birth (554626, r=igor).

This commit is contained in:
Brendan Eich 2010-03-30 00:44:28 -07:00
parent cf75d4b5c6
commit c0b3caa942
12 changed files with 267 additions and 123 deletions

View File

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

View File

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

View File

@ -979,6 +979,7 @@ struct JSRuntime {
JSBackgroundThread *deallocatorThread;
#endif
JSEmptyScope *emptyArgumentsScope;
JSEmptyScope *emptyBlockScope;
/*

View File

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

View File

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

View File

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

View File

@ -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, &regs.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,
&regs.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 {

View File

@ -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___ */

View File

@ -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<JSEmptyScope>(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<JSEmptyScope>(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<JSEmptyScope>(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;

View File

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

View File

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

View File

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