Bug 1127167 - Avoid creating mutant half-native half-non-native objects when making unboxed layouts, r=jandem.

This commit is contained in:
Brian Hackett 2015-02-14 08:48:08 -07:00
Родитель 3d4e7776e7
Коммит dd9b8bc230
8 изменённых файлов: 88 добавлений и 84 удалений

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

@ -9353,24 +9353,28 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
// as a constructor, for later use during Ion compilation.
RootedObject templateObject(cx);
if (constructing) {
JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
if (!thisObject)
return false;
// If we are calling a constructor for which the new script
// properties analysis has not been performed yet, don't attach a
// stub. After the analysis is performed, CreateThisForFunction may
// start returning objects with a different type, and the Ion
// compiler will get confused.
if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>()) {
templateObject = thisObject;
// Only attach a stub if the function already has a prototype and
// we can look it up without causing side effects.
RootedValue protov(cx);
if (!GetPropertyPure(cx, fun, NameToId(cx->names().prototype), protov.address())) {
JitSpew(JitSpew_BaselineIC, " Can't purely lookup function prototype");
return true;
}
// If we are calling a constructor for which the new script
// properties analysis has not been performed yet, don't attach a
// stub. After the analysis is performed, CreateThisForFunction may
// start returning objects with a different type, and the Ion
// compiler might get confused.
TypeNewScript *newScript = templateObject->group()->newScript();
if (newScript && !newScript->analyzed()) {
// Clear the object just created from the preliminary objects
// on the TypeNewScript, as it will not be used or filled in by
// running code.
newScript->unregisterNewObject(&templateObject->as<PlainObject>());
if (protov.isObject()) {
TaggedProto proto(&protov.toObject());
ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, fun);
if (!group)
return false;
if (group->newScript() && !group->newScript()->analyzed()) {
JitSpew(JitSpew_BaselineIC, " Function newScript has not been analyzed");
// This is temporary until the analysis is perfomed, so
// don't treat this as unoptimizable.
@ -9378,6 +9382,13 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
return true;
}
}
JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
if (!thisObject)
return false;
if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
templateObject = thisObject;
}
JitSpew(JitSpew_BaselineIC,

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

@ -1299,13 +1299,13 @@ ClassProtoKeyOrAnonymousOrNull(const js::Class *clasp)
}
static inline bool
NativeGetPureInline(NativeObject *pobj, Shape *shape, MutableHandleValue vp)
NativeGetPureInline(NativeObject *pobj, Shape *shape, Value *vp)
{
if (shape->hasSlot()) {
vp.set(pobj->getSlot(shape->slot()));
MOZ_ASSERT(!vp.isMagic());
*vp = pobj->getSlot(shape->slot());
MOZ_ASSERT(!vp->isMagic());
} else {
vp.setUndefined();
vp->setUndefined();
}
/* Fail if we have a custom getter. */
@ -1344,7 +1344,7 @@ FindClassPrototype(ExclusiveContext *cx, MutableHandleObject protop, const Class
return false;
} else {
Shape *shape = nctor->lookup(cx, cx->names().prototype);
if (!shape || !NativeGetPureInline(nctor, shape, &v))
if (!shape || !NativeGetPureInline(nctor, shape, v.address()))
return false;
}
if (v.isObject())
@ -1478,6 +1478,7 @@ js::NewObjectWithGroupCommon(JSContext *cx, HandleObjectGroup group, HandleObjec
parent == group->proto().toObject()->getParent() &&
newKind == GenericObject &&
group->clasp()->isNative() &&
(!group->newScript() || group->newScript()->analyzed()) &&
!cx->compartment()->hasObjectMetadataCallback())
{
if (cache.lookupGroup(group, allocKind, &entry)) {
@ -2136,7 +2137,7 @@ js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
AllocKind kind = GetBackgroundAllocKind(GuessObjectGCKind(srcObj->as<PlainObject>().numFixedSlots()));
MOZ_ASSERT_IF(srcObj->isTenured(), kind == srcObj->asTenured().getAllocKind());
JSObject *proto = cx->global()->getOrCreateObjectPrototype(cx);
RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
if (!proto)
return nullptr;
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
@ -2144,8 +2145,17 @@ js::CloneObjectLiteral(JSContext *cx, HandleObject parent, HandleObject srcObj)
if (!group)
return nullptr;
RootedShape shape(cx, srcObj->lastProperty());
return NewReshapedObject(cx, group, parent, kind, shape);
RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, parent, kind,
MaybeSingletonObject));
if (!res)
return nullptr;
RootedShape newShape(cx, ReshapeForParentAndAllocKind(cx, srcObj->lastProperty(),
TaggedProto(proto), parent, kind));
if (!newShape || !NativeObject::setLastProperty(cx, res, newShape))
return nullptr;
return res;
}
RootedArrayObject srcArray(cx, &srcObj->as<ArrayObject>());
@ -3000,6 +3010,16 @@ js::LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **
return true;
}
bool
js::GetPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, Value *vp)
{
JSObject *pobj;
Shape *shape;
if (!LookupPropertyPure(cx, obj, id, &pobj, &shape))
return false;
return pobj->isNative() && NativeGetPureInline(&pobj->as<NativeObject>(), shape, vp);
}
bool
JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report)
{

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

@ -1199,6 +1199,9 @@ bool
LookupPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, JSObject **objp,
Shape **propp);
bool
GetPropertyPure(ExclusiveContext *cx, JSObject *obj, jsid id, Value *vp);
bool
GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
MutableHandle<PropertyDescriptor> desc);

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

