diff --git a/js/src/jsapi-tests/testConservativeGC.cpp b/js/src/jsapi-tests/testConservativeGC.cpp index 8b4afd88ac6..0d424992a24 100644 --- a/js/src/jsapi-tests/testConservativeGC.cpp +++ b/js/src/jsapi-tests/testConservativeGC.cpp @@ -52,7 +52,6 @@ bool checkObjectFields(JSObject *savedCopy, JSObject *obj) CHECK(savedCopy->flags == obj->flags); CHECK(savedCopy->getProto() == obj->getProto()); CHECK(savedCopy->parent == obj->parent); - CHECK(savedCopy->privateData == obj->privateData); return true; } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 1c125b07335..14037549341 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1259,7 +1259,6 @@ Class js::ArrayClass = { Class js::SlowArrayClass = { "Array", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), slowarray_addProperty, JS_PropertyStub, /* delProperty */ diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d1def802a49..7be1f174c42 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -191,19 +191,20 @@ struct Object { TypeObject *type; uint32 flags; JSObject *parent; - void *privateData; js::Value *slots; js::Value *_1; -#if JS_BITS_PER_WORD == 32 - js::Value *_2; -#endif static const uint32 FIXED_SLOTS_SHIFT = 27; + size_t numFixedSlots() const { return flags >> FIXED_SLOTS_SHIFT; } + Value *fixedSlots() const { + return (Value *)((jsuword) this + sizeof(shadow::Object)); + } + js::Value &slotRef(size_t slot) const { - size_t nfixed = flags >> FIXED_SLOTS_SHIFT; + size_t nfixed = numFixedSlots(); if (slot < nfixed) - return ((Value *)((jsuword) this + sizeof(shadow::Object)))[slot]; + return fixedSlots()[slot]; return slots[slot - nfixed]; } }; @@ -250,7 +251,9 @@ GetObjectProto(const JSObject *obj) inline void * GetObjectPrivate(const JSObject *obj) { - return reinterpret_cast(obj)->privateData; + const shadow::Object *nobj = reinterpret_cast(obj); + void **addr = reinterpret_cast(&nobj->fixedSlots()[nobj->numFixedSlots()]); + return *addr; } /* diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 84142f4495f..fc9e08013c0 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -199,7 +199,7 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee) JS_STATIC_ASSERT(NormalArgumentsObject::RESERVED_SLOTS == 2); JS_STATIC_ASSERT(StrictArgumentsObject::RESERVED_SLOTS == 2); - JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2); + JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT4); if (!obj) return NULL; @@ -215,7 +215,7 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee) SetValueRangeToUndefined(data->slots, argc); /* Can't fail from here on, so initialize everything in argsobj. */ - obj->init(cx, type, proto->getParent(), NULL, false); + obj->init(cx, type, proto->getParent(), false); obj->setInitialPropertyInfallible(emptyArgumentsShape); ArgumentsObject *argsobj = obj->asArguments(); @@ -225,6 +225,9 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee) argsobj->setCalleeAndData(callee, data); + JS_ASSERT(argsobj->numFixedSlots() == NormalArgumentsObject::NFIXED_SLOTS); + JS_ASSERT(argsobj->numFixedSlots() == StrictArgumentsObject::NFIXED_SLOTS); + return argsobj; } @@ -768,8 +771,9 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp) EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx); if (!emptyDeclEnvShape) return NULL; - envobj->init(cx, type, &fp->scopeChain(), fp, false); + envobj->init(cx, type, &fp->scopeChain(), false); envobj->setInitialPropertyInfallible(emptyDeclEnvShape); + envobj->setPrivate(fp); return envobj; } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 79f9cda07eb..6c86359c98d 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -100,7 +100,7 @@ #define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure optimization level -- see above */ -struct JSFunction : public JSObject_Slots2 +struct JSFunction : public JSObject_Slots4 { /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */ diff --git a/js/src/jsgcinlines.h b/js/src/jsgcinlines.h index c1bdef33fd7..5707693e439 100644 --- a/js/src/jsgcinlines.h +++ b/js/src/jsgcinlines.h @@ -77,6 +77,15 @@ GetGCObjectKind(size_t numSlots) return slotsToThingKind[numSlots]; } +static inline AllocKind +GetGCObjectKind(Class *clasp) +{ + uint32 nslots = JSCLASS_RESERVED_SLOTS(clasp); + if (clasp->flags & JSCLASS_HAS_PRIVATE) + nslots++; + return GetGCObjectKind(nslots); +} + /* As for GetGCObjectKind, but for dense array allocation. */ static inline AllocKind GetGCArrayKind(size_t numSlots) @@ -400,7 +409,7 @@ js_NewGCFunction(JSContext *cx) { JSFunction *fun = NewGCThing(cx, js::gc::FINALIZE_FUNCTION, sizeof(JSFunction)); if (fun) - fun->earlyInit(JSObject::FUN_CLASS_RESERVED_SLOTS); + fun->earlyInit(JSObject::FUN_CLASS_NFIXED_SLOTS + 1); /* Add one for private data. */ return fun; } diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 58193c7e962..72534cae0fc 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -405,7 +405,7 @@ NewIteratorObject(JSContext *cx, uintN flags) * helper objects) expect it to have a non-null map pointer, so we * share an empty Enumerator scope in the runtime. */ - JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT0); + JSObject *obj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!obj) return NULL; @@ -417,8 +417,10 @@ NewIteratorObject(JSContext *cx, uintN flags) if (!emptyEnumeratorShape) return NULL; - obj->init(cx, type, NULL, NULL, false); + obj->init(cx, type, NULL, false); obj->setInitialPropertyInfallible(emptyEnumeratorShape); + + JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS); return obj; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index a03a5f0a046..abf8795260c 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3464,13 +3464,13 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) if (!type) return NULL; - obj = js_NewGCObject(cx, FINALIZE_OBJECT2); + obj = js_NewGCObject(cx, FINALIZE_OBJECT4); if (!obj) return NULL; StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp()); - obj->init(cx, type, parent, priv, false); + obj->init(cx, type, parent, false); EmptyShape *emptyWithShape = EmptyShape::getEmptyWithShape(cx); if (!emptyWithShape) @@ -3479,6 +3479,8 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) obj->setInitialPropertyInfallible(emptyWithShape); OBJ_SET_BLOCK_DEPTH(cx, obj, depth); + obj->setPrivate(priv); + AutoObjectRooter tvr(cx, obj); JSObject *thisp = proto->thisObject(cx); if (!thisp) @@ -3509,7 +3511,7 @@ js_NewBlockObject(JSContext *cx) if (!emptyBlockShape) return NULL; - blockObj->init(cx, type, NULL, NULL, false); + blockObj->init(cx, type, NULL, false); blockObj->setInitialPropertyInfallible(emptyBlockShape); return blockObj; @@ -3757,7 +3759,7 @@ JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent) return NULL; } - if (obj->getClass()->flags & JSCLASS_HAS_PRIVATE) + if (obj->hasPrivate()) clone->setPrivate(obj->getPrivate()); } else { JS_ASSERT(obj->isProxy()); @@ -3772,11 +3774,15 @@ struct JSObject::TradeGutsReserved { JSContext *cx; Vector avals; Vector bvals; + uint32 newafixed; + uint32 newbfixed; Value *newaslots; Value *newbslots; TradeGutsReserved(JSContext *cx) - : cx(cx), avals(cx), bvals(cx), newaslots(NULL), newbslots(NULL) + : cx(cx), avals(cx), bvals(cx), + newafixed(0), newbfixed(0), + newaslots(NULL), newbslots(NULL) {} ~TradeGutsReserved() @@ -3798,7 +3804,7 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, * swaps can be performed infallibly. */ - if (a->structSize() == b->structSize()) + if (a->structSize() == b->structSize() && a->hasPrivate() == b->hasPrivate()) return true; /* @@ -3821,17 +3827,32 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b, JS_ASSERT(a->elements == emptyObjectElements); JS_ASSERT(b->elements == emptyObjectElements); + /* + * The newafixed/newbfixed hold the number of fixed slots in the objects + * after the swap. Adjust these counts according to whether the objects + * use their last fixed slot for storing private data. + */ + + reserved.newafixed = a->numFixedSlots(); + reserved.newbfixed = b->numFixedSlots(); + + if (a->hasPrivate()) { + reserved.newafixed++; + reserved.newbfixed--; + } + if (b->hasPrivate()) { + reserved.newbfixed++; + reserved.newafixed--; + } + /* * The newaslots/newbslots arrays hold any dynamic slots for the objects * if they do not have enough fixed slots to accomodate the slots in the * other object. */ - unsigned afixed = a->numFixedSlots(); - unsigned bfixed = b->numFixedSlots(); - - unsigned adynamic = dynamicSlotsCount(afixed, b->slotSpan()); - unsigned bdynamic = dynamicSlotsCount(bfixed, a->slotSpan()); + unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan()); + unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan()); if (adynamic) { reserved.newaslots = (Value *) cx->malloc_(sizeof(Value) * adynamic); @@ -3880,7 +3901,7 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & /* Trade the guts of the objects. */ const size_t size = a->structSize(); - if (size == b->structSize()) { + if (size == b->structSize() && a->hasPrivate() == b->hasPrivate()) { /* * If the objects are the same size, then we make no assumptions about * whether they have dynamically allocated slots and instead just copy @@ -3914,21 +3935,25 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved & if (b->hasDynamicSlots()) cx->free_(b->slots); - unsigned afixed = a->numFixedSlots(); - unsigned bfixed = b->numFixedSlots(); + void *apriv = a->hasPrivate() ? a->getPrivate() : NULL; + void *bpriv = b->hasPrivate() ? b->getPrivate() : NULL; JSObject tmp; memcpy(&tmp, a, sizeof tmp); memcpy(a, b, sizeof tmp); memcpy(b, &tmp, sizeof tmp); - a->updateFixedSlots(afixed); + a->updateFixedSlots(reserved.newafixed); a->slots = reserved.newaslots; a->copySlotRange(0, reserved.bvals.begin(), bcap); + if (a->hasPrivate()) + a->setPrivate(bpriv); - b->updateFixedSlots(bfixed); + b->updateFixedSlots(reserved.newbfixed); b->slots = reserved.newbslots; b->copySlotRange(0, reserved.avals.begin(), acap); + if (b->hasPrivate()) + b->setPrivate(apriv); /* Make sure the destructor for reserved doesn't free the slots. */ reserved.newaslots = NULL; @@ -4495,6 +4520,19 @@ JSObject::updateSlotsForSpan(size_t oldSpan, size_t newSpan) invalidateSlotRange(newSpan, oldSpan - newSpan); } +inline void +JSObject::initializePrivate() +{ + size_t nfixed = numFixedSlots(); + JS_ASSERT(nfixed != 0); + + /* Remove a fixed slot, to make room for the private data. */ + flags = flags ^ (nfixed << FIXED_SLOTS_SHIFT); + flags |= (nfixed - 1) << FIXED_SLOTS_SHIFT; + + setPrivate(NULL); +} + bool JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape) { @@ -4502,6 +4540,9 @@ JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape) JS_ASSERT(shape->compartment() == compartment()); JS_ASSERT(!shape->inDictionary()); + if (shape->getClass()->flags & JSCLASS_HAS_PRIVATE) + initializePrivate(); + size_t span = shape->slotSpan(); if (!span) { @@ -4524,6 +4565,10 @@ JSObject::setInitialPropertyInfallible(const js::Shape *shape) JS_ASSERT(isNewborn()); JS_ASSERT(shape->compartment() == compartment()); JS_ASSERT(!shape->inDictionary()); + + if (shape->getClass()->flags & JSCLASS_HAS_PRIVATE) + initializePrivate(); + JS_ASSERT(dynamicSlotsCount(numFixedSlots(), shape->slotSpan()) == 0); shape_ = const_cast(shape); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 6c7afc9afbd..9c4dac47e60 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -544,18 +544,12 @@ struct JSObject : js::gc::Cell }; uint32 flags; /* flags */ - JSObject *parent; /* object's parent */ - void *privateData; /* private data */ private: js::Value *slots; /* Slots for object properties. */ js::Value *elements; /* Slots for object elements. */ -#if JS_BITS_PER_WORD == 32 - void *padding; -#endif - public: inline bool isNative() const; @@ -668,6 +662,8 @@ struct JSObject : js::gc::Cell inline size_t numFixedSlots() const; + static const uint32 MAX_FIXED_SLOTS = 16; + private: inline js::Value* fixedSlots() const; public: @@ -685,6 +681,7 @@ struct JSObject : js::gc::Cell /* JIT Accessors */ static inline size_t getFixedSlotOffset(size_t slot); + static inline size_t getPrivateDataOffset(size_t nfixed); static inline size_t offsetOfSlots() { return offsetof(JSObject, slots); } /* Minimum size for dynamically allocated slots. */ @@ -891,7 +888,9 @@ struct JSObject : js::gc::Cell inline bool isGlobal() const; inline js::GlobalObject *asGlobal(); + inline bool hasPrivate() const; inline void *getPrivate() const; + inline void *getPrivate(size_t nfixed) const; inline void setPrivate(void *data); /* N.B. Infallible: NULL means 'no principal', not an error. */ @@ -914,6 +913,9 @@ struct JSObject : js::gc::Cell bool isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp); + inline void *&privateAddress(uint32 nfixed) const; + inline void initializePrivate(); + public: bool isExtensible() const { return !(flags & NOT_EXTENSIBLE); } bool preventExtensions(JSContext *cx, js::AutoIdVector *props); @@ -1095,6 +1097,7 @@ struct JSObject : js::gc::Cell public: static const uint32 FUN_CLASS_RESERVED_SLOTS = 2; + static const uint32 FUN_CLASS_NFIXED_SLOTS = 3; static size_t getFlatClosureUpvarsOffset() { return getFixedSlotOffset(JSSLOT_FLAT_CLOSURE_UPVARS); @@ -1161,6 +1164,8 @@ struct JSObject : js::gc::Cell * Iterator-specific getters and setters. */ + static const uint32 ITER_CLASS_NFIXED_SLOTS = 1; + inline js::NativeIterator *getNativeIterator() const; inline void setNativeIterator(js::NativeIterator *); @@ -1236,7 +1241,7 @@ struct JSObject : js::gc::Cell /* The last property is not initialized here and should be set separately. */ void init(JSContext *cx, js::types::TypeObject *type, - JSObject *parent, void *priv, bool denseArray); + JSObject *parent, bool denseArray); inline void finish(JSContext *cx); JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background); @@ -1433,7 +1438,6 @@ struct JSObject : js::gc::Cell JS_STATIC_ASSERT(offsetof(JSObject, shape_) == offsetof(js::shadow::Object, shape)); JS_STATIC_ASSERT(offsetof(JSObject, flags) == offsetof(js::shadow::Object, flags)); JS_STATIC_ASSERT(offsetof(JSObject, parent) == offsetof(js::shadow::Object, parent)); - JS_STATIC_ASSERT(offsetof(JSObject, privateData) == offsetof(js::shadow::Object, privateData)); JS_STATIC_ASSERT(offsetof(JSObject, slots) == offsetof(js::shadow::Object, slots)); JS_STATIC_ASSERT(offsetof(JSObject, type_) == offsetof(js::shadow::Object, type)); JS_STATIC_ASSERT(sizeof(JSObject) == sizeof(js::shadow::Object)); @@ -1474,6 +1478,11 @@ JSObject::getFixedSlotOffset(size_t slot) { return sizeof(JSObject) + (slot * sizeof(js::Value)); } +/* static */ inline size_t +JSObject::getPrivateDataOffset(size_t nfixed) { + return getFixedSlotOffset(nfixed); +} + struct JSObject_Slots2 : JSObject { js::Value fslots[2]; }; struct JSObject_Slots4 : JSObject { js::Value fslots[4]; }; struct JSObject_Slots8 : JSObject { js::Value fslots[8]; }; diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index adef5426b0e..b769fe7bba3 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -93,25 +93,43 @@ JSObject::asGlobal() return reinterpret_cast(this); } -inline void * -JSObject::getPrivate() const +inline bool +JSObject::hasPrivate() const { - JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - return privateData; + return getClass()->flags & JSCLASS_HAS_PRIVATE; } +inline void *& +JSObject::privateAddress(uint32 nfixed) const +{ + /* + * The private pointer of an object can hold any word sized value. + * Private pointers are stored immediately after the last fixed slot of + * the object. + */ + JS_ASSERT(nfixed == numFixedSlots()); + JS_ASSERT_IF(!isNewborn(), hasPrivate()); + js::Value *end = &fixedSlots()[nfixed]; + return *reinterpret_cast(end); +} + +inline void * +JSObject::getPrivate() const { return privateAddress(numFixedSlots()); } + +inline void * +JSObject::getPrivate(size_t nfixed) const { return privateAddress(nfixed); } + inline JSFunction * JSObject::getFunctionPrivate() const { JS_ASSERT(isFunction()); - return reinterpret_cast(getPrivate()); + return reinterpret_cast(getPrivate(FUN_CLASS_NFIXED_SLOTS)); } inline void JSObject::setPrivate(void *data) { - JS_ASSERT(getClass()->flags & JSCLASS_HAS_PRIVATE); - privateData = data; + privateAddress(numFixedSlots()) = data; } inline bool @@ -315,7 +333,7 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent if (!type) return false; - init(cx, type, parent, NULL, false); + init(cx, type, parent, false); if (!setInitialProperty(cx, bindings.lastShape())) return false; @@ -338,11 +356,13 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent inline bool JSObject::initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *frame) { - init(cx, type, NULL, frame, false); + init(cx, type, NULL, false); if (!setInitialProperty(cx, getProto()->lastProperty())) return false; + setPrivate(frame); + JS_ASSERT(!inDictionaryMode()); JS_ASSERT(isClonedBlock()); @@ -929,10 +949,8 @@ JSObject::isQName() const inline void JSObject::init(JSContext *cx, js::types::TypeObject *type, - JSObject *parent, void *priv, bool denseArray) + JSObject *parent, bool denseArray) { - privateData = priv; - JS_STATIC_ASSERT(sizeof(js::ObjectElements) == 2 * sizeof(js::Value)); uint32 numSlots = numFixedSlots(); @@ -948,7 +966,6 @@ JSObject::init(JSContext *cx, js::types::TypeObject *type, new (getElementsHeader()) js::ObjectElements(numSlots - 2); } else { elements = js::emptyObjectElements; - js::ClearValueRange(fixedSlots(), numSlots); } setType(type); @@ -972,7 +989,7 @@ JSObject::initSharingEmptyShape(JSContext *cx, void *privateValue, js::gc::AllocKind kind) { - init(cx, type, parent, privateValue, false); + init(cx, type, parent, false); js::EmptyShape *empty = type->getEmptyShape(cx, aclasp, kind); if (!empty) @@ -981,6 +998,9 @@ JSObject::initSharingEmptyShape(JSContext *cx, if (!setInitialProperty(cx, empty)) return false; + if (privateValue) + setPrivate(privateValue); + JS_ASSERT(!isDenseArray()); return true; } @@ -1132,9 +1152,10 @@ JSObject::hasPropertyTable() const inline size_t JSObject::structSize() const { - return (isFunction() && !getPrivate()) - ? sizeof(JSFunction) - : (sizeof(JSObject) + sizeof(js::Value) * numFixedSlots()); + if (isFunction() && !getFunctionPrivate()) + return sizeof(JSFunction); + uint32 nfixed = numFixedSlots() + (hasPrivate() ? 1 : 0); + return sizeof(JSObject) + (nfixed * sizeof(js::Value)); } inline size_t @@ -1424,7 +1445,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, * the parent of the prototype's constructor. */ bool denseArray = (clasp == &ArrayClass); - obj->init(cx, type, parent, NULL, denseArray); + obj->init(cx, type, parent, denseArray); JS_ASSERT(type->canProvideEmptyShape(clasp)); js::EmptyShape *empty = type->getEmptyShape(cx, clasp, kind); @@ -1437,7 +1458,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, static inline JSObject * NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent) { - gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(clasp); return NewNativeClassInstance(cx, clasp, proto, parent, kind); } @@ -1486,7 +1507,7 @@ NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::AllocKind kind) static inline JSObject * NewBuiltinClassInstance(JSContext *cx, Class *clasp) { - gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(clasp); return NewBuiltinClassInstance(cx, clasp, kind); } @@ -1578,8 +1599,8 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, if (!obj) goto out; - /* This needs to match up with the size of JSFunction::data_padding. */ - JS_ASSERT_IF(isFunction, kind == gc::FINALIZE_OBJECT2); + /* This needs to match up with the superclass of JSFunction. */ + JS_ASSERT_IF(isFunction, kind == gc::FINALIZE_OBJECT4); /* * Default parent to the parent of the prototype, which was set from @@ -1587,7 +1608,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent, */ obj->init(cx, type, (!parent && proto) ? proto->getParent() : parent, - NULL, clasp == &ArrayClass); + clasp == &ArrayClass); if (clasp->isNative() ? !InitScopeForObject(cx, obj, clasp, type, kind) @@ -1608,14 +1629,14 @@ NewFunction(JSContext *cx, js::GlobalObject &global) if (!js_GetClassPrototype(cx, &global, JSProto_Function, &proto)) return NULL; return detail::NewObject(cx, &FunctionClass, proto, &global, - gc::FINALIZE_OBJECT2); + gc::FINALIZE_OBJECT4); } static JS_ALWAYS_INLINE JSObject * NewFunction(JSContext *cx, JSObject *parent) { return detail::NewObject(cx, &FunctionClass, NULL, parent, - gc::FINALIZE_OBJECT2); + gc::FINALIZE_OBJECT4); } template @@ -1630,7 +1651,7 @@ template static JS_ALWAYS_INLINE JSObject * NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(clasp); return detail::NewObject(cx, clasp, proto, parent, kind); } @@ -1648,7 +1669,7 @@ template static JS_ALWAYS_INLINE JSObject * NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent) { - gc::AllocKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp)); + gc::AllocKind kind = gc::GetGCObjectKind(clasp); return NewObject(cx, clasp, proto, parent, kind); } @@ -1674,7 +1695,7 @@ NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc:: */ obj->init(cx, type, (!parent && type->proto) ? type->proto->getParent() : parent, - NULL, false); + false); if (!InitScopeForObject(cx, obj, &ObjectClass, type, kind)) { obj = NULL; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index e34879d62de..2eb0f286d52 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -74,8 +74,12 @@ using namespace js; using namespace js::gc; using namespace js::types; -/* slots can only be upto 255 */ -static const uint8 ARRAYBUFFER_RESERVED_SLOTS = 16; +/* + * Allocate array buffers with the maximum number of fixed slots marked as + * reserved, so that the fixed slots may be used for the buffer's contents. + * The last fixed slot is kept for the object's private data. + */ +static const uint8 ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1; static bool ValueIsLength(JSContext *cx, const Value &v, jsuint *len) @@ -765,7 +769,7 @@ TypedArray::lengthOffset() /* static */ int TypedArray::dataOffset() { - return offsetof(JSObject, privateData); + return JSObject::getPrivateDataOffset(NUM_FIXED_SLOTS); } /* Helper clamped uint8 type */ @@ -1298,6 +1302,8 @@ class TypedArrayTemplate return false; obj->setLastPropertyInfallible(empty); + JS_ASSERT(obj->numFixedSlots() == NUM_FIXED_SLOTS); + // FIXME Bug 599008: make it ok to call preventExtensions here. obj->flags |= JSObject::NOT_EXTENSIBLE; diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index 153560e66ab..a5b20d3060d 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -193,7 +193,8 @@ struct JS_FRIEND_API(TypedArray) { FIELD_BYTELENGTH, FIELD_TYPE, FIELD_BUFFER, - FIELD_MAX + FIELD_MAX, + NUM_FIXED_SLOTS = 7 }; // and MUST NOT be used to construct new objects. diff --git a/js/src/jstypedarrayinlines.h b/js/src/jstypedarrayinlines.h index efe84dcc436..a425a20a6aa 100644 --- a/js/src/jstypedarrayinlines.h +++ b/js/src/jstypedarrayinlines.h @@ -95,7 +95,7 @@ TypedArray::getBuffer(JSObject *obj) { inline void * TypedArray::getDataOffset(JSObject *obj) { - return (void *)obj->getPrivate(); + return (void *)obj->getPrivate(NUM_FIXED_SLOTS); } } diff --git a/js/src/methodjit/BaseAssembler.h b/js/src/methodjit/BaseAssembler.h index 3a03c2fe472..685fe2742ba 100644 --- a/js/src/methodjit/BaseAssembler.h +++ b/js/src/methodjit/BaseAssembler.h @@ -1253,6 +1253,7 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist JS_ASSERT(cx->typeInferenceEnabled()); JS_ASSERT(!templateObject->hasDynamicSlots()); JS_ASSERT(!templateObject->hasDynamicElements()); + JS_ASSERT(!(templateObject->getClass()->flags & JSCLASS_HAS_PRIVATE)); #ifdef JS_GC_ZEAL if (cx->runtime->needZealousGC()) @@ -1304,7 +1305,6 @@ static const JSC::MacroAssembler::RegisterID JSParamReg_Argc = JSC::SparcRegist store32(Imm32(templateObject->flags), Address(result, offsetof(JSObject, flags))); storePtr(ImmPtr(NULL), Address(result, JSObject::offsetOfSlots())); storePtr(ImmPtr(templateObject->parent), Address(result, offsetof(JSObject, parent))); - storePtr(ImmPtr(templateObject->privateData), Address(result, offsetof(JSObject, privateData))); if (templateObject->isDenseArray()) { /* Fill in the elements header. */ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index cf59448c180..4d9a9708b87 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -3340,7 +3340,7 @@ mjit::Compiler::checkCallApplySpeculation(uint32 callImmArgc, uint32 speculatedA if (origCalleeType.isSet()) isObj = masm.testObject(Assembler::NotEqual, origCalleeType.reg()); Jump isFun = masm.testFunction(Assembler::NotEqual, origCalleeData, temp); - masm.loadObjPrivate(origCalleeData, origCalleeData); + masm.loadObjPrivate(origCalleeData, origCalleeData, JSObject::FUN_CLASS_NFIXED_SLOTS); Native native = *PC == JSOP_FUNCALL ? js_fun_call : js_fun_apply; Jump isNative = masm.branchPtr(Assembler::NotEqual, Address(origCalleeData, JSFunction::offsetOfNativeOrScript()), @@ -3621,7 +3621,7 @@ mjit::Compiler::inlineCallHelper(uint32 callImmArgc, bool callingNew, FrameSize Jump notFunction = stubcc.masm.testFunction(Assembler::NotEqual, icCalleeData, tmp); /* Test if the function is scripted. */ - stubcc.masm.loadObjPrivate(icCalleeData, funPtrReg); + stubcc.masm.loadObjPrivate(icCalleeData, funPtrReg, JSObject::FUN_CLASS_NFIXED_SLOTS); stubcc.masm.load16(Address(funPtrReg, offsetof(JSFunction, flags)), tmp); stubcc.masm.and32(Imm32(JSFUN_KINDMASK), tmp); Jump isNative = stubcc.masm.branch32(Assembler::Below, tmp, Imm32(JSFUN_INTERPRETED)); @@ -5670,7 +5670,7 @@ mjit::Compiler::iter(uintN flags) stubcc.linkExit(nullIterator, Uses(1)); /* Get NativeIterator from iter obj. */ - masm.loadObjPrivate(ioreg, nireg); + masm.loadObjPrivate(ioreg, nireg, JSObject::ITER_CLASS_NFIXED_SLOTS); /* Test for active iterator. */ Address flagsAddr(nireg, offsetof(NativeIterator, flags)); @@ -5759,7 +5759,7 @@ mjit::Compiler::iterNext(ptrdiff_t offset) stubcc.linkExit(notFast, Uses(1)); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, T1); + masm.loadObjPrivate(reg, T1, JSObject::ITER_CLASS_NFIXED_SLOTS); RegisterID T3 = frame.allocReg(); RegisterID T4 = frame.allocReg(); @@ -5814,7 +5814,7 @@ mjit::Compiler::iterMore(jsbytecode *target) stubcc.linkExitForBranch(notFast); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, reg); + masm.loadObjPrivate(reg, reg, JSObject::ITER_CLASS_NFIXED_SLOTS); /* Test that the iterator supports fast iteration. */ notFast = masm.branchTest32(Assembler::NonZero, Address(reg, offsetof(NativeIterator, flags)), @@ -5853,7 +5853,7 @@ mjit::Compiler::iterEnd() stubcc.linkExit(notIterator, Uses(1)); /* Get private from iter obj. */ - masm.loadObjPrivate(reg, T1); + masm.loadObjPrivate(reg, T1, JSObject::ITER_CLASS_NFIXED_SLOTS); RegisterID T2 = frame.allocReg(); diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 054d44e3cb1..6cfedd134fc 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -747,7 +747,7 @@ class CallCompiler : public BaseCompiler /* Guard that it's the same function. */ JSFunction *fun = obj->getFunctionPrivate(); - masm.loadObjPrivate(ic.funObjReg, t0); + masm.loadObjPrivate(ic.funObjReg, t0, JSObject::FUN_CLASS_NFIXED_SLOTS); Jump funGuard = masm.branchPtr(Assembler::NotEqual, t0, ImmPtr(fun)); Jump done = masm.jump(); diff --git a/js/src/methodjit/NunboxAssembler.h b/js/src/methodjit/NunboxAssembler.h index db41f966e7f..132795a5b5a 100644 --- a/js/src/methodjit/NunboxAssembler.h +++ b/js/src/methodjit/NunboxAssembler.h @@ -345,8 +345,8 @@ class NunboxAssembler : public JSC::MacroAssembler loadPtr(payloadOf(privAddr), to); } - void loadObjPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); + void loadObjPrivate(RegisterID base, RegisterID to, uint32 nfixed) { + Address priv(base, JSObject::getPrivateDataOffset(nfixed)); loadPtr(priv, to); } diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index 6adf63bb0f8..5e22d4d80e3 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -357,7 +357,7 @@ class SetPropCompiler : public PICStubCompiler uint16 slot = uint16(shape->shortid()); /* Guard that the call object has a frame. */ - masm.loadObjPrivate(pic.objReg, pic.shapeReg); + masm.loadObjPrivate(pic.objReg, pic.shapeReg, obj->numFixedSlots()); Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg); { @@ -1554,7 +1554,7 @@ class ScopeNameCompiler : public PICStubCompiler } /* Get callobj's stack frame. */ - masm.loadObjPrivate(pic.objReg, pic.shapeReg); + masm.loadObjPrivate(pic.objReg, pic.shapeReg, getprop.holder->numFixedSlots()); JSFunction *fun = getprop.holder->asCall().getCalleeFunction(); uint16 slot = uint16(shape->shortid()); @@ -2556,7 +2556,7 @@ GetElementIC::attachArguments(JSContext *cx, JSObject *obj, const Value &v, jsid } Jump holeCheck = masm.branchPtr(Assembler::Equal, objReg, ImmType(JSVAL_TYPE_MAGIC)); - Address privateData(typeReg, offsetof(JSObject, privateData)); + Address privateData(typeReg, JSObject::getPrivateDataOffset(ArgumentsObject::NFIXED_SLOTS)); Jump liveArguments = masm.branchPtr(Assembler::NotEqual, privateData, ImmPtr(0)); masm.loadPrivate(Address(typeReg, JSObject::getFixedSlotOffset(ArgumentsObject::DATA_SLOT)), objReg); diff --git a/js/src/methodjit/PunboxAssembler.h b/js/src/methodjit/PunboxAssembler.h index 7ebac0e6653..9cab8f807ba 100644 --- a/js/src/methodjit/PunboxAssembler.h +++ b/js/src/methodjit/PunboxAssembler.h @@ -274,8 +274,8 @@ class PunboxAssembler : public JSC::MacroAssembler lshiftPtr(Imm32(1), to); } - void loadObjPrivate(RegisterID base, RegisterID to) { - Address priv(base, offsetof(JSObject, privateData)); + void loadObjPrivate(RegisterID base, RegisterID to, uint32 nfixed) { + Address priv(base, JSObject::getPrivateDataOffset(nfixed)); loadPtr(priv, to); } diff --git a/js/src/tracejit/Writer.cpp b/js/src/tracejit/Writer.cpp index 563b32ef0af..faf9ea5f4fc 100644 --- a/js/src/tracejit/Writer.cpp +++ b/js/src/tracejit/Writer.cpp @@ -436,10 +436,13 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac case ACCSET_OBJ_PRIVATE: // base = // ins = {ld,st}p.objprivate base[offsetof(JSObject, privateData)] + ok = false; + /* ok = (op == LIR_ldi || op == LIR_ldp || op == LIR_sti || op == LIR_stp) && disp == offsetof(JSObject, privateData) && couldBeObjectOrString(base); + */ break; case ACCSET_OBJ_CAPACITY: @@ -497,9 +500,12 @@ void ValidateWriter::checkAccSet(LOpcode op, LIns *base, int32_t disp, AccSet ac case ACCSET_ITER: // base = ldp.objprivate ...[offsetof(JSObject, privateData)] // ins = {ld,st}p.iter base[] + ok = false; + /* ok = (op == LIR_ldp || op == LIR_stp) && dispWithin(NativeIterator) && match(base, LIR_ldp, ACCSET_OBJ_PRIVATE, offsetof(JSObject, privateData)); + */ break; case ACCSET_ITER_PROPS: diff --git a/js/src/tracejit/Writer.h b/js/src/tracejit/Writer.h index 2111aba24cb..aa3bb11b7ad 100644 --- a/js/src/tracejit/Writer.h +++ b/js/src/tracejit/Writer.h @@ -510,19 +510,22 @@ class Writer } nj::LIns *ldpObjPrivate(nj::LIns *obj) const { - return name(lir->insLoad(nj::LIR_ldp, obj, offsetof(JSObject, privateData), + JS_NOT_REACHED("FIXME"); + return name(lir->insLoad(nj::LIR_ldp, obj, 0, ACCSET_OBJ_PRIVATE), "private"); } nj::LIns *lduiObjPrivate(nj::LIns *obj) const { - return name(lir->insLoad(nj::LIR_ldi, obj, offsetof(JSObject, privateData), + JS_NOT_REACHED("FIXME"); + return name(lir->insLoad(nj::LIR_ldi, obj, 0, ACCSET_OBJ_PRIVATE), "private_uint32"); } nj::LIns *stuiObjPrivate(nj::LIns *obj, nj::LIns *value) const { - return name(lir->insStore(nj::LIR_sti, value, obj, offsetof(JSObject, privateData), + JS_NOT_REACHED("FIXME"); + return name(lir->insStore(nj::LIR_sti, value, obj, 0, ACCSET_OBJ_PRIVATE), "private_uint32"); } @@ -557,7 +560,8 @@ class Writer } nj::LIns *ldpConstTypedArrayData(nj::LIns *obj) const { - return name(lir->insLoad(nj::LIR_ldp, obj, offsetof(JSObject, privateData), ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData"); + JS_NOT_REACHED("FIXME"); + return name(lir->insLoad(nj::LIR_ldp, obj, 0, ACCSET_TARRAY, nj::LOAD_CONST), "typedArrayData"); } nj::LIns *ldc2iTypedArrayElement(nj::LIns *elems, nj::LIns *index) const { diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 7b64716f1cb..de36f87b1da 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -151,6 +151,7 @@ class ArgumentsObject : public ::JSObject public: static const uint32 RESERVED_SLOTS = 2; + static const uint32 NFIXED_SLOTS = 3; private: /* Lower-order bit stolen from the length slot. */ diff --git a/js/src/vm/CallObject.cpp b/js/src/vm/CallObject.cpp index 4c19bc674a0..8ab671347df 100644 --- a/js/src/vm/CallObject.cpp +++ b/js/src/vm/CallObject.cpp @@ -56,7 +56,7 @@ CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObje { Bindings &bindings = script->bindings; size_t argsVars = bindings.countArgsAndVars(); - size_t slots = RESERVED_SLOTS + argsVars; + size_t slots = RESERVED_SLOTS + argsVars + 1; /* Add one for privateData. */ gc::AllocKind kind = gc::GetGCObjectKind(slots); /*