mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 09:05:45 +00:00
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:
parent
cf75d4b5c6
commit
c0b3caa942
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -979,6 +979,7 @@ struct JSRuntime {
|
||||
JSBackgroundThread *deallocatorThread;
|
||||
#endif
|
||||
|
||||
JSEmptyScope *emptyArgumentsScope;
|
||||
JSEmptyScope *emptyBlockScope;
|
||||
|
||||
/*
|
||||
|
127
js/src/jsfun.cpp
127
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++;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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___ */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user