Bug 1127167 - Backout 825f6ee63f7f for causing massive regressions on a CLOSED TREE

This commit is contained in:
Brian Hackett 2015-02-11 17:47:13 -07:00
parent b3fc1b8237
commit f93fe5bf9e
8 changed files with 83 additions and 81 deletions

View file

@ -9335,28 +9335,24 @@ TryAttachCallStub(JSContext *cx, ICCall_Fallback *stub, HandleScript script, jsb
// as a constructor, for later use during Ion compilation. // as a constructor, for later use during Ion compilation.
RootedObject templateObject(cx); RootedObject templateObject(cx);
if (constructing) { if (constructing) {
// If we are calling a constructor for which the new script JSObject *thisObject = CreateThisForFunction(cx, fun, MaybeSingletonObject);
// properties analysis has not been performed yet, don't attach a if (!thisObject)
// stub. After the analysis is performed, CreateThisForFunction may return false;
// start returning objects with a different type, and the Ion
// compiler will get confused.
// Only attach a stub if the function already has a prototype and if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>()) {
// we can look it up without causing side effects. templateObject = thisObject;
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 (protov.isObject()) { // If we are calling a constructor for which the new script
TaggedProto proto(&protov.toObject()); // properties analysis has not been performed yet, don't attach a
ObjectGroup *group = ObjectGroup::defaultNewGroup(cx, nullptr, proto, fun); // stub. After the analysis is performed, CreateThisForFunction may
if (!group) // start returning objects with a different type, and the Ion
return false; // compiler might get confused.
TypeNewScript *newScript = templateObject->group()->newScript();
if (group->newScript() && !group->newScript()->analyzed()) { if (newScript && !newScript->analyzed()) {
JitSpew(JitSpew_BaselineIC, " Function newScript has not been 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>());
return true; return true;
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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