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_NAMESPACES = vm
|
||||||
|
|
||||||
EXPORTS_vm = \
|
EXPORTS_vm = \
|
||||||
|
ArgumentsObject.h \
|
||||||
GlobalObject.h \
|
GlobalObject.h \
|
||||||
Stack.h \
|
Stack.h \
|
||||||
StringObject.h \
|
StringObject.h \
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
#include "jsstdint.h"
|
#include "jsstdint.h"
|
||||||
#include "jsutil.h"
|
#include "jsutil.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "jsarray.h"
|
#include "jsarray.h"
|
||||||
#include "jsatom.h"
|
#include "jsatom.h"
|
||||||
@ -105,12 +106,15 @@
|
|||||||
#include "jsvector.h"
|
#include "jsvector.h"
|
||||||
#include "jswrapper.h"
|
#include "jswrapper.h"
|
||||||
|
|
||||||
|
#include "vm/ArgumentsObject.h"
|
||||||
|
|
||||||
#include "jsatominlines.h"
|
#include "jsatominlines.h"
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
#include "jsinterpinlines.h"
|
#include "jsinterpinlines.h"
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsstrinlines.h"
|
#include "jsstrinlines.h"
|
||||||
|
|
||||||
|
#include "vm/ArgumentsObject-inl.h"
|
||||||
#include "vm/Stack-inl.h"
|
#include "vm/Stack-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
@ -217,9 +221,12 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
|
if (obj->isArguments()) {
|
||||||
*lengthp = obj->getArgsInitialLength();
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
return true;
|
if (!argsobj->hasOverriddenLength()) {
|
||||||
|
*lengthp = argsobj->initialLength();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoValueRooter tvr(cx);
|
AutoValueRooter tvr(cx);
|
||||||
@ -263,7 +270,7 @@ BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
|
|||||||
*/
|
*/
|
||||||
if (!createAtom &&
|
if (!createAtom &&
|
||||||
((clasp = obj->getClass()) == &js_SlowArrayClass ||
|
((clasp = obj->getClass()) == &js_SlowArrayClass ||
|
||||||
clasp == &js_ArgumentsClass ||
|
obj->isArguments() ||
|
||||||
clasp == &js_ObjectClass)) {
|
clasp == &js_ObjectClass)) {
|
||||||
atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
|
atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start);
|
||||||
if (!atom) {
|
if (!atom) {
|
||||||
@ -349,15 +356,17 @@ GetElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole, Value *vp
|
|||||||
*hole = JS_FALSE;
|
*hole = JS_FALSE;
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
if (obj->isArguments() &&
|
if (obj->isArguments()) {
|
||||||
index < obj->getArgsInitialLength() &&
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
!(*vp = obj->getArgsElement(uint32(index))).isMagic(JS_ARGS_HOLE)) {
|
if (index < argsobj->initialLength() &&
|
||||||
*hole = JS_FALSE;
|
!(*vp = argsobj->element(uint32(index))).isMagic(JS_ARGS_HOLE)) {
|
||||||
StackFrame *fp = (StackFrame *)obj->getPrivate();
|
*hole = JS_FALSE;
|
||||||
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate());
|
||||||
if (fp)
|
if (fp != JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
||||||
*vp = fp->canonicalActualArg(index);
|
if (fp)
|
||||||
return JS_TRUE;
|
*vp = fp->canonicalActualArg(index);
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,17 +399,28 @@ namespace js {
|
|||||||
|
|
||||||
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
|
struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
|
||||||
{
|
{
|
||||||
CopyNonHoleArgsTo(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {}
|
CopyNonHoleArgsTo(ArgumentsObject *argsobj, Value *dst) : argsobj(argsobj), dst(dst) {}
|
||||||
JSObject *aobj;
|
ArgumentsObject *argsobj;
|
||||||
Value *dst;
|
Value *dst;
|
||||||
bool operator()(uintN argi, Value *src) {
|
bool operator()(uintN argi, Value *src) {
|
||||||
if (aobj->getArgsElement(argi).isMagic(JS_ARGS_HOLE))
|
if (argsobj->element(argi).isMagic(JS_ARGS_HOLE))
|
||||||
return false;
|
return false;
|
||||||
*dst++ = *src;
|
*dst++ = *src;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
GetElementsSlow(JSContext *cx, JSObject *aobj, uint32 length, Value *vp)
|
||||||
|
{
|
||||||
|
for (uint32 i = 0; i < length; i++) {
|
||||||
|
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &vp[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
|
GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
|
||||||
{
|
{
|
||||||
@ -411,38 +431,38 @@ GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
|
|||||||
Value *srcend = srcbeg + length;
|
Value *srcend = srcbeg + length;
|
||||||
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src)
|
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src)
|
||||||
*dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
|
*dst = src->isMagic(JS_ARRAY_HOLE) ? UndefinedValue() : *src;
|
||||||
} else if (aobj->isArguments() && !aobj->isArgsLengthOverridden() &&
|
return true;
|
||||||
!js_PrototypeHasIndexedProperties(cx, aobj)) {
|
}
|
||||||
/*
|
|
||||||
* If the argsobj is for an active call, then the elements are the
|
if (aobj->isArguments()) {
|
||||||
* live args on the stack. Otherwise, the elements are the args that
|
ArgumentsObject *argsobj = aobj->asArguments();
|
||||||
* were copied into the argsobj by PutActivationObjects when the
|
if (!argsobj->hasOverriddenLength() && !js_PrototypeHasIndexedProperties(cx, argsobj)) {
|
||||||
* function returned. In both cases, it is necessary to fall off the
|
/*
|
||||||
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
|
* If the argsobj is for an active call, then the elements are the
|
||||||
* this requires general-purpose property lookup.
|
* live args on the stack. Otherwise, the elements are the args that
|
||||||
*/
|
* were copied into the argsobj by PutActivationObjects when the
|
||||||
if (StackFrame *fp = (StackFrame *) aobj->getPrivate()) {
|
* function returned. In both cases, it is necessary to fall off the
|
||||||
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
|
* fast path for deleted properties (MagicValue(JS_ARGS_HOLE) since
|
||||||
if (!fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(aobj, vp)))
|
* this requires general-purpose property lookup.
|
||||||
goto found_deleted_prop;
|
*/
|
||||||
} else {
|
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
|
||||||
Value *srcbeg = aobj->getArgsElements();
|
JS_ASSERT(fp->numActualArgs() <= JS_ARGS_LENGTH_MAX);
|
||||||
Value *srcend = srcbeg + length;
|
if (fp->forEachCanonicalActualArg(CopyNonHoleArgsTo(argsobj, vp)))
|
||||||
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) {
|
return true;
|
||||||
if (src->isMagic(JS_ARGS_HOLE))
|
} else {
|
||||||
goto found_deleted_prop;
|
Value *srcbeg = argsobj->elements();
|
||||||
*dst = *src;
|
Value *srcend = srcbeg + length;
|
||||||
|
for (Value *dst = vp, *src = srcbeg; src < srcend; ++dst, ++src) {
|
||||||
|
if (src->isMagic(JS_ARGS_HOLE))
|
||||||
|
return GetElementsSlow(cx, argsobj, length, vp);
|
||||||
|
*dst = *src;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
found_deleted_prop:
|
|
||||||
for (uintN i = 0; i < length; i++) {
|
|
||||||
if (!aobj->getProperty(cx, INT_TO_JSID(jsint(i)), &vp[i]))
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return GetElementsSlow(cx, aobj, length, vp);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
207
js/src/jsfun.cpp
207
js/src/jsfun.cpp
@ -90,6 +90,7 @@
|
|||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsscriptinlines.h"
|
#include "jsscriptinlines.h"
|
||||||
|
|
||||||
|
#include "vm/ArgumentsObject-inl.h"
|
||||||
#include "vm/Stack-inl.h"
|
#include "vm/Stack-inl.h"
|
||||||
|
|
||||||
using namespace js;
|
using namespace js;
|
||||||
@ -145,10 +146,10 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
|
|||||||
vp->setUndefined();
|
vp->setUndefined();
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uint32 arg = uint32(JSID_TO_INT(id));
|
uint32 arg = uint32(JSID_TO_INT(id));
|
||||||
JSObject *argsobj = fp->maybeArgsObj();
|
ArgumentsObject *argsobj = fp->maybeArgsObj();
|
||||||
if (arg < fp->numActualArgs()) {
|
if (arg < fp->numActualArgs()) {
|
||||||
if (argsobj) {
|
if (argsobj) {
|
||||||
const Value &v = argsobj->getArgsElement(arg);
|
const Value &v = argsobj->element(arg);
|
||||||
if (v.isMagic(JS_ARGS_HOLE))
|
if (v.isMagic(JS_ARGS_HOLE))
|
||||||
return argsobj->getProperty(cx, id, vp);
|
return argsobj->getProperty(cx, id, vp);
|
||||||
if (fp->functionScript()->strictModeCode) {
|
if (fp->functionScript()->strictModeCode) {
|
||||||
@ -174,24 +175,27 @@ js_GetArgsProperty(JSContext *cx, StackFrame *fp, jsid id, Value *vp)
|
|||||||
return argsobj->getProperty(cx, id, vp);
|
return argsobj->getProperty(cx, id, vp);
|
||||||
}
|
}
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
||||||
JSObject *argsobj = fp->maybeArgsObj();
|
ArgumentsObject *argsobj = fp->maybeArgsObj();
|
||||||
if (argsobj && argsobj->isArgsLengthOverridden())
|
if (argsobj && argsobj->hasOverriddenLength())
|
||||||
return argsobj->getProperty(cx, id, vp);
|
return argsobj->getProperty(cx, id, vp);
|
||||||
vp->setInt32(fp->numActualArgs());
|
vp->setInt32(fp->numActualArgs());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSObject *
|
js::ArgumentsObject *
|
||||||
NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
|
ArgumentsObject::create(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
|
||||||
{
|
{
|
||||||
|
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
||||||
|
|
||||||
JSObject *proto;
|
JSObject *proto;
|
||||||
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
|
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
JS_STATIC_ASSERT(JSObject::ARGS_CLASS_RESERVED_SLOTS == 2);
|
JS_STATIC_ASSERT(NormalArgumentsObject::RESERVED_SLOTS == 2);
|
||||||
JSObject *argsobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
|
JS_STATIC_ASSERT(StrictArgumentsObject::RESERVED_SLOTS == 2);
|
||||||
if (!argsobj)
|
JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
|
||||||
|
if (!obj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
|
EmptyShape *emptyArgumentsShape = EmptyShape::getEmptyArgumentsShape(cx);
|
||||||
@ -206,16 +210,18 @@ NewArguments(JSContext *cx, JSObject *parent, uint32 argc, JSObject &callee)
|
|||||||
SetValueRangeToUndefined(data->slots, argc);
|
SetValueRangeToUndefined(data->slots, argc);
|
||||||
|
|
||||||
/* Can't fail from here on, so initialize everything in argsobj. */
|
/* Can't fail from here on, so initialize everything in argsobj. */
|
||||||
argsobj->init(cx, callee.getFunctionPrivate()->inStrictMode()
|
obj->init(cx, callee.getFunctionPrivate()->inStrictMode()
|
||||||
? &StrictArgumentsClass
|
? &StrictArgumentsObject::jsClass
|
||||||
: &js_ArgumentsClass,
|
: &NormalArgumentsObject::jsClass,
|
||||||
proto, parent, NULL, false);
|
proto, parent, NULL, false);
|
||||||
|
obj->setMap(emptyArgumentsShape);
|
||||||
|
|
||||||
argsobj->setMap(emptyArgumentsShape);
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
|
||||||
argsobj->setArgsLength(argc);
|
JS_ASSERT(UINT32_MAX > (uint64(argc) << PACKED_BITS_COUNT));
|
||||||
argsobj->setArgsData(data);
|
argsobj->setInitialLength(argc);
|
||||||
data->callee.setObject(callee);
|
|
||||||
|
argsobj->setCalleeAndData(callee, data);
|
||||||
|
|
||||||
return argsobj;
|
return argsobj;
|
||||||
}
|
}
|
||||||
@ -250,7 +256,8 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
|
|||||||
|
|
||||||
/* Compute the arguments object's parent slot from fp's scope chain. */
|
/* Compute the arguments object's parent slot from fp's scope chain. */
|
||||||
JSObject *global = fp->scopeChain().getGlobal();
|
JSObject *global = fp->scopeChain().getGlobal();
|
||||||
JSObject *argsobj = NewArguments(cx, global, fp->numActualArgs(), fp->callee());
|
ArgumentsObject *argsobj =
|
||||||
|
ArgumentsObject::create(cx, global, fp->numActualArgs(), fp->callee());
|
||||||
if (!argsobj)
|
if (!argsobj)
|
||||||
return argsobj;
|
return argsobj;
|
||||||
|
|
||||||
@ -264,7 +271,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
|
|||||||
* retrieve up-to-date parameter values.
|
* retrieve up-to-date parameter values.
|
||||||
*/
|
*/
|
||||||
if (argsobj->isStrictArguments())
|
if (argsobj->isStrictArguments())
|
||||||
fp->forEachCanonicalActualArg(PutArg(argsobj->getArgsData()->slots));
|
fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots));
|
||||||
else
|
else
|
||||||
argsobj->setPrivate(fp);
|
argsobj->setPrivate(fp);
|
||||||
|
|
||||||
@ -275,10 +282,10 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp)
|
|||||||
void
|
void
|
||||||
js_PutArgsObject(StackFrame *fp)
|
js_PutArgsObject(StackFrame *fp)
|
||||||
{
|
{
|
||||||
JSObject &argsobj = fp->argsObj();
|
ArgumentsObject &argsobj = fp->argsObj();
|
||||||
if (argsobj.isNormalArguments()) {
|
if (argsobj.isNormalArguments()) {
|
||||||
JS_ASSERT(argsobj.getPrivate() == fp);
|
JS_ASSERT(argsobj.getPrivate() == fp);
|
||||||
fp->forEachCanonicalActualArg(PutArg(argsobj.getArgsData()->slots));
|
fp->forEachCanonicalActualArg(PutArg(argsobj.data()->slots));
|
||||||
argsobj.setPrivate(NULL);
|
argsobj.setPrivate(NULL);
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(!argsobj.getPrivate());
|
JS_ASSERT(!argsobj.getPrivate());
|
||||||
@ -293,7 +300,7 @@ js_PutArgsObject(StackFrame *fp)
|
|||||||
JSObject * JS_FASTCALL
|
JSObject * JS_FASTCALL
|
||||||
js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
|
js_NewArgumentsOnTrace(JSContext *cx, JSObject *parent, uint32 argc, JSObject *callee)
|
||||||
{
|
{
|
||||||
JSObject *argsobj = NewArguments(cx, parent, argc, *callee);
|
ArgumentsObject *argsobj = ArgumentsObject::create(cx, parent, argc, *callee);
|
||||||
if (!argsobj)
|
if (!argsobj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -314,9 +321,10 @@ JS_DEFINE_CALLINFO_4(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, OBJECT, UI
|
|||||||
|
|
||||||
/* FIXME change the return type to void. */
|
/* FIXME change the return type to void. */
|
||||||
JSBool JS_FASTCALL
|
JSBool JS_FASTCALL
|
||||||
js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args)
|
js_PutArgumentsOnTrace(JSContext *cx, JSObject *obj, Value *argv)
|
||||||
{
|
{
|
||||||
JS_ASSERT(argsobj->isNormalArguments());
|
NormalArgumentsObject *argsobj = obj->asNormalArguments();
|
||||||
|
|
||||||
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
|
JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -324,9 +332,9 @@ js_PutArgumentsOnTrace(JSContext *cx, JSObject *argsobj, Value *args)
|
|||||||
* the arguments, regardless of whether #actuals > #formals so there is no
|
* the arguments, regardless of whether #actuals > #formals so there is no
|
||||||
* need to worry about actual vs. formal arguments.
|
* need to worry about actual vs. formal arguments.
|
||||||
*/
|
*/
|
||||||
Value *srcend = args + argsobj->getArgsInitialLength();
|
Value *srcend = argv + argsobj->initialLength();
|
||||||
Value *dst = argsobj->getArgsData()->slots;
|
Value *dst = argsobj->data()->slots;
|
||||||
for (Value *src = args; src != srcend; ++src, ++dst) {
|
for (Value *src = argv; src < srcend; ++src, ++dst) {
|
||||||
if (!dst->isMagic(JS_ARGS_HOLE))
|
if (!dst->isMagic(JS_ARGS_HOLE))
|
||||||
*dst = *src;
|
*dst = *src;
|
||||||
}
|
}
|
||||||
@ -342,16 +350,15 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALU
|
|||||||
static JSBool
|
static JSBool
|
||||||
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
args_delProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isArguments());
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uintN arg = uintN(JSID_TO_INT(id));
|
uintN arg = uintN(JSID_TO_INT(id));
|
||||||
if (arg < obj->getArgsInitialLength())
|
if (arg < argsobj->initialLength())
|
||||||
obj->setArgsElement(arg, MagicValue(JS_ARGS_HOLE));
|
argsobj->setElement(arg, MagicValue(JS_ARGS_HOLE));
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
||||||
obj->setArgsLengthOverridden();
|
argsobj->markLengthOverridden();
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom)) {
|
||||||
obj->setArgsCallee(MagicValue(JS_ARGS_HOLE));
|
argsobj->asNormalArguments()->clearCallee();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -518,25 +525,26 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
if (!obj->isNormalArguments())
|
if (!obj->isNormalArguments())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
NormalArgumentsObject *argsobj = obj->asNormalArguments();
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
/*
|
/*
|
||||||
* arg can exceed the number of arguments if a script changed the
|
* arg can exceed the number of arguments if a script changed the
|
||||||
* prototype to point to another Arguments object with a bigger argc.
|
* prototype to point to another Arguments object with a bigger argc.
|
||||||
*/
|
*/
|
||||||
uintN arg = uintN(JSID_TO_INT(id));
|
uintN arg = uintN(JSID_TO_INT(id));
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
JS_ASSERT(!obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE));
|
JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
|
||||||
if (StackFrame *fp = (StackFrame *) obj->getPrivate())
|
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
|
||||||
*vp = fp->canonicalActualArg(arg);
|
*vp = fp->canonicalActualArg(arg);
|
||||||
else
|
else
|
||||||
*vp = obj->getArgsElement(arg);
|
*vp = argsobj->element(arg);
|
||||||
}
|
}
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
||||||
if (!obj->isArgsLengthOverridden())
|
if (!argsobj->hasOverriddenLength())
|
||||||
vp->setInt32(obj->getArgsInitialLength());
|
vp->setInt32(argsobj->initialLength());
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
|
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom));
|
||||||
const Value &v = obj->getArgsCallee();
|
const Value &v = argsobj->callee();
|
||||||
if (!v.isMagic(JS_ARGS_HOLE)) {
|
if (!v.isMagic(JS_ARGS_HOLE)) {
|
||||||
/*
|
/*
|
||||||
* If this function or one in it needs upvars that reach above it
|
* If this function or one in it needs upvars that reach above it
|
||||||
@ -572,11 +580,12 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
|||||||
if (!obj->isNormalArguments())
|
if (!obj->isNormalArguments())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
NormalArgumentsObject *argsobj = obj->asNormalArguments();
|
||||||
|
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uintN arg = uintN(JSID_TO_INT(id));
|
uintN arg = uintN(JSID_TO_INT(id));
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
StackFrame *fp = (StackFrame *) obj->getPrivate();
|
if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
|
||||||
if (fp) {
|
|
||||||
JSScript *script = fp->functionScript();
|
JSScript *script = fp->functionScript();
|
||||||
if (script->usesArguments)
|
if (script->usesArguments)
|
||||||
fp->canonicalActualArg(arg) = *vp;
|
fp->canonicalActualArg(arg) = *vp;
|
||||||
@ -597,54 +606,54 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
|||||||
* that has a setter for this id.
|
* that has a setter for this id.
|
||||||
*/
|
*/
|
||||||
AutoValueRooter tvr(cx);
|
AutoValueRooter tvr(cx);
|
||||||
return js_DeleteProperty(cx, obj, id, tvr.addr(), false) &&
|
return js_DeleteProperty(cx, argsobj, id, tvr.addr(), false) &&
|
||||||
js_DefineProperty(cx, obj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
|
js_DefineProperty(cx, argsobj, id, vp, NULL, NULL, JSPROP_ENUMERATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
args_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||||
JSObject **objp)
|
JSObject **objp)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isNormalArguments());
|
|
||||||
|
|
||||||
*objp = NULL;
|
*objp = NULL;
|
||||||
|
|
||||||
|
NormalArgumentsObject *argsobj = obj->asNormalArguments();
|
||||||
|
|
||||||
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
|
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uint32 arg = uint32(JSID_TO_INT(id));
|
uint32 arg = uint32(JSID_TO_INT(id));
|
||||||
if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
|
if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
attrs |= JSPROP_ENUMERATE;
|
attrs |= JSPROP_ENUMERATE;
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
||||||
if (obj->isArgsLengthOverridden())
|
if (argsobj->hasOverriddenLength())
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
|
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (obj->getArgsCallee().isMagic(JS_ARGS_HOLE))
|
if (argsobj->callee().isMagic(JS_ARGS_HOLE))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value undef = UndefinedValue();
|
Value undef = UndefinedValue();
|
||||||
if (!js_DefineProperty(cx, obj, id, &undef, ArgGetter, ArgSetter, attrs))
|
if (!js_DefineProperty(cx, argsobj, id, &undef, ArgGetter, ArgSetter, attrs))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
*objp = obj;
|
*objp = argsobj;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
args_enumerate(JSContext *cx, JSObject *obj)
|
args_enumerate(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isNormalArguments());
|
NormalArgumentsObject *argsobj = obj->asNormalArguments();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trigger reflection in args_resolve using a series of js_LookupProperty
|
* Trigger reflection in args_resolve using a series of js_LookupProperty
|
||||||
* calls.
|
* calls.
|
||||||
*/
|
*/
|
||||||
int argc = int(obj->getArgsInitialLength());
|
int argc = int(argsobj->initialLength());
|
||||||
for (int i = -2; i != argc; i++) {
|
for (int i = -2; i != argc; i++) {
|
||||||
jsid id = (i == -2)
|
jsid id = (i == -2)
|
||||||
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
|
? ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)
|
||||||
@ -654,7 +663,7 @@ args_enumerate(JSContext *cx, JSObject *obj)
|
|||||||
|
|
||||||
JSObject *pobj;
|
JSObject *pobj;
|
||||||
JSProperty *prop;
|
JSProperty *prop;
|
||||||
if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
|
if (!js_LookupProperty(cx, argsobj, id, &pobj, &prop))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -668,21 +677,23 @@ StrictArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||||||
if (!obj->isStrictArguments())
|
if (!obj->isStrictArguments())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
StrictArgumentsObject *argsobj = obj->asStrictArguments();
|
||||||
|
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
/*
|
/*
|
||||||
* arg can exceed the number of arguments if a script changed the
|
* arg can exceed the number of arguments if a script changed the
|
||||||
* prototype to point to another Arguments object with a bigger argc.
|
* prototype to point to another Arguments object with a bigger argc.
|
||||||
*/
|
*/
|
||||||
uintN arg = uintN(JSID_TO_INT(id));
|
uintN arg = uintN(JSID_TO_INT(id));
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
const Value &v = obj->getArgsElement(arg);
|
const Value &v = argsobj->element(arg);
|
||||||
if (!v.isMagic(JS_ARGS_HOLE))
|
if (!v.isMagic(JS_ARGS_HOLE))
|
||||||
*vp = v;
|
*vp = v;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
|
JS_ASSERT(JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom));
|
||||||
if (!obj->isArgsLengthOverridden())
|
if (!argsobj->hasOverriddenLength())
|
||||||
vp->setInt32(obj->getArgsInitialLength());
|
vp->setInt32(argsobj->initialLength());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -694,10 +705,12 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
|||||||
if (!obj->isStrictArguments())
|
if (!obj->isStrictArguments())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
StrictArgumentsObject *argsobj = obj->asStrictArguments();
|
||||||
|
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uintN arg = uintN(JSID_TO_INT(id));
|
uintN arg = uintN(JSID_TO_INT(id));
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
obj->setArgsElement(arg, *vp);
|
argsobj->setElement(arg, *vp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -711,29 +724,29 @@ StrictArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
|
|||||||
* collect its value.
|
* collect its value.
|
||||||
*/
|
*/
|
||||||
AutoValueRooter tvr(cx);
|
AutoValueRooter tvr(cx);
|
||||||
return js_DeleteProperty(cx, obj, id, tvr.addr(), strict) &&
|
return js_DeleteProperty(cx, argsobj, id, tvr.addr(), strict) &&
|
||||||
js_SetProperty(cx, obj, id, vp, strict);
|
js_SetProperty(cx, argsobj, id, vp, strict);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
|
strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isStrictArguments());
|
|
||||||
|
|
||||||
*objp = NULL;
|
*objp = NULL;
|
||||||
|
|
||||||
|
StrictArgumentsObject *argsobj = obj->asStrictArguments();
|
||||||
|
|
||||||
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
|
uintN attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
|
||||||
PropertyOp getter = StrictArgGetter;
|
PropertyOp getter = StrictArgGetter;
|
||||||
StrictPropertyOp setter = StrictArgSetter;
|
StrictPropertyOp setter = StrictArgSetter;
|
||||||
|
|
||||||
if (JSID_IS_INT(id)) {
|
if (JSID_IS_INT(id)) {
|
||||||
uint32 arg = uint32(JSID_TO_INT(id));
|
uint32 arg = uint32(JSID_TO_INT(id));
|
||||||
if (arg >= obj->getArgsInitialLength() || obj->getArgsElement(arg).isMagic(JS_ARGS_HOLE))
|
if (arg >= argsobj->initialLength() || argsobj->element(arg).isMagic(JS_ARGS_HOLE))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
attrs |= JSPROP_ENUMERATE;
|
attrs |= JSPROP_ENUMERATE;
|
||||||
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
} else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
|
||||||
if (obj->isArgsLengthOverridden())
|
if (argsobj->hasOverriddenLength())
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
|
if (!JSID_IS_ATOM(id, cx->runtime->atomState.calleeAtom) &&
|
||||||
@ -742,22 +755,22 @@ strictargs_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
|
attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
|
||||||
getter = CastAsPropertyOp(obj->getThrowTypeError());
|
getter = CastAsPropertyOp(argsobj->getThrowTypeError());
|
||||||
setter = CastAsStrictPropertyOp(obj->getThrowTypeError());
|
setter = CastAsStrictPropertyOp(argsobj->getThrowTypeError());
|
||||||
}
|
}
|
||||||
|
|
||||||
Value undef = UndefinedValue();
|
Value undef = UndefinedValue();
|
||||||
if (!js_DefineProperty(cx, obj, id, &undef, getter, setter, attrs))
|
if (!js_DefineProperty(cx, argsobj, id, &undef, getter, setter, attrs))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
*objp = obj;
|
*objp = argsobj;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSBool
|
static JSBool
|
||||||
strictargs_enumerate(JSContext *cx, JSObject *obj)
|
strictargs_enumerate(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isStrictArguments());
|
StrictArgumentsObject *argsobj = obj->asStrictArguments();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Trigger reflection in strictargs_resolve using a series of
|
* Trigger reflection in strictargs_resolve using a series of
|
||||||
@ -767,19 +780,19 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
|
|||||||
JSProperty *prop;
|
JSProperty *prop;
|
||||||
|
|
||||||
// length
|
// length
|
||||||
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
|
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), &pobj, &prop))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// callee
|
// callee
|
||||||
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
|
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), &pobj, &prop))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// caller
|
// caller
|
||||||
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
|
if (!js_LookupProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.callerAtom), &pobj, &prop))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (uint32 i = 0, argc = obj->getArgsInitialLength(); i < argc; i++) {
|
for (uint32 i = 0, argc = argsobj->initialLength(); i < argc; i++) {
|
||||||
if (!js_LookupProperty(cx, obj, INT_TO_JSID(i), &pobj, &prop))
|
if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,7 +802,7 @@ strictargs_enumerate(JSContext *cx, JSObject *obj)
|
|||||||
static void
|
static void
|
||||||
args_finalize(JSContext *cx, JSObject *obj)
|
args_finalize(JSContext *cx, JSObject *obj)
|
||||||
{
|
{
|
||||||
cx->free_((void *) obj->getArgsData());
|
cx->free_(reinterpret_cast<void *>(obj->asArguments()->data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -815,43 +828,39 @@ MaybeMarkGenerator(JSTracer *trc, JSObject *obj)
|
|||||||
static void
|
static void
|
||||||
args_trace(JSTracer *trc, JSObject *obj)
|
args_trace(JSTracer *trc, JSObject *obj)
|
||||||
{
|
{
|
||||||
JS_ASSERT(obj->isArguments());
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
if (obj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
if (argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
|
||||||
JS_ASSERT(!obj->isStrictArguments());
|
JS_ASSERT(!argsobj->isStrictArguments());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArgumentsData *data = obj->getArgsData();
|
ArgumentsData *data = argsobj->data();
|
||||||
if (data->callee.isObject())
|
if (data->callee.isObject())
|
||||||
MarkObject(trc, data->callee.toObject(), js_callee_str);
|
MarkObject(trc, data->callee.toObject(), js_callee_str);
|
||||||
MarkValueRange(trc, obj->getArgsInitialLength(), data->slots, js_arguments_str);
|
MarkValueRange(trc, argsobj->initialLength(), data->slots, js_arguments_str);
|
||||||
|
|
||||||
MaybeMarkGenerator(trc, obj);
|
MaybeMarkGenerator(trc, argsobj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Arguments classes aren't initialized via js_InitClass, because arguments
|
* The classes below collaborate to lazily reflect and synchronize actual
|
||||||
* objects have the initial value of Object.prototype as their [[Prototype]].
|
* argument values, argument count, and callee function object stored in a
|
||||||
* However, Object.prototype.toString.call(arguments) === "[object Arguments]"
|
* StackFrame with their corresponding property values in the frame's
|
||||||
* per ES5 (although not ES3), so the class name is "Arguments" rather than
|
|
||||||
* "Object".
|
|
||||||
*
|
|
||||||
* The JSClass functions below collaborate to lazily reflect and synchronize
|
|
||||||
* actual argument values, argument count, and callee function object stored
|
|
||||||
* in a StackFrame with their corresponding property values in the frame's
|
|
||||||
* arguments object.
|
* arguments object.
|
||||||
*/
|
*/
|
||||||
Class js_ArgumentsClass = {
|
Class NormalArgumentsObject::jsClass = {
|
||||||
"Arguments",
|
"Arguments",
|
||||||
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
|
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
|
||||||
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
|
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||||
PropertyStub, /* addProperty */
|
PropertyStub, /* addProperty */
|
||||||
args_delProperty,
|
args_delProperty,
|
||||||
PropertyStub, /* getProperty */
|
PropertyStub, /* getProperty */
|
||||||
StrictPropertyStub, /* setProperty */
|
StrictPropertyStub, /* setProperty */
|
||||||
args_enumerate,
|
args_enumerate,
|
||||||
(JSResolveOp) args_resolve,
|
reinterpret_cast<JSResolveOp>(args_resolve),
|
||||||
ConvertStub,
|
ConvertStub,
|
||||||
args_finalize, /* finalize */
|
args_finalize, /* finalize */
|
||||||
NULL, /* reserved0 */
|
NULL, /* reserved0 */
|
||||||
@ -863,17 +872,15 @@ Class js_ArgumentsClass = {
|
|||||||
args_trace
|
args_trace
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Strict mode arguments is significantly less magical than non-strict mode
|
* Strict mode arguments is significantly less magical than non-strict mode
|
||||||
* arguments, so it is represented by a different class while sharing some
|
* arguments, so it is represented by a different class while sharing some
|
||||||
* functionality.
|
* functionality.
|
||||||
*/
|
*/
|
||||||
Class StrictArgumentsClass = {
|
Class StrictArgumentsObject::jsClass = {
|
||||||
"Arguments",
|
"Arguments",
|
||||||
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
|
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
|
||||||
JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_CLASS_RESERVED_SLOTS) |
|
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
|
||||||
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
|
||||||
PropertyStub, /* addProperty */
|
PropertyStub, /* addProperty */
|
||||||
args_delProperty,
|
args_delProperty,
|
||||||
|
@ -247,53 +247,6 @@ struct JSFunction : public JSObject_Slots2
|
|||||||
JS_FN(name, fastcall, nargs, flags)
|
JS_FN(name, fastcall, nargs, flags)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* NB: the Arguments classes are uninitialized internal classes that masquerade
|
|
||||||
* (according to Object.prototype.toString.call(arguments)) as "Arguments",
|
|
||||||
* while having Object.getPrototypeOf(arguments) === Object.prototype.
|
|
||||||
*
|
|
||||||
* WARNING (to alert embedders reading this private .h file): arguments objects
|
|
||||||
* are *not* thread-safe and should not be used concurrently -- they should be
|
|
||||||
* used by only one thread at a time, preferably by only one thread over their
|
|
||||||
* lifetime (a JS worker that migrates from one OS thread to another but shares
|
|
||||||
* nothing is ok).
|
|
||||||
*
|
|
||||||
* Yes, this is an incompatible change, which prefigures the impending move to
|
|
||||||
* single-threaded objects and GC heaps.
|
|
||||||
*/
|
|
||||||
extern js::Class js_ArgumentsClass;
|
|
||||||
|
|
||||||
namespace js {
|
|
||||||
|
|
||||||
extern Class StrictArgumentsClass;
|
|
||||||
|
|
||||||
struct ArgumentsData {
|
|
||||||
js::Value callee;
|
|
||||||
js::Value slots[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
JSObject::isNormalArguments() const
|
|
||||||
{
|
|
||||||
return getClass() == &js_ArgumentsClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
JSObject::isStrictArguments() const
|
|
||||||
{
|
|
||||||
return getClass() == &js::StrictArgumentsClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
JSObject::isArguments() const
|
|
||||||
{
|
|
||||||
return isNormalArguments() || isStrictArguments();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
|
|
||||||
|
|
||||||
extern JS_PUBLIC_DATA(js::Class) js_CallClass;
|
extern JS_PUBLIC_DATA(js::Class) js_CallClass;
|
||||||
extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
|
extern JS_PUBLIC_DATA(js::Class) js_FunctionClass;
|
||||||
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
|
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
|
||||||
|
@ -4096,20 +4096,26 @@ BEGIN_CASE(JSOP_LENGTH)
|
|||||||
vp = ®s.sp[-1];
|
vp = ®s.sp[-1];
|
||||||
if (vp->isString()) {
|
if (vp->isString()) {
|
||||||
vp->setInt32(vp->toString()->length());
|
vp->setInt32(vp->toString()->length());
|
||||||
} else if (vp->isObject()) {
|
|
||||||
JSObject *obj = &vp->toObject();
|
|
||||||
if (obj->isArray()) {
|
|
||||||
jsuint length = obj->getArrayLength();
|
|
||||||
regs.sp[-1].setNumber(length);
|
|
||||||
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
|
|
||||||
uint32 length = obj->getArgsInitialLength();
|
|
||||||
JS_ASSERT(length < INT32_MAX);
|
|
||||||
regs.sp[-1].setInt32(int32_t(length));
|
|
||||||
} else {
|
|
||||||
i = -2;
|
|
||||||
goto do_getprop_with_lval;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
if (vp->isObject()) {
|
||||||
|
JSObject *obj = &vp->toObject();
|
||||||
|
if (obj->isArray()) {
|
||||||
|
jsuint length = obj->getArrayLength();
|
||||||
|
regs.sp[-1].setNumber(length);
|
||||||
|
DO_NEXT_OP(JSOP_LENGTH_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj->isArguments()) {
|
||||||
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
if (!argsobj->hasOverriddenLength()) {
|
||||||
|
uint32 length = argsobj->initialLength();
|
||||||
|
JS_ASSERT(length < INT32_MAX);
|
||||||
|
regs.sp[-1].setInt32(int32_t(length));
|
||||||
|
DO_NEXT_OP(JSOP_LENGTH_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i = -2;
|
i = -2;
|
||||||
goto do_getprop_with_lval;
|
goto do_getprop_with_lval;
|
||||||
}
|
}
|
||||||
@ -4405,11 +4411,12 @@ BEGIN_CASE(JSOP_GETELEM)
|
|||||||
}
|
}
|
||||||
} else if (obj->isArguments()) {
|
} else if (obj->isArguments()) {
|
||||||
uint32 arg = uint32(i);
|
uint32 arg = uint32(i);
|
||||||
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
copyFrom = obj->addressOfArgsElement(arg);
|
copyFrom = argsobj->addressOfElement(arg);
|
||||||
if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
|
if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
|
||||||
if (StackFrame *afp = (StackFrame *) obj->getPrivate())
|
if (StackFrame *afp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
|
||||||
copyFrom = &afp->canonicalActualArg(arg);
|
copyFrom = &afp->canonicalActualArg(arg);
|
||||||
goto end_getelem;
|
goto end_getelem;
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,11 @@ namespace js {
|
|||||||
|
|
||||||
struct NativeIterator;
|
struct NativeIterator;
|
||||||
class RegExp;
|
class RegExp;
|
||||||
|
|
||||||
class GlobalObject;
|
class GlobalObject;
|
||||||
|
class ArgumentsObject;
|
||||||
|
class NormalArgumentsObject;
|
||||||
|
class StrictArgumentsObject;
|
||||||
class StringObject;
|
class StringObject;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -794,93 +798,10 @@ struct JSObject : js::gc::Cell {
|
|||||||
|
|
||||||
JSBool makeDenseArraySlow(JSContext *cx);
|
JSBool makeDenseArraySlow(JSContext *cx);
|
||||||
|
|
||||||
/*
|
|
||||||
* Arguments-specific getters and setters.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private:
|
|
||||||
/*
|
|
||||||
* We represent arguments objects using js_ArgumentsClass and
|
|
||||||
* js::StrictArgumentsClass. The two are structured similarly, and methods
|
|
||||||
* valid on arguments objects of one class are also generally valid on
|
|
||||||
* arguments objects of the other.
|
|
||||||
*
|
|
||||||
* Arguments objects of either class store arguments length in a slot:
|
|
||||||
*
|
|
||||||
* JSSLOT_ARGS_LENGTH - the number of actual arguments and a flag
|
|
||||||
* indicating whether arguments.length was
|
|
||||||
* overwritten. This slot is not used to represent
|
|
||||||
* arguments.length after that property has been
|
|
||||||
* assigned, even if the new value is integral: it's
|
|
||||||
* always the original length.
|
|
||||||
*
|
|
||||||
* Both arguments classes use a slot for storing arguments data:
|
|
||||||
*
|
|
||||||
* JSSLOT_ARGS_DATA - pointer to an ArgumentsData structure
|
|
||||||
*
|
|
||||||
* ArgumentsData for normal arguments stores the value of arguments.callee,
|
|
||||||
* as long as that property has not been overwritten. If arguments.callee
|
|
||||||
* is overwritten, the corresponding value in ArgumentsData is set to
|
|
||||||
* MagicValue(JS_ARGS_HOLE). Strict arguments do not store this value
|
|
||||||
* because arguments.callee is a poison pill for strict mode arguments.
|
|
||||||
*
|
|
||||||
* The ArgumentsData structure also stores argument values. For normal
|
|
||||||
* arguments this occurs after the corresponding function has returned, and
|
|
||||||
* for strict arguments this occurs when the arguments object is created,
|
|
||||||
* or sometimes shortly after (but not observably so). arguments[i] is
|
|
||||||
* stored in ArgumentsData.slots[i], accessible via getArgsElement() and
|
|
||||||
* setArgsElement(). Deletion of arguments[i] overwrites that slot with
|
|
||||||
* MagicValue(JS_ARGS_HOLE); subsequent redefinition of arguments[i] will
|
|
||||||
* use a normal property to store the value, ignoring the slot.
|
|
||||||
*
|
|
||||||
* Non-strict arguments have a private:
|
|
||||||
*
|
|
||||||
* private - the function's stack frame until the function
|
|
||||||
* returns, when it is replaced with null; also,
|
|
||||||
* JS_ARGUMENTS_OBJECT_ON_TRACE while on trace, if
|
|
||||||
* arguments was created on trace
|
|
||||||
*
|
|
||||||
* Technically strict arguments have a private, but it's always null.
|
|
||||||
* Conceptually it would be better to remove this oddity, but preserving it
|
|
||||||
* allows us to work with arguments objects of either kind more abstractly,
|
|
||||||
* so we keep it for now.
|
|
||||||
*/
|
|
||||||
static const uint32 JSSLOT_ARGS_DATA = 1;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* Number of extra fixed arguments object slots besides JSSLOT_PRIVATE. */
|
inline js::ArgumentsObject *asArguments();
|
||||||
static const uint32 JSSLOT_ARGS_LENGTH = 0;
|
inline js::NormalArgumentsObject *asNormalArguments();
|
||||||
static const uint32 ARGS_CLASS_RESERVED_SLOTS = 2;
|
inline js::StrictArgumentsObject *asStrictArguments();
|
||||||
static const uint32 ARGS_FIRST_FREE_SLOT = ARGS_CLASS_RESERVED_SLOTS + 1;
|
|
||||||
|
|
||||||
/* Lower-order bit stolen from the length slot. */
|
|
||||||
static const uint32 ARGS_LENGTH_OVERRIDDEN_BIT = 0x1;
|
|
||||||
static const uint32 ARGS_PACKED_BITS_COUNT = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the initial length of the arguments, and mark it as not overridden.
|
|
||||||
*/
|
|
||||||
inline void setArgsLength(uint32 argc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return the initial length of the arguments. This may differ from the
|
|
||||||
* current value of arguments.length!
|
|
||||||
*/
|
|
||||||
inline uint32 getArgsInitialLength() const;
|
|
||||||
|
|
||||||
inline void setArgsLengthOverridden();
|
|
||||||
inline bool isArgsLengthOverridden() const;
|
|
||||||
|
|
||||||
inline js::ArgumentsData *getArgsData() const;
|
|
||||||
inline void setArgsData(js::ArgumentsData *data);
|
|
||||||
|
|
||||||
inline const js::Value &getArgsCallee() const;
|
|
||||||
inline void setArgsCallee(const js::Value &callee);
|
|
||||||
|
|
||||||
inline const js::Value &getArgsElement(uint32 i) const;
|
|
||||||
inline js::Value *getArgsElements() const;
|
|
||||||
inline js::Value *addressOfArgsElement(uint32 i);
|
|
||||||
inline void setArgsElement(uint32 i, const js::Value &v);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
|
@ -409,97 +409,6 @@ JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
|
|||||||
shrinkSlots(cx, cap);
|
shrinkSlots(cx, cap);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
|
||||||
JSObject::setArgsLength(uint32 argc)
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
|
||||||
JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
|
|
||||||
getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
|
|
||||||
JS_ASSERT(!isArgsLengthOverridden());
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32
|
|
||||||
JSObject::getArgsInitialLength() const
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
|
|
||||||
JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
|
|
||||||
return argc;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
JSObject::setArgsLengthOverridden()
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
JSObject::isArgsLengthOverridden() const
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH);
|
|
||||||
return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline js::ArgumentsData *
|
|
||||||
JSObject::getArgsData() const
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
JSObject::setArgsData(js::ArgumentsData *data)
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const js::Value &
|
|
||||||
JSObject::getArgsCallee() const
|
|
||||||
{
|
|
||||||
return getArgsData()->callee;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
JSObject::setArgsCallee(const js::Value &callee)
|
|
||||||
{
|
|
||||||
getArgsData()->callee = callee;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const js::Value &
|
|
||||||
JSObject::getArgsElement(uint32 i) const
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
JS_ASSERT(i < getArgsInitialLength());
|
|
||||||
return getArgsData()->slots[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline js::Value *
|
|
||||||
JSObject::getArgsElements() const
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
return getArgsData()->slots;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline js::Value *
|
|
||||||
JSObject::addressOfArgsElement(uint32 i)
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
JS_ASSERT(i < getArgsInitialLength());
|
|
||||||
return &getArgsData()->slots[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
JSObject::setArgsElement(uint32 i, const js::Value &v)
|
|
||||||
{
|
|
||||||
JS_ASSERT(isArguments());
|
|
||||||
JS_ASSERT(i < getArgsInitialLength());
|
|
||||||
getArgsData()->slots[i] = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
JSObject::callIsForEval() const
|
JSObject::callIsForEval() const
|
||||||
{
|
{
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
|
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "jscompartment.h"
|
#include "jscompartment.h"
|
||||||
#include "jshashtable.h"
|
#include "jshashtable.h"
|
||||||
@ -58,6 +59,8 @@
|
|||||||
#include "jspubtd.h"
|
#include "jspubtd.h"
|
||||||
#include "jspropertytree.h"
|
#include "jspropertytree.h"
|
||||||
|
|
||||||
|
#include "vm/ArgumentsObject.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable:4800)
|
#pragma warning(disable:4800)
|
||||||
@ -635,7 +638,7 @@ struct EmptyShape : public js::Shape
|
|||||||
}
|
}
|
||||||
|
|
||||||
static EmptyShape *getEmptyArgumentsShape(JSContext *cx) {
|
static EmptyShape *getEmptyArgumentsShape(JSContext *cx) {
|
||||||
return ensure(cx, &js_ArgumentsClass, &cx->compartment->emptyArgumentsShape);
|
return ensure(cx, &NormalArgumentsObject::jsClass, &cx->compartment->emptyArgumentsShape);
|
||||||
}
|
}
|
||||||
|
|
||||||
static EmptyShape *getEmptyBlockShape(JSContext *cx) {
|
static EmptyShape *getEmptyBlockShape(JSContext *cx) {
|
||||||
|
@ -3158,8 +3158,7 @@ public:
|
|||||||
if (p == fp->addressOfArgs()) {
|
if (p == fp->addressOfArgs()) {
|
||||||
if (frameobj) {
|
if (frameobj) {
|
||||||
JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
|
JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
|
||||||
fp->setArgsObj(*frameobj);
|
fp->setArgsObj(*frameobj->asArguments());
|
||||||
JS_ASSERT(frameobj->isArguments());
|
|
||||||
if (frameobj->isNormalArguments())
|
if (frameobj->isNormalArguments())
|
||||||
frameobj->setPrivate(fp);
|
frameobj->setPrivate(fp);
|
||||||
else
|
else
|
||||||
@ -12881,7 +12880,7 @@ JS_REQUIRES_STACK void
|
|||||||
TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins)
|
TraceRecorder::guardNotHole(LIns *argsobj_ins, LIns *idx_ins)
|
||||||
{
|
{
|
||||||
// vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx]
|
// vp = &argsobj->slots[JSSLOT_ARGS_DATA].slots[idx]
|
||||||
LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, JSObject::JSSLOT_ARGS_DATA);
|
LIns* argsData_ins = w.getObjPrivatizedSlot(argsobj_ins, ArgumentsObject::DATA_SLOT);
|
||||||
LIns* slotOffset_ins = w.addp(w.nameImmw(offsetof(ArgumentsData, slots)),
|
LIns* slotOffset_ins = w.addp(w.nameImmw(offsetof(ArgumentsData, slots)),
|
||||||
w.ui2p(w.muliN(idx_ins, sizeof(Value))));
|
w.ui2p(w.muliN(idx_ins, sizeof(Value))));
|
||||||
LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins);
|
LIns* vp_ins = w.addp(argsData_ins, slotOffset_ins);
|
||||||
@ -12934,17 +12933,18 @@ TraceRecorder::record_JSOP_GETELEM()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (obj->isArguments()) {
|
if (obj->isArguments()) {
|
||||||
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
|
||||||
// Don't even try to record if out of range or reading a deleted arg
|
// Don't even try to record if out of range or reading a deleted arg
|
||||||
int32 int_idx = idx.toInt32();
|
int32 int_idx = idx.toInt32();
|
||||||
if (int_idx < 0 || int_idx >= (int32)obj->getArgsInitialLength())
|
if (int_idx < 0 || int_idx >= (int32)argsobj->initialLength())
|
||||||
RETURN_STOP_A("cannot trace arguments with out of range index");
|
RETURN_STOP_A("cannot trace arguments with out of range index");
|
||||||
if (obj->getArgsElement(int_idx).isMagic(JS_ARGS_HOLE))
|
if (argsobj->element(int_idx).isMagic(JS_ARGS_HOLE))
|
||||||
RETURN_STOP_A("reading deleted args element");
|
RETURN_STOP_A("reading deleted args element");
|
||||||
|
|
||||||
// Only trace reading arguments out of active, tracked frame
|
// Only trace reading arguments out of active, tracked frame
|
||||||
unsigned depth;
|
unsigned depth;
|
||||||
StackFrame *afp = guardArguments(obj, obj_ins, &depth);
|
if (StackFrame *afp = guardArguments(obj, obj_ins, &depth)) {
|
||||||
if (afp) {
|
|
||||||
Value* vp = &afp->canonicalActualArg(int_idx);
|
Value* vp = &afp->canonicalActualArg(int_idx);
|
||||||
if (idx_ins->isImmD()) {
|
if (idx_ins->isImmD()) {
|
||||||
JS_ASSERT(int_idx == (int32)idx_ins->immD());
|
JS_ASSERT(int_idx == (int32)idx_ins->immD());
|
||||||
@ -13863,7 +13863,7 @@ TraceRecorder::record_JSOP_FUNAPPLY()
|
|||||||
StackFrame *afp = guardArguments(aobj, aobj_ins, &depth);
|
StackFrame *afp = guardArguments(aobj, aobj_ins, &depth);
|
||||||
if (!afp)
|
if (!afp)
|
||||||
RETURN_STOP_A("can't reach arguments object's frame");
|
RETURN_STOP_A("can't reach arguments object's frame");
|
||||||
if (aobj->isArgsLengthOverridden())
|
if (aobj->asArguments()->hasOverriddenLength())
|
||||||
RETURN_STOP_A("can't trace arguments with overridden length");
|
RETURN_STOP_A("can't trace arguments with overridden length");
|
||||||
guardArgsLengthNotAssigned(aobj_ins);
|
guardArgsLengthNotAssigned(aobj_ins);
|
||||||
length = afp->numActualArgs();
|
length = afp->numActualArgs();
|
||||||
@ -15671,13 +15671,27 @@ TraceRecorder::record_JSOP_ARGSUB()
|
|||||||
RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
|
RETURN_STOP_A("can't trace JSOP_ARGSUB hard case");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace tjit {
|
||||||
|
|
||||||
|
nj::LIns *
|
||||||
|
Writer::getArgsLength(nj::LIns *args) const
|
||||||
|
{
|
||||||
|
uint32 slot = js::ArgumentsObject::INITIAL_LENGTH_SLOT;
|
||||||
|
nj::LIns *vaddr_ins = ldpObjSlots(args);
|
||||||
|
return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
|
||||||
|
ACCSET_SLOTS),
|
||||||
|
"argsLength");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tjit
|
||||||
|
|
||||||
JS_REQUIRES_STACK LIns*
|
JS_REQUIRES_STACK LIns*
|
||||||
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
|
TraceRecorder::guardArgsLengthNotAssigned(LIns* argsobj_ins)
|
||||||
{
|
{
|
||||||
// The following implements JSObject::isArgsLengthOverridden on trace.
|
// The following implements JSObject::isArgsLengthOverridden on trace.
|
||||||
// ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
|
// ARGS_LENGTH_OVERRIDDEN_BIT is set if length was overridden.
|
||||||
LIns *len_ins = w.getArgsLength(argsobj_ins);
|
LIns *len_ins = w.getArgsLength(argsobj_ins);
|
||||||
LIns *ovr_ins = w.andi(len_ins, w.nameImmi(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
|
LIns *ovr_ins = w.andi(len_ins, w.nameImmi(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
|
||||||
guard(true, w.eqi0(ovr_ins), MISMATCH_EXIT);
|
guard(true, w.eqi0(ovr_ins), MISMATCH_EXIT);
|
||||||
return len_ins;
|
return len_ins;
|
||||||
}
|
}
|
||||||
@ -15696,7 +15710,7 @@ TraceRecorder::record_JSOP_ARGCNT()
|
|||||||
// We also have to check that arguments.length has not been mutated
|
// We also have to check that arguments.length has not been mutated
|
||||||
// at record time, because if so we will generate incorrect constant
|
// at record time, because if so we will generate incorrect constant
|
||||||
// LIR, which will assert in tryToDemote().
|
// LIR, which will assert in tryToDemote().
|
||||||
if (fp->hasArgsObj() && fp->argsObj().isArgsLengthOverridden())
|
if (fp->hasArgsObj() && fp->argsObj().hasOverriddenLength())
|
||||||
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
|
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
|
||||||
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());
|
LIns *a_ins = getFrameObjPtr(fp->addressOfArgs());
|
||||||
if (callDepth == 0) {
|
if (callDepth == 0) {
|
||||||
@ -16359,13 +16373,13 @@ TraceRecorder::record_JSOP_LENGTH()
|
|||||||
|
|
||||||
// We must both check at record time and guard at run time that
|
// We must both check at record time and guard at run time that
|
||||||
// arguments.length has not been reassigned, redefined or deleted.
|
// arguments.length has not been reassigned, redefined or deleted.
|
||||||
if (obj->isArgsLengthOverridden())
|
if (obj->asArguments()->hasOverriddenLength())
|
||||||
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
|
RETURN_STOP_A("can't trace JSOP_ARGCNT if arguments.length has been modified");
|
||||||
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
|
LIns* slot_ins = guardArgsLengthNotAssigned(obj_ins);
|
||||||
|
|
||||||
// slot_ins is the value from the slot; right-shift to get the length
|
// slot_ins is the value from the slot; right-shift to get the length;
|
||||||
// (see JSObject::getArgsInitialLength in jsfun.cpp).
|
// see ArgumentsObject.h.
|
||||||
LIns* v_ins = w.i2d(w.rshiN(slot_ins, JSObject::ARGS_PACKED_BITS_COUNT));
|
LIns* v_ins = w.i2d(w.rshiN(slot_ins, ArgumentsObject::PACKED_BITS_COUNT));
|
||||||
set(&l, v_ins);
|
set(&l, v_ins);
|
||||||
return ARECORD_CONTINUE;
|
return ARECORD_CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -770,12 +770,12 @@ class GetPropCompiler : public PICStubCompiler
|
|||||||
Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
|
Jump notArgs = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
|
||||||
|
|
||||||
masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
|
masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
|
||||||
masm.load32(Address(pic.objReg, JSObject::JSSLOT_ARGS_LENGTH * sizeof(Value)),
|
masm.load32(Address(pic.objReg, ArgumentsObject::INITIAL_LENGTH_SLOT * sizeof(Value)),
|
||||||
pic.objReg);
|
pic.objReg);
|
||||||
masm.move(pic.objReg, pic.shapeReg);
|
masm.move(pic.objReg, pic.shapeReg);
|
||||||
Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
|
Jump overridden = masm.branchTest32(Assembler::NonZero, pic.shapeReg,
|
||||||
Imm32(JSObject::ARGS_LENGTH_OVERRIDDEN_BIT));
|
Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT));
|
||||||
masm.rshift32(Imm32(JSObject::ARGS_PACKED_BITS_COUNT), pic.objReg);
|
masm.rshift32(Imm32(ArgumentsObject::PACKED_BITS_COUNT), pic.objReg);
|
||||||
|
|
||||||
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
|
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
|
||||||
Jump done = masm.jump();
|
Jump done = masm.jump();
|
||||||
@ -1648,7 +1648,8 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
|||||||
return;
|
return;
|
||||||
} else if (!f.regs.sp[-1].isPrimitive()) {
|
} else if (!f.regs.sp[-1].isPrimitive()) {
|
||||||
JSObject *obj = &f.regs.sp[-1].toObject();
|
JSObject *obj = &f.regs.sp[-1].toObject();
|
||||||
if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden()) ||
|
if (obj->isArray() ||
|
||||||
|
(obj->isArguments() && !obj->asArguments()->hasOverriddenLength()) ||
|
||||||
obj->isString()) {
|
obj->isString()) {
|
||||||
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
|
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
|
||||||
if (obj->isArray()) {
|
if (obj->isArray()) {
|
||||||
@ -1660,7 +1661,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
|||||||
LookupStatus status = cc.generateArgsLengthStub();
|
LookupStatus status = cc.generateArgsLengthStub();
|
||||||
if (status == Lookup_Error)
|
if (status == Lookup_Error)
|
||||||
THROW();
|
THROW();
|
||||||
f.regs.sp[-1].setInt32(int32_t(obj->getArgsInitialLength()));
|
f.regs.sp[-1].setInt32(int32_t(obj->asArguments()->initialLength()));
|
||||||
} else if (obj->isString()) {
|
} else if (obj->isString()) {
|
||||||
LookupStatus status = cc.generateStringObjLengthStub();
|
LookupStatus status = cc.generateStringObjLengthStub();
|
||||||
if (status == Lookup_Error)
|
if (status == Lookup_Error)
|
||||||
|
@ -447,11 +447,12 @@ stubs::GetElem(VMFrame &f)
|
|||||||
}
|
}
|
||||||
} else if (obj->isArguments()) {
|
} else if (obj->isArguments()) {
|
||||||
uint32 arg = uint32(i);
|
uint32 arg = uint32(i);
|
||||||
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
|
|
||||||
if (arg < obj->getArgsInitialLength()) {
|
if (arg < argsobj->initialLength()) {
|
||||||
copyFrom = obj->addressOfArgsElement(arg);
|
copyFrom = argsobj->addressOfElement(arg);
|
||||||
if (!copyFrom->isMagic()) {
|
if (!copyFrom->isMagic()) {
|
||||||
if (StackFrame *afp = (StackFrame *) obj->getPrivate())
|
if (StackFrame *afp = (StackFrame *) argsobj->getPrivate())
|
||||||
copyFrom = &afp->canonicalActualArg(arg);
|
copyFrom = &afp->canonicalActualArg(arg);
|
||||||
goto end_getelem;
|
goto end_getelem;
|
||||||
}
|
}
|
||||||
@ -2011,17 +2012,24 @@ stubs::Length(VMFrame &f)
|
|||||||
if (vp->isString()) {
|
if (vp->isString()) {
|
||||||
vp->setInt32(vp->toString()->length());
|
vp->setInt32(vp->toString()->length());
|
||||||
return;
|
return;
|
||||||
} else if (vp->isObject()) {
|
}
|
||||||
|
|
||||||
|
if (vp->isObject()) {
|
||||||
JSObject *obj = &vp->toObject();
|
JSObject *obj = &vp->toObject();
|
||||||
if (obj->isArray()) {
|
if (obj->isArray()) {
|
||||||
jsuint length = obj->getArrayLength();
|
jsuint length = obj->getArrayLength();
|
||||||
regs.sp[-1].setNumber(length);
|
regs.sp[-1].setNumber(length);
|
||||||
return;
|
return;
|
||||||
} else if (obj->isArguments() && !obj->isArgsLengthOverridden()) {
|
}
|
||||||
uint32 length = obj->getArgsInitialLength();
|
|
||||||
JS_ASSERT(length < INT32_MAX);
|
if (obj->isArguments()) {
|
||||||
regs.sp[-1].setInt32(int32_t(length));
|
ArgumentsObject *argsobj = obj->asArguments();
|
||||||
return;
|
if (!argsobj->hasOverriddenLength()) {
|
||||||
|
uint32 length = argsobj->initialLength();
|
||||||
|
JS_ASSERT(length < INT32_MAX);
|
||||||
|
regs.sp[-1].setInt32(int32_t(length));
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,10 +39,13 @@
|
|||||||
|
|
||||||
#include "jsprf.h"
|
#include "jsprf.h"
|
||||||
#include "jstl.h"
|
#include "jstl.h"
|
||||||
|
|
||||||
#include "jscompartment.h"
|
#include "jscompartment.h"
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
#include "nanojit.h"
|
#include "nanojit.h"
|
||||||
|
|
||||||
|
#include "vm/ArgumentsObject.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace tjit {
|
namespace tjit {
|
||||||
|
|
||||||
@ -544,9 +547,9 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
|
|||||||
// base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
|
// base_oprnd1 = <const private ptr slots[JSSLOT_ARGS_DATA]>
|
||||||
// base = addp base_oprnd1, ...
|
// base = addp base_oprnd1, ...
|
||||||
// ins = {ld,st}X.argsdata base[...]
|
// ins = {ld,st}X.argsdata base[...]
|
||||||
ok = (isConstPrivatePtr(base, JSObject::JSSLOT_ARGS_DATA) ||
|
ok = (isConstPrivatePtr(base, ArgumentsObject::DATA_SLOT) ||
|
||||||
(base->isop(LIR_addp) &&
|
(base->isop(LIR_addp) &&
|
||||||
isConstPrivatePtr(base->oprnd1(), JSObject::JSSLOT_ARGS_DATA)));
|
isConstPrivatePtr(base->oprnd1(), ArgumentsObject::DATA_SLOT)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -565,7 +568,7 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace nanojit
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
#define tracejit_Writer_h___
|
#define tracejit_Writer_h___
|
||||||
|
|
||||||
#include "jsiter.h"
|
#include "jsiter.h"
|
||||||
#include "jsobj.h"
|
|
||||||
#include "jsstr.h"
|
#include "jsstr.h"
|
||||||
#include "jstypedarray.h"
|
#include "jstypedarray.h"
|
||||||
#include "nanojit.h"
|
#include "nanojit.h"
|
||||||
@ -1217,13 +1216,7 @@ class Writer
|
|||||||
"strChar");
|
"strChar");
|
||||||
}
|
}
|
||||||
|
|
||||||
nj::LIns *getArgsLength(nj::LIns *args) const {
|
inline nj::LIns *getArgsLength(nj::LIns *args) const;
|
||||||
uint32 slot = JSObject::JSSLOT_ARGS_LENGTH;
|
|
||||||
nj::LIns *vaddr_ins = ldpObjSlots(args);
|
|
||||||
return name(lir->insLoad(nj::LIR_ldi, vaddr_ins, slot * sizeof(Value) + sPayloadOffset,
|
|
||||||
ACCSET_SLOTS),
|
|
||||||
"argsLength");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace tjit */
|
} /* namespace tjit */
|
||||||
|
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 "Stack.h"
|
||||||
|
|
||||||
|
#include "ArgumentsObject-inl.h"
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -494,12 +496,11 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasArgsObj()) {
|
if (hasArgsObj()) {
|
||||||
JSObject &args = argsObj();
|
ArgumentsObject &argsobj = argsObj();
|
||||||
JS_ASSERT(args.isArguments());
|
if (argsobj.isNormalArguments())
|
||||||
if (args.isNormalArguments())
|
argsobj.setPrivate(this);
|
||||||
args.setPrivate(this);
|
|
||||||
else
|
else
|
||||||
JS_ASSERT(!args.getPrivate());
|
JS_ASSERT(!argsobj.getPrivate());
|
||||||
otherfp->flags_ &= ~HAS_ARGS_OBJ;
|
otherfp->flags_ &= ~HAS_ARGS_OBJ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -580,7 +581,7 @@ StackFrame::numActualArgs() const
|
|||||||
{
|
{
|
||||||
JS_ASSERT(hasArgs());
|
JS_ASSERT(hasArgs());
|
||||||
if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
|
if (JS_UNLIKELY(flags_ & (OVERFLOW_ARGS | UNDERFLOW_ARGS)))
|
||||||
return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
|
return hasArgsObj() ? argsObj().initialLength() : args.nactual;
|
||||||
return numFormalArgs();
|
return numFormalArgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +591,7 @@ StackFrame::actualArgs() const
|
|||||||
JS_ASSERT(hasArgs());
|
JS_ASSERT(hasArgs());
|
||||||
Value *argv = formalArgs();
|
Value *argv = formalArgs();
|
||||||
if (JS_UNLIKELY(flags_ & OVERFLOW_ARGS)) {
|
if (JS_UNLIKELY(flags_ & OVERFLOW_ARGS)) {
|
||||||
uintN nactual = hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
|
uintN nactual = hasArgsObj() ? argsObj().initialLength() : args.nactual;
|
||||||
return argv - (2 + nactual);
|
return argv - (2 + nactual);
|
||||||
}
|
}
|
||||||
return argv;
|
return argv;
|
||||||
@ -606,10 +607,10 @@ StackFrame::actualArgsEnd() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
StackFrame::setArgsObj(JSObject &obj)
|
StackFrame::setArgsObj(ArgumentsObject &obj)
|
||||||
{
|
{
|
||||||
JS_ASSERT_IF(hasArgsObj(), &obj == args.obj);
|
JS_ASSERT_IF(hasArgsObj(), &obj == args.obj);
|
||||||
JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.getArgsInitialLength());
|
JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.initialLength());
|
||||||
args.obj = &obj;
|
args.obj = &obj;
|
||||||
flags_ |= HAS_ARGS_OBJ;
|
flags_ |= HAS_ARGS_OBJ;
|
||||||
}
|
}
|
||||||
@ -674,7 +675,7 @@ StackFrame::markActivationObjectsAsPut()
|
|||||||
{
|
{
|
||||||
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
|
if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
|
||||||
if (hasArgsObj() && !argsObj().getPrivate()) {
|
if (hasArgsObj() && !argsObj().getPrivate()) {
|
||||||
args.nactual = args.obj->getArgsInitialLength();
|
args.nactual = args.obj->initialLength();
|
||||||
flags_ &= ~HAS_ARGS_OBJ;
|
flags_ &= ~HAS_ARGS_OBJ;
|
||||||
}
|
}
|
||||||
if (hasCallObj() && !callObj().getPrivate()) {
|
if (hasCallObj() && !callObj().getPrivate()) {
|
||||||
|
@ -58,6 +58,8 @@ class ExecuteFrameGuard;
|
|||||||
class DummyFrameGuard;
|
class DummyFrameGuard;
|
||||||
class GeneratorFrameGuard;
|
class GeneratorFrameGuard;
|
||||||
|
|
||||||
|
class ArgumentsObject;
|
||||||
|
|
||||||
namespace mjit { struct JITScript; }
|
namespace mjit { struct JITScript; }
|
||||||
namespace detail { struct OOMCheck; }
|
namespace detail { struct OOMCheck; }
|
||||||
|
|
||||||
@ -259,8 +261,8 @@ class StackFrame
|
|||||||
JSFunction *fun; /* function frame, pre GetScopeChain */
|
JSFunction *fun; /* function frame, pre GetScopeChain */
|
||||||
} exec;
|
} exec;
|
||||||
union { /* describes the arguments of a function */
|
union { /* describes the arguments of a function */
|
||||||
uintN nactual; /* pre GetArgumentsObject */
|
uintN nactual; /* before js_GetArgsObject */
|
||||||
JSObject *obj; /* post GetArgumentsObject */
|
ArgumentsObject *obj; /* after js_GetArgsObject */
|
||||||
JSScript *script; /* eval has no args, but needs a script */
|
JSScript *script; /* eval has no args, but needs a script */
|
||||||
} args;
|
} args;
|
||||||
mutable JSObject *scopeChain_; /* current scope chain */
|
mutable JSObject *scopeChain_; /* current scope chain */
|
||||||
@ -546,17 +548,17 @@ class StackFrame
|
|||||||
return !!(flags_ & HAS_ARGS_OBJ);
|
return !!(flags_ & HAS_ARGS_OBJ);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject &argsObj() const {
|
ArgumentsObject &argsObj() const {
|
||||||
JS_ASSERT(hasArgsObj());
|
JS_ASSERT(hasArgsObj());
|
||||||
JS_ASSERT(!isEvalFrame());
|
JS_ASSERT(!isEvalFrame());
|
||||||
return *args.obj;
|
return *args.obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject *maybeArgsObj() const {
|
ArgumentsObject *maybeArgsObj() const {
|
||||||
return hasArgsObj() ? &argsObj() : NULL;
|
return hasArgsObj() ? &argsObj() : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setArgsObj(JSObject &obj);
|
inline void setArgsObj(ArgumentsObject &obj);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This value
|
* This value
|
||||||
|
Loading…
Reference in New Issue
Block a user