Avoid splintering property trees when changing the last property of an empty object, bug 701509.

This commit is contained in:
Brian Hackett 2011-11-16 18:02:32 -08:00
parent fdac79a1f2
commit 12dec0f7aa
2 changed files with 41 additions and 32 deletions

View File

@ -341,29 +341,35 @@ Shape::getChildBinding(JSContext *cx, const js::Shape &child, HeapPtrShape *last
}
/* static */ bool
Shape::replaceLastProperty(JSContext *cx, const BaseShape &base, HeapPtrShape *lastp)
Shape::replaceLastProperty(JSContext *cx, const BaseShape &base, JSObject *proto, HeapPtrShape *lastp)
{
Shape *shape = *lastp;
JS_ASSERT(!shape->inDictionary());
if (!shape->parent) {
/* Treat as resetting the initial property of the shape hierarchy. */
AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
Shape *newShape =
EmptyShape::lookupInitialShape(cx, base.clasp, proto,
base.parent, kind,
base.flags & BaseShape::OBJECT_FLAG_MASK);
if (!newShape)
return false;
JS_ASSERT(newShape->numFixedSlots() == shape->numFixedSlots());
*lastp = newShape;
return true;
}
BaseShape *nbase = BaseShape::lookup(cx, base);
if (!nbase)
return false;
Shape *shape = *lastp;
JS_ASSERT(!shape->inDictionary());
Shape child(shape);
child.base_ = nbase;
Shape *newShape;
if (shape->parent) {
newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
if (!newShape)
return false;
} else {
newShape = js_NewGCShape(cx);
if (!newShape)
return false;
new (newShape) Shape(&child);
}
Shape *newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
if (!newShape)
return false;
*lastp = newShape;
return true;
@ -1155,11 +1161,11 @@ JSObject::setParent(JSContext *cx, JSObject *parent)
return true;
}
return Shape::setObjectParent(cx, parent, &shape_);
return Shape::setObjectParent(cx, parent, getProto(), &shape_);
}
/* static */ bool
Shape::setObjectParent(JSContext *cx, JSObject *parent, HeapPtrShape *listp)
Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, HeapPtrShape *listp)
{
if ((*listp)->getObjectParent() == parent)
return true;
@ -1167,7 +1173,7 @@ Shape::setObjectParent(JSContext *cx, JSObject *parent, HeapPtrShape *listp)
BaseShape base(*(*listp)->base()->unowned());
base.setParent(parent);
return replaceLastProperty(cx, base, listp);
return replaceLastProperty(cx, base, proto, listp);
}
bool
@ -1208,11 +1214,11 @@ JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32 flag_, GenerateShape
return true;
}
return Shape::setObjectFlag(cx, flag, &shape_);
return Shape::setObjectFlag(cx, flag, getProto(), &shape_);
}
/* static */ bool
Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, HeapPtrShape *listp)
Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, HeapPtrShape *listp)
{
if ((*listp)->getObjectFlags() & flag)
return true;
@ -1220,7 +1226,7 @@ Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, HeapPtrShape *listp)
BaseShape base(*(*listp)->base()->unowned());
base.flags |= flag;
return replaceLastProperty(cx, base, listp);
return replaceLastProperty(cx, base, proto, listp);
}
/* static */ inline HashNumber
@ -1312,7 +1318,7 @@ Shape::setExtensibleParents(JSContext *cx, HeapPtrShape *listp)
BaseShape base(*shape->base()->unowned());
base.flags |= BaseShape::EXTENSIBLE_PARENTS;
return replaceLastProperty(cx, base, listp);
return replaceLastProperty(cx, base, NULL, listp);
}
bool
@ -1328,7 +1334,7 @@ Bindings::setParent(JSContext *cx, JSObject *obj)
{
if (!ensureShape(cx))
return false;
return Shape::setObjectParent(cx, obj, &lastBinding);
return Shape::setObjectParent(cx, obj, NULL, &lastBinding);
}
/* static */ inline HashNumber
@ -1346,7 +1352,8 @@ InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
return lookup.clasp == key.shape->getObjectClass()
&& lookup.proto == key.proto
&& lookup.parent == key.shape->getObjectParent()
&& lookup.nfixed == key.shape->numFixedSlots();
&& lookup.nfixed == key.shape->numFixedSlots()
&& lookup.baseFlags == key.shape->getObjectFlags();
}
/* static */ Shape *
@ -1359,7 +1366,7 @@ EmptyShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSO
return NULL;
size_t nfixed = GetGCKindSlots(kind, clasp);
InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed);
InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
@ -1418,7 +1425,7 @@ NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject
EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
{
InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
shape->numFixedSlots());
shape->numFixedSlots(), shape->getObjectFlags());
InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
JS_ASSERT(p);

View File

@ -559,7 +559,7 @@ struct Shape : public js::gc::Cell
Shape *getChildBinding(JSContext *cx, const Shape &child, HeapPtrShape *lastBinding);
/* Replace the base shape of the last shape in a non-dictionary lineage with base. */
static bool replaceLastProperty(JSContext *cx, const BaseShape &base, HeapPtrShape *lastp);
static bool replaceLastProperty(JSContext *cx, const BaseShape &base, JSObject *proto, HeapPtrShape *lastp);
bool hashify(JSContext *cx);
void handoffTableTo(Shape *newShape);
@ -631,8 +631,8 @@ struct Shape : public js::gc::Cell
Class *getObjectClass() const { return base()->clasp; }
JSObject *getObjectParent() const { return base()->parent; }
static bool setObjectParent(JSContext *cx, JSObject *obj, HeapPtrShape *listp);
static bool setObjectFlag(JSContext *cx, BaseShape::Flag flag, HeapPtrShape *listp);
static bool setObjectParent(JSContext *cx, JSObject *obj, JSObject *proto, HeapPtrShape *listp);
static bool setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, HeapPtrShape *listp);
uint32 getObjectFlags() const { return base()->flags & BaseShape::OBJECT_FLAG_MASK; }
bool hasObjectFlag(BaseShape::Flag flag) const {
@ -982,9 +982,11 @@ struct InitialShapeEntry
Class *clasp;
JSObject *proto;
JSObject *parent;
size_t nfixed;
Lookup(Class *clasp, JSObject *proto, JSObject *parent, size_t nfixed)
: clasp(clasp), proto(proto), parent(parent), nfixed(nfixed)
uint32 nfixed;
uint32 baseFlags;
Lookup(Class *clasp, JSObject *proto, JSObject *parent, uint32 nfixed, uint32 baseFlags)
: clasp(clasp), proto(proto), parent(parent),
nfixed(nfixed), baseFlags(baseFlags)
{}
};