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_vm = \
ArgumentsObject.h \
GlobalObject.h \
Stack.h \
StringObject.h \

View File

@ -82,6 +82,7 @@
#include "jstypes.h"
#include "jsstdint.h"
#include "jsutil.h"
#include "jsapi.h"
#include "jsarray.h"
#include "jsatom.h"
@ -105,12 +106,15 @@
#include "jsvector.h"
#include "jswrapper.h"
#include "vm/ArgumentsObject.h"
#include "jsatominlines.h"
#include "jscntxtinlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
#include "jsstrinlines.h"
#include "vm/ArgumentsObject-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
@ -217,10 +221,13 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
return true;
}
if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
*lengthp = obj->getArgsInitialLength();
if (obj->isArguments()) {
ArgumentsObject *argsobj = obj->asArguments();
if (!argsobj->hasOverriddenLength()) {
*lengthp = argsobj->initialLength();
return true;
}
}
AutoValueRooter tvr(cx);
if (!obj->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), tvr.addr()))
@ -263,7 +270,7 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
*/
if (!createAtom &&
((clasp = obj->getClass()) == &js_SlowArrayClass ||
clasp == &js_ArgumentsClass ||
obj->isArguments() ||
clasp == &js_ObjectClass)) {
atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
if (!atom) {
@ -349,17 +356,19 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp
*hole = JS_FALSE;
return JS_TRUE;
}
if (obj->isArguments() &&
index < obj->getArgsInitialLength() &&
!(*vp = obj->getArgsElement(uint32(index))).isMagic(JS_ARGS_HOLE)) {
if (obj->isArguments()) {
ArgumentsObject *argsobj = obj->asArguments();
if (index < argsobj->initialLength() &&
!(*vp = argsobj->element(uint32(index))).isMagic(JS_ARGS_HOLE)) {
*hole = JS_FALSE;
StackFrame *fp = (StackFrame *)obj->getPrivate();
StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate());
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
if (fp)
*vp = fp->canonicalActualArg(index);
return JS_TRUE;
}
}
}
AutoIdRooter idr(cx);
@ -390,17 +399,28 @@ namespace js {
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
{
CopyNonHoleArgsTo(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {}
JSObject *aobj;
CopyNonHoleArgsTo(ArgumentsObject *argsobj, Value *dst) : argsobj(argsobj), dst(dst) {}
ArgumentsObject *argsobj;
Value *dst;
bool operator()(uintN argi, Value *src) {
if (aobj->getArgsElement(argi).isMagic(JS_ARGS_HOLE))
if (argsobj->element(argi).isMagic(JS_ARGS_HOLE))
return false;
*dst++ = *src;
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
GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
{
@ -411,8 +431,12 @@ GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
Value *srcend = srcbeg + length;
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src)
*dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
} else if (aobj->isArguments() && !aobj->isArgsLengthOverridden() &&
!js_PrototypeHasIndexedProperties(cx, aobj)) {
return true;
}
if (aobj->isArguments()) {
ArgumentsObject *argsobj = aobj->asArguments();
if (!argsobj->hasOverriddenLength() && !js_PrototypeHasIndexedProperties(cx, argsobj)) {
/*
* If the argsobj is for an active call, then the elements are the
* live args on the stack. Otherwise, the elements are the args that
@ -421,28 +445,24 @@ GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
* this requires general-purpose property lookup.
*/
if (StackFrame *fp = (StackFrame *) aobj->getPrivate()) {
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
if (!fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(aobj, vp)))
goto found_deleted_prop;
if (fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(argsobj, vp)))
return true;
} else {
Value *srcbeg = aobj->getArgsElements();
Value *srcbeg = argsobj->elements();
Value *srcend = srcbeg + length;
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) {
if (src->isMagic(JS_ARGS_HOLE))
goto found_deleted_prop;
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 "jsscriptinlines.h"
#include "vm/ArgumentsObject-inl.h"
#include "vm/Stack-inl.h"
using namespace js;
@ -145,10 +146,10 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
vp->setUndefined();
if (JSID_IS_INT(id)) {
uint32 arg = uint32(JSID_TO_INT(id));
JSObject *argsobj = fp->maybeArgsObj();
ArgumentsObject *argsobj = fp->maybeArgsObj();
if (arg < fp->numActualArgs()) {
if (argsobj) {
const Value &v = argsobj->getArgsElement(arg);
const Value &v = argsobj->element(arg);
if (v.isMagic(JS_ARGS_HOLE))
return argsobj->getProperty(cx, id, vp);
if (fp->functionScript()->strictModeCode) {
@ -174,24 +175,27 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
return argsobj->getProperty(cx, id, vp);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
JSObject *argsobj = fp->maybeArgsObj();
if (argsobj && argsobj->isArgsLengthOverridden())
ArgumentsObject *argsobj = fp->maybeArgsObj();
if (argsobj && argsobj->hasOverriddenLength())
return argsobj->getProperty(cx, id, vp);
vp->setInt32(fp->numActualArgs());
}
return true;
}
static JSObject *
NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
js::ArgumentsObject *
ArgumentsObject::create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
{
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2);
JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
if (!argsobj)
JS_STATIC_ASSERT(NormalArgumentsObject::RESERVED_SLOTS == 2);
JS_STATIC_ASSERT(StrictArgumentsObject::RESERVED_SLOTS == 2);
JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
if (!obj)
return NULL;
EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
@ -206,16 +210,18 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
SetValueRangeToUndefined(data->slots, argc);
/* Can't fail from here on, so initialize everything in argsobj. */
argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode()
? &StrictArgumentsClass
: &js_ArgumentsClass,
obj->init(cx, callee.getFunctionPrivate()->inStrictMode()
? &StrictArgumentsObject::jsClass
: &NormalArgumentsObject::jsClass,
proto, parent, NULL, false);
obj->setMap(emptyArgumentsShape);
argsobj->setMap(emptyArgumentsShape);
ArgumentsObject *argsobj = obj->asArguments();
argsobj->setArgsLength(argc);
argsobj->setArgsData(data);
data->callee.setObject(callee);
JS_ASSERT(UINT32_MAX > (uint64(argc) << PACKED_BITS_COUNT));
argsobj->setInitialLength(argc);
argsobj->setCalleeAndData(callee, data);
return argsobj;
}
@ -250,7 +256,8 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
/* Compute the arguments object's parent slot from fp's scope chain. */
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)
return argsobj;
@ -264,7 +271,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
* retrieve up-to-date parameter values.
*/
if (argsobj->isStrictArguments())
fp->forEachCanonicalActualArg(PutArg(argsobj->getArgsData()->slots));
fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots));
else
argsobj->setPrivate(fp);
@ -275,10 +282,10 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
void
js_PutArgsObject(StackFrame *fp)
{
JSObject &argsobj = fp->argsObj();
ArgumentsObject &argsobj = fp->argsObj();
if (argsobj.isNormalArguments()) {
JS_ASSERT(argsobj.getPrivate() == fp);
fp->forEachCanonicalActualArg(PutArg(argsobj.getArgsData()->slots));
fp->forEachCanonicalActualArg(PutArg(argsobj.data()->slots));
argsobj.setPrivate(NULL);
} else {
JS_ASSERT(!argsobj.getPrivate());
@ -293,7 +300,7 @@ js_PutArgsObject(StackFrame *fp)
JSObject * JS_FASTCALL
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)
return NULL;
@ -314,9 +321,10 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, OBJECT, UI
/* FIXME change the return type to void. */
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);
/*
@ -324,9 +332,9 @@ js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args)
* the arguments, regardless of whether #actuals > #formals so there is no
* need to worry about actual vs. formal arguments.
*/
Value *srcend = args + argsobj->getArgsInitialLength();
Value *dst = argsobj->getArgsData()->slots;
for (Value *src = args; src != srcend; ++src, ++dst) {
Value *srcend = argv + argsobj->initialLength();
Value *dst = argsobj->data()->slots;
for (Value *src = argv; src < srcend; ++src, ++dst) {
if (!dst->isMagic(JS_ARGS_HOLE))
*dst = *src;
}
@ -342,16 +350,15 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALU
static JSBool
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
JS_ASSERT(obj->isArguments());
ArgumentsObject *argsobj = obj->asArguments();
if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength())
obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE));
if (arg < argsobj->initialLength())
argsobj->setElement(arg, MagicValue(JS_ARGS_HOLE));
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
obj->setArgsLengthOverridden();
argsobj->markLengthOverridden();
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
obj->setArgsCallee(MagicValue(JS_ARGS_HOLE));
argsobj->asNormalArguments()->clearCallee();
}
return true;
}
@ -518,25 +525,26 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (!obj->isNormalArguments())
return true;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) {
JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = (StackFrame *) obj->getPrivate())
if (arg < argsobj->initialLength()) {
JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
*vp = fp->canonicalActualArg(arg);
else
*vp = obj->getArgsElement(arg);
*vp = argsobj->element(arg);
}
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (!obj->isArgsLengthOverridden())
vp->setInt32(obj->getArgsInitialLength());
if (!argsobj->hasOverriddenLength())
vp->setInt32(argsobj->initialLength());
} else {
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 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())
return true;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) {
StackFrame *fp = (StackFrame *) obj->getPrivate();
if (fp) {
if (arg < argsobj->initialLength()) {
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
JSScript *script = fp->functionScript();
if (script->usesArguments)
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.
*/
AutoValueRooter tvr(cx);
return js_DeleteProperty(cx, obj, id, tvr.addr(), false) &&
js_DefineProperty(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
return js_DeleteProperty(cx, argsobj, id, tvr.addr(), false) &&
js_DefineProperty(cx, argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
}
static JSBool
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp)
{
JS_ASSERT(obj->isNormalArguments());
*objp = NULL;
NormalArgumentsObject *argsobj = obj->asNormalArguments();
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
if (JSID_IS_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;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (obj->isArgsLengthOverridden())
if (argsobj->hasOverriddenLength())
return true;
} else {
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
return true;
if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
if (argsobj->callee().isMagic(JS_ARGS_HOLE))
return true;
}
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;
*objp = obj;
*objp = argsobj;
return true;
}
static JSBool
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
* calls.
*/
int argc = int(obj->getArgsInitialLength());
int argc = int(argsobj->initialLength());
for (int i = -2; i != argc; i++) {
jsid id = (i == -2)
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
@ -654,7 +663,7 @@ args_enumerate(JSContext *cx, JSObject *obj)
JSObject *pobj;
JSProperty *prop;
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
if (!js_LookupProperty(cx, argsobj, id, &pobj, &prop))
return false;
}
return true;
@ -668,21 +677,23 @@ StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
/*
* arg can exceed the number of arguments if a script changed the
* prototype to point to another Arguments object with a bigger argc.
*/
uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) {
const Value &v = obj->getArgsElement(arg);
if (arg < argsobj->initialLength()) {
const Value &v = argsobj->element(arg);
if (!v.isMagic(JS_ARGS_HOLE))
*vp = v;
}
} else {
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
if (!obj->isArgsLengthOverridden())
vp->setInt32(obj->getArgsInitialLength());
if (!argsobj->hasOverriddenLength())
vp->setInt32(argsobj->initialLength());
}
return true;
}
@ -694,10 +705,12 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
if (!obj->isStrictArguments())
return true;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
if (JSID_IS_INT(id)) {
uintN arg = uintN(JSID_TO_INT(id));
if (arg < obj->getArgsInitialLength()) {
obj->setArgsElement(arg, *vp);
if (arg < argsobj->initialLength()) {
argsobj->setElement(arg, *vp);
return true;
}
} else {
@ -711,29 +724,29 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
* collect its value.
*/
AutoValueRooter tvr(cx);
return js_DeleteProperty(cx, obj, id, tvr.addr(), strict) &&
js_SetProperty(cx, obj, id, vp, strict);
return js_DeleteProperty(cx, argsobj, id, tvr.addr(), strict) &&
js_SetProperty(cx, argsobj, id, vp, strict);
}
static JSBool
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
{
JS_ASSERT(obj->isStrictArguments());
*objp = NULL;
StrictArgumentsObject *argsobj = obj->asStrictArguments();
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
PropertyOp getter = StrictArgGetter;
StrictPropertyOp setter = StrictArgSetter;
if (JSID_IS_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;
attrs |= JSPROP_ENUMERATE;
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
if (obj->isArgsLengthOverridden())
if (argsobj->hasOverriddenLength())
return true;
} else {
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;
getter = CastAsPropertyOp(obj->getThrowTypeError());
setter = CastAsStrictPropertyOp(obj->getThrowTypeError());
getter = CastAsPropertyOp(argsobj->getThrowTypeError());
setter = CastAsStrictPropertyOp(argsobj->getThrowTypeError());
}
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;
*objp = obj;
*objp = argsobj;
return true;
}
static JSBool
strictargs_enumerate(JSContext *cx, JSObject *obj)
{
JS_ASSERT(obj->isStrictArguments());
StrictArgumentsObject *argsobj = obj->asStrictArguments();
/*
* Trigger reflection in strictargs_resolve using a series of
@ -767,19 +780,19 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
JSProperty *prop;
// 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;
// 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;
// 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;
for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) {
if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop))
for (uint32 i = 0, argc = argsobj->initialLength(); i < argc; i++) {
if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
return false;
}
@ -789,7 +802,7 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
static void
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
args_trace(JSTracer *trc, JSObject *obj)
{
JS_ASSERT(obj->isArguments());
if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
JS_ASSERT(!obj->isStrictArguments());
ArgumentsObject *argsobj = obj->asArguments();
if (argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
JS_ASSERT(!argsobj->isStrictArguments());
return;
}
ArgumentsData *data = obj->getArgsData();
ArgumentsData *data = argsobj->data();
if (data->callee.isObject())
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
* objects have the initial value of Object.prototype as their [[Prototype]].
* However, Object.prototype.toString.call(arguments) === "[object Arguments]"
* 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
* The classes 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.
*/
Class js_ArgumentsClass = {
Class NormalArgumentsObject::jsClass = {
"Arguments",
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),
PropertyStub, /* addProperty */
args_delProperty,
PropertyStub, /* getProperty */
StrictPropertyStub, /* setProperty */
args_enumerate,
(JSResolveOp) args_resolve,
reinterpret_cast<JSResolveOp>(args_resolve),
ConvertStub,
args_finalize, /* finalize */
NULL, /* reserved0 */
@ -863,17 +872,15 @@ Class js_ArgumentsClass = {
args_trace
};
namespace js {
/*
* Strict mode arguments is significantly less magical than non-strict mode
* arguments, so it is represented by a different class while sharing some
* functionality.
*/
Class StrictArgumentsClass = {
Class StrictArgumentsObject::jsClass = {
"Arguments",
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),
PropertyStub, /* addProperty */
args_delProperty,

View File

@ -247,53 +247,6 @@ struct JSFunction : public JSObject_Slots2
JS_FN(name, fastcall, nargs, flags)
#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_FunctionClass;
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;

View File

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

View File

@ -261,7 +261,11 @@ namespace js {
struct NativeIterator;
class RegExp;
class GlobalObject;
class ArgumentsObject;
class NormalArgumentsObject;
class StrictArgumentsObject;
class StringObject;
}
@ -794,93 +798,10 @@ struct JSObject : js::gc::Cell {
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:
/* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */
static const uint32 JSSLOT_ARGS_LENGTH = 0;
static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2;
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);
inline js::ArgumentsObject *asArguments();
inline js::NormalArgumentsObject *asNormalArguments();
inline js::StrictArgumentsObject *asStrictArguments();
private:
/*

View File

@ -409,97 +409,6 @@ JSObject::shrinkDenseArrayElements(JSContext *cx, uintN 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
JSObject::callIsForEval() const
{

View File

@ -49,6 +49,7 @@
#endif
#include "jstypes.h"
#include "jscntxt.h"
#include "jscompartment.h"
#include "jshashtable.h"
@ -58,6 +59,8 @@
#include "jspubtd.h"
#include "jspropertytree.h"
#include "vm/ArgumentsObject.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4800)
@ -635,7 +638,7 @@ struct EmptyShape : public js::Shape
}
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) {

View File

@ -3158,8 +3158,7 @@ public:
if (p == fp->addressOfArgs()) {
if (frameobj) {
JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
fp->setArgsObj(*frameobj);
JS_ASSERT(frameobj->isArguments());
fp->setArgsObj(*frameobj->asArguments());
if (frameobj->isNormalArguments())
frameobj->setPrivate(fp);
else
@ -12881,7 +12880,7 @@ JS_REQUIRES_STACK void
TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins)
{
// 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)),
w.ui2p(w.muliN(idx_ins, sizeof(Value))));
LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins);
@ -12934,17 +12933,18 @@ TraceRecorder::record_JSOP_GETELEM()
}
if (obj->isArguments()) {
ArgumentsObject *argsobj = obj->asArguments();
// Don't even try to record if out of range or reading a deleted arg
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");
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");
// Only trace reading arguments out of active, tracked frame
unsigned depth;
StackFrame *afp = guardArguments(obj, obj_ins, &depth);
if (afp) {
if (StackFrame *afp = guardArguments(obj, obj_ins, &depth)) {
Value* vp = &afp->canonicalActualArg(int_idx);
if (idx_ins->isImmD()) {
JS_ASSERT(int_idx == (int32)idx_ins->immD());
@ -13863,7 +13863,7 @@ TraceRecorder::record_JSOP_FUNAPPLY()
StackFrame *afp = guardArguments(aobj, aobj_ins, &depth);
if (!afp)
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");
guardArgsLengthNotAssigned(aobj_ins);
length = afp->numActualArgs();
@ -15671,13 +15671,27 @@ TraceRecorder::record_JSOP_ARGSUB()
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*
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
{
// The following implements JSObject::isArgsLengthOverridden on trace.
// ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
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);
return len_ins;
}
@ -15696,7 +15710,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 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");
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());
if (callDepth == 0) {
@ -16359,13 +16373,13 @@ 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 (obj->isArgsLengthOverridden())
if (obj->asArguments()->hasOverriddenLength())
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
// slot_ins is the value from the slot; right-shift to get the length
// (see JSObject::getArgsInitialLength in jsfun.cpp).
LIns* v_ins = w.i2d(w.rshiN(slot_ins, JSObject::ARGS_PACKED_BITS_COUNT));
// slot_ins is the value from the slot; right-shift to get the length;
// see ArgumentsObject.h.
LIns* v_ins = w.i2d(w.rshiN(slot_ins, ArgumentsObject::PACKED_BITS_COUNT));
set(&l, v_ins);
return ARECORD_CONTINUE;
}

View File

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

View File

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

View File

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

View File

@ -41,7 +41,6 @@
#define tracejit_Writer_h___
#include "jsiter.h"
#include "jsobj.h"
#include "jsstr.h"
#include "jstypedarray.h"
#include "nanojit.h"
@ -1217,13 +1216,7 @@ class Writer
"strChar");
}
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");
}
inline nj::LIns *getArgsLength(nj::LIns *args) const;
};
} /* 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 "ArgumentsObject-inl.h"
namespace js {
/*****************************************************************************/
@ -494,12 +496,11 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
}
}
if (hasArgsObj()) {
JSObject &args = argsObj();
JS_ASSERT(args.isArguments());
if (args.isNormalArguments())
args.setPrivate(this);
ArgumentsObject &argsobj = argsObj();
if (argsobj.isNormalArguments())
argsobj.setPrivate(this);
else
JS_ASSERT(!args.getPrivate());
JS_ASSERT(!argsobj.getPrivate());
otherfp->flags_ &= ~HAS_ARGS_OBJ;
}
}
@ -580,7 +581,7 @@ StackFrame::numActualArgs() const
{
JS_ASSERT(hasArgs());
if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
return hasArgsObj() ? argsObj().initialLength() : args.nactual;
return numFormalArgs();
}
@ -590,7 +591,7 @@ StackFrame::actualArgs() const
JS_ASSERT(hasArgs());
Value *argv = formalArgs();
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;
@ -606,10 +607,10 @@ StackFrame::actualArgsEnd() const
}
inline void
StackFrame::setArgsObj(JSObject &obj)
StackFrame::setArgsObj(ArgumentsObject &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;
flags_ |= HAS_ARGS_OBJ;
}
@ -674,7 +675,7 @@ StackFrame::markActivationObjectsAsPut()
{
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
if (hasArgsObj() && !argsObj().getPrivate()) {
args.nactual = args.obj->getArgsInitialLength();
args.nactual = args.obj->initialLength();
flags_ &= ~HAS_ARGS_OBJ;
}
if (hasCallObj() && !callObj().getPrivate()) {

View File

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