mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 652746 - Implement js::ArgumentsObject, js::NormalArgumentsObject, and js::StrictArgumentsObject. r=njn
--HG-- extra : rebase_source : 84258ffa658ba09928ab9b86f8017fa6683f3146
This commit is contained in:
parent
77b801ddde
commit
5d29251946
@ -277,6 +277,7 @@ VPATH += \
|
||||
EXPORTS_NAMESPACES = vm
|
||||
|
||||
EXPORTS_vm = \
|
||||
ArgumentsObject.h \
|
||||
GlobalObject.h \
|
||||
Stack.h \
|
||||
StringObject.h \
|
||||
|
@ -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,9 +221,12 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
|
||||
*lengthp = obj->getArgsInitialLength();
|
||||
return true;
|
||||
if (obj->isArguments()) {
|
||||
ArgumentsObject *argsobj = obj->asArguments();
|
||||
if (!argsobj->hasOverriddenLength()) {
|
||||
*lengthp = argsobj->initialLength();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
AutoValueRooter tvr(cx);
|
||||
@ -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,15 +356,17 @@ 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)) {
|
||||
*hole = JS_FALSE;
|
||||
StackFrame *fp = (StackFrame *)obj->getPrivate();
|
||||
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
||||
if (fp)
|
||||
*vp = fp->canonicalActualArg(index);
|
||||
return JS_TRUE;
|
||||
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 = reinterpret_cast<StackFrame *>(argsobj->getPrivate());
|
||||
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
||||
if (fp)
|
||||
*vp = fp->canonicalActualArg(index);
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,38 +431,38 @@ 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)) {
|
||||
/*
|
||||
* 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
|
||||
* were copied into the argsobj by PutActivationObjects when the
|
||||
* function returned. In both cases, it is necessary to fall off the
|
||||
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
|
||||
* this requires general-purpose property lookup.
|
||||
*/
|
||||
if (StackFrame *fp = (StackFrame *) aobj->getPrivate()) {
|
||||
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
|
||||
if (!fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(aobj, vp)))
|
||||
goto found_deleted_prop;
|
||||
} else {
|
||||
Value *srcbeg = aobj->getArgsElements();
|
||||
Value *srcend = srcbeg + length;
|
||||
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) {
|
||||
if (src->isMagic(JS_ARGS_HOLE))
|
||||
goto found_deleted_prop;
|
||||
*dst = *src;
|
||||
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
|
||||
* were copied into the argsobj by PutActivationObjects when the
|
||||
* function returned. In both cases, it is necessary to fall off the
|
||||
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
|
||||
* this requires general-purpose property lookup.
|
||||
*/
|
||||
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
|
||||
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
|
||||
if (fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(argsobj, vp)))
|
||||
return true;
|
||||
} else {
|
||||
Value *srcbeg = argsobj->elements();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
207
js/src/jsfun.cpp
207
js/src/jsfun.cpp
@ -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,
|
||||
proto, parent, NULL, false);
|
||||
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,
|
||||
|
@ -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;
|
||||
|
@ -4096,20 +4096,26 @@ BEGIN_CASE(JSOP_LENGTH)
|
||||
vp = ®s.sp[-1];
|
||||
if (vp->isString()) {
|
||||
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 {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
@ -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:
|
||||
/*
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,17 +2012,24 @@ 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();
|
||||
JS_ASSERT(length < INT32_MAX);
|
||||
regs.sp[-1].setInt32(int32_t(length));
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
133
js/src/vm/ArgumentsObject-inl.h
Normal file
133
js/src/vm/ArgumentsObject-inl.h
Normal 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
234
js/src/vm/ArgumentsObject.h
Normal 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___ */
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user