Bug 1073700 - Move getter/setter data out of BaseShape into a new AccessorShape type. r=bhackett

This commit is contained in:
Jan de Mooij 2014-10-10 11:32:14 +02:00
parent ba30c1f026
commit 0fd1291839
12 changed files with 411 additions and 233 deletions

View File

@ -87,6 +87,7 @@ enum AllocKind {
FINALIZE_SCRIPT,
FINALIZE_LAZY_SCRIPT,
FINALIZE_SHAPE,
FINALIZE_ACCESSOR_SHAPE,
FINALIZE_BASE_SHAPE,
FINALIZE_TYPE_OBJECT,
FINALIZE_FAT_INLINE_STRING,
@ -119,6 +120,7 @@ MapAllocToTraceKind(AllocKind kind)
JSTRACE_SCRIPT, /* FINALIZE_SCRIPT */
JSTRACE_LAZY_SCRIPT,/* FINALIZE_LAZY_SCRIPT */
JSTRACE_SHAPE, /* FINALIZE_SHAPE */
JSTRACE_SHAPE, /* FINALIZE_ACCESSOR_SHAPE */
JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
JSTRACE_STRING, /* FINALIZE_FAT_INLINE_STRING */

View File

@ -1214,6 +1214,12 @@ ScanShape(GCMarker *gcmarker, Shape *shape)
else if (JSID_IS_SYMBOL(id))
PushMarkStack(gcmarker, JSID_TO_SYMBOL(id));
if (shape->hasGetterObject())
MaybePushMarkStackBetweenSlices(gcmarker, shape->getterObject());
if (shape->hasSetterObject())
MaybePushMarkStackBetweenSlices(gcmarker, shape->setterObject());
shape = shape->previous();
if (shape && shape->markIfUnmarked(gcmarker->getMarkColor()))
goto restart;
@ -1226,12 +1232,6 @@ ScanBaseShape(GCMarker *gcmarker, BaseShape *base)
base->compartment()->mark();
if (base->hasGetterObject())
MaybePushMarkStackBetweenSlices(gcmarker, base->getterObject());
if (base->hasSetterObject())
MaybePushMarkStackBetweenSlices(gcmarker, base->setterObject());
if (JSObject *parent = base->getObjectParent()) {
MaybePushMarkStackBetweenSlices(gcmarker, parent);
} else if (GlobalObject *global = base->compartment()->unsafeUnbarrieredMaybeGlobal()) {
@ -1427,9 +1427,8 @@ gc::MarkChildren(JSTracer *trc, BaseShape *base)
* This function is used by the cycle collector to trace through the
* children of a BaseShape (and its baseUnowned(), if any). The cycle
* collector does not directly care about BaseShapes, so only the
* getter, setter, and parent are marked. Furthermore, the parent is
* marked only if it isn't the same as prevParent, which will be
* updated to the current shape's parent.
* parent is marked. Furthermore, the parent is marked only if it isn't the
* same as prevParent, which will be updated to the current shape's parent.
*/
static inline void
MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent)
@ -1438,23 +1437,10 @@ MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent
/*
* The cycle collector does not need to trace unowned base shapes,
* as they have the same getter, setter and parent as the original
* base shape.
* as they have the same parent as the original base shape.
*/
base->assertConsistency();
if (base->hasGetterObject()) {
JSObject *tmp = base->getterObject();
MarkObjectUnbarriered(trc, &tmp, "getter");
MOZ_ASSERT(tmp == base->getterObject());
}
if (base->hasSetterObject()) {
JSObject *tmp = base->setterObject();
MarkObjectUnbarriered(trc, &tmp, "setter");
MOZ_ASSERT(tmp == base->setterObject());
}
JSObject *parent = base->getObjectParent();
if (parent && parent != *prevParent) {
MarkObjectUnbarriered(trc, &parent, "parent");
@ -1478,6 +1464,19 @@ gc::MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
do {
MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
MarkId(trc, &shape->propidRef(), "propid");
if (shape->hasGetterObject()) {
JSObject *tmp = shape->getterObject();
MarkObjectUnbarriered(trc, &tmp, "getter");
MOZ_ASSERT(tmp == shape->getterObject());
}
if (shape->hasSetterObject()) {
JSObject *tmp = shape->setterObject();
MarkObjectUnbarriered(trc, &tmp, "setter");
MOZ_ASSERT(tmp == shape->setterObject());
}
shape = shape->previous();
} while (shape);
}

View File

@ -357,7 +357,14 @@ StackShape::trace(JSTracer *trc)
{
if (base)
MarkBaseShapeRoot(trc, (BaseShape**) &base, "StackShape base");
MarkIdRoot(trc, (jsid*) &propid, "StackShape id");
if ((attrs & JSPROP_GETTER) && rawGetter)
MarkObjectRoot(trc, (JSObject**)&rawGetter, "StackShape getter");
if ((attrs & JSPROP_SETTER) && rawSetter)
MarkObjectRoot(trc, (JSObject**)&rawSetter, "StackShape setter");
}
void

View File

@ -282,6 +282,7 @@ const uint32_t Arena::ThingSizes[] = CHECK_MIN_THING_SIZE(
sizeof(JSScript), /* FINALIZE_SCRIPT */
sizeof(LazyScript), /* FINALIZE_LAZY_SCRIPT */
sizeof(Shape), /* FINALIZE_SHAPE */
sizeof(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */
sizeof(BaseShape), /* FINALIZE_BASE_SHAPE */
sizeof(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
sizeof(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */
@ -312,6 +313,7 @@ const uint32_t Arena::FirstThingOffsets[] = {
OFFSET(JSScript), /* FINALIZE_SCRIPT */
OFFSET(LazyScript), /* FINALIZE_LAZY_SCRIPT */
OFFSET(Shape), /* FINALIZE_SHAPE */
OFFSET(AccessorShape), /* FINALIZE_ACCESSOR_SHAPE */
OFFSET(BaseShape), /* FINALIZE_BASE_SHAPE */
OFFSET(types::TypeObject), /* FINALIZE_TYPE_OBJECT */
OFFSET(JSFatInlineString), /* FINALIZE_FAT_INLINE_STRING */
@ -397,6 +399,7 @@ static const AllocKind BackgroundPhaseStringsAndSymbols[] = {
static const AllocKind BackgroundPhaseShapes[] = {
FINALIZE_SHAPE,
FINALIZE_ACCESSOR_SHAPE,
FINALIZE_BASE_SHAPE,
FINALIZE_TYPE_OBJECT
};
@ -623,6 +626,8 @@ FinalizeArenas(FreeOp *fop,
return FinalizeTypedArenas<LazyScript>(fop, src, dest, thingKind, budget);
case FINALIZE_SHAPE:
return FinalizeTypedArenas<Shape>(fop, src, dest, thingKind, budget);
case FINALIZE_ACCESSOR_SHAPE:
return FinalizeTypedArenas<AccessorShape>(fop, src, dest, thingKind, budget);
case FINALIZE_BASE_SHAPE:
return FinalizeTypedArenas<BaseShape>(fop, src, dest, thingKind, budget);
case FINALIZE_TYPE_OBJECT:
@ -2684,6 +2689,7 @@ ArenaLists::queueShapesForSweep(FreeOp *fop)
gcstats::AutoPhase ap(fop->runtime()->gc.stats, gcstats::PHASE_SWEEP_SHAPE);
queueForBackgroundSweep(fop, FINALIZE_SHAPE);
queueForBackgroundSweep(fop, FINALIZE_ACCESSOR_SHAPE);
queueForBackgroundSweep(fop, FINALIZE_BASE_SHAPE);
queueForBackgroundSweep(fop, FINALIZE_TYPE_OBJECT);
}
@ -4881,6 +4887,8 @@ GCRuntime::beginSweepingZoneGroup()
zone->allocator.arenas.queueShapesForSweep(&fop);
zone->allocator.arenas.gcShapeArenasToSweep =
zone->allocator.arenas.arenaListsToSweep[FINALIZE_SHAPE];
zone->allocator.arenas.gcAccessorShapeArenasToSweep =
zone->allocator.arenas.arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
}
finalizePhase = 0;
@ -4978,6 +4986,25 @@ GCRuntime::drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase)
return marker.drainMarkStack(sliceBudget);
}
static bool
SweepShapes(ArenaHeader **arenasToSweep, size_t thingsPerArena, SliceBudget &sliceBudget)
{
while (ArenaHeader *arena = *arenasToSweep) {
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
Shape *shape = i.get<Shape>();
if (!shape->isMarked())
shape->sweep();
}
*arenasToSweep = arena->next;
sliceBudget.step(thingsPerArena);
if (sliceBudget.isOverBudget())
return false; /* Yield to the mutator. */
}
return true;
}
bool
GCRuntime::sweepPhase(SliceBudget &sliceBudget)
{
@ -5023,17 +5050,17 @@ GCRuntime::sweepPhase(SliceBudget &sliceBudget)
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
Zone *zone = sweepZone;
while (ArenaHeader *arena = zone->allocator.arenas.gcShapeArenasToSweep) {
for (ArenaCellIterUnderGC i(arena); !i.done(); i.next()) {
Shape *shape = i.get<Shape>();
if (!shape->isMarked())
shape->sweep();
}
zone->allocator.arenas.gcShapeArenasToSweep = arena->next;
sliceBudget.step(Arena::thingsPerArena(Arena::thingSize(FINALIZE_SHAPE)));
if (sliceBudget.isOverBudget())
return false; /* Yield to the mutator. */
if (!SweepShapes(&zone->allocator.arenas.gcShapeArenasToSweep,
Arena::thingsPerArena(Arena::thingSize(FINALIZE_SHAPE)),
sliceBudget))
{
return false; /* Yield to the mutator. */
}
if (!SweepShapes(&zone->allocator.arenas.gcAccessorShapeArenasToSweep,
Arena::thingsPerArena(Arena::thingSize(FINALIZE_ACCESSOR_SHAPE)),
sliceBudget))
{
return false; /* Yield to the mutator. */
}
}
}

View File

@ -62,6 +62,7 @@ template <typename T> struct MapTypeToFinalizeKind {};
template <> struct MapTypeToFinalizeKind<JSScript> { static const AllocKind kind = FINALIZE_SCRIPT; };
template <> struct MapTypeToFinalizeKind<LazyScript> { static const AllocKind kind = FINALIZE_LAZY_SCRIPT; };
template <> struct MapTypeToFinalizeKind<Shape> { static const AllocKind kind = FINALIZE_SHAPE; };
template <> struct MapTypeToFinalizeKind<AccessorShape> { static const AllocKind kind = FINALIZE_ACCESSOR_SHAPE; };
template <> struct MapTypeToFinalizeKind<BaseShape> { static const AllocKind kind = FINALIZE_BASE_SHAPE; };
template <> struct MapTypeToFinalizeKind<types::TypeObject> { static const AllocKind kind = FINALIZE_TYPE_OBJECT; };
template <> struct MapTypeToFinalizeKind<JSFatInlineString> { static const AllocKind kind = FINALIZE_FAT_INLINE_STRING; };
@ -91,6 +92,7 @@ IsNurseryAllocable(AllocKind kind)
false, /* FINALIZE_SCRIPT */
false, /* FINALIZE_LAZY_SCRIPT */
false, /* FINALIZE_SHAPE */
false, /* FINALIZE_ACCESSOR_SHAPE */
false, /* FINALIZE_BASE_SHAPE */
false, /* FINALIZE_TYPE_OBJECT */
false, /* FINALIZE_FAT_INLINE_STRING */
@ -128,6 +130,7 @@ IsFJNurseryAllocable(AllocKind kind)
false, /* FINALIZE_SCRIPT */
false, /* FINALIZE_LAZY_SCRIPT */
false, /* FINALIZE_SHAPE */
false, /* FINALIZE_ACCESSOR_SHAPE */
false, /* FINALIZE_BASE_SHAPE */
false, /* FINALIZE_TYPE_OBJECT */
false, /* FINALIZE_FAT_INLINE_STRING */
@ -161,6 +164,7 @@ IsBackgroundFinalized(AllocKind kind)
false, /* FINALIZE_SCRIPT */
false, /* FINALIZE_LAZY_SCRIPT */
true, /* FINALIZE_SHAPE */
true, /* FINALIZE_ACCESSOR_SHAPE */
true, /* FINALIZE_BASE_SHAPE */
true, /* FINALIZE_TYPE_OBJECT */
true, /* FINALIZE_FAT_INLINE_STRING */
@ -615,6 +619,7 @@ class ArenaLists
/* Shape arenas to be swept in the foreground. */
ArenaHeader *gcShapeArenasToSweep;
ArenaHeader *gcAccessorShapeArenasToSweep;
public:
ArenaLists() {
@ -626,6 +631,7 @@ class ArenaLists
arenaListsToSweep[i] = nullptr;
incrementalSweptArenaKind = FINALIZE_LIMIT;
gcShapeArenasToSweep = nullptr;
gcAccessorShapeArenasToSweep = nullptr;
}
~ArenaLists() {

View File

@ -738,6 +738,18 @@ NewGCExternalString(js::ThreadSafeContext *cx)
return js::gc::AllocateNonObject<JSExternalString, js::CanGC>(cx);
}
inline Shape *
NewGCShape(ThreadSafeContext *cx)
{
return gc::AllocateNonObject<Shape, CanGC>(cx);
}
inline Shape *
NewGCAccessorShape(ThreadSafeContext *cx)
{
return gc::AllocateNonObject<AccessorShape, CanGC>(cx);
}
} /* namespace js */
inline JSScript *
@ -752,12 +764,6 @@ js_NewGCLazyScript(js::ThreadSafeContext *cx)
return js::gc::AllocateNonObject<js::LazyScript, js::CanGC>(cx);
}
inline js::Shape *
js_NewGCShape(js::ThreadSafeContext *cx)
{
return js::gc::AllocateNonObject<js::Shape, js::CanGC>(cx);
}
template <js::AllowGC allowGC>
inline js::BaseShape *
js_NewGCBaseShape(js::ThreadSafeContext *cx)

View File

@ -31,15 +31,6 @@ ShapeHasher::match(const Key k, const Lookup &l)
return k->matches(l);
}
Shape *
PropertyTree::newShape(ExclusiveContext *cx)
{
Shape *shape = js_NewGCShape(cx);
if (!shape)
js_ReportOutOfMemory(cx);
return shape;
}
static KidsHash *
HashChildren(Shape *kid1, Shape *kid2)
{
@ -113,9 +104,15 @@ Shape::removeChild(Shape *child)
KidsHash *hash = kidp->toHash();
MOZ_ASSERT(hash->count() >= 2); /* otherwise kidp->isShape() should be true */
#ifdef DEBUG
size_t oldCount = hash->count();
#endif
hash->remove(StackShape(child));
child->parent = nullptr;
MOZ_ASSERT(hash->count() == oldCount - 1);
if (hash->count() == 1) {
/* Convert from HASH form back to SHAPE form. */
KidsHash::Range r = hash->all();
@ -184,14 +181,10 @@ PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, StackShape &unroo
if (existingShape)
return existingShape;
RootedGeneric<StackShape*> child(cx, &unrootedChild);
Shape *shape = newShape(cx);
Shape *shape = Shape::new_(cx, unrootedChild, parent->numFixedSlots());
if (!shape)
return nullptr;
new (shape) Shape(*child, parent->numFixedSlots());
if (!insertChild(cx, parent, shape))
return nullptr;
@ -287,8 +280,10 @@ Shape::fixupDictionaryShapeAfterMovingGC()
MOZ_ASSERT(!IsInsideNursery(reinterpret_cast<Cell *>(listp)));
AllocKind kind = TenuredCell::fromPointer(listp)->getAllocKind();
MOZ_ASSERT(kind == FINALIZE_SHAPE || kind <= FINALIZE_OBJECT_LAST);
if (kind == FINALIZE_SHAPE) {
MOZ_ASSERT(kind == FINALIZE_SHAPE ||
kind == FINALIZE_ACCESSOR_SHAPE ||
kind <= FINALIZE_OBJECT_LAST);
if (kind == FINALIZE_SHAPE || kind == FINALIZE_ACCESSOR_SHAPE) {
// listp points to the parent field of the next shape.
Shape *next = reinterpret_cast<Shape *>(uintptr_t(listp) -
offsetof(Shape, parent));
@ -317,21 +312,30 @@ Shape::fixupShapeTreeAfterMovingGC()
KidsHash *kh = kids.toHash();
for (KidsHash::Enum e(*kh); !e.empty(); e.popFront()) {
Shape *key = e.front();
if (!IsForwarded(key))
continue;
if (IsForwarded(key))
key = Forwarded(key);
key = Forwarded(key);
BaseShape *base = key->base();
if (IsForwarded(base))
base = Forwarded(base);
UnownedBaseShape *unowned = base->unowned();
if (IsForwarded(unowned))
unowned = Forwarded(unowned);
PropertyOp getter = key->getter();
if (key->hasGetterObject() && IsForwarded(key->getterObject()))
getter = PropertyOp(Forwarded(key->getterObject()));
StrictPropertyOp setter = key->setter();
if (key->hasSetterObject() && IsForwarded(key->setterObject()))
setter = StrictPropertyOp(Forwarded(key->setterObject()));
StackShape lookup(unowned,
const_cast<Shape *>(key)->propidRef(),
key->slotInfo & Shape::SLOT_MASK,
key->attrs,
key->flags);
lookup.updateGetterSetter(getter, setter);
e.rekeyFront(lookup, key);
}
}
@ -347,6 +351,34 @@ Shape::fixupAfterMovingGC()
#endif // JSGC_COMPACTING
#ifdef JSGC_GENERATIONAL
void
ShapeGetterSetterRef::mark(JSTracer *trc)
{
// Update the current shape's entry in the parent KidsHash table if needed.
// This is necessary as the computed hash includes the getter/setter
// pointers.
JSObject *obj = *objp;
JSObject *prior = obj;
trc->setTracingLocation(&*prior);
gc::Mark(trc, &obj, "AccessorShape getter or setter");
if (obj == *objp)
return;
Shape *parent = shape->parent;
if (shape->inDictionary() || !parent->kids.isHash()) {
*objp = obj;
return;
}
KidsHash *kh = parent->kids.toHash();
kh->remove(StackShape(shape));
*objp = obj;
MOZ_ALWAYS_TRUE(kh->putNew(StackShape(shape), shape));
}
#endif
#ifdef DEBUG
void
@ -385,8 +417,8 @@ Shape::dump(JSContext *cx, FILE *fp) const
}
fprintf(fp, " g/s %p/%p slot %d attrs %x ",
JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
JS_FUNC_TO_DATA_PTR(void *, getter()),
JS_FUNC_TO_DATA_PTR(void *, setter()),
hasSlot() ? slot() : -1, attrs);
if (attrs) {

View File

@ -96,7 +96,6 @@ class PropertyTree
JSCompartment *compartment() { return compartment_; }
Shape *newShape(ExclusiveContext *cx);
Shape *getChild(ExclusiveContext *cx, Shape *parent, StackShape &child);
Shape *lookupChild(ThreadSafeContext *cx, Shape *parent, const StackShape &child);
};

View File

@ -407,7 +407,8 @@ class NativeObject : public JSObject
Shape *
replaceWithNewEquivalentShape(ThreadSafeContext *cx,
Shape *existingShape, Shape *newShape = nullptr);
Shape *existingShape, Shape *newShape = nullptr,
bool accessorShape = false);
/*
* Remove the last property of an object, provided that it is safe to do so

View File

@ -30,8 +30,6 @@ StackBaseShape::StackBaseShape(ThreadSafeContext *cx, const Class *clasp,
clasp(clasp),
parent(parent),
metadata(metadata),
rawGetter(nullptr),
rawSetter(nullptr),
compartment(cx->compartment_)
{}
@ -153,6 +151,24 @@ Shape::search(ExclusiveContext *cx, Shape *start, jsid id, Shape ***pspp, bool a
return nullptr;
}
inline Shape *
Shape::new_(ExclusiveContext *cx, StackShape &unrootedOther, uint32_t nfixed)
{
RootedGeneric<StackShape*> other(cx, &unrootedOther);
Shape *shape = other->isAccessorShape() ? NewGCAccessorShape(cx) : NewGCShape(cx);
if (!shape) {
js_ReportOutOfMemory(cx);
return nullptr;
}
if (other->isAccessorShape())
new (shape) AccessorShape(*other, nfixed);
else
new (shape) Shape(*other, nfixed);
return shape;
}
template<class ObjectSubclass>
/* static */ inline bool
EmptyShape::ensureInitialCustomShape(ExclusiveContext *cx, Handle<ObjectSubclass*> obj)

View File

@ -385,7 +385,7 @@ NativeObject::getChildPropertyOnDictionary(ThreadSafeContext *cx, HandleNativeOb
if (obj->inDictionaryMode()) {
MOZ_ASSERT(parent == obj->lastProperty());
RootedGeneric<StackShape*> childRoot(cx, &child);
shape = js_NewGCShape(cx);
shape = childRoot->isAccessorShape() ? NewGCAccessorShape(cx) : NewGCShape(cx);
if (!shape)
return nullptr;
if (childRoot->hasSlot() && childRoot->slot() >= obj->lastProperty()->base()->slotSpan()) {
@ -467,7 +467,7 @@ js::NativeObject::toDictionaryMode(ThreadSafeContext *cx)
while (shape) {
MOZ_ASSERT(!shape->inDictionary());
Shape *dprop = js_NewGCShape(cx);
Shape *dprop = shape->isAccessorShape() ? NewGCAccessorShape(cx) : NewGCShape(cx);
if (!dprop) {
js_ReportOutOfMemory(cx);
return false;
@ -626,19 +626,18 @@ NativeObject::addPropertyInternal(typename ExecutionModeTraits<mode>::ExclusiveC
bool indexed = js_IdIsIndex(id, &index);
Rooted<UnownedBaseShape*> nbase(cx);
if (last->base()->matchesGetterSetter(getter, setter) && !indexed) {
if (!indexed) {
nbase = last->base()->unowned();
} else {
StackBaseShape base(last->base());
base.updateGetterSetter(attrs, getter, setter);
if (indexed)
base.flags |= BaseShape::INDEXED;
base.flags |= BaseShape::INDEXED;
nbase = GetOrLookupUnownedBaseShape<mode>(cx, base);
if (!nbase)
return nullptr;
}
StackShape child(nbase, id, slot, attrs, flags);
child.updateGetterSetter(getter, setter);
shape = getOrLookupChildProperty<mode>(cx, obj, last, child);
}
@ -845,7 +844,6 @@ NativeObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextTy
uint32_t index;
bool indexed = js_IdIsIndex(id, &index);
StackBaseShape base(obj->lastProperty()->base());
base.updateGetterSetter(attrs, getter, setter);
if (indexed)
base.flags |= BaseShape::INDEXED;
nbase = GetOrLookupUnownedBaseShape<mode>(cx, base);
@ -857,7 +855,7 @@ NativeObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextTy
* Now that we've possibly preserved slot, check whether all members match.
* If so, this is a redundant "put" and we can return without more work.
*/
if (shape->matchesParamsAfterId(nbase, slot, attrs, flags))
if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, getter, setter))
return shape;
/*
@ -882,7 +880,8 @@ NativeObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextTy
* is also the last property).
*/
bool updateLast = (shape == obj->lastProperty());
shape = obj->replaceWithNewEquivalentShape(cx, shape);
bool accessorShape = getter || setter || (attrs & (JSPROP_GETTER | JSPROP_SETTER));
shape = obj->replaceWithNewEquivalentShape(cx, shape, nullptr, accessorShape);
if (!shape)
return nullptr;
if (!updateLast && !obj->generateOwnShape(cx))
@ -903,14 +902,25 @@ NativeObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextTy
shape->setSlot(slot);
shape->attrs = uint8_t(attrs);
shape->flags = flags | Shape::IN_DICTIONARY;
shape->flags = flags | Shape::IN_DICTIONARY | (accessorShape ? Shape::ACCESSOR_SHAPE : 0);
if (shape->isAccessorShape()) {
AccessorShape &accShape = shape->asAccessorShape();
accShape.rawGetter = getter;
if (accShape.hasGetterObject())
GetterSetterWriteBarrierPost(&accShape, &accShape.getterObj);
accShape.rawSetter = setter;
if (accShape.hasSetterObject())
GetterSetterWriteBarrierPost(&accShape, &accShape.setterObj);
} else {
MOZ_ASSERT(!getter);
MOZ_ASSERT(!setter);
}
} else {
/*
* Updating the last property in a non-dictionary-mode object. Find an
* alternate shared child of the last property's previous shape.
*/
StackBaseShape base(obj->lastProperty()->base());
base.updateGetterSetter(attrs, getter, setter);
UnownedBaseShape *nbase = GetOrLookupUnownedBaseShape<mode>(cx, base);
if (!nbase)
@ -920,6 +930,7 @@ NativeObject::putProperty(typename ExecutionModeTraits<mode>::ExclusiveContextTy
/* Find or create a property tree node labeled by our arguments. */
StackShape child(nbase, id, slot, attrs, flags);
child.updateGetterSetter(getter, setter);
RootedShape parent(cx, shape->parent);
Shape *newShape = getOrLookupChildProperty<mode>(cx, obj, parent, child);
@ -1053,7 +1064,8 @@ NativeObject::removeProperty(ExclusiveContext *cx, jsid id_)
*/
RootedShape spare(cx);
if (self->inDictionaryMode()) {
spare = js_NewGCShape(cx);
/* For simplicity, always allocate an accessor shape for now. */
spare = NewGCAccessorShape(cx);
if (!spare)
return false;
new (spare) Shape(shape->base()->unowned(), 0);
@ -1066,7 +1078,6 @@ NativeObject::removeProperty(ExclusiveContext *cx, jsid id_)
*/
RootedShape previous(cx, self->lastProperty()->parent);
StackBaseShape base(self->lastProperty()->base());
base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
BaseShape *nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
@ -1188,7 +1199,8 @@ NativeObject::rollbackProperties(ExclusiveContext *cx, HandleNativeObject obj, u
}
Shape *
NativeObject::replaceWithNewEquivalentShape(ThreadSafeContext *cx, Shape *oldShape, Shape *newShape)
NativeObject::replaceWithNewEquivalentShape(ThreadSafeContext *cx, Shape *oldShape, Shape *newShape,
bool accessorShape)
{
MOZ_ASSERT(cx->isThreadLocal(this));
MOZ_ASSERT(cx->isThreadLocal(oldShape));
@ -1214,7 +1226,9 @@ NativeObject::replaceWithNewEquivalentShape(ThreadSafeContext *cx, Shape *oldSha
if (!newShape) {
RootedNativeObject selfRoot(cx, self);
RootedShape oldRoot(cx, oldShape);
newShape = js_NewGCShape(cx);
newShape = (oldShape->isAccessorShape() || accessorShape)
? NewGCAccessorShape(cx)
: NewGCShape(cx);
if (!newShape)
return nullptr;
new (newShape) Shape(oldRoot->base()->unowned(), 0);
@ -1430,8 +1444,6 @@ StackBaseShape::hash(const StackBaseShape *base)
hash = RotateLeft(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
hash = RotateLeft(hash, 4) ^ (uintptr_t(base->parent) >> 3);
hash = RotateLeft(hash, 4) ^ (uintptr_t(base->metadata) >> 3);
hash = RotateLeft(hash, 4) ^ uintptr_t(base->rawGetter);
hash = RotateLeft(hash, 4) ^ uintptr_t(base->rawSetter);
return hash;
}
@ -1441,30 +1453,17 @@ StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
return key->flags == lookup->flags
&& key->clasp_ == lookup->clasp
&& key->parent == lookup->parent
&& key->metadata == lookup->metadata
&& key->rawGetter == lookup->rawGetter
&& key->rawSetter == lookup->rawSetter;
&& key->metadata == lookup->metadata;
}
void
StackBaseShape::trace(JSTracer *trc)
{
if (parent) {
gc::MarkObjectRoot(trc, (JSObject**)&parent,
"StackBaseShape parent");
}
if (metadata) {
gc::MarkObjectRoot(trc, (JSObject**)&metadata,
"StackBaseShape metadata");
}
if ((flags & BaseShape::HAS_GETTER_OBJECT) && rawGetter) {
gc::MarkObjectRoot(trc, (JSObject**)&rawGetter,
"StackBaseShape getter");
}
if ((flags & BaseShape::HAS_SETTER_OBJECT) && rawSetter) {
gc::MarkObjectRoot(trc, (JSObject**)&rawSetter,
"StackBaseShape setter");
}
if (parent)
gc::MarkObjectRoot(trc, (JSObject**)&parent, "StackBaseShape parent");
if (metadata)
gc::MarkObjectRoot(trc, (JSObject**)&metadata, "StackBaseShape metadata");
}
/* static */ UnownedBaseShape*
@ -1513,10 +1512,6 @@ BaseShape::assertConsistency()
#ifdef DEBUG
if (isOwned()) {
UnownedBaseShape *unowned = baseUnowned();
MOZ_ASSERT(hasGetterObject() == unowned->hasGetterObject());
MOZ_ASSERT(hasSetterObject() == unowned->hasSetterObject());
MOZ_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject());
MOZ_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject());
MOZ_ASSERT(getObjectParent() == unowned->getObjectParent());
MOZ_ASSERT(getObjectMetadata() == unowned->getObjectMetadata());
MOZ_ASSERT(getObjectFlags() == unowned->getObjectFlags());
@ -1698,6 +1693,19 @@ JSCompartment::checkInitialShapesTableAfterMovingGC()
#endif // JSGC_HASH_TABLE_CHECKS
Shape *
EmptyShape::new_(ExclusiveContext *cx, Handle<UnownedBaseShape *> base, uint32_t nfixed)
{
Shape *shape = NewGCShape(cx);
if (!shape) {
js_ReportOutOfMemory(cx);
return nullptr;
}
new (shape) EmptyShape(base, nfixed);
return shape;
}
/* static */ Shape *
EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProto proto,
JSObject *parent, JSObject *metadata,
@ -1726,10 +1734,9 @@ EmptyShape::getInitialShape(ExclusiveContext *cx, const Class *clasp, TaggedProt
if (!nbase)
return nullptr;
Shape *shape = cx->compartment()->propertyTree.newShape(cx);
Shape *shape = EmptyShape::new_(cx, nbase, nfixed);
if (!shape)
return nullptr;
new (shape) EmptyShape(nbase, nfixed);
Lookup lookup(clasp, protoRoot, parentRoot, metadataRoot, nfixed, objectFlags);
if (!p.add(cx, table, lookup, InitialShapeEntry(ReadBarrieredShape(shape), protoRoot)))

View File

@ -98,6 +98,9 @@
* trees are more space-efficient than alternatives. This was removed in bug
* 631138; see that bug for the full details.
*
* For getters/setters, an AccessorShape is allocated. This is a slightly fatter
* type with extra fields for the getter/setter data.
*
* Because many Shapes have similar data, there is actually a secondary type
* called a BaseShape that holds some of a Shape's data. Many shapes can share
* a single BaseShape.
@ -251,6 +254,7 @@ struct ShapeTable {
* an earlier property, however.
*/
class AccessorShape;
class Shape;
class UnownedBaseShape;
struct StackBaseShape;
@ -259,16 +263,33 @@ namespace gc {
void MergeCompartments(JSCompartment *source, JSCompartment *target);
}
#ifdef JSGC_GENERATIONAL
// This class is used to add a post barrier on the AccessorShape's getter/setter
// objects. It updates the shape's entry in the parent's KidsHash table.
class ShapeGetterSetterRef : public gc::BufferableRef
{
AccessorShape *shape;
JSObject **objp;
public:
ShapeGetterSetterRef(AccessorShape *shape, JSObject **objp)
: shape(shape), objp(objp)
{}
void mark(JSTracer *trc);
};
#endif
static inline void
GetterSetterWriteBarrierPost(JSRuntime *rt, JSObject **objp)
GetterSetterWriteBarrierPost(AccessorShape *shape, JSObject **objp)
{
#ifdef JSGC_GENERATIONAL
MOZ_ASSERT(shape);
MOZ_ASSERT(objp);
MOZ_ASSERT(*objp);
gc::Cell **cellp = reinterpret_cast<gc::Cell **>(objp);
gc::StoreBuffer *storeBuffer = (*cellp)->storeBuffer();
if (storeBuffer)
storeBuffer->putRelocatableCellFromAnyThread(cellp);
if (gc::StoreBuffer *sb = (*cellp)->storeBuffer())
sb->putGeneric(ShapeGetterSetterRef(shape, objp));
#endif
}
@ -293,9 +314,7 @@ class BaseShape : public gc::TenuredCell
/* Owned by the referring shape. */
OWNED_SHAPE = 0x1,
/* getterObj/setterObj are active in unions below. */
HAS_GETTER_OBJECT = 0x2,
HAS_SETTER_OBJECT = 0x4,
/* (0x2 and 0x4 are unused) */
/*
* Flags set which describe the referring object. Once set these cannot
@ -341,18 +360,6 @@ class BaseShape : public gc::TenuredCell
uint32_t slotSpan_; /* Object slot span for BaseShapes at
* dictionary last properties. */
union {
PropertyOp rawGetter; /* getter hook for shape */
JSObject *getterObj; /* user-defined callable "get" object or
null if shape->hasGetterValue() */
};
union {
StrictPropertyOp rawSetter; /* setter hook for shape */
JSObject *setterObj; /* user-defined callable "set" object or
null if shape->hasSetterValue() */
};
/* For owned BaseShapes, the canonical unowned BaseShape. */
HeapPtrUnownedBaseShape unowned_;
@ -377,8 +384,7 @@ class BaseShape : public gc::TenuredCell
}
BaseShape(JSCompartment *comp, const Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags, uint8_t attrs,
PropertyOp rawGetter, StrictPropertyOp rawSetter)
uint32_t objectFlags, uint8_t attrs)
{
MOZ_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
@ -386,16 +392,6 @@ class BaseShape : public gc::TenuredCell
this->parent = parent;
this->metadata = metadata;
this->flags = objectFlags;
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
if ((attrs & JSPROP_GETTER) && rawGetter) {
this->flags |= HAS_GETTER_OBJECT;
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
}
if ((attrs & JSPROP_SETTER) && rawSetter) {
this->flags |= HAS_SETTER_OBJECT;
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj);
}
this->compartment_ = comp;
}
@ -410,22 +406,6 @@ class BaseShape : public gc::TenuredCell
metadata = other.metadata;
flags = other.flags;
slotSpan_ = other.slotSpan_;
if (flags & HAS_GETTER_OBJECT) {
getterObj = other.getterObj;
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &getterObj);
} else {
if (rawGetter)
GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &getterObj);
rawGetter = other.rawGetter;
}
if (flags & HAS_SETTER_OBJECT) {
setterObj = other.setterObj;
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &setterObj);
} else {
if (rawSetter)
GetterSetterWriteBarrierPostRemove(runtimeFromMainThread(), &setterObj);
rawSetter = other.rawSetter;
}
compartment_ = other.compartment_;
return *this;
}
@ -434,10 +414,6 @@ class BaseShape : public gc::TenuredCell
bool isOwned() const { return !!(flags & OWNED_SHAPE); }
bool matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const {
return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
}
inline void adoptUnowned(UnownedBaseShape *other);
void setOwned(UnownedBaseShape *unowned) {
@ -449,12 +425,6 @@ class BaseShape : public gc::TenuredCell
JSObject *getObjectMetadata() const { return metadata; }
uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
bool hasGetterObject() const { return !!(flags & HAS_GETTER_OBJECT); }
JSObject *getterObject() const { MOZ_ASSERT(hasGetterObject()); return getterObj; }
bool hasSetterObject() const { return !!(flags & HAS_SETTER_OBJECT); }
JSObject *setterObject() const { MOZ_ASSERT(hasSetterObject()); return setterObj; }
bool hasTable() const { MOZ_ASSERT_IF(table_, isOwned()); return table_ != nullptr; }
ShapeTable &table() const { MOZ_ASSERT(table_ && isOwned()); return *table_; }
void setTable(ShapeTable *table) { MOZ_ASSERT(isOwned()); table_ = table; }
@ -495,12 +465,6 @@ class BaseShape : public gc::TenuredCell
static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
void markChildren(JSTracer *trc) {
if (hasGetterObject())
gc::MarkObjectUnbarriered(trc, &getterObj, "getter");
if (hasSetterObject())
gc::MarkObjectUnbarriered(trc, &setterObj, "setter");
if (isOwned())
gc::MarkBaseShape(trc, &unowned_, "base");
@ -568,8 +532,6 @@ struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
const Class *clasp;
JSObject *parent;
JSObject *metadata;
PropertyOp rawGetter;
StrictPropertyOp rawSetter;
JSCompartment *compartment;
explicit StackBaseShape(BaseShape *base)
@ -577,8 +539,6 @@ struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
clasp(base->clasp_),
parent(base->parent),
metadata(base->metadata),
rawGetter(nullptr),
rawSetter(nullptr),
compartment(base->compartment())
{}
@ -586,21 +546,6 @@ struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
JSObject *parent, JSObject *metadata, uint32_t objectFlags);
explicit inline StackBaseShape(Shape *shape);
void updateGetterSetter(uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter) {
flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT);
if ((attrs & JSPROP_GETTER) && rawGetter) {
MOZ_ASSERT(!IsPoisonedPtr(rawGetter));
flags |= BaseShape::HAS_GETTER_OBJECT;
}
if ((attrs & JSPROP_SETTER) && rawSetter) {
MOZ_ASSERT(!IsPoisonedPtr(rawSetter));
flags |= BaseShape::HAS_SETTER_OBJECT;
}
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
}
static inline HashNumber hash(const StackBaseShape *lookup);
static inline bool match(UnownedBaseShape *key, const StackBaseShape *lookup);
@ -616,12 +561,6 @@ BaseShape::BaseShape(const StackBaseShape &base)
this->parent = base.parent;
this->metadata = base.metadata;
this->flags = base.flags;
this->rawGetter = base.rawGetter;
this->rawSetter = base.rawSetter;
if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter)
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->getterObj);
if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter)
GetterSetterWriteBarrierPost(runtimeFromMainThread(), &this->setterObj);
this->compartment_ = base.compartment;
}
@ -640,6 +579,7 @@ class Shape : public gc::TenuredCell
friend class js::NativeObject;
friend class js::PropertyTree;
friend class js::StaticBlockObject;
friend class js::ShapeGetterSetterRef;
friend struct js::StackShape;
friend struct js::StackBaseShape;
@ -698,14 +638,7 @@ class Shape : public gc::TenuredCell
void removeFromDictionary(NativeObject *obj);
void insertIntoDictionary(HeapPtrShape *dictp);
void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp) {
new (this) Shape(child, nfixed);
this->flags |= IN_DICTIONARY;
this->listp = nullptr;
if (dictp)
insertIntoDictionary(dictp);
}
inline void initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp);
/* Replace the base shape of the last shape in a non-dictionary lineage with base. */
static Shape *replaceLastProperty(ExclusiveContext *cx, StackBaseShape &base,
@ -758,6 +691,15 @@ class Shape : public gc::TenuredCell
return !(flags & NON_NATIVE);
}
bool isAccessorShape() const {
MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::FINALIZE_ACCESSOR_SHAPE);
return flags & ACCESSOR_SHAPE;
}
AccessorShape &asAccessorShape() const {
MOZ_ASSERT(isAccessorShape());
return *(AccessorShape *)this;
}
const HeapPtrShape &previous() const { return parent; }
JSCompartment *compartment() const { return base()->compartment(); }
@ -830,7 +772,13 @@ class Shape : public gc::TenuredCell
*/
OVERWRITTEN = 0x04,
UNUSED_BITS = 0x38
/*
* This shape is an AccessorShape, a fat Shape that can store
* getter/setter information.
*/
ACCESSOR_SHAPE = 0x08,
UNUSED_BITS = 0x3C
};
/* Get a shape identical to this one, without parent/kids information. */
@ -842,6 +790,9 @@ class Shape : public gc::TenuredCell
/* Copy constructor disabled, to avoid misuse of the above form. */
Shape(const Shape &other) MOZ_DELETE;
/* Allocate a new shape based on the given StackShape. */
static inline Shape *new_(ExclusiveContext *cx, StackShape &unrootedOther, uint32_t nfixed);
/*
* Whether this shape has a valid slot value. This may be true even if
* !hasSlot() (see SlotInfo comment above), and may be false even if
@ -855,38 +806,40 @@ class Shape : public gc::TenuredCell
return (flags & IN_DICTIONARY) != 0;
}
PropertyOp getter() const { return base()->rawGetter; }
bool hasDefaultGetter() const {return !base()->rawGetter; }
PropertyOp getterOp() const { MOZ_ASSERT(!hasGetterValue()); return base()->rawGetter; }
JSObject *getterObject() const { MOZ_ASSERT(hasGetterValue()); return base()->getterObj; }
inline PropertyOp getter() const;
bool hasDefaultGetter() const { return !getter(); }
PropertyOp getterOp() const { MOZ_ASSERT(!hasGetterValue()); return getter(); }
inline JSObject *getterObject() const;
bool hasGetterObject() const { return hasGetterValue() && getterObject(); }
// Per ES5, decode null getterObj as the undefined value, which encodes as null.
Value getterValue() const {
MOZ_ASSERT(hasGetterValue());
return base()->getterObj ? ObjectValue(*base()->getterObj) : UndefinedValue();
if (JSObject *getterObj = getterObject())
return ObjectValue(*getterObj);
return UndefinedValue();
}
Value getterOrUndefined() const {
return (hasGetterValue() && base()->getterObj)
? ObjectValue(*base()->getterObj)
: UndefinedValue();
return hasGetterValue() ? getterValue() : UndefinedValue();
}
StrictPropertyOp setter() const { return base()->rawSetter; }
bool hasDefaultSetter() const { return !base()->rawSetter; }
StrictPropertyOp setterOp() const { MOZ_ASSERT(!hasSetterValue()); return base()->rawSetter; }
JSObject *setterObject() const { MOZ_ASSERT(hasSetterValue()); return base()->setterObj; }
inline StrictPropertyOp setter() const;
bool hasDefaultSetter() const { return !setter(); }
StrictPropertyOp setterOp() const { MOZ_ASSERT(!hasSetterValue()); return setter(); }
inline JSObject *setterObject() const;
bool hasSetterObject() const { return hasSetterValue() && setterObject(); }
// Per ES5, decode null setterObj as the undefined value, which encodes as null.
Value setterValue() const {
MOZ_ASSERT(hasSetterValue());
return base()->setterObj ? ObjectValue(*base()->setterObj) : UndefinedValue();
if (JSObject *setterObj = setterObject())
return ObjectValue(*setterObj);
return UndefinedValue();
}
Value setterOrUndefined() const {
return (hasSetterValue() && base()->setterObj)
? ObjectValue(*base()->setterObj)
: UndefinedValue();
return hasSetterValue() ? setterValue() : UndefinedValue();
}
void setOverwritten() {
@ -900,16 +853,20 @@ class Shape : public gc::TenuredCell
bool matches(const Shape *other) const {
return propid_.get() == other->propid_.get() &&
matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags);
matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags,
other->getter(), other->setter());
}
inline bool matches(const StackShape &other) const;
bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags) const
bool matchesParamsAfterId(BaseShape *base, uint32_t aslot, unsigned aattrs, unsigned aflags,
PropertyOp rawGetter, StrictPropertyOp rawSetter) const
{
return base->unowned() == this->base()->unowned() &&
maybeSlot() == aslot &&
attrs == aattrs;
attrs == aattrs &&
getter() == rawGetter &&
setter() == rawSetter;
}
bool get(JSContext* cx, HandleObject receiver, JSObject *obj, JSObject *pobj, MutableHandleValue vp);
@ -1047,12 +1004,7 @@ class Shape : public gc::TenuredCell
static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
void markChildren(JSTracer *trc) {
MarkBaseShape(trc, &base_, "base");
gc::MarkId(trc, &propidRef(), "propid");
if (parent)
MarkShape(trc, &parent, "parent");
}
inline void markChildren(JSTracer *trc);
inline Shape *search(ExclusiveContext *cx, jsid id);
inline Shape *searchLinear(jsid id);
@ -1079,6 +1031,29 @@ class Shape : public gc::TenuredCell
}
};
/* Fat Shape used for accessor properties. */
class AccessorShape : public Shape
{
friend class Shape;
friend class ShapeGetterSetterRef;
friend class NativeObject;
union {
PropertyOp rawGetter; /* getter hook for shape */
JSObject *getterObj; /* user-defined callable "get" object or
null if shape->hasGetterValue() */
};
union {
StrictPropertyOp rawSetter; /* setter hook for shape */
JSObject *setterObj; /* user-defined callable "set" object or
null if shape->hasSetterValue() */
};
public:
/* Get a shape identical to this one, without parent/kids information. */
inline AccessorShape(const StackShape &other, uint32_t nfixed);
};
inline
StackBaseShape::StackBaseShape(Shape *shape)
: flags(shape->getObjectFlags()),
@ -1086,9 +1061,7 @@ StackBaseShape::StackBaseShape(Shape *shape)
parent(shape->getObjectParent()),
metadata(shape->getObjectMetadata()),
compartment(shape->compartment())
{
updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
}
{}
class AutoRooterGetterSetter
{
@ -1126,6 +1099,8 @@ struct EmptyShape : public js::Shape
flags |= NON_NATIVE;
}
static Shape *new_(ExclusiveContext *cx, Handle<UnownedBaseShape *> base, uint32_t nfixed);
/*
* Lookup an initial shape matching the given parameters, creating an empty
* shape if none was found.
@ -1233,6 +1208,8 @@ struct StackShape
/* For performance, StackShape only roots when absolutely necessary. */
UnownedBaseShape *base;
jsid propid;
PropertyOp rawGetter;
StrictPropertyOp rawSetter;
uint32_t slot_;
uint8_t attrs;
uint8_t flags;
@ -1241,6 +1218,8 @@ struct StackShape
unsigned attrs, unsigned flags)
: base(base),
propid(propid),
rawGetter(nullptr),
rawSetter(nullptr),
slot_(slot),
attrs(uint8_t(attrs)),
flags(uint8_t(flags))
@ -1254,11 +1233,26 @@ struct StackShape
explicit StackShape(Shape *shape)
: base(shape->base()->unowned()),
propid(shape->propidRef()),
rawGetter(shape->getter()),
rawSetter(shape->setter()),
slot_(shape->maybeSlot()),
attrs(shape->attrs),
flags(shape->flags)
{}
void updateGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) {
MOZ_ASSERT_IF((attrs & JSPROP_GETTER) && rawGetter, !IsPoisonedPtr(rawGetter));
MOZ_ASSERT_IF((attrs & JSPROP_SETTER) && rawSetter, !IsPoisonedPtr(rawSetter));
if (rawGetter || rawSetter || (attrs & (JSPROP_GETTER|JSPROP_SETTER)))
flags |= Shape::ACCESSOR_SHAPE;
else
flags &= ~Shape::ACCESSOR_SHAPE;
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
}
bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
@ -1275,6 +1269,10 @@ struct StackShape
slot_ = slot;
}
bool isAccessorShape() const {
return flags & Shape::ACCESSOR_SHAPE;
}
HashNumber hash() const {
HashNumber hash = uintptr_t(base);
@ -1282,6 +1280,8 @@ struct StackShape
hash = mozilla::RotateLeft(hash, 4) ^ attrs;
hash = mozilla::RotateLeft(hash, 4) ^ slot_;
hash = mozilla::RotateLeft(hash, 4) ^ JSID_BITS(propid);
hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawGetter);
hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawSetter);
return hash;
}
@ -1357,10 +1357,30 @@ Shape::Shape(const StackShape &other, uint32_t nfixed)
flags(other.flags),
parent(nullptr)
{
#ifdef DEBUG
gc::AllocKind allocKind = getAllocKind();
MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::FINALIZE_ACCESSOR_SHAPE);
MOZ_ASSERT_IF(allocKind == gc::FINALIZE_SHAPE, !other.isAccessorShape());
#endif
MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
kids.setNull();
}
inline
AccessorShape::AccessorShape(const StackShape &other, uint32_t nfixed)
: Shape(other, nfixed),
rawGetter(other.rawGetter),
rawSetter(other.rawSetter)
{
MOZ_ASSERT(getAllocKind() == gc::FINALIZE_ACCESSOR_SHAPE);
if ((attrs & JSPROP_GETTER) && rawGetter)
GetterSetterWriteBarrierPost(this, &this->getterObj);
if ((attrs & JSPROP_SETTER) && rawSetter)
GetterSetterWriteBarrierPost(this, &this->setterObj);
}
inline
Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
: base_(base),
@ -1374,6 +1394,46 @@ Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
kids.setNull();
}
inline PropertyOp
Shape::getter() const
{
return isAccessorShape() ? asAccessorShape().rawGetter : nullptr;
}
inline StrictPropertyOp
Shape::setter() const
{
return isAccessorShape() ? asAccessorShape().rawSetter : nullptr;
}
inline JSObject *
Shape::getterObject() const
{
MOZ_ASSERT(hasGetterValue());
return asAccessorShape().getterObj;
}
inline JSObject *
Shape::setterObject() const
{
MOZ_ASSERT(hasSetterValue());
return asAccessorShape().setterObj;
}
inline void
Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp)
{
if (child.isAccessorShape())
new (this) AccessorShape(child, nfixed);
else
new (this) Shape(child, nfixed);
this->flags |= IN_DICTIONARY;
this->listp = nullptr;
if (dictp)
insertIntoDictionary(dictp);
}
inline Shape *
Shape::searchLinear(jsid id)
{
@ -1394,6 +1454,21 @@ Shape::searchLinear(jsid id)
return nullptr;
}
inline void
Shape::markChildren(JSTracer *trc)
{
MarkBaseShape(trc, &base_, "base");
gc::MarkId(trc, &propidRef(), "propid");
if (parent)
MarkShape(trc, &parent, "parent");
if (hasGetterObject())
gc::MarkObjectUnbarriered(trc, &asAccessorShape().getterObj, "getter");
if (hasSetterObject())
gc::MarkObjectUnbarriered(trc, &asAccessorShape().setterObj, "setter");
}
/*
* Keep this function in sync with search. It neither hashifies the start
* shape nor increments linear search count.
@ -1417,7 +1492,8 @@ inline bool
Shape::matches(const StackShape &other) const
{
return propid_.get() == other.propid &&
matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags);
matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
other.rawGetter, other.rawSetter);
}
template<> struct RootKind<Shape *> : SpecificRootKind<Shape *, THING_ROOT_SHAPE> {};