зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1073700 - Move getter/setter data out of BaseShape into a new AccessorShape type. r=bhackett
This commit is contained in:
Родитель
359a42220e
Коммит
184ec9a743
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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> {};
|
||||
|
|
Загрузка…
Ссылка в новой задаче