Bug 652746 - Implement js::ArgumentsObject, js::NormalArgumentsObject, and js::StrictArgumentsObject. r=njn

--HG--
extra : rebase_source : 84258ffa658ba09928ab9b86f8017fa6683f3146
This commit is contained in:
Jeff Walden 2011-04-25 22:40:00 -04:00
parent 77b801ddde
commit 5d29251946
17 changed files with 649 additions and 439 deletions

View File

@ -277,6 +277,7 @@ VPATH += \
EXPORTS_NAMESPACES = vm EXPORTS_NAMESPACES = vm
EXPORTS_vm = \ EXPORTS_vm = \
ArgumentsObject.h \
GlobalObject.h \ GlobalObject.h \
Stack.h \ Stack.h \
StringObject.h \ StringObject.h \

View File

@ -82,6 +82,7 @@
#include "jstypes.h" #include "jstypes.h"
#include "jsstdint.h" #include "jsstdint.h"
#include "jsutil.h" #include "jsutil.h"
#include "jsapi.h" #include "jsapi.h"
#include "jsarray.h" #include "jsarray.h"
#include "jsatom.h" #include "jsatom.h"
@ -105,12 +106,15 @@
#include "jsvector.h" #include "jsvector.h"
#include "jswrapper.h" #include "jswrapper.h"
#include "vm/ArgumentsObject.h"
#include "jsatominlines.h" #include "jsatominlines.h"
#include "jscntxtinlines.h" #include "jscntxtinlines.h"
#include "jsinterpinlines.h" #include "jsinterpinlines.h"
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jsstrinlines.h" #include "jsstrinlines.h"
#include "vm/ArgumentsObject-inl.h"
#include "vm/Stack-inl.h" #include "vm/Stack-inl.h"
using namespace js; using namespace js;
@ -217,9 +221,12 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
return true; return true;
} }
if (obj->isArguments() && !obj->isArgsLengthOverridden()) { if (obj->isArguments()) {
*lengthp = obj->getArgsInitialLength(); ArgumentsObject *argsobj = obj->asArguments();
return true; if (!argsobj->hasOverriddenLength()) {
*lengthp = argsobj->initialLength();
return true;
}
} }
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
@ -263,7 +270,7 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
*/ */
if (!createAtom && if (!createAtom &&
((clasp = obj->getClass()) == &js_SlowArrayClass || ((clasp = obj->getClass()) == &js_SlowArrayClass ||
clasp == &js_ArgumentsClass || obj->isArguments() ||
clasp == &js_ObjectClass)) { clasp == &js_ObjectClass)) {
atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
if (!atom) { if (!atom) {
@ -349,15 +356,17 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp
*hole = JS_FALSE; *hole = JS_FALSE;
return JS_TRUE; return JS_TRUE;
} }
if (obj->isArguments() && if (obj->isArguments()) {
index < obj->getArgsInitialLength() && ArgumentsObject *argsobj = obj->asArguments();
!(*vp = obj->getArgsElement(uint32(index))).isMagic(JS_ARGS_HOLE)) { if (index < argsobj->initialLength() &&
*hole = JS_FALSE; !(*vp = argsobj->element(uint32(index))).isMagic(JS_ARGS_HOLE)) {
StackFrame *fp = (StackFrame *)obj->getPrivate(); *hole = JS_FALSE;
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) { StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate());
if (fp) if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
*vp = fp->canonicalActualArg(index); if (fp)
return JS_TRUE; *vp = fp->canonicalActualArg(index);
return JS_TRUE;
}
} }
} }
@ -390,17 +399,28 @@ namespace js {
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
{ {
CopyNonHoleArgsTo(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {} CopyNonHoleArgsTo(ArgumentsObject *argsobj, Value *dst) : argsobj(argsobj), dst(dst) {}
JSObject *aobj; ArgumentsObject *argsobj;
Value *dst; Value *dst;
bool operator()(uintN argi, Value *src) { bool operator()(uintN argi, Value *src) {
if (aobj->getArgsElement(argi).isMagic(JS_ARGS_HOLE)) if (argsobj->element(argi).isMagic(JS_ARGS_HOLE))
return false; return false;
*dst++ = *src; *dst++ = *src;
return true; return true;
} }
}; };
static bool
GetElementsSlow(JSContext *cx, JSObject *aobj, uint32 length, Value *vp)
{
for (uint32 i = 0; i < length; i++) {
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &vp[i]))
return false;
}
return true;
}
bool bool
GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp) GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
{ {
@ -411,38 +431,38 @@ GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
Value *srcend = srcbeg + length; Value *srcend = srcbeg + length;
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src)
*dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src; *dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
} else if (aobj->isArguments() && !aobj->isArgsLengthOverridden() && return true;
!js_PrototypeHasIndexedProperties(cx, aobj)) { }
/*
* If the argsobj is for an active call, then the elements are the if (aobj->isArguments()) {
* live args on the stack. Otherwise, the elements are the args that ArgumentsObject *argsobj = aobj->asArguments();
* were copied into the argsobj by PutActivationObjects when the if (!argsobj->hasOverriddenLength() && !js_PrototypeHasIndexedProperties(cx, argsobj)) {
* function returned. In both cases, it is necessary to fall off the /*
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since * If the argsobj is for an active call, then the elements are the
* this requires general-purpose property lookup. * live args on the stack. Otherwise, the elements are the args that
*/ * were copied into the argsobj by PutActivationObjects when the
if (StackFrame *fp = (StackFrame *) aobj->getPrivate()) { * function returned. In both cases, it is necessary to fall off the
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX); * fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
if (!fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(aobj, vp))) * this requires general-purpose property lookup.
goto found_deleted_prop; */
} else { if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
Value *srcbeg = aobj->getArgsElements(); JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
Value *srcend = srcbeg + length; if (fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(argsobj, vp)))
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) { return true;
if (src->isMagic(JS_ARGS_HOLE)) } else {
goto found_deleted_prop; Value *srcbeg = argsobj->elements();
*dst = *src; Value *srcend = srcbeg + length;
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) {
if (src->isMagic(JS_ARGS_HOLE))
return GetElementsSlow(cx, argsobj, length, vp);
*dst = *src;
}
return true;
} }
} }
} else {
found_deleted_prop:
for (uintN i = 0; i < length; i++) {
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &vp[i]))
return JS_FALSE;
}
} }
return true; return GetElementsSlow(cx, aobj, length, vp);
} }
} }

View File

