Move fixed slot count from JSObject to Shape, bug 594561.

This commit is contained in:
Brian Hackett 2011-10-14 11:06:15 -07:00
Родитель 83de21de11
Коммит 52d3c3bbb6
23 изменённых файлов: 394 добавлений и 247 удалений

Просмотреть файл

@ -282,7 +282,7 @@ JSObject::willBeSparseDenseArray(uintN requiredCapacity, uintN newElementsHint)
uintN cap = getDenseArrayCapacity();
JS_ASSERT(requiredCapacity >= cap);
if (requiredCapacity >= JSObject::NSLOTS_LIMIT)
if (requiredCapacity >= JSObject::NELEMENTS_LIMIT)
return true;
uintN minimalDenseCount = requiredCapacity / 4;
@ -3491,6 +3491,7 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
if (!obj)
return NULL;
obj->initDenseArray();
obj->setArrayLength(cx, length);
if (allocateCapacity && !obj->ensureElements(cx, length))

Просмотреть файл

@ -489,7 +489,7 @@ struct JS_FRIEND_API(JSCompartment) {
struct BaseShapeEntry {
js::UnownedBaseShape *base;
js::Shape *shape;
js::ShapeKindArray *shapes;
typedef const js::BaseShape *Lookup;

Просмотреть файл

@ -185,19 +185,21 @@ struct BaseShape {
struct Shape {
BaseShape *base;
jsid _1;
uint32 slotInfo;
static const uint32 FIXED_SLOTS_SHIFT = 27;
};
struct Object {
Shape *shape;
TypeObject *type;
uint32 flags;
uint32 _1;
uint32 _2;
js::Value *slots;
js::Value *_1;
js::Value *_3;
static const uint32 FIXED_SLOTS_SHIFT = 27;
size_t numFixedSlots() const { return flags >> FIXED_SLOTS_SHIFT; }
size_t numFixedSlots() const { return shape->slotInfo >> Shape::FIXED_SLOTS_SHIFT; }
Value *fixedSlots() const {
return (Value *)((jsuword) this + sizeof(shadow::Object));
}

Просмотреть файл

@ -205,7 +205,8 @@ ArgumentsObject::create(JSContext *cx, uint32 argc, JSObject &callee)
bool strict = callee.toFunction()->inStrictMode();
Class *clasp = strict ? &StrictArgumentsObjectClass : &NormalArgumentsObjectClass;
Shape *emptyArgumentsShape = BaseShape::lookupInitialShape(cx, clasp, proto->getParent());
Shape *emptyArgumentsShape = BaseShape::lookupInitialShape(cx, clasp, proto->getParent(),
FINALIZE_OBJECT4);
if (!emptyArgumentsShape)
return NULL;
@ -216,7 +217,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, false);
obj->init(cx, type);
obj->setInitialPropertyInfallible(emptyArgumentsShape);
ArgumentsObject *argsobj = obj->asArguments();
@ -772,10 +773,11 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp)
return NULL;
JSObject *parent = fp->scopeChain().getGlobal();
Shape *emptyDeclEnvShape = BaseShape::lookupInitialShape(cx, &DeclEnvClass, parent);
Shape *emptyDeclEnvShape = BaseShape::lookupInitialShape(cx, &DeclEnvClass, parent,
FINALIZE_OBJECT2);
if (!emptyDeclEnvShape)
return NULL;
envobj->init(cx, type, false);
envobj->init(cx, type);
envobj->setInitialPropertyInfallible(emptyDeclEnvShape);
envobj->setPrivate(fp);

Просмотреть файл

@ -101,7 +101,7 @@ GetGCArrayKind(size_t numSlots)
* unused.
*/
JS_STATIC_ASSERT(sizeof(ObjectElements) == 2 * sizeof(Value));
if (numSlots > JSObject::NSLOTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
if (numSlots > JSObject::NELEMENTS_LIMIT || numSlots + 2 >= SLOTS_TO_THING_KIND_LIMIT)
return FINALIZE_OBJECT2;
return slotsToThingKind[numSlots + 2];
}
@ -174,6 +174,17 @@ GetGCKindSlots(AllocKind thingKind)
}
}
static inline size_t
GetGCKindSlots(AllocKind thingKind, Class *clasp)
{
size_t nslots = GetGCKindSlots(thingKind);
if (clasp->flags & JSCLASS_HAS_PRIVATE) {
JS_ASSERT(nslots > 0);
nslots--;
}
return nslots;
}
static inline void
GCPoke(JSContext *cx, Value oldval)
{
@ -384,7 +395,7 @@ js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_FUNCTION);
JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
if (obj)
obj->earlyInit(js::gc::GetGCKindSlots(kind));
obj->earlyInit();
return obj;
}

Просмотреть файл

@ -900,9 +900,10 @@ ScanTypeObject(GCMarker *gcmarker, types::TypeObject *type)
}
if (type->emptyShapes) {
for (size_t i = 0; i < TYPE_OBJECT_EMPTY_SHAPE_COUNT; i++) {
if (type->emptyShapes[i])
PushMarkStack(gcmarker, type->emptyShapes[i]);
for (unsigned i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
Shape *shape = type->emptyShapes->getIndex(i);
if (shape)
PushMarkStack(gcmarker, shape);
}
}
@ -939,9 +940,10 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
}
if (type->emptyShapes) {
for (size_t i = 0; i < TYPE_OBJECT_EMPTY_SHAPE_COUNT; i++) {
if (type->emptyShapes[i])
MarkShape(trc, type->emptyShapes[i], "empty_shape");
for (unsigned i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
Shape *shape = type->emptyShapes->getIndex(i);
if (shape)
MarkShape(trc, shape, "empty_shape");
}
}

Просмотреть файл

@ -719,7 +719,7 @@ struct TypeObject : gc::Cell
JSObject *singleton;
/* Lazily filled array of empty shapes for each size of objects with this type. */
js::EmptyShape **emptyShapes;
js::ShapeKindArray *emptyShapes;
/* Flags for this object. */
TypeObjectFlags flags;

Просмотреть файл

@ -413,11 +413,12 @@ NewIteratorObject(JSContext *cx, uintN flags)
if (!type)
return NULL;
Shape *emptyEnumeratorShape = BaseShape::lookupInitialShape(cx, &IteratorClass, NULL);
Shape *emptyEnumeratorShape = BaseShape::lookupInitialShape(cx, &IteratorClass, NULL,
FINALIZE_OBJECT2);
if (!emptyEnumeratorShape)
return NULL;
obj->init(cx, type, false);
obj->init(cx, type);
obj->setInitialPropertyInfallible(emptyEnumeratorShape);
JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);

