diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 7784c95466a..b446fa8d1f9 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -277,6 +277,7 @@ VPATH += \ EXPORTS_NAMESPACES = vm EXPORTS_vm = \ + ArgumentsObject.h \ GlobalObject.h \ Stack.h \ StringObject.h \ diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 0b0f13ecbbd..dd0a852a6bd 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -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(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(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); } } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index cf3f44c517d..97945c28bec 100644 --- a/js/src/jsfun.cpp +++ b/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(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(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(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(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, diff --git a/js/src/jsfun.h b/js/src/jsfun.h index fab27b7782b..4af2fa0a478 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -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; diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index eed0b96a1b4..f16fda7cf99 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -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(argsobj->getPrivate())) copyFrom = &afp->canonicalActualArg(arg); goto end_getelem; } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 0c56922005b..4cd0954ac96 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -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: /* diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index d25527e6171..de9931d3d89 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -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 { diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 231792bbbe4..9ae84519400 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -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) { diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 1f3b304b27c..199f074bf44 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -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; } diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 4dc89e59631..3581e64e1bd 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -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) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 91105eba050..d1d12b47781 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -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; + } } } diff --git a/js/src/tracejit/Writer.cpp b/js/src/tracejit/Writer.cpp index c91797d95a2..0099d300980 100644 --- a/js/src/tracejit/Writer.cpp +++ b/js/src/tracejit/Writer.cpp @@ -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 = // 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 diff --git a/js/src/tracejit/Writer.h b/js/src/tracejit/Writer.h index d9531d025a9..67c3c398177 100644 --- a/js/src/tracejit/Writer.h +++ b/js/src/tracejit/Writer.h @@ -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 */ diff --git a/js/src/vm/ArgumentsObject-inl.h b/js/src/vm/ArgumentsObject-inl.h new file mode 100644 index 00000000000..1ec8a987310 --- /dev/null +++ b/js/src/vm/ArgumentsObject-inl.h @@ -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 (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(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___ */ diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h new file mode 100644 index 00000000000..81a6142d990 --- /dev/null +++ b/js/src/vm/ArgumentsObject.h @@ -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 (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(this); +} + +inline bool +JSObject::isStrictArguments() const +{ + return getClass() == &js::StrictArgumentsObject::jsClass; +} + +js::StrictArgumentsObject * +JSObject::asStrictArguments() +{ + JS_ASSERT(isStrictArguments()); + return reinterpret_cast(this); +} + +inline bool +JSObject::isArguments() const +{ + return isNormalArguments() || isStrictArguments(); +} + +js::ArgumentsObject * +JSObject::asArguments() +{ + JS_ASSERT(isArguments()); + return reinterpret_cast(this); +} + +#endif /* ArgumentsObject_h___ */ diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 5ad22be1055..a7ab4d0b4ec 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.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()) { diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index b45d12793ef..84463062304 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -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