@ -643,11 +643,6 @@ NewObjectWithGroup(JSContext *cx, HandleObjectGroup group, HandleObject parent,
return NewObjectWithGroup<T>(cx, group, parent, allocKind, newKind);
}
JSObject *
NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, HandleShape shape,
NewObjectKind newKind = GenericObject);
/*
* As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
* the object, zero if the final size is unknown. This should only be used for

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

@ -601,21 +601,17 @@ NativeObject::addPropertyInternal(ExclusiveContext *cx,
return nullptr;
}
JSObject *
js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject parent,
gc::AllocKind allocKind, HandleShape shape, NewObjectKind newKind)
Shape *
js::ReshapeForParentAndAllocKind(JSContext *cx, Shape *shape, TaggedProto proto, JSObject *parent,
gc::AllocKind allocKind)
{
RootedPlainObject res(cx, NewObjectWithGroup<PlainObject>(cx, group, parent, allocKind, newKind));
if (!res)
return nullptr;
// Compute the number of fixed slots with the new allocation kind.
size_t nfixed = gc::GetGCKindSlots(allocKind, shape->getObjectClass());
if (shape->isEmptyShape())
return res;
/* Get all the ids in the object, in order. */
// Get all the ids in the shape, in order.
js::AutoIdVector ids(cx);
{
for (unsigned i = 0; i <= shape->slot(); i++) {
for (unsigned i = 0; i < shape->slotSpan(); i++) {
if (!ids.append(JSID_VOID))
return nullptr;
}
@ -626,17 +622,13 @@ js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject paren
}
}
/* Construct the new shape, without updating type information. */
// Construct the new shape, without updating type information.
RootedId id(cx);
RootedShape newShape(cx, EmptyShape::getInitialShape(cx, res->getClass(),
res->getTaggedProto(),
res->getParent(),
res->getMetadata(),
res->numFixedSlots(),
shape->getObjectFlags()));
RootedShape newShape(cx, EmptyShape::getInitialShape(cx, shape->getObjectClass(),
proto, parent, shape->getObjectMetadata(),
nfixed, shape->getObjectFlags()));
for (unsigned i = 0; i < ids.length(); i++) {
id = ids[i];
MOZ_ASSERT(!res->contains(cx, id));
uint32_t index;
bool indexed = js_IdIsIndex(id, &index);
@ -654,11 +646,9 @@ js::NewReshapedObject(JSContext *cx, HandleObjectGroup group, HandleObject paren
newShape = cx->compartment()->propertyTree.getChild(cx, newShape, child);
if (!newShape)
return nullptr;
if (!NativeObject::setLastProperty(cx, res, newShape))
return nullptr;
}
return res;
return newShape;
}
/*

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

@ -1553,6 +1553,10 @@ IsImplicitDenseOrTypedArrayElement(Shape *prop)
return prop == reinterpret_cast<Shape*>(1);
}
Shape *
ReshapeForParentAndAllocKind(JSContext *cx, Shape *shape, TaggedProto proto, JSObject *parent,
gc::AllocKind allocKind);
} // namespace js
#ifdef _MSC_VER

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

@ -3152,19 +3152,6 @@ PreliminaryObjectArray::registerNewObject(JSObject *res)
MOZ_CRASH("There should be room for registering the new object");
}
void
PreliminaryObjectArray::unregisterNewObject(JSObject *res)
{
for (size_t i = 0; i < COUNT; i++) {
if (objects[i] == res) {
objects[i] = nullptr;
return;
}
}
MOZ_CRASH("The object should be one of the preliminary objects");
}
bool
PreliminaryObjectArray::full() const
{
@ -3240,13 +3227,6 @@ TypeNewScript::registerNewObject(PlainObject *res)
preliminaryObjects->registerNewObject(res);
}
void
TypeNewScript::unregisterNewObject(PlainObject *res)
{
MOZ_ASSERT(!analyzed());
preliminaryObjects->unregisterNewObject(res);
}
// Return whether shape consists entirely of plain data properties.
static bool
OnlyHasDataProperties(Shape *shape)
@ -3294,15 +3274,13 @@ ChangeObjectFixedSlotCount(JSContext *cx, PlainObject *obj, gc::AllocKind allocK
{
MOZ_ASSERT(OnlyHasDataProperties(obj->lastProperty()));
// Make a clone of the object, with the new allocation kind.
RootedShape oldShape(cx, obj->lastProperty());
RootedObjectGroup group(cx, obj->group());
RootedObject parent(cx, obj->getParent());
JSObject *clone = NewReshapedObject(cx, group, parent, allocKind, oldShape);
if (!clone)
Shape *newShape = ReshapeForParentAndAllocKind(cx, obj->lastProperty(),
obj->getTaggedProto(), obj->getParent(),
allocKind);
if (!newShape)
return false;
obj->setLastPropertyShrinkFixedSlots(clone->lastProperty());
obj->setLastPropertyShrinkFixedSlots(newShape);
return true;
}
@ -3490,10 +3468,15 @@ TypeNewScript::maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate,
MOZ_ASSERT(group->unboxedLayout().newScript() == this);
destroyNewScript.group = nullptr;
// Clear out the template object. This is not used for TypeNewScripts
// with an unboxed layout, and additionally this template is now a
// mutant object with a non-native class and native shape, and must be
// collected by the next GC.
// Clear out the template object, which is not used for TypeNewScripts
// with an unboxed layout. Currently it is a mutant object with a
// non-native group and native shape, so make it safe for GC by changing
// its group to the default for its prototype.
ObjectGroup *plainGroup = ObjectGroup::defaultNewGroup(cx, &PlainObject::class_,
group->proto());
if (!plainGroup)
CrashAtUnhandlableOOM("TypeNewScript::maybeAnalyze");
templateObject_->setGroup(plainGroup);
templateObject_ = nullptr;
return true;

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

@ -753,7 +753,6 @@ class PreliminaryObjectArray
}
void registerNewObject(JSObject *res);
void unregisterNewObject(JSObject *res);
JSObject *get(size_t i) const {
MOZ_ASSERT(i < COUNT);
@ -889,7 +888,6 @@ class TypeNewScript
void sweep();
void registerNewObject(PlainObject *res);
void unregisterNewObject(PlainObject *res);
bool maybeAnalyze(JSContext *cx, ObjectGroup *group, bool *regenerate, bool force = false);
bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group);