Просмотреть файл

@ -3463,9 +3463,11 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
obj->init(cx, type, false);
obj->init(cx, type);
Shape *emptyWithShape = BaseShape::lookupInitialShape(cx, &WithClass, parent->getGlobal());
Shape *emptyWithShape = BaseShape::lookupInitialShape(cx, &WithClass,
parent->getGlobal(),
FINALIZE_OBJECT4);
if (!emptyWithShape)
return NULL;
@ -3501,11 +3503,12 @@ js_NewBlockObject(JSContext *cx)
if (!type)
return NULL;
Shape *emptyBlockShape = BaseShape::lookupInitialShape(cx, &BlockClass, NULL);
Shape *emptyBlockShape = BaseShape::lookupInitialShape(cx, &BlockClass, NULL,
FINALIZE_OBJECT4);
if (!emptyBlockShape)
return NULL;
blockObj->init(cx, type, false);
blockObj->init(cx, type);
blockObj->setInitialPropertyInfallible(emptyBlockShape);
return blockObj;
@ -3519,13 +3522,12 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
JS_ASSERT(proto->isStaticBlock());
size_t count = OBJ_BLOCK_COUNT(cx, proto);
gc::AllocKind kind = gc::GetGCObjectKind(count + BLOCK_RESERVED_SLOTS + 1);
TypeObject *type = proto->getNewType(cx);
if (!type)
return NULL;
JSObject *clone = js_NewGCObject(cx, kind);
JSObject *clone = js_NewGCObject(cx, FINALIZE_OBJECT4);
if (!clone)
return NULL;
@ -3772,12 +3774,15 @@ struct JSObject::TradeGutsReserved {
Vector<Value> bvals;
int newafixed;
int newbfixed;
Shape *newashape;
Shape *newbshape;
Value *newaslots;
Value *newbslots;
TradeGutsReserved(JSContext *cx)
: cx(cx), avals(cx), bvals(cx),
newafixed(0), newbfixed(0),
newashape(NULL), newbshape(NULL),
newaslots(NULL), newbslots(NULL)
{}
@ -3800,18 +3805,33 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
* swaps can be performed infallibly.
*/
if (a->structSize() == b->structSize() && a->hasPrivate() == b->hasPrivate())
if (a->structSize() == b->structSize())
return true;
/*
* If either object is native, it needs a new shape to preserve the
* invariant that objects with the same shape have the same number of
* inline slots.
* inline slots. The fixed slots will be updated in place during TradeGuts.
* Non-native objects need to be reshaped according to the new count.
*/
if (a->isNative() && !a->generateOwnShape(cx))
if (a->isNative()) {
if (!a->generateOwnShape(cx))
return false;
if (b->isNative() && !b->generateOwnShape(cx))
} else {
reserved.newbshape = BaseShape::lookupInitialShape(cx, a->getClass(), a->getParent(),
b->getAllocKind());
if (!reserved.newbshape)
return false;
}
if (b->isNative()) {
if (!b->generateOwnShape(cx))
return false;
} else {
reserved.newashape = BaseShape::lookupInitialShape(cx, b->getClass(), b->getParent(),
a->getAllocKind());
if (!reserved.newashape)
return false;
}
/* The avals/bvals vectors hold all original values from the objects. */
@ -3869,12 +3889,6 @@ JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
return true;
}
void
JSObject::updateFixedSlots(uintN fixed)
{
flags = (flags & ~FIXED_SLOTS_MASK) | (fixed << FIXED_SLOTS_SHIFT);
}
void
JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
{
@ -3900,7 +3914,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() && a->hasPrivate() == b->hasPrivate()) {
if (size == b->structSize()) {
/*
* If the objects are the same size, then we make no assumptions about
* whether they have dynamically allocated slots and instead just copy
@ -3942,13 +3956,21 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
memcpy(a, b, sizeof tmp);
memcpy(b, &tmp, sizeof tmp);
a->updateFixedSlots(reserved.newafixed);
if (a->isNative())
a->shape_->setNumFixedSlots(reserved.newafixed);
else
a->shape_ = reserved.newashape;
a->slots = reserved.newaslots;
a->copySlotRange(0, reserved.bvals.begin(), bcap);
if (a->hasPrivate())
a->setPrivate(bpriv);
b->updateFixedSlots(reserved.newbfixed);
if (b->isNative())
b->shape_->setNumFixedSlots(reserved.newbfixed);
else
b->shape_ = reserved.newbshape;
b->slots = reserved.newbslots;
b->copySlotRange(0, reserved.avals.begin(), acap);
if (b->hasPrivate())
@ -4521,18 +4543,23 @@ JSObject::updateSlotsForSpan(size_t oldSpan, size_t newSpan)
invalidateSlotRange(newSpan, oldSpan - newSpan);
}
inline void
JSObject::initializePrivate()
#ifdef DEBUG
size_t
JSObject::numFixedSlotsFromAllocationKind(Class *clasp) const
{
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);
/*
* For checking that the fixed slot information in a shape is consistent
* with the allocation kind of this object.
*/
gc::AllocKind kind = getAllocKind();
size_t slots = gc::GetGCKindSlots(kind);
if (clasp->flags & JSCLASS_HAS_PRIVATE) {
JS_ASSERT(slots > 0);
slots--;
}
return slots;
}
#endif
bool
JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape)
@ -4540,22 +4567,21 @@ JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape)
JS_ASSERT(isNewborn());
JS_ASSERT(shape->compartment() == compartment());
JS_ASSERT(!shape->inDictionary());
if (shape->getObjectClass()->flags & JSCLASS_HAS_PRIVATE)
initializePrivate();
JS_ASSERT(numFixedSlotsFromAllocationKind(shape->getObjectClass()) == shape->numFixedSlots());
size_t span = shape->slotSpan();
if (!span) {
shape_ = const_cast<js::Shape *>(shape);
return true;
}
size_t count = dynamicSlotsCount(numFixedSlots(), span);
if (span) {
size_t count = dynamicSlotsCount(shape->numFixedSlots(), span);
if (count && !growSlots(cx, 0, count))
return false;
}
shape_ = const_cast<js::Shape *>(shape);
if (hasPrivate())
setPrivate(NULL);
if (span)
updateSlotsForSpan(0, span);
return true;
@ -4567,13 +4593,13 @@ JSObject::setInitialPropertyInfallible(const js::Shape *shape)
JS_ASSERT(isNewborn());
JS_ASSERT(shape->compartment() == compartment());
JS_ASSERT(!shape->inDictionary());
JS_ASSERT(numFixedSlotsFromAllocationKind(shape->getObjectClass()) == shape->numFixedSlots());
if (shape->getObjectClass()->flags & JSCLASS_HAS_PRIVATE)
initializePrivate();
JS_ASSERT(dynamicSlotsCount(numFixedSlots(), shape->slotSpan()) == 0);
JS_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()) == 0);
shape_ = const_cast<js::Shape *>(shape);
if (hasPrivate())
setPrivate(NULL);
size_t span = shape->slotSpan();
if (span)
@ -4587,6 +4613,7 @@ JSObject::setLastProperty(JSContext *cx, const js::Shape *shape)
JS_ASSERT(!inDictionaryMode());
JS_ASSERT(!shape->inDictionary());
JS_ASSERT(shape->compartment() == compartment());
JS_ASSERT(shape->numFixedSlots() == numFixedSlots());
size_t oldSpan = lastProperty()->slotSpan();
size_t newSpan = shape->slotSpan();
@ -4644,11 +4671,12 @@ JSObject::growSlots(JSContext *cx, uint32 oldCount, uint32 newCount)
*/
JS_ASSERT_IF(!isNewborn() && isCall(), asCall().maybeStackFrame() != NULL);
/* Don't let nslots get close to wrapping around uint32. */
if (newCount >= NSLOTS_LIMIT) {
JS_ReportOutOfMemory(cx);
return false;
}
/*
* Slot capacities are determined by the span of allocated objects. Due to
* the limited number of bits to store shape slots, object growth is
* throttled well before the slot capacity can overflow.
*/
JS_ASSERT(newCount < NELEMENTS_LIMIT);
size_t oldSize = Probes::objectResizeActive() ? slotsAndStructSize() : 0;
size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value);
@ -4756,8 +4784,8 @@ JSObject::growElements(JSContext *cx, uintN newcap)
else if (actualCapacity < SLOT_CAPACITY_MIN)
actualCapacity = SLOT_CAPACITY_MIN;
/* Don't let nslots get close to wrapping around uint32. */
if (actualCapacity >= NSLOTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
/* Don't let nelements get close to wrapping around uint32. */
if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
JS_ReportOutOfMemory(cx);
return false;
}
@ -5115,6 +5143,11 @@ JSObject::allocSlot(JSContext *cx, uint32 *slotp)
}
}
if (slot >= SHAPE_MAXIMUM_SLOT) {
js_ReportOutOfMemory(cx);
return false;
}
*slotp = slot;
if (inDictionaryMode() && !setSlotSpan(cx, slot + 1))