@ -90,6 +90,7 @@
#include "jsobjinlines.h" #include "jsobjinlines.h"
#include "jsscriptinlines.h" #include "jsscriptinlines.h"
#include "vm/ArgumentsObject-inl.h"
#include "vm/Stack-inl.h" #include "vm/Stack-inl.h"
using namespace js; using namespace js;
@ -145,10 +146,10 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
vp->setUndefined(); vp->setUndefined();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_INT(id)); uint32 arg = uint32(JSID_TO_INT(id));
JSObject *argsobj = fp->maybeArgsObj(); ArgumentsObject *argsobj = fp->maybeArgsObj();
if (arg < fp->numActualArgs()) { if (arg < fp->numActualArgs()) {
if (argsobj) { if (argsobj) {
const Value &v = argsobj->getArgsElement(arg); const Value &v = argsobj->element(arg);
if (v.isMagic(JS_ARGS_HOLE)) if (v.isMagic(JS_ARGS_HOLE))
return argsobj->getProperty(cx, id, vp); return argsobj->getProperty(cx, id, vp);
if (fp->functionScript()->strictModeCode) { if (fp->functionScript()->strictModeCode) {
@ -174,24 +175,27 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
return argsobj->getProperty(cx, id, vp); return argsobj->getProperty(cx, id, vp);
} }
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
JSObject *argsobj = fp->maybeArgsObj(); ArgumentsObject *argsobj = fp->maybeArgsObj();
if (argsobj && argsobj->isArgsLengthOverridden()) if (argsobj && argsobj->hasOverriddenLength())
return argsobj->getProperty(cx, id, vp); return argsobj->getProperty(cx, id, vp);
vp->setInt32(fp->numActualArgs()); vp->setInt32(fp->numActualArgs());
} }
return true; return true;
} }
static JSObject * js::ArgumentsObject *
NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee) ArgumentsObject::create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
{ {
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JSObject *proto; JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL; return NULL;
JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2); JS_STATIC_ASSERT(NormalArgumentsObject::RESERVED_SLOTS == 2);
JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2); JS_STATIC_ASSERT(StrictArgumentsObject::RESERVED_SLOTS == 2);
if (!argsobj) JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
if (!obj)
return NULL; return NULL;
EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx); EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
@ -206,16 +210,18 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
SetValueRangeToUndefined(data->slots, argc); SetValueRangeToUndefined(data->slots, argc);
/* Can't fail from here on, so initialize everything in argsobj. */ /* Can't fail from here on, so initialize everything in argsobj. */
argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode() obj->init(cx, callee.getFunctionPrivate()->inStrictMode()
? &StrictArgumentsClass ? &StrictArgumentsObject::jsClass
: &js_ArgumentsClass, : &NormalArgumentsObject::jsClass,
proto, parent, NULL, false); proto, parent, NULL, false);
obj->setMap(emptyArgumentsShape);
argsobj->setMap(emptyArgumentsShape); ArgumentsObject *argsobj = obj->asArguments();
argsobj->setArgsLength(argc); JS_ASSERT(UINT32_MAX > (uint64(argc) << PACKED_BITS_COUNT));
argsobj->setArgsData(data); argsobj->setInitialLength(argc);
data->callee.setObject(callee);
argsobj->setCalleeAndData(callee, data);
return argsobj; return argsobj;
} }
@ -250,7 +256,8 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
/* Compute the arguments object's parent slot from fp's scope chain. */ /* Compute the arguments object's parent slot from fp's scope chain. */
JSObject *global = fp->scopeChain().getGlobal(); JSObject *global = fp->scopeChain().getGlobal();
JSObject *argsobj = NewArguments(cx, global, fp->numActualArgs(), fp->callee()); ArgumentsObject *argsobj =
ArgumentsObject::create(cx, global, fp->numActualArgs(), fp->callee());
if (!argsobj) if (!argsobj)
return argsobj; return argsobj;
@ -264,7 +271,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
* retrieve up-to-date parameter values. * retrieve up-to-date parameter values.
*/ */
if (argsobj->isStrictArguments()) if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(argsobj->getArgsData()->slots)); fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots));
else else
argsobj->setPrivate(fp); argsobj->setPrivate(fp);
@ -275,10 +282,10 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
void void
js_PutArgsObject(StackFrame *fp) js_PutArgsObject(StackFrame *fp)
{ {
JSObject &argsobj = fp->argsObj(); ArgumentsObject &argsobj = fp->argsObj();
if (argsobj.isNormalArguments()) { if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.getPrivate() == fp); JS_ASSERT(argsobj.getPrivate() == fp);
fp->forEachCanonicalActualArg(PutArg(argsobj.getArgsData()->slots)); fp->forEachCanonicalActualArg(PutArg(argsobj.data()->slots));
argsobj.setPrivate(NULL); argsobj.setPrivate(NULL);
} else { } else {
JS_ASSERT(!argsobj.getPrivate()); JS_ASSERT(!argsobj.getPrivate());
@ -293,7 +300,7 @@ js_PutArgsObject(StackFrame *fp)
JSObject * JS_FASTCALL JSObject * JS_FASTCALL
js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee) js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
{ {
JSObject *argsobj = NewArguments(cx, parent, argc, *callee); ArgumentsObject *argsobj = ArgumentsObject::create(cx, parent, argc, *callee);
if (!argsobj) if (!argsobj)
return NULL; return NULL;
@ -314,9 +321,10 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, OBJECT, UI
/* FIXME change the return type to void. */ /* FIXME change the return type to void. */
JSBool JS_FASTCALL JSBool JS_FASTCALL
js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args) js_PutArgumentsOnTrace(JSContext *cx, JSObject *obj, Value *argv)
{ {
JS_ASSERT(argsobj->isNormalArguments()); NormalArgumentsObject *argsobj = obj->asNormalArguments();
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE); JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
/* /*
@ -324,9 +332,9 @@ js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args)
* the arguments, regardless of whether #actuals > #formals so there is no * the arguments, regardless of whether #actuals > #formals so there is no
* need to worry about actual vs. formal arguments. * need to worry about actual vs. formal arguments.
*/ */
Value *srcend = args + argsobj->getArgsInitialLength(); Value *srcend = argv + argsobj->initialLength();
Value *dst = argsobj->getArgsData()->slots; Value *dst = argsobj->data()->slots;
for (Value *src = args; src != srcend; ++src, ++dst) { for (Value *src = argv; src < srcend; ++src, ++dst) {
if (!dst->isMagic(JS_ARGS_HOLE)) if (!dst->isMagic(JS_ARGS_HOLE))
*dst = *src; *dst = *src;
} }
@ -342,16 +350,15 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALU
static JSBool static JSBool
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{ {
JS_ASSERT(obj->isArguments()); ArgumentsObject *argsobj = obj->asArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) if (arg < argsobj->initialLength())
obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE)); argsobj->setElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
obj->setArgsLengthOverridden(); argsobj->markLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
obj->setArgsCallee(MagicValue(JS_ARGS_HOLE)); argsobj->asNormalArguments()->clearCallee();
} }
return true; return true;
} }
@ -518,25 +525,26 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (!obj->isNormalArguments()) if (!obj->isNormalArguments())
return true; return true;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
/* /*
* arg can exceed the number of arguments if a script changed the * arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc. * prototype to point to another Arguments object with a bigger argc.
*/ */
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)); JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = (StackFrame *) obj->getPrivate()) if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
*vp = fp->canonicalActualArg(arg); *vp = fp->canonicalActualArg(arg);
else else
*vp = obj->getArgsElement(arg); *vp = argsobj->element(arg);
} }
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!obj->isArgsLengthOverridden()) if (!argsobj->hasOverriddenLength())
vp->setInt32(obj->getArgsInitialLength()); vp->setInt32(argsobj->initialLength());
} else { } else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)); JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
const Value &v = obj->getArgsCallee(); const Value &v = argsobj->callee();
if (!v.isMagic(JS_ARGS_HOLE)) { if (!v.isMagic(JS_ARGS_HOLE)) {
/* /*
* If this function or one in it needs upvars that reach above it * If this function or one in it needs upvars that reach above it
@ -572,11 +580,12 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
if (!obj->isNormalArguments()) if (!obj->isNormalArguments())
return true; return true;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
StackFrame *fp = (StackFrame *) obj->getPrivate(); if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
if (fp) {
JSScript *script = fp->functionScript(); JSScript *script = fp->functionScript();
if (script->usesArguments) if (script->usesArguments)
fp->canonicalActualArg(arg) = *vp; fp->canonicalActualArg(arg) = *vp;
@ -597,54 +606,54 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
* that has a setter for this id. * that has a setter for this id.
*/ */
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
return js_DeleteProperty(cx, obj, id, tvr.addr(), false) && return js_DeleteProperty(cx, argsobj, id, tvr.addr(), false) &&
js_DefineProperty(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE); js_DefineProperty(cx, argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
} }
static JSBool static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp) JSObject **objp)
{ {
JS_ASSERT(obj->isNormalArguments());
*objp = NULL; *objp = NULL;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_INT(id)); uint32 arg = uint32(JSID_TO_INT(id));
if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
return true; return true;
attrs |= JSPROP_ENUMERATE; attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (obj->isArgsLengthOverridden()) if (argsobj->hasOverriddenLength())
return true; return true;
} else { } else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
return true; return true;
if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE)) if (argsobj->callee().isMagic(JS_ARGS_HOLE))
return true; return true;
} }
Value undef = UndefinedValue(); Value undef = UndefinedValue();
if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs)) if (!js_DefineProperty(cx, argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
return JS_FALSE; return JS_FALSE;
*objp = obj; *objp = argsobj;
return true; return true;
} }
static JSBool static JSBool
args_enumerate(JSContext *cx, JSObject *obj) args_enumerate(JSContext *cx, JSObject *obj)
{ {
JS_ASSERT(obj->isNormalArguments()); NormalArgumentsObject *argsobj = obj->asNormalArguments();
/* /*
* Trigger reflection in args_resolve using a series of js_LookupProperty * Trigger reflection in args_resolve using a series of js_LookupProperty
* calls. * calls.
*/ */
int argc = int(obj->getArgsInitialLength()); int argc = int(argsobj->initialLength());
for (int i = -2; i != argc; i++) { for (int i = -2; i != argc; i++) {
jsid id = (i == -2) jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
@ -654,7 +663,7 @@ args_enumerate(JSContext *cx, JSObject *obj)
JSObject *pobj; JSObject *pobj;
JSProperty *prop; JSProperty *prop;
if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) if (!js_LookupProperty(cx, argsobj, id, &pobj, &prop))
return false; return false;
} }
return true; return true;
@ -668,21 +677,23 @@ StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (!obj->isStrictArguments()) if (!obj->isStrictArguments())
return true; return true;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
/* /*
* arg can exceed the number of arguments if a script changed the * arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc. * prototype to point to another Arguments object with a bigger argc.
*/ */
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
const Value &v = obj->getArgsElement(arg); const Value &v = argsobj->element(arg);
if (!v.isMagic(JS_ARGS_HOLE)) if (!v.isMagic(JS_ARGS_HOLE))
*vp = v; *vp = v;
} }
} else { } else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)); JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!obj->isArgsLengthOverridden()) if (!argsobj->hasOverriddenLength())
vp->setInt32(obj->getArgsInitialLength()); vp->setInt32(argsobj->initialLength());
} }
return true; return true;
} }
@ -694,10 +705,12 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
if (!obj->isStrictArguments()) if (!obj->isStrictArguments())
return true; return true;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id)); uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
obj->setArgsElement(arg, *vp); argsobj->setElement(arg, *vp);
return true; return true;
} }
} else { } else {
@ -711,29 +724,29 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
* collect its value. * collect its value.
*/ */
AutoValueRooter tvr(cx); AutoValueRooter tvr(cx);
return js_DeleteProperty(cx, obj, id, tvr.addr(), strict) && return js_DeleteProperty(cx, argsobj, id, tvr.addr(), strict) &&
js_SetProperty(cx, obj, id, vp, strict); js_SetProperty(cx, argsobj, id, vp, strict);
} }
static JSBool static JSBool
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp) strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{ {
JS_ASSERT(obj->isStrictArguments());
*objp = NULL; *objp = NULL;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
PropertyOp getter = StrictArgGetter; PropertyOp getter = StrictArgGetter;
StrictPropertyOp setter = StrictArgSetter; StrictPropertyOp setter = StrictArgSetter;
if (JSID_IS_INT(id)) { if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_INT(id)); uint32 arg = uint32(JSID_TO_INT(id));
if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE)) if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
return true; return true;
attrs |= JSPROP_ENUMERATE; attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (obj->isArgsLengthOverridden()) if (argsobj->hasOverriddenLength())
return true; return true;
} else { } else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) && if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
@ -742,22 +755,22 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject
} }
attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
getter = CastAsPropertyOp(obj->getThrowTypeError()); getter = CastAsPropertyOp(argsobj->getThrowTypeError());
setter = CastAsStrictPropertyOp(obj->getThrowTypeError()); setter = CastAsStrictPropertyOp(argsobj->getThrowTypeError());
} }
Value undef = UndefinedValue(); Value undef = UndefinedValue();
if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs)) if (!js_DefineProperty(cx, argsobj, id, &undef, getter, setter, attrs))
return false; return false;
*objp = obj; *objp = argsobj;
return true; return true;
} }
static JSBool static JSBool
strictargs_enumerate(JSContext *cx, JSObject *obj) strictargs_enumerate(JSContext *cx, JSObject *obj)
{ {
JS_ASSERT(obj->isStrictArguments()); StrictArgumentsObject *argsobj = obj->asStrictArguments();
/* /*
* Trigger reflection in strictargs_resolve using a series of * Trigger reflection in strictargs_resolve using a series of
@ -767,19 +780,19 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
JSProperty *prop; JSProperty *prop;
// length // length
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop)) if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
return false; return false;
// callee // callee
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop)) if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
return false; return false;
// caller // caller
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop)) if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
return false; return false;
for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) { for (uint32 i = 0, argc = argsobj->initialLength(); i < argc; i++) {
if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop)) if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
return false; return false;
} }
@ -789,7 +802,7 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
static void static void
args_finalize(JSContext *cx, JSObject *obj) args_finalize(JSContext *cx, JSObject *obj)
{ {
cx->free_((void *) obj->getArgsData()); cx->free_(reinterpret_cast<void *>(obj->asArguments()->data()));
} }
/* /*
@ -815,43 +828,39 @@ MaybeMarkGenerator(JSTracer *trc, JSObject *obj)
static void static void
args_trace(JSTracer *trc, JSObject *obj) args_trace(JSTracer *trc, JSObject *obj)
{ {
JS_ASSERT(obj->isArguments()); ArgumentsObject *argsobj = obj->asArguments();
if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) { if (argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
JS_ASSERT(!obj->isStrictArguments()); JS_ASSERT(!argsobj->isStrictArguments());
return; return;
} }
ArgumentsData *data = obj->getArgsData(); ArgumentsData *data = argsobj->data();
if (data->callee.isObject()) if (data->callee.isObject())
MarkObject(trc, data->callee.toObject(), js_callee_str); MarkObject(trc, data->callee.toObject(), js_callee_str);
MarkValueRange(trc, obj->getArgsInitialLength(), data->slots, js_arguments_str); MarkValueRange(trc, argsobj->initialLength(), data->slots, js_arguments_str);
MaybeMarkGenerator(trc, obj); MaybeMarkGenerator(trc, argsobj);
} }
namespace js {
/* /*
* The Arguments classes aren't initialized via js_InitClass, because arguments * The classes below collaborate to lazily reflect and synchronize actual
* objects have the initial value of Object.prototype as their [[Prototype]]. * argument values, argument count, and callee function object stored in a
* However, Object.prototype.toString.call(arguments) === "[object Arguments]" * StackFrame with their corresponding property values in the frame's
* per ES5 (although not ES3), so the class name is "Arguments" rather than
* "Object".
*
* The JSClass functions below collaborate to lazily reflect and synchronize
* actual argument values, argument count, and callee function object stored
* in a StackFrame with their corresponding property values in the frame's
* arguments object. * arguments object.
*/ */
Class js_ArgumentsClass = { Class NormalArgumentsObject::jsClass = {
"Arguments", "Arguments",
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
PropertyStub, /* addProperty */ PropertyStub, /* addProperty */
args_delProperty, args_delProperty,
PropertyStub, /* getProperty */ PropertyStub, /* getProperty */
StrictPropertyStub, /* setProperty */ StrictPropertyStub, /* setProperty */
args_enumerate, args_enumerate,
(JSResolveOp) args_resolve, reinterpret_cast<JSResolveOp>(args_resolve),
ConvertStub, ConvertStub,
args_finalize, /* finalize */ args_finalize, /* finalize */
NULL, /* reserved0 */ NULL, /* reserved0 */
@ -863,17 +872,15 @@ Class js_ArgumentsClass = {
args_trace args_trace
}; };
namespace js {
/* /*
* Strict mode arguments is significantly less magical than non-strict mode * Strict mode arguments is significantly less magical than non-strict mode
* arguments, so it is represented by a different class while sharing some * arguments, so it is represented by a different class while sharing some
* functionality. * functionality.
*/ */
Class StrictArgumentsClass = { Class StrictArgumentsObject::jsClass = {
"Arguments", "Arguments",
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
PropertyStub, /* addProperty */ PropertyStub, /* addProperty */
args_delProperty, args_delProperty,

View File

@ -247,53 +247,6 @@ struct JSFunction : public JSObject_Slots2
JS_FN(name, fastcall, nargs, flags) JS_FN(name, fastcall, nargs, flags)
#endif #endif
/*
* NB: the Arguments classes are uninitialized internal classes that masquerade
* (according to Object.prototype.toString.call(arguments)) as "Arguments",
* while having Object.getPrototypeOf(arguments) === Object.prototype.
*
* 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 js::Class js_ArgumentsClass;
namespace js {
extern Class StrictArgumentsClass;
struct ArgumentsData {
js::Value callee;
js::Value slots[1];
};
}
inline bool
JSObject::isNormalArguments() const
{
return getClass() == &js_ArgumentsClass;
}
inline bool
JSObject::isStrictArguments() const
{
return getClass() == &js::StrictArgumentsClass;
}
inline bool
JSObject::isArguments() const
{
return isNormalArguments() || isStrictArguments();
}
#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
extern JS_PUBLIC_DATA(js::Class) js_CallClass; extern JS_PUBLIC_DATA(js::Class) js_CallClass;
extern JS_PUBLIC_DATA(js::Class) js_FunctionClass; extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass; extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;

View File

@ -4096,20 +4096,26 @@ BEGIN_CASE(JSOP_LENGTH)
vp = &regs.sp[-1]; vp = &regs.sp[-1];
if (vp->isString()) { if (vp->isString()) {
vp->setInt32(vp->toString()->length()); vp->setInt32(vp->toString()->length());
} else if (vp->isObject()) {
JSObject *obj = &vp->toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length);
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length));
} else {
i = -2;
goto do_getprop_with_lval;
}
} else { } else {
if (vp->isObject()) {
JSObject *obj = &vp->toObject();
if (obj->isArray()) {
jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length);
DO_NEXT_OP(JSOP_LENGTH_LENGTH);
}
if (obj->isArguments()) {
ArgumentsObject *argsobj = obj->asArguments();
if (!argsobj->hasOverriddenLength()) {
uint32 length = argsobj->initialLength();
JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length));
DO_NEXT_OP(JSOP_LENGTH_LENGTH);
}
}
}
i = -2; i = -2;
goto do_getprop_with_lval; goto do_getprop_with_lval;
} }
@ -4405,11 +4411,12 @@ BEGIN_CASE(JSOP_GETELEM)
} }
} else if (obj->isArguments()) { } else if (obj->isArguments()) {
uint32 arg = uint32(i); uint32 arg = uint32(i);
ArgumentsObject *argsobj = obj->asArguments();
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
copyFrom = obj->addressOfArgsElement(arg); copyFrom = argsobj->addressOfElement(arg);
if (!copyFrom->isMagic(JS_ARGS_HOLE)) { if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
if (StackFrame *afp = (StackFrame *) obj->getPrivate()) if (StackFrame *afp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
copyFrom = &afp->canonicalActualArg(arg); copyFrom = &afp->canonicalActualArg(arg);
goto end_getelem; goto end_getelem;
} }

View File

@ -261,7 +261,11 @@ namespace js {
struct NativeIterator; struct NativeIterator;
class RegExp; class RegExp;
class GlobalObject; class GlobalObject;
class ArgumentsObject;
class NormalArgumentsObject;
class StrictArgumentsObject;
class StringObject; class StringObject;
} }
@ -794,93 +798,10 @@ struct JSObject : js::gc::Cell {
JSBool makeDenseArraySlow(JSContext *cx); JSBool makeDenseArraySlow(JSContext *cx);
/*
* Arguments-specific getters and setters.
*/
private:
/*
* We represent arguments objects using js_ArgumentsClass and
* js::StrictArgumentsClass. The two are structured similarly, and methods
* valid on arguments objects of one class are also generally valid on
* arguments objects of the other.
*
* Arguments objects of either class store arguments length in a slot:
*
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag
* indicating whether arguments.length was
* overwritten. This slot is not used to represent
* arguments.length after that property has been
* assigned, even if the new value is integral: it's
* always the original length.
*
* Both arguments classes use a slot for storing arguments data:
*
* JSSLOT_ARGS_DATA - pointer to an ArgumentsData structure
*
* ArgumentsData for normal arguments stores the value of arguments.callee,
* as long as that property has not been overwritten. If arguments.callee
* is overwritten, the corresponding value in ArgumentsData is set to
* MagicValue(JS_ARGS_HOLE). Strict arguments do not store this value
* because arguments.callee is a poison pill for strict mode arguments.
*
* The ArgumentsData structure also stores argument values. For normal
* arguments this occurs after the corresponding function has returned, and
* for strict arguments this occurs when the arguments object is created,
* or sometimes shortly after (but not observably so). arguments[i] is
* stored in ArgumentsData.slots[i], accessible via getArgsElement() and
* setArgsElement(). Deletion of arguments[i] overwrites that slot with
* MagicValue(JS_ARGS_HOLE); subsequent redefinition of arguments[i] will
* use a normal property to store the value, ignoring the slot.
*
* Non-strict arguments have a private:
*
* private - the function's stack frame until the function
* returns, when it is replaced with null; also,
* JS_ARGUMENTS_OBJECT_ON_TRACE while on trace, if
* arguments was created on trace
*
* Technically strict arguments have a private, but it's always null.
* Conceptually it would be better to remove this oddity, but preserving it
* allows us to work with arguments objects of either kind more abstractly,
* so we keep it for now.
*/
static const uint32 JSSLOT_ARGS_DATA = 1;
public: public:
/* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */ inline js::ArgumentsObject *asArguments();
static const uint32 JSSLOT_ARGS_LENGTH = 0; inline js::NormalArgumentsObject *asNormalArguments();
static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2; inline js::StrictArgumentsObject *asStrictArguments();
static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1;
/* Lower-order bit stolen from the length slot. */
static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1;
static const uint32 ARGS_PACKED_BITS_COUNT = 1;
/*
* Set the initial length of the arguments, and mark it as not overridden.
*/
inline void setArgsLength(uint32 argc);
/*
* Return the initial length of the arguments. This may differ from the
* current value of arguments.length!
*/
inline uint32 getArgsInitialLength() const;
inline void setArgsLengthOverridden();
inline bool isArgsLengthOverridden() const;
inline js::ArgumentsData *getArgsData() const;
inline void setArgsData(js::ArgumentsData *data);
inline const js::Value &getArgsCallee() const;
inline void setArgsCallee(const js::Value &callee);
inline const js::Value &getArgsElement(uint32 i) const;
inline js::Value *getArgsElements() const;
inline js::Value *addressOfArgsElement(uint32 i);
inline void setArgsElement(uint32 i, const js::Value &v);
private: private:
/* /*

View File

@ -409,97 +409,6 @@ JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
shrinkSlots(cx, cap); shrinkSlots(cx, cap);
} }
inline void
JSObject::setArgsLength(uint32 argc)
{
JS_ASSERT(isArguments());
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
JS_ASSERT(!isArgsLengthOverridden());
}
inline uint32
JSObject::getArgsInitialLength() const
{
JS_ASSERT(isArguments());
uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
return argc;
}
inline void
JSObject::setArgsLengthOverridden()
{
JS_ASSERT(isArguments());
getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
}
inline bool
JSObject::isArgsLengthOverridden() const
{
JS_ASSERT(isArguments());
const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH);
return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
}
inline js::ArgumentsData *
JSObject::getArgsData() const
{
JS_ASSERT(isArguments());
return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate();
}
inline void
JSObject::setArgsData(js::ArgumentsData *data)
{
JS_ASSERT(isArguments());
getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
}
inline const js::Value &
JSObject::getArgsCallee() const
{
return getArgsData()->callee;
}
inline void
JSObject::setArgsCallee(const js::Value &callee)
{
getArgsData()->callee = callee;
}
inline const js::Value &
JSObject::getArgsElement(uint32 i) const
{
JS_ASSERT(isArguments());
JS_ASSERT(i < getArgsInitialLength());
return getArgsData()->slots[i];
}
inline js::Value *
JSObject::getArgsElements() const
{
JS_ASSERT(isArguments());
return getArgsData()->slots;
}
inline js::Value *
JSObject::addressOfArgsElement(uint32 i)
{
JS_ASSERT(isArguments());
JS_ASSERT(i < getArgsInitialLength());
return &getArgsData()->slots[i];
}
inline void
JSObject::setArgsElement(uint32 i, const js::Value &v)
{
JS_ASSERT(isArguments());
JS_ASSERT(i < getArgsInitialLength());
getArgsData()->slots[i] = v;
}
inline bool inline bool
JSObject::callIsForEval() const JSObject::callIsForEval() const
{ {

View File

@ -49,6 +49,7 @@
#endif #endif
#include "jstypes.h" #include "jstypes.h"
#include "jscntxt.h" #include "jscntxt.h"
#include "jscompartment.h" #include "jscompartment.h"
#include "jshashtable.h" #include "jshashtable.h"
@ -58,6 +59,8 @@
#include "jspubtd.h" #include "jspubtd.h"
#include "jspropertytree.h" #include "jspropertytree.h"
#include "vm/ArgumentsObject.h"
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) #pragma warning(push)
#pragma warning(disable:4800) #pragma warning(disable:4800)
@ -635,7 +638,7 @@ struct EmptyShape : public js::Shape
} }
static EmptyShape *getEmptyArgumentsShape(JSContext *cx) { static EmptyShape *getEmptyArgumentsShape(JSContext *cx) {
return ensure(cx, &js_ArgumentsClass, &cx->compartment->emptyArgumentsShape); return ensure(cx, &NormalArgumentsObject::jsClass, &cx->compartment->emptyArgumentsShape);
} }
static EmptyShape *getEmptyBlockShape(JSContext *cx) { static EmptyShape *getEmptyBlockShape(JSContext *cx) {

View File

@ -3158,8 +3158,7 @@ public:
if (p == fp->addressOfArgs()) { if (p == fp->addressOfArgs()) {
if (frameobj) { if (frameobj) {
JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj()); JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
fp->setArgsObj(*frameobj); fp->setArgsObj(*frameobj->asArguments());
JS_ASSERT(frameobj->isArguments());
if (frameobj->isNormalArguments()) if (frameobj->isNormalArguments())
frameobj->setPrivate(fp); frameobj->setPrivate(fp);
else else
@ -12881,7 +12880,7 @@ JS_REQUIRES_STACK void
TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins) TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins)
{ {
// vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx] // vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx]
LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, JSObject::JSSLOT_ARGS_DATA); LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, ArgumentsObject::DATA_SLOT);
LIns* slotOffset_ins = w.addp(w.nameImmw(offsetof(ArgumentsData, slots)), LIns* slotOffset_ins = w.addp(w.nameImmw(offsetof(ArgumentsData, slots)),
w.ui2p(w.muliN(idx_ins, sizeof(Value)))); w.ui2p(w.muliN(idx_ins, sizeof(Value))));
LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins); LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins);
@ -12934,17 +12933,18 @@ TraceRecorder::record_JSOP_GETELEM()
} }
if (obj->isArguments()) { if (obj->isArguments()) {
ArgumentsObject *argsobj = obj->asArguments();
// Don't even try to record if out of range or reading a deleted arg // Don't even try to record if out of range or reading a deleted arg
int32 int_idx = idx.toInt32(); int32 int_idx = idx.toInt32();
if (int_idx < 0 || int_idx >= (int32)obj->getArgsInitialLength()) if (int_idx < 0 || int_idx >= (int32)argsobj->initialLength())
RETURN_STOP_A("cannot trace arguments with out of range index"); RETURN_STOP_A("cannot trace arguments with out of range index");
if (obj->getArgsElement(int_idx).isMagic(JS_ARGS_HOLE)) if (argsobj->element(int_idx).isMagic(JS_ARGS_HOLE))
RETURN_STOP_A("reading deleted args element"); RETURN_STOP_A("reading deleted args element");
// Only trace reading arguments out of active, tracked frame // Only trace reading arguments out of active, tracked frame
unsigned depth; unsigned depth;
StackFrame *afp = guardArguments(obj, obj_ins, &depth); if (StackFrame *afp = guardArguments(obj, obj_ins, &depth)) {
if (afp) {
Value* vp = &afp->canonicalActualArg(int_idx); Value* vp = &afp->canonicalActualArg(int_idx);
if (idx_ins->isImmD()) { if (idx_ins->isImmD()) {
JS_ASSERT(int_idx == (int32)idx_ins->immD()); JS_ASSERT(int_idx == (int32)idx_ins->immD());
@ -13863,7 +13863,7 @@ TraceRecorder::record_JSOP_FUNAPPLY()
StackFrame *afp = guardArguments(aobj, aobj_ins, &depth); StackFrame *afp = guardArguments(aobj, aobj_ins, &depth);
if (!afp) if (!afp)
RETURN_STOP_A("can't reach arguments object's frame"); RETURN_STOP_A("can't reach arguments object's frame");
if (aobj->isArgsLengthOverridden()) if (aobj->asArguments()->hasOverriddenLength())
RETURN_STOP_A("can't trace arguments with overridden length"); RETURN_STOP_A("can't trace arguments with overridden length");
guardArgsLengthNotAssigned(aobj_ins); guardArgsLengthNotAssigned(aobj_ins);
length = afp->numActualArgs(); length = afp->numActualArgs();
@ -15671,13 +15671,27 @@ TraceRecorder::record_JSOP_ARGSUB()
RETURN_STOP_A("can't trace JSOP_ARGSUB hard case"); RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
} }
namespace tjit {
nj::LIns *
Writer::getArgsLength(nj::LIns *args) const
{
uint32 slot = js::ArgumentsObject::INITIAL_LENGTH_SLOT;
nj::LIns *vaddr_ins = ldpObjSlots(args);
return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
ACCSET_SLOTS),
"argsLength");
}
} // namespace tjit
JS_REQUIRES_STACK LIns* JS_REQUIRES_STACK LIns*
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins) TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
{ {
// The following implements JSObject::isArgsLengthOverridden on trace. // The following implements JSObject::isArgsLengthOverridden on trace.
// ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden. // ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
LIns *len_ins = w.getArgsLength(argsobj_ins); LIns *len_ins = w.getArgsLength(argsobj_ins);
LIns *ovr_ins = w.andi(len_ins, w.nameImmi(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); LIns *ovr_ins = w.andi(len_ins, w.nameImmi(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
guard(true, w.eqi0(ovr_ins), MISMATCH_EXIT); guard(true, w.eqi0(ovr_ins), MISMATCH_EXIT);
return len_ins; return len_ins;
} }
@ -15696,7 +15710,7 @@ TraceRecorder::record_JSOP_ARGCNT()
// We also have to check that arguments.length has not been mutated // We also have to check that arguments.length has not been mutated
// at record time, because if so we will generate incorrect constant // at record time, because if so we will generate incorrect constant
// LIR, which will assert in tryToDemote(). // LIR, which will assert in tryToDemote().
if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden()) if (fp->hasArgsObj() && fp->argsObj().hasOverriddenLength())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs()); LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());
if (callDepth == 0) { if (callDepth == 0) {
@ -16359,13 +16373,13 @@ TraceRecorder::record_JSOP_LENGTH()
// We must both check at record time and guard at run time that // We must both check at record time and guard at run time that
// arguments.length has not been reassigned, redefined or deleted. // arguments.length has not been reassigned, redefined or deleted.
if (obj->isArgsLengthOverridden()) if (obj->asArguments()->hasOverriddenLength())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified"); RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins); LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
// slot_ins is the value from the slot; right-shift to get the length // slot_ins is the value from the slot; right-shift to get the length;
// (see JSObject::getArgsInitialLength in jsfun.cpp). // see ArgumentsObject.h.
LIns* v_ins = w.i2d(w.rshiN(slot_ins, JSObject::ARGS_PACKED_BITS_COUNT)); LIns* v_ins = w.i2d(w.rshiN(slot_ins, ArgumentsObject::PACKED_BITS_COUNT));
set(&l, v_ins); set(&l, v_ins);
return ARECORD_CONTINUE; return ARECORD_CONTINUE;
} }

View File

@ -770,12 +770,12 @@ class GetPropCompiler : public PICStubCompiler
Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass()); Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg); masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)), masm.load32(Address(pic.objReg, ArgumentsObject::INITIAL_LENGTH_SLOT * sizeof(Value)),
pic.objReg); pic.objReg);
masm.move(pic.objReg, pic.shapeReg); masm.move(pic.objReg, pic.shapeReg);
Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg, Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
Imm32(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT)); Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg); masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), pic.objReg);
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg); masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
Jump done = masm.jump(); Jump done = masm.jump();
@ -1648,7 +1648,8 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
return; return;
} else if (!f.regs.sp[-1].isPrimitive()) { } else if (!f.regs.sp[-1].isPrimitive()) {
JSObject *obj = &f.regs.sp[-1].toObject(); JSObject *obj = &f.regs.sp[-1].toObject();
if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden()) || if (obj->isArray() ||
(obj->isArguments() && !obj->asArguments()->hasOverriddenLength()) ||
obj->isString()) { obj->isString()) {
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC); GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
if (obj->isArray()) { if (obj->isArray()) {
@ -1660,7 +1661,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
LookupStatus status = cc.generateArgsLengthStub(); LookupStatus status = cc.generateArgsLengthStub();
if (status == Lookup_Error) if (status == Lookup_Error)
THROW(); THROW();
f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength())); f.regs.sp[-1].setInt32(int32_t(obj->asArguments()->initialLength()));
} else if (obj->isString()) { } else if (obj->isString()) {
LookupStatus status = cc.generateStringObjLengthStub(); LookupStatus status = cc.generateStringObjLengthStub();
if (status == Lookup_Error) if (status == Lookup_Error)

View File

@ -447,11 +447,12 @@ stubs::GetElem(VMFrame &f)
} }
} else if (obj->isArguments()) { } else if (obj->isArguments()) {
uint32 arg = uint32(i); uint32 arg = uint32(i);
ArgumentsObject *argsobj = obj->asArguments();
if (arg < obj->getArgsInitialLength()) { if (arg < argsobj->initialLength()) {
copyFrom = obj->addressOfArgsElement(arg); copyFrom = argsobj->addressOfElement(arg);
if (!copyFrom->isMagic()) { if (!copyFrom->isMagic()) {
if (StackFrame *afp = (StackFrame *) obj->getPrivate()) if (StackFrame *afp = (StackFrame *) argsobj->getPrivate())
copyFrom = &afp->canonicalActualArg(arg); copyFrom = &afp->canonicalActualArg(arg);
goto end_getelem; goto end_getelem;
} }
@ -2011,17 +2012,24 @@ stubs::Length(VMFrame &f)
if (vp->isString()) { if (vp->isString()) {
vp->setInt32(vp->toString()->length()); vp->setInt32(vp->toString()->length());
return; return;
} else if (vp->isObject()) { }
if (vp->isObject()) {
JSObject *obj = &vp->toObject(); JSObject *obj = &vp->toObject();
if (obj->isArray()) { if (obj->isArray()) {
jsuint length = obj->getArrayLength(); jsuint length = obj->getArrayLength();
regs.sp[-1].setNumber(length); regs.sp[-1].setNumber(length);
return; return;
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) { }
uint32 length = obj->getArgsInitialLength();
JS_ASSERT(length < INT32_MAX); if (obj->isArguments()) {
regs.sp[-1].setInt32(int32_t(length)); ArgumentsObject *argsobj = obj->asArguments();
return; if (!argsobj->hasOverriddenLength()) {
uint32 length = argsobj->initialLength();
JS_ASSERT(length < INT32_MAX);
regs.sp[-1].setInt32(int32_t(length));
return;
}
} }
} }

View File

@ -39,10 +39,13 @@
#include "jsprf.h" #include "jsprf.h"
#include "jstl.h" #include "jstl.h"
#include "jscompartment.h" #include "jscompartment.h"
#include "Writer.h" #include "Writer.h"
#include "nanojit.h" #include "nanojit.h"
#include "vm/ArgumentsObject.h"
namespace js { namespace js {
namespace tjit { namespace tjit {
@ -544,9 +547,9 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
// base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]> // base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
// base = addp base_oprnd1, ... // base = addp base_oprnd1, ...
// ins = {ld,st}X.argsdata base[...] // ins = {ld,st}X.argsdata base[...]
ok = (isConstPrivatePtr(base, JSObject::JSSLOT_ARGS_DATA) || ok = (isConstPrivatePtr(base, ArgumentsObject::DATA_SLOT) ||
(base->isop(LIR_addp) && (base->isop(LIR_addp) &&
isConstPrivatePtr(base->oprnd1(), JSObject::JSSLOT_ARGS_DATA))); isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
break; break;
default: default:
@ -565,7 +568,7 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
} }
} }
} } // namespace nanojit
#endif #endif

View File

@ -41,7 +41,6 @@
#define tracejit_Writer_h___ #define tracejit_Writer_h___
#include "jsiter.h" #include "jsiter.h"
#include "jsobj.h"
#include "jsstr.h" #include "jsstr.h"
#include "jstypedarray.h" #include "jstypedarray.h"
#include "nanojit.h" #include "nanojit.h"
@ -1217,13 +1216,7 @@ class Writer
"strChar"); "strChar");
} }
nj::LIns *getArgsLength(nj::LIns *args) const { inline nj::LIns *getArgsLength(nj::LIns *args) const;
uint32 slot = JSObject::JSSLOT_ARGS_LENGTH;
nj::LIns *vaddr_ins = ldpObjSlots(args);
return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
ACCSET_SLOTS),
"argsLength");
}
}; };
} /* namespace tjit */ } /* namespace tjit */

View File

@ -0,0 +1,133 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is SpiderMonkey arguments object code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jeff Walden <jwalden+code@mit.edu> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef ArgumentsObject_inl_h___
#define ArgumentsObject_inl_h___
#include "ArgumentsObject.h"
namespace js {
inline void
ArgumentsObject::setInitialLength(uint32 length)
{
JS_ASSERT(getSlot(INITIAL_LENGTH_SLOT).isUndefined());
setSlot(INITIAL_LENGTH_SLOT, Int32Value(length << PACKED_BITS_COUNT));
JS_ASSERT((getSlot(INITIAL_LENGTH_SLOT).toInt32() >> PACKED_BITS_COUNT) == length);
JS_ASSERT(!hasOverriddenLength());
}
inline uint32
ArgumentsObject::initialLength() const
{
uint32 argc = uint32(getSlot(INITIAL_LENGTH_SLOT).toInt32()) >> PACKED_BITS_COUNT;
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
return argc;
}
inline void
ArgumentsObject::markLengthOverridden()
{
getSlotRef(INITIAL_LENGTH_SLOT).getInt32Ref() |= LENGTH_OVERRIDDEN_BIT;
}
inline bool
ArgumentsObject::hasOverriddenLength() const
{
const js::Value &v = getSlot(INITIAL_LENGTH_SLOT);
return v.toInt32() & LENGTH_OVERRIDDEN_BIT;
}
inline void
ArgumentsObject::setCalleeAndData(JSObject &callee, ArgumentsData *data)
{
JS_ASSERT(getSlot(DATA_SLOT).isUndefined());
setSlot(DATA_SLOT, PrivateValue(data));
data->callee.setObject(callee);
}
inline ArgumentsData *
ArgumentsObject::data() const
{
return reinterpret_cast<js::ArgumentsData *>(getSlot(DATA_SLOT).toPrivate());
}
inline const js::Value &
ArgumentsObject::element(uint32 i) const
{
JS_ASSERT(i < initialLength());
return data()->slots[i];
}
inline js::Value *
ArgumentsObject::elements() const
{
return data()->slots;
}
inline Value *
ArgumentsObject::addressOfElement(uint32 i)
{
JS_ASSERT(i < initialLength());
return &data()->slots[i];
}
inline void
ArgumentsObject::setElement(uint32 i, const js::Value &v)
{
JS_ASSERT(i < initialLength());
data()->slots[i] = v;
}
inline const js::Value &
NormalArgumentsObject::callee() const
{
return data()->callee;
}
inline void
NormalArgumentsObject::clearCallee()
{
data()->callee = MagicValue(JS_ARGS_HOLE);
}
} // namespace js
#endif /* ArgumentsObject_inl_h___ */

234
js/src/vm/ArgumentsObject.h Normal file
View File

@ -0,0 +1,234 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is SpiderMonkey arguments object code.
*
* The Initial Developer of the Original Code is
* the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jeff Walden <jwalden+code@mit.edu> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef ArgumentsObject_h___
#define ArgumentsObject_h___
#include "jsfun.h"
#include "jstracer.h"
#include "Writer.h"
#ifdef JS_POLYIC
class GetPropCompiler;
#endif
#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
namespace js {
#ifdef JS_POLYIC
struct VMFrame;
namespace mjit {
namespace ic {
struct PICInfo;
extern void GetProp(VMFrame &f, PICInfo *pic);
}
}
#endif
struct EmptyShape;
struct ArgumentsData
{
/*
* arguments.callee, or MagicValue(JS_ARGS_HOLE) if arguments.callee has
* been modified.
*/
js::Value callee;
/*
* Values of the arguments for this object, or MagicValue(JS_ARGS_HOLE) if
* the indexed argument has been modified.
*/
js::Value slots[1];
};
class ArgumentsObject : public ::JSObject
{
/*
* Stores the initial arguments length, plus a flag indicating whether
* arguments.length has been overwritten. Use initialLength() to access the
* initial arguments length.
*/
static const uint32 INITIAL_LENGTH_SLOT = 0;
/* Stores an ArgumentsData for these arguments; access with data(). */
static const uint32 DATA_SLOT = 1;
protected:
static const uint32 RESERVED_SLOTS = 2;
private:
/* Lower-order bit stolen from the length slot. */
static const uint32 LENGTH_OVERRIDDEN_BIT = 0x1;
static const uint32 PACKED_BITS_COUNT = 1;
/* Needs access to LENGTH_SLOT. */
friend ::nanojit::LIns*
tjit::Writer::getArgsLength(::nanojit::LIns*) const;
/*
* Need access to DATA_SLOT, LENGTH_SLOT, LENGTH_OVERRIDDEN_BIT, and
* PACKED_BIT_COUNT.
*/
friend class TraceRecorder;
#ifdef JS_POLYIC
friend class ::GetPropCompiler;
#endif
/*
* Needs access to DATA_SLOT -- technically just checkAccSet needs it, but
* that's private, and exposing turns into a mess.
*/
friend class ::nanojit::ValidateWriter;
void setInitialLength(uint32 length);
void setCalleeAndData(JSObject &callee, ArgumentsData *data);
public:
/*
* Create arguments parented to parent, for the given callee function.
* Is parent redundant with callee->getGlobal()?
*/
static ArgumentsObject *create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee);
/*
* Return the initial length of the arguments. This may differ from the
* current value of arguments.length!
*/
inline uint32 initialLength() const;
/* True iff arguments.length has been assigned or its attributes changed. */
inline bool hasOverriddenLength() const;
inline void markLengthOverridden();
inline js::ArgumentsData *data() const;
inline const js::Value &element(uint32 i) const;
inline js::Value *elements() const;
inline js::Value *addressOfElement(uint32 i);
inline void setElement(uint32 i, const js::Value &v);
};
/*
* Non-strict arguments have a private: the function's stack frame until the
* function returns, when it is replaced with null. When an arguments object
* is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE, and when
* the trace exits its private is replaced with the stack frame or null, as
* appropriate.
*/
class NormalArgumentsObject : public ArgumentsObject
{
static js::Class jsClass;
friend bool JSObject::isNormalArguments() const;
friend struct EmptyShape; // for EmptyShape::getEmptyArgumentsShape
friend ArgumentsObject *
ArgumentsObject::create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee);
public:
/*
* Stores arguments.callee, or MagicValue(JS_ARGS_HOLE) if the callee has
* been cleared.
*/
inline const js::Value &callee() const;
/* Clear the location storing arguments.callee's initial value. */
inline void clearCallee();
};
/*
* Technically strict arguments have a private, but it's always null.
* Conceptually it would be better to remove this oddity, but preserving it
* allows us to work with arguments objects of either kind more abstractly,
* so we keep it for now.
*/
class StrictArgumentsObject : public ArgumentsObject
{
static js::Class jsClass;
friend bool JSObject::isStrictArguments() const;
friend ArgumentsObject *
ArgumentsObject::create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee);
};
} // namespace js
inline bool
JSObject::isNormalArguments() const
{
return getClass() == &js::NormalArgumentsObject::jsClass;
}
js::NormalArgumentsObject *
JSObject::asNormalArguments()
{
JS_ASSERT(isNormalArguments());
return reinterpret_cast<js::NormalArgumentsObject *>(this);
}
inline bool
JSObject::isStrictArguments() const
{
return getClass() == &js::StrictArgumentsObject::jsClass;
}
js::StrictArgumentsObject *
JSObject::asStrictArguments()
{
JS_ASSERT(isStrictArguments());
return reinterpret_cast<js::StrictArgumentsObject *>(this);
}
inline bool
JSObject::isArguments() const
{
return isNormalArguments() || isStrictArguments();
}
js::ArgumentsObject *
JSObject::asArguments()
{
JS_ASSERT(isArguments());
return reinterpret_cast<js::ArgumentsObject *>(this);
}
#endif /* ArgumentsObject_h___ */

View File

@ -43,6 +43,8 @@
#include "Stack.h" #include "Stack.h"
#include "ArgumentsObject-inl.h"
namespace js { namespace js {
/*****************************************************************************/ /*****************************************************************************/
@ -494,12 +496,11 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
} }
} }
if (hasArgsObj()) { if (hasArgsObj()) {
JSObject &args = argsObj(); ArgumentsObject &argsobj = argsObj();
JS_ASSERT(args.isArguments()); if (argsobj.isNormalArguments())
if (args.isNormalArguments()) argsobj.setPrivate(this);
args.setPrivate(this);
else else
JS_ASSERT(!args.getPrivate()); JS_ASSERT(!argsobj.getPrivate());
otherfp->flags_ &= ~HAS_ARGS_OBJ; otherfp->flags_ &= ~HAS_ARGS_OBJ;
} }
} }
@ -580,7 +581,7 @@ StackFrame::numActualArgs() const
{ {
JS_ASSERT(hasArgs()); JS_ASSERT(hasArgs());
if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS))) if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual; return hasArgsObj() ? argsObj().initialLength() : args.nactual;
return numFormalArgs(); return numFormalArgs();
} }
@ -590,7 +591,7 @@ StackFrame::actualArgs() const
JS_ASSERT(hasArgs()); JS_ASSERT(hasArgs());
Value *argv = formalArgs(); Value *argv = formalArgs();
if (JS_UNLIKELY(flags_ & OVERFLOW_ARGS)) { if (JS_UNLIKELY(flags_ & OVERFLOW_ARGS)) {
uintN nactual = hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual; uintN nactual = hasArgsObj() ? argsObj().initialLength() : args.nactual;
return argv - (2 + nactual); return argv - (2 + nactual);
} }
return argv; return argv;
@ -606,10 +607,10 @@ StackFrame::actualArgsEnd() const
} }
inline void inline void
StackFrame::setArgsObj(JSObject &obj) StackFrame::setArgsObj(ArgumentsObject &obj)
{ {
JS_ASSERT_IF(hasArgsObj(), &obj == args.obj); JS_ASSERT_IF(hasArgsObj(), &obj == args.obj);
JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.getArgsInitialLength()); JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.initialLength());
args.obj = &obj; args.obj = &obj;
flags_ |= HAS_ARGS_OBJ; flags_ |= HAS_ARGS_OBJ;
} }
@ -674,7 +675,7 @@ StackFrame::markActivationObjectsAsPut()
{ {
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) { if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
if (hasArgsObj() && !argsObj().getPrivate()) { if (hasArgsObj() && !argsObj().getPrivate()) {
args.nactual = args.obj->getArgsInitialLength(); args.nactual = args.obj->initialLength();
flags_ &= ~HAS_ARGS_OBJ; flags_ &= ~HAS_ARGS_OBJ;
} }
if (hasCallObj() && !callObj().getPrivate()) { if (hasCallObj() && !callObj().getPrivate()) {

View File

@ -58,6 +58,8 @@ class ExecuteFrameGuard;
class DummyFrameGuard; class DummyFrameGuard;
class GeneratorFrameGuard; class GeneratorFrameGuard;
class ArgumentsObject;
namespace mjit { struct JITScript; } namespace mjit { struct JITScript; }
namespace detail { struct OOMCheck; } namespace detail { struct OOMCheck; }
@ -259,8 +261,8 @@ class StackFrame
JSFunction *fun; /* function frame, pre GetScopeChain */ JSFunction *fun; /* function frame, pre GetScopeChain */
} exec; } exec;
union { /* describes the arguments of a function */ union { /* describes the arguments of a function */
uintN nactual; /* pre GetArgumentsObject */ uintN nactual; /* before js_GetArgsObject */
JSObject *obj; /* post GetArgumentsObject */ ArgumentsObject *obj; /* after js_GetArgsObject */
JSScript *script; /* eval has no args, but needs a script */ JSScript *script; /* eval has no args, but needs a script */
} args; } args;
mutable JSObject *scopeChain_; /* current scope chain */ mutable JSObject *scopeChain_; /* current scope chain */
@ -546,17 +548,17 @@ class StackFrame
return !!(flags_ & HAS_ARGS_OBJ); return !!(flags_ & HAS_ARGS_OBJ);
} }
JSObject &argsObj() const { ArgumentsObject &argsObj() const {
JS_ASSERT(hasArgsObj()); JS_ASSERT(hasArgsObj());
JS_ASSERT(!isEvalFrame()); JS_ASSERT(!isEvalFrame());
return *args.obj; return *args.obj;
} }
JSObject *maybeArgsObj() const { ArgumentsObject *maybeArgsObj() const {
return hasArgsObj() ? &argsObj() : NULL; return hasArgsObj() ? &argsObj() : NULL;
} }
inline void setArgsObj(JSObject &obj); inline void setArgsObj(ArgumentsObject &obj);
/* /*
* This value * This value