Просмотреть файл

@ -535,26 +535,16 @@ struct JSObject : js::gc::Cell
SINGLETON_TYPE = 0x10000,
LAZY_TYPE = 0x20000,
/* The top 5 bits of an object's flags are its number of fixed slots. */
FIXED_SLOTS_SHIFT = 27,
FIXED_SLOTS_MASK = 0x1f << FIXED_SLOTS_SHIFT,
UNUSED_FLAG_BITS = 0x07FC70A0
};
/*
* Impose a sane upper bound, originally checked only for dense arrays, on
* number of slots in an object.
*/
enum {
NSLOTS_BITS = 29,
NSLOTS_LIMIT = JS_BIT(NSLOTS_BITS)
};
uint32 flags; /* flags */
uint32 padding;
/* Upper bound on the number of elements in an object. */
static const uint32 NELEMENTS_LIMIT = JS_BIT(29);
private:
js::Value *slots; /* Slots for object properties. */
js::Value *elements; /* Slots for object elements. */
@ -671,6 +661,10 @@ struct JSObject : js::gc::Cell
inline size_t numFixedSlots() const;
#ifdef DEBUG
size_t numFixedSlotsFromAllocationKind(js::Class *clasp) const;
#endif
static const uint32 MAX_FIXED_SLOTS = 16;
private:
@ -929,7 +923,6 @@ 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); }
@ -1240,16 +1233,20 @@ struct JSObject : js::gc::Cell
inline bool isCallable();
/* Do initialization required immediately after allocation. */
void earlyInit(jsuword capacity)
void earlyInit()
{
flags = capacity << FIXED_SLOTS_SHIFT;
/* Stops obj from being scanned until initializated. */
shape_ = NULL;
}
/* The last property is not initialized here and should be set separately. */
void init(JSContext *cx, js::types::TypeObject *type, bool denseArray);
void init(JSContext *cx, js::types::TypeObject *type);
/*
* Finish initializing the elements in a dense array, after its initial
* property has been set.
*/
void initDenseArray();
inline void finish(JSContext *cx);
JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
@ -1314,8 +1311,6 @@ struct JSObject : js::gc::Cell
static void TradeGuts(JSContext *cx, JSObject *a, JSObject *b,
TradeGutsReserved &reserved);
void updateFixedSlots(uintN fixed);
public:
/* Add a property whose id is not yet in this scope. */
const js::Shape *addProperty(JSContext *cx, jsid id,
@ -1443,11 +1438,9 @@ struct JSObject : js::gc::Cell
JS_STATIC_ASSERT(sizeof(JSObject) % sizeof(js::Value) == 0);
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, 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));
JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Object::FIXED_SLOTS_SHIFT);
}
};
@ -1476,7 +1469,7 @@ JSObject::fixedSlots() const {
inline size_t
JSObject::numFixedSlots() const
{
return flags >> FIXED_SLOTS_SHIFT;
return reinterpret_cast<const js::shadow::Object *>(this)->numFixedSlots();
}
/* static */ inline size_t

Просмотреть файл

@ -382,7 +382,7 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent
if (!type)
return false;
init(cx, type, false);
init(cx, type);
if (!setInitialProperty(cx, bindings.lastShape()))
return false;
@ -419,7 +419,7 @@ 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, false);
init(cx, type);
if (!setInitialProperty(cx, getProto()->lastProperty()))
return false;
@ -537,6 +537,7 @@ JSObject::setLastPropertyInfallible(const js::Shape *shape)
JS_ASSERT(shape->compartment() == compartment());
JS_ASSERT(!inDictionaryMode());
JS_ASSERT(slotSpan() == shape->slotSpan());
JS_ASSERT(numFixedSlots() == shape->numFixedSlots());
shape_ = const_cast<js::Shape *>(shape);
}
@ -1019,28 +1020,28 @@ JSObject::isQName() const
}
inline void
JSObject::init(JSContext *cx, js::types::TypeObject *type, bool denseArray)
JSObject::init(JSContext *cx, js::types::TypeObject *type)
{
JS_STATIC_ASSERT(sizeof(js::ObjectElements) == 2 * sizeof(js::Value));
uint32 numSlots = numFixedSlots();
/*
* Fill the fixed slots with undefined if needed. This object must
* already have its numFixedSlots() filled in, as by js_NewGCObject.
*/
slots = NULL;
if (denseArray) {
JS_ASSERT(numSlots >= 2);
elements = fixedElements();
new (getElementsHeader()) js::ObjectElements(numSlots - 2);
} else {
elements = js::emptyObjectElements;
}
flags = 0;
setType(type);
}
inline void
JSObject::initDenseArray()
{
JS_ASSERT(hasClass(&js::ArrayClass));
JS_STATIC_ASSERT(sizeof(js::ObjectElements) == 2 * sizeof(js::Value));
JS_ASSERT(numFixedSlots() >= 2);
/* Fill in the object's inline elements header. */
elements = fixedElements();
new (getElementsHeader()) js::ObjectElements(numFixedSlots() - 2);
}
inline void
JSObject::finish(JSContext *cx)
{
@ -1057,7 +1058,7 @@ JSObject::initSharingEmptyShape(JSContext *cx,
void *privateValue,
js::gc::AllocKind kind)
{
init(cx, type, false);
init(cx, type);
js::EmptyShape *empty = type->getEmptyShape(cx, aclasp, kind);
if (!empty)
@ -1438,7 +1439,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject *par
if (type->canProvideEmptyShape(clasp) && parent == type->proto->getParent())
empty = type->getEmptyShape(cx, clasp, kind);
else
empty = js::EmptyShape::create(cx, clasp, parent);
empty = js::EmptyShape::create(cx, clasp, parent, gc::GetGCKindSlots(kind, clasp));
if (!empty) {
JS_ASSERT(obj->isNewborn());
return false;
@ -1448,11 +1449,12 @@ InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject *par
}
static inline bool
InitScopeForNonNativeObject(JSContext *cx, JSObject *obj, js::Class *clasp, JSObject *parent)
InitScopeForNonNativeObject(JSContext *cx, JSObject *obj, js::Class *clasp, JSObject *parent,
gc::AllocKind kind)
{
JS_ASSERT(!clasp->isNative());
const js::Shape *empty = js::BaseShape::lookupInitialShape(cx, clasp, parent);
const js::Shape *empty = js::BaseShape::lookupInitialShape(cx, clasp, parent, kind);
if (!empty)
return false;
JS_ASSERT(empty->isEmptyShape());
@ -1510,8 +1512,7 @@ NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, gc::AllocKi
* Default parent to the parent of the prototype, which was set from
* the parent of the prototype's constructor.
*/
bool denseArray = (clasp == &ArrayClass);
obj->init(cx, type, denseArray);
obj->init(cx, type);
JS_ASSERT(type->canProvideEmptyShape(clasp));
@ -1666,7 +1667,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
if (!obj)
goto out;
obj->init(cx, type, clasp == &ArrayClass);
obj->init(cx, type);
/*
* Default parent to the parent of the prototype, which was set from
@ -1677,7 +1678,7 @@ NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
if (clasp->isNative()
? !InitScopeForObject(cx, obj, clasp, parent, type, kind)
: !InitScopeForNonNativeObject(cx, obj, clasp, parent)) {
: !InitScopeForNonNativeObject(cx, obj, clasp, parent, kind)) {
obj = NULL;
}
@ -1734,7 +1735,7 @@ NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::
if (!obj)
goto out;
obj->init(cx, type, false);
obj->init(cx, type);
/*
* Default parent to the parent of the prototype, which was set from

Просмотреть файл

@ -179,8 +179,8 @@ PropertyTree::getChild(JSContext *cx, Shape *parent, const Shape &child)
UnownedBaseShape *base = child.base()->unowned();
new (shape) Shape(base, child.propid_, child.slot_, child.attrs,
child.flags, child.shortid_);
new (shape) Shape(&child);
shape->base_ = base;
if (!insertChild(cx, parent, shape))
return NULL;

Просмотреть файл

@ -199,6 +199,7 @@ class BaseShape;
class UnownedBaseShape;
struct Shape;
struct EmptyShape;
class ShapeKindArray;
class Bindings;
class MultiDeclRange;

Просмотреть файл

@ -741,6 +741,7 @@ inline bool
JSObject::initRegExp(JSContext *cx, js::RegExp *re)
{
JS_ASSERT(isRegExp());
JS_ASSERT(getAllocKind() == js::gc::FINALIZE_OBJECT8);
/*
* It's currently possible to swap RegExp guts. In that case this object
@ -748,14 +749,14 @@ JSObject::initRegExp(JSContext *cx, js::RegExp *re)
*/
if (nativeEmpty()) {
const js::Shape *shape = js::BaseShape::lookupInitialShape(cx, getClass(), getParent(),
lastProperty());
js::gc::FINALIZE_OBJECT8, lastProperty());
if (!shape)
return false;
if (shape == lastProperty()) {
shape = assignInitialRegExpShape(cx);
if (!shape)
return false;
js::BaseShape::insertInitialShape(cx, shape);
js::BaseShape::insertInitialShape(cx, js::gc::FINALIZE_OBJECT8, shape);
}
setLastPropertyInfallible(shape);
JS_ASSERT(!nativeEmpty());

Просмотреть файл

@ -313,6 +313,30 @@ Shape::getChildBinding(JSContext *cx, const js::Shape &child, Shape **lastBindin
JS_ASSERT(shape->parent == this);
JS_ASSERT(this == *lastBinding);
*lastBinding = shape;
/*
* Update the number of fixed slots which bindings of this shape will
* have. Bindings are constructed as new properties come in, so the
* call object allocation class is not known ahead of time. Compute
* the fixed slot count here, which will feed into call objects created
* off of the bindings.
*/
uint32 slots = child.slotSpan() + 1; /* Add one for private data. */
gc::AllocKind kind = gc::GetGCObjectKind(slots);
/*
* Make sure that the arguments and variables in the call object all
* end up in a contiguous range of slots. We need this to be able to
* embed the args/vars arrays in the TypeScriptNesting for the function
* after the call object's frame has finished.
*/
uint32 nfixed = gc::GetGCKindSlots(kind);
if (nfixed < slots) {
nfixed = CallObject::RESERVED_SLOTS + 1;
JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1);
}
shape->setNumFixedSlots(nfixed - 1);
}
return shape;
}
@ -355,13 +379,13 @@ JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
* JS_ClearScope call.
*/
if (!child.hasSlot()) {
child.slot_ = parent->maybeSlot();
child.setSlot(parent->maybeSlot());
} else {
if (child.hasMissingSlot()) {
uint32 slot;
if (!allocSlot(cx, &slot))
return NULL;
child.slot_ = slot;
child.setSlot(slot);
} else {
/* Slots can only be allocated out of order on objects in dictionary mode. */
JS_ASSERT(inDictionaryMode() ||
@ -625,7 +649,7 @@ JSObject::addPropertyInternal(JSContext *cx, jsid id,
if (!nbase)
return NULL;
Shape child(nbase, id, slot, attrs, flags, shortid);
Shape child(nbase, id, slot, numFixedSlots(), attrs, flags, shortid);
shape = getChildProperty(cx, lastProperty(), child);
}
@ -773,7 +797,7 @@ JSObject::putProperty(JSContext *cx, jsid id,
else
shape->base_ = nbase;
shape->slot_ = slot;
shape->setSlot(slot);
shape->attrs = uint8(attrs);
shape->flags = flags | Shape::IN_DICTIONARY;
shape->shortid_ = int16(shortid);
@ -803,7 +827,7 @@ JSObject::putProperty(JSContext *cx, jsid id,
JS_ASSERT(shape == lastProperty());
/* Find or create a property tree node labeled by our arguments. */
Shape child(nbase, id, slot, attrs, flags, shortid);
Shape child(nbase, id, slot, numFixedSlots(), attrs, flags, shortid);
Shape *newShape = getChildProperty(cx, shape->parent, child);
if (!newShape) {
@ -1178,7 +1202,7 @@ LookupBaseShape(JSContext *cx, const BaseShape &base)
JSCompartment::BaseShapeEntry entry;
entry.base = static_cast<UnownedBaseShape *>(nbase);
entry.shape = NULL;
entry.shapes = NULL;
if (!table.relookupOrAdd(p, &base, entry))
return NULL;
@ -1194,32 +1218,41 @@ BaseShape::lookup(JSContext *cx, const BaseShape &base)
}
/* static */ Shape *
BaseShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *parent, Shape *initial)
BaseShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *parent,
AllocKind kind, Shape *initial)
{
js::BaseShape base(clasp, parent);
JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, base);
if (!entry)
return NULL;
if (entry->shape)
return entry->shape;
if (initial) {
entry->shape = initial;
return entry->shape;
if (!entry->shapes) {
entry->shapes = cx->new_<ShapeKindArray>();
if (!entry->shapes)
return NULL;
}
entry->shape = JS_PROPERTY_TREE(cx).newShape(cx);
if (!entry->shape)
Shape *&shape = entry->shapes->get(kind);
if (shape)
return shape;
if (initial) {
shape = initial;
return initial;
}
shape = JS_PROPERTY_TREE(cx).newShape(cx);
if (!shape)
return NULL;
return new (entry->shape) EmptyShape(entry->base);
return new (shape) EmptyShape(entry->base, gc::GetGCKindSlots(kind, clasp));
}
/* static */ void
BaseShape::insertInitialShape(JSContext *cx, const Shape *initial)
BaseShape::insertInitialShape(JSContext *cx, AllocKind kind, const Shape *initial)
{
JSCompartment::BaseShapeEntry *entry = LookupBaseShape(cx, *initial->base());
JS_ASSERT(entry && entry->base == initial->base());
entry->shape = const_cast<Shape *>(initial);
JS_ASSERT(entry && entry->base == initial->base() && entry->shapes);
entry->shapes->get(kind) = const_cast<Shape *>(initial);
}
void
@ -1229,10 +1262,17 @@ JSCompartment::sweepBaseShapeTable(JSContext *cx)
for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
JSCompartment::BaseShapeEntry &entry =
const_cast<JSCompartment::BaseShapeEntry &>(e.front());
if (!entry.base->isMarked())
if (!entry.base->isMarked()) {
if (entry.shapes)
cx->delete_(entry.shapes);
e.removeFront();
else if (entry.shape && !entry.shape->isMarked())
entry.shape = NULL;
} else if (entry.shapes) {
for (size_t i = 0; i < ShapeKindArray::SHAPE_COUNT; i++) {
Shape *&shape = entry.shapes->getIndex(i);
if (shape && !shape->isMarked())
shape = NULL;
}
}
}
}
}

Просмотреть файл

@ -197,22 +197,23 @@
* scope->table isn't worth it. So instead of always allocating scope->table,
* we leave it null while initializing all the other scope members as if it
* were non-null and minimal-length. Until a scope is searched
* MAX_LINEAR_SEARCHES times, we use linear search from obj->lastProp to find a
* LINEAR_SEARCHES_MAX times, we use linear search from obj->lastProp to find a
* given id, and save on the time and space overhead of creating a hash table.
*/
#define SHAPE_INVALID_SLOT 0xffffffff
namespace js {
/* Limit on the number of slotful properties in an object. */
static const uint32 SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
static const uint32 SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
/*
* Shapes use multiplicative hashing, _a la_ jsdhash.[ch], but specialized to
* minimize footprint. But if a Shape lineage has been searched fewer than
* MAX_LINEAR_SEARCHES times, we use linear search and avoid allocating
* LINEAR_SEARCHES_MAX times, we use linear search and avoid allocating
* scope->table.
*/
struct PropertyTable {
static const uint32 MAX_LINEAR_SEARCHES = 7;
static const uint32 MIN_SIZE_LOG2 = 4;
static const uint32 MIN_SIZE = JS_BIT(MIN_SIZE_LOG2);
@ -425,10 +426,10 @@ class BaseShape : public js::gc::Cell
* if none was found.
*/
static Shape *lookupInitialShape(JSContext *cx, Class *clasp, JSObject *parent,
Shape *initial = NULL);
gc::AllocKind kind, Shape *initial = NULL);
/* Reinsert a possibly modified initial shape to the baseShapes table. */
static void insertInitialShape(JSContext *cx, const Shape *initial);
static void insertInitialShape(JSContext *cx, gc::AllocKind kind, const Shape *initial);
/* Get the canonical base shape. */
inline UnownedBaseShape *unowned();
@ -481,24 +482,32 @@ struct Shape : public js::gc::Cell
BaseShape *base_;
jsid propid_;
/*
* numLinearSearches starts at zero and is incremented initially on each
* search() call. Once numLinearSearches reaches MAX_LINEAR_SEARCHES
* (which is a small integer), the table is created on the next search()
* call. The table can also be created when hashifying for dictionary
* mode.
*/
uint8 numLinearSearches:3;
enum {
/* Number of fixed slots in objects with this shape. */
FIXED_SLOTS_MAX = 0x1f,
FIXED_SLOTS_SHIFT = 27,
FIXED_SLOTS_MASK = FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT,
/*
* Index in object slots for shapes which hasSlot(). For !hasSlot() shapes
* in the property tree with a parent, stores the parent's slot (which may
* be invalid), and invalid for all other shapes.
* numLinearSearches starts at zero and is incremented initially on
* search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
* the table is created on the next search() call. The table can also
* be created when hashifying for dictionary mode.
*/
uint32 slot_:29;
LINEAR_SEARCHES_MAX = 0x7,
LINEAR_SEARCHES_SHIFT = 24,
LINEAR_SEARCHES_MASK = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
static const size_t SLOT_BITS = 29;
/*
* Mask to get the index in object slots for shapes which hasSlot().
* For !hasSlot() shapes in the property tree with a parent, stores the
* parent's slot index (which may be invalid), and invalid for all
* other shapes.
*/
SLOT_MASK = JS_BIT(24) - 1
};
uint32 slotInfo; /* mask of above info */
uint8 attrs; /* attributes, see jsapi.h JSPROP_* */
uint8 flags; /* flags, see below for defines */
int16 shortid_; /* tinyid, or local arg/var index */
@ -532,8 +541,10 @@ struct Shape : public js::gc::Cell
void handoffTableTo(Shape *newShape);
void setParent(js::Shape *p) {
JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(), p->slot_ <= slot_);
JS_ASSERT_IF(p && !inDictionary(), hasSlot() == (p->slot_ != slot_));
JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
p->maybeSlot() <= maybeSlot());
JS_ASSERT_IF(p && !inDictionary(),
hasSlot() == (p->maybeSlot() != maybeSlot()));
parent = p;
}
@ -619,13 +630,13 @@ struct Shape : public js::gc::Cell
UNUSED_BITS = 0x3C
};
Shape(BaseShape *base, jsid id, uint32 slot, uintN attrs, uintN flags, intN shortid);
Shape(BaseShape *base, jsid id, uint32 slot, uint32 nfixed, uintN attrs, uintN flags, intN shortid);
/* Get a shape identical to this one, without parent/kids information. */
Shape(const Shape *other);
/* Used by EmptyShape (see jsscopeinlines.h). */
Shape(BaseShape *base);
Shape(BaseShape *base, uint32 nfixed);
/* Copy constructor disabled, to avoid misuse of the above form. */
Shape(const Shape &other);
@ -638,7 +649,7 @@ struct Shape : public js::gc::Cell
* if the shape is being constructed and has not had a slot assigned yet.
* After construction, hasSlot() implies !hasMissingSlot().
*/
bool hasMissingSlot() const { return slot_ == (SHAPE_INVALID_SLOT >> (32 - SLOT_BITS)); }
bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
public:
/* Public bits stored in shape->flags. */
@ -724,8 +735,8 @@ struct Shape : public js::gc::Cell
BaseShape *base() const { return base_; }
bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
uint32 slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
uint32 maybeSlot() const { return hasMissingSlot() ? SHAPE_INVALID_SLOT : slot_; }
uint32 slot() const { JS_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
uint32 maybeSlot() const { return slotInfo & SLOT_MASK; }
bool isEmptyShape() const {
JS_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
@ -738,6 +749,33 @@ struct Shape : public js::gc::Cell
return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
}
void setSlot(uint32 slot) {
JS_ASSERT(slot <= SHAPE_INVALID_SLOT);
slotInfo = slotInfo & ~SLOT_MASK;
slotInfo = slotInfo | slot;
}
uint32 numFixedSlots() const {
return (slotInfo >> FIXED_SLOTS_SHIFT);
}
void setNumFixedSlots(uint32 nfixed) {
JS_ASSERT(nfixed < FIXED_SLOTS_MAX);
slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
}
uint32 numLinearSearches() const {
return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
}
void incrementNumLinearSearches() {
uint32 count = numLinearSearches();
JS_ASSERT(count < LINEAR_SEARCHES_MAX);
slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
}
jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); }
jsid maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
@ -852,14 +890,16 @@ struct Shape : public js::gc::Cell
private:
static void staticAsserts() {
JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
}
};
struct EmptyShape : public js::Shape
{
EmptyShape(BaseShape *base);
EmptyShape(BaseShape *base, uint32 nfixed);
static EmptyShape *create(JSContext *cx, js::Class *clasp, JSObject *parent) {
static EmptyShape *create(JSContext *cx, js::Class *clasp, JSObject *parent, uint32 nfixed) {
BaseShape lookup(clasp, parent);
BaseShape *base = BaseShape::lookup(cx, lookup);
if (!base)
@ -868,7 +908,37 @@ struct EmptyShape : public js::Shape
js::Shape *eprop = JS_PROPERTY_TREE(cx).newShape(cx);
if (!eprop)
return NULL;
return new (eprop) EmptyShape(base);
return new (eprop) EmptyShape(base, nfixed);
}
};
/* Heap-allocated array of shapes for each object allocation size. */
class ShapeKindArray
{
public:
static const uint32 SHAPE_COUNT =
((js::gc::FINALIZE_FUNCTION - js::gc::FINALIZE_OBJECT0) / 2) + 1;
ShapeKindArray() { PodZero(this); }
Shape *&get(gc::AllocKind kind) {
JS_ASSERT(kind >= gc::FINALIZE_OBJECT0 &&
kind <= gc::FINALIZE_FUNCTION_AND_OBJECT_LAST);
int i = (kind - gc::FINALIZE_OBJECT0) / 2;
return shapes[i];
}
Shape *&getIndex(size_t i) {
JS_ASSERT(i < SHAPE_COUNT);
return shapes[i];
}
private:
Shape *shapes[SHAPE_COUNT];
void staticAsserts() {
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT0 % 2 == 0);
JS_STATIC_ASSERT(gc::FINALIZE_FUNCTION == gc::FINALIZE_OBJECT_LAST + 1);
}
};
@ -902,14 +972,13 @@ Shape::search(JSContext *cx, js::Shape **pstart, jsid id, bool adding)
if (start->hasTable())
return start->table().search(id, adding);
if (start->numLinearSearches == PropertyTable::MAX_LINEAR_SEARCHES) {
if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
if (start->hashify(cx))
return start->table().search(id, adding);
/* OOM! Don't increment numLinearSearches, to keep hasTable() false. */
JS_ASSERT(!start->hasTable());
} else {
JS_ASSERT(start->numLinearSearches < PropertyTable::MAX_LINEAR_SEARCHES);
start->numLinearSearches++;
start->incrementNumLinearSearches();
}
/*

Просмотреть файл

@ -57,11 +57,6 @@
#include "jsgcinlines.h"
#include "jsobjinlines.h"
JS_STATIC_ASSERT(js::gc::FINALIZE_OBJECT0 % 2 == 0);
JS_STATIC_ASSERT(js::gc::FINALIZE_FUNCTION == js::gc::FINALIZE_OBJECT_LAST + 1);
static const uint32 TYPE_OBJECT_EMPTY_SHAPE_COUNT =
((js::gc::FINALIZE_FUNCTION - js::gc::FINALIZE_OBJECT0) / 2) + 1;
inline js::EmptyShape *
js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp, gc::AllocKind kind)
{
@ -74,46 +69,40 @@ js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp, gc::Alloc
*/
JS_ASSERT(proto->hasNewType(this));
JS_ASSERT(kind >= gc::FINALIZE_OBJECT0 &&
kind <= gc::FINALIZE_FUNCTION_AND_OBJECT_LAST);
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT0 % 2 == 0);
JS_STATIC_ASSERT(gc::FINALIZE_FUNCTION == gc::FINALIZE_OBJECT_LAST + 1);
int i = (kind - gc::FINALIZE_OBJECT0) / 2;
if (!emptyShapes) {
emptyShapes = (EmptyShape**)
cx->calloc_(sizeof(EmptyShape*) * TYPE_OBJECT_EMPTY_SHAPE_COUNT);
emptyShapes = cx->new_<ShapeKindArray>();
if (!emptyShapes)
return NULL;
/*
* Always fill in emptyShapes[0], so canProvideEmptyShape works.
* Always fill in the first empty shape, so canProvideEmptyShape works.
* Other empty shapes are filled in lazily.
*/
emptyShapes[0] = EmptyShape::create(cx, aclasp, proto->getParent());
if (!emptyShapes[0]) {
cx->free_(emptyShapes);
Shape *first = EmptyShape::create(cx, aclasp, proto->getParent(), 0);
if (!first) {
cx->delete_(emptyShapes);
emptyShapes = NULL;
return NULL;
}
emptyShapes->get(gc::FINALIZE_OBJECT0) = first;
}
JS_ASSERT(aclasp == emptyShapes[0]->getObjectClass());
JS_ASSERT(aclasp == emptyShapes->get(gc::FINALIZE_OBJECT0)->getObjectClass());
if (!emptyShapes[i]) {
emptyShapes[i] = EmptyShape::create(cx, aclasp, proto->getParent());
if (!emptyShapes[i])
return NULL;
Shape *&empty = emptyShapes->get(kind);
if (!empty) {
empty = EmptyShape::create(cx, aclasp, proto->getParent(),
gc::GetGCKindSlots(kind, aclasp));
}
return emptyShapes[i];
return static_cast<EmptyShape *>(empty);
}
inline bool
js::types::TypeObject::canProvideEmptyShape(js::Class *aclasp)
{
return proto && !singleton && (!emptyShapes || emptyShapes[0]->getObjectClass() == aclasp);
return proto && !singleton &&
(!emptyShapes || emptyShapes->get(gc::FINALIZE_OBJECT0)->getObjectClass() == aclasp);
}
inline void
@ -139,9 +128,10 @@ inline bool
StringObject::init(JSContext *cx, JSString *str)
{
JS_ASSERT(nativeEmpty());
JS_ASSERT(getAllocKind() == gc::FINALIZE_OBJECT2);
const js::Shape *shape = BaseShape::lookupInitialShape(cx, getClass(), getParent(),
lastProperty());
gc::FINALIZE_OBJECT2, lastProperty());
if (!shape)
return false;
if (shape != lastProperty()) {
@ -150,7 +140,7 @@ StringObject::init(JSContext *cx, JSString *str)
shape = assignInitialShape(cx);
if (!shape)
return false;
BaseShape::insertInitialShape(cx, shape);
BaseShape::insertInitialShape(cx, gc::FINALIZE_OBJECT2, shape);
}
JS_ASSERT(!nativeEmpty());
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))->slot() == LENGTH_SLOT);
@ -179,12 +169,11 @@ BaseShape::adoptUnowned(UnownedBaseShape *other)
}
inline
Shape::Shape(BaseShape *base, jsid propid, uint32 slot,
Shape::Shape(BaseShape *base, jsid propid, uint32 slot, uint32 nfixed,
uintN attrs, uintN flags, intN shortid)
: base_(base),
propid_(propid),
numLinearSearches(0),
slot_(slot),
slotInfo(slot | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(uint8(attrs)),
flags(uint8(flags)),
shortid_(int16(shortid)),
@ -200,8 +189,7 @@ inline
Shape::Shape(const Shape *other)
: base_(other->base()->unowned()),
propid_(other->maybePropid()),
numLinearSearches(0),
slot_(other->maybeSlot()),
slotInfo(other->slotInfo & ~LINEAR_SEARCHES_MASK),
attrs(other->attrs),
flags(other->flags),
shortid_(other->maybeShortid()),
@ -211,11 +199,10 @@ Shape::Shape(const Shape *other)
}
inline
Shape::Shape(BaseShape *base)
Shape::Shape(BaseShape *base, uint32 nfixed)
: base_(base),
propid_(JSID_EMPTY),
numLinearSearches(0),
slot_(SHAPE_INVALID_SLOT >> (32 - SLOT_BITS)),
slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(JSPROP_SHARED),
flags(0),
shortid_(0),
@ -234,7 +221,7 @@ Shape::hash() const
hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & PUBLIC_FLAGS);
hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs;
hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid_;
hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_;
hash = JS_ROTATE_LEFT32(hash, 4) ^ maybeSlot();
hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid_);
return hash;
}
@ -243,7 +230,7 @@ inline bool
Shape::matches(const js::Shape *other) const
{
return propid_ == other->propid_ &&
matchesParamsAfterId(other->base(), other->slot_, other->attrs,
matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs,
other->flags, other->shortid_);
}
@ -252,7 +239,7 @@ Shape::matchesParamsAfterId(BaseShape *base, uint32 aslot,
uintN aattrs, uintN aflags, intN ashortid) const
{
return base->unowned() == this->base()->unowned() &&
slot_ == aslot &&
maybeSlot() == aslot &&
attrs == aattrs &&
((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
shortid_ == ashortid;
@ -344,7 +331,8 @@ Shape::initDictionaryShape(const Shape &child, Shape **dictp)
{
UnownedBaseShape *base = child.base()->unowned();
new (this) Shape(base, child.maybePropid(), child.maybeSlot(), child.attrs,
new (this) Shape(base, child.maybePropid(),
child.maybeSlot(), child.numFixedSlots(), child.attrs,
child.flags | IN_DICTIONARY, child.maybeShortid());
this->listp = NULL;
@ -352,8 +340,8 @@ Shape::initDictionaryShape(const Shape &child, Shape **dictp)
}
inline
EmptyShape::EmptyShape(BaseShape *base)
: js::Shape(base)
EmptyShape::EmptyShape(BaseShape *base, uint32 nfixed)
: js::Shape(base, nfixed)
{
/* Only empty shapes can be NON_NATIVE. */
if (!getObjectClass()->isNative())

Просмотреть файл

@ -168,7 +168,7 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
if (!nbase)
return NULL;
Shape child(nbase, id, slot, attrs, Shape::HAS_SHORTID, *indexp);
Shape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
/* Shapes in bindings cannot be dictionaries. */
Shape *shape = lastBinding->getChildBinding(cx, child, &lastBinding);

Просмотреть файл

@ -48,6 +48,7 @@
#include "jsregexp.h"
#include "jsscript.h"
#include "jsscope.h"
#include "vm/CallObject.h"
#include "vm/GlobalObject.h"
#include "jsscopeinlines.h"
@ -93,7 +94,11 @@ bool
Bindings::ensureShape(JSContext *cx)
{
if (!lastBinding) {
lastBinding = BaseShape::lookupInitialShape(cx, &CallClass, NULL);
/* Get an allocation kind to match an empty call object. */
gc::AllocKind kind = gc::FINALIZE_OBJECT4;
JS_ASSERT(gc::GetGCKindSlots(kind) == CallObject::RESERVED_SLOTS + 1);
lastBinding = BaseShape::lookupInitialShape(cx, &CallClass, NULL, kind);
if (!lastBinding)
return false;
}

Просмотреть файл

@ -11093,6 +11093,8 @@ TraceRecorder::record_JSOP_OBJTOP()
RecordingStatus
TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins)
{
JS_NOT_REACHED("FIXME");
#if 0
/*
* This function requires that |ctor| be a built-in class function in order
* to have an immutable |ctor.prototype| that can be burned into the trace
@ -11134,12 +11136,15 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins)
JS_ASSERT_IF(clasp != &ArrayClass, proto->getNewType(cx)->emptyShapes[0]->getObjectClass() == clasp);
proto_ins = w.immpObjGC(proto);
#endif
return RECORD_CONTINUE;
}
RecordingStatus
TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins)
{
JS_NOT_REACHED("FIXME");
#if 0
#ifdef DEBUG
TraceMonitor &localtm = *traceMonitor;
#endif
@ -11163,6 +11168,7 @@ TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins)
#endif
proto_ins = w.immpObjGC(proto);
#endif
return RECORD_CONTINUE;
}

Просмотреть файл

@ -208,6 +208,7 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes)
JSObject *obj = NewBuiltinClassInstance(cx, &ArrayBuffer::slowClass);
if (!obj)
return NULL;
JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT16);
if (nbytes < 0) {
/*
@ -221,7 +222,8 @@ ArrayBuffer::create(JSContext *cx, int32 nbytes)
JS_ASSERT(obj->getClass() == &ArrayBuffer::slowClass);
js::Shape *empty = BaseShape::lookupInitialShape(cx, &ArrayBufferClass, obj->getParent());
js::Shape *empty = BaseShape::lookupInitialShape(cx, &ArrayBufferClass, obj->getParent(),
gc::FINALIZE_OBJECT16);
if (!empty)
return false;
obj->setLastPropertyInfallible(empty);
@ -1264,6 +1266,7 @@ class TypedArrayTemplate
JSObject *obj = NewBuiltinClassInstance(cx, slowClass());
if (!obj)
return NULL;
JS_ASSERT(obj->getAllocKind() == gc::FINALIZE_OBJECT8);
/*
* Specialize the type of the object on the current scripted location,
@ -1297,7 +1300,8 @@ class TypedArrayTemplate
JS_ASSERT(obj->getClass() == slowClass());
js::Shape *empty = BaseShape::lookupInitialShape(cx, fastClass(), obj->getParent());
js::Shape *empty = BaseShape::lookupInitialShape(cx, fastClass(), obj->getParent(),
gc::FINALIZE_OBJECT8);
if (!empty)
return false;
obj->setLastPropertyInfallible(empty);

Просмотреть файл

@ -316,7 +316,7 @@ LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
int32 c1 = e1.u.check.constant;
/*
* initialized lengths are always <= JSObject::NSLOTS_LIMIT, check for
* initialized lengths are always <= JSObject::NELEMENTS_LIMIT, check for
* integer overflow checks redundant given initialized length checks.
* If Y <= c0 and Y + c1 < initlen(array):
*
@ -331,7 +331,7 @@ LoopState::entryRedundant(const InvariantEntry &e0, const InvariantEntry &e1)
constant = c0;
else if (!SafeAdd(c0, c1, &constant))
return false;
return constant >= JSObject::NSLOTS_LIMIT;
return constant >= (int32) JSObject::NELEMENTS_LIMIT;
}
/* Look for matching tests that differ only in their constants. */

Просмотреть файл

@ -55,20 +55,7 @@ CallObject *
CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
{
Bindings &bindings = script->bindings;
size_t argsVars = bindings.countArgsAndVars();
size_t slots = RESERVED_SLOTS + argsVars + 1; /* Add one for privateData. */
gc::AllocKind kind = gc::GetGCObjectKind(slots);
/*
* Make sure that the arguments and variables in the call object all end up
* in a contiguous range of slots. We need this to be able to embed the
* args/vars arrays in the TypeScriptNesting for the function, after the
* call object's frame has finished.
*/
if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
kind = gc::GetGCObjectKind(RESERVED_SLOTS + 1);
JS_ASSERT(gc::GetGCKindSlots(kind) == RESERVED_SLOTS + 1);
}
gc::AllocKind kind = gc::GetGCObjectKind(bindings.lastShape()->numFixedSlots() + 1);
JSObject *obj = js_NewGCObject(cx, kind);
if (!obj)