Bug 1133369 - Use consistent allocation kinds for new objects after converting an unboxed group, r=jandem.

This commit is contained in:
Brian Hackett 2015-02-24 16:02:09 -06:00
Родитель 5024d7a796
Коммит 17643d2924
5 изменённых файлов: 103 добавлений и 23 удалений

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

@ -252,7 +252,7 @@ class ObjectGroup : public gc::TenuredCell
} }
TypeNewScript *anyNewScript(); TypeNewScript *anyNewScript();
void detachNewScript(bool writeBarrier); void detachNewScript(bool writeBarrier, ObjectGroup *replacement);
ObjectGroupFlags flagsDontCheckGeneration() { ObjectGroupFlags flagsDontCheckGeneration() {
return flags_; return flags_;
@ -480,7 +480,7 @@ class ObjectGroup : public gc::TenuredCell
void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags); void setFlags(ExclusiveContext *cx, ObjectGroupFlags flags);
void markUnknown(ExclusiveContext *cx); void markUnknown(ExclusiveContext *cx);
void maybeClearNewScriptOnOOM(); void maybeClearNewScriptOnOOM();
void clearNewScript(ExclusiveContext *cx); void clearNewScript(ExclusiveContext *cx, ObjectGroup *replacement = nullptr);
bool isPropertyNonData(jsid id); bool isPropertyNonData(jsid id);
bool isPropertyNonWritable(jsid id); bool isPropertyNonWritable(jsid id);

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

@ -2765,7 +2765,7 @@ ObjectGroup::anyNewScript()
} }
void void
ObjectGroup::detachNewScript(bool writeBarrier) ObjectGroup::detachNewScript(bool writeBarrier, ObjectGroup *replacement)
{ {
// Clear the TypeNewScript from this ObjectGroup and, if it has been // Clear the TypeNewScript from this ObjectGroup and, if it has been
// analyzed, remove it from the newObjectGroups table so that it will not be // analyzed, remove it from the newObjectGroups table so that it will not be
@ -2775,8 +2775,16 @@ ObjectGroup::detachNewScript(bool writeBarrier)
MOZ_ASSERT(newScript); MOZ_ASSERT(newScript);
if (newScript->analyzed()) { if (newScript->analyzed()) {
newScript->function()->compartment()->objectGroups.removeDefaultNewGroup(nullptr, proto(), ObjectGroupCompartment &objectGroups = newScript->function()->compartment()->objectGroups;
newScript->function()); if (replacement) {
MOZ_ASSERT(replacement->newScript()->function() == newScript->function());
objectGroups.replaceDefaultNewGroup(nullptr, proto(), newScript->function(),
replacement);
} else {
objectGroups.removeDefaultNewGroup(nullptr, proto(), newScript->function());
}
} else {
MOZ_ASSERT(!replacement);
} }
if (this->newScript()) if (this->newScript())
@ -2800,13 +2808,13 @@ ObjectGroup::maybeClearNewScriptOnOOM()
addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED); addFlags(OBJECT_FLAG_NEW_SCRIPT_CLEARED);
// This method is called during GC sweeping, so don't trigger pre barriers. // This method is called during GC sweeping, so don't trigger pre barriers.
detachNewScript(/* writeBarrier = */ false); detachNewScript(/* writeBarrier = */ false, nullptr);
js_delete(newScript); js_delete(newScript);
} }
void void
ObjectGroup::clearNewScript(ExclusiveContext *cx) ObjectGroup::clearNewScript(ExclusiveContext *cx, ObjectGroup *replacement /* = nullptr*/)
{ {
TypeNewScript *newScript = anyNewScript(); TypeNewScript *newScript = anyNewScript();
if (!newScript) if (!newScript)
@ -2814,6 +2822,7 @@ ObjectGroup::clearNewScript(ExclusiveContext *cx)
AutoEnterAnalysis enter(cx); AutoEnterAnalysis enter(cx);
if (!replacement) {
// Invalidate any Ion code constructing objects of this type. // Invalidate any Ion code constructing objects of this type.
setFlags(cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED); setFlags(cx, OBJECT_FLAG_NEW_SCRIPT_CLEARED);
@ -2821,8 +2830,9 @@ ObjectGroup::clearNewScript(ExclusiveContext *cx)
// will not try to construct another one later. // will not try to construct another one later.
if (!newScript->function()->setNewScriptCleared(cx)) if (!newScript->function()->setNewScriptCleared(cx))
cx->recoverFromOutOfMemory(); cx->recoverFromOutOfMemory();
}
detachNewScript(/* writeBarrier = */ true); detachNewScript(/* writeBarrier = */ true, replacement);
if (cx->isJSContext()) { if (cx->isJSContext()) {
bool found = newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this); bool found = newScript->rollbackPartiallyInitializedObjects(cx->asJSContext(), this);
@ -3268,6 +3278,33 @@ TypeNewScript::make(JSContext *cx, ObjectGroup *group, JSFunction *fun)
gc::TraceTypeNewScript(group); gc::TraceTypeNewScript(group);
} }
// Make a TypeNewScript with the same initializer list as |newScript| but with
// a new template object.
/* static */ TypeNewScript *
TypeNewScript::makeNativeVersion(JSContext *cx, TypeNewScript *newScript,
PlainObject *templateObject)
{
MOZ_ASSERT(cx->zone()->types.activeAnalysis);
ScopedJSDeletePtr<TypeNewScript> nativeNewScript(cx->new_<TypeNewScript>());
if (!nativeNewScript)
return nullptr;
nativeNewScript->function_ = newScript->function();
nativeNewScript->templateObject_ = templateObject;
Initializer *cursor = newScript->initializerList;
while (cursor->kind != Initializer::DONE) { cursor++; }
size_t initializerLength = cursor - newScript->initializerList + 1;
nativeNewScript->initializerList = cx->zone()->pod_calloc<Initializer>(initializerLength);
if (!nativeNewScript->initializerList)
return nullptr;
PodCopy(nativeNewScript->initializerList, newScript->initializerList, initializerLength);
return nativeNewScript.forget();
}
size_t size_t
TypeNewScript::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const TypeNewScript::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{ {

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

@ -827,8 +827,6 @@ class TypeNewScript
private: private:
// Scripted function which this information was computed for. // Scripted function which this information was computed for.
// If instances of the associated group are created without calling
// 'new' on this function, the new script information is cleared.
HeapPtrFunction function_; HeapPtrFunction function_;
// Any preliminary objects with the type. The analyses are not performed // Any preliminary objects with the type. The analyses are not performed
@ -902,6 +900,8 @@ class TypeNewScript
bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group); bool rollbackPartiallyInitializedObjects(JSContext *cx, ObjectGroup *group);
static void make(JSContext *cx, ObjectGroup *group, JSFunction *fun); static void make(JSContext *cx, ObjectGroup *group, JSFunction *fun);
static TypeNewScript *makeNativeVersion(JSContext *cx, TypeNewScript *newScript,
PlainObject *templateObject);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
}; };

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

@ -35,6 +35,9 @@ UnboxedLayout::trace(JSTracer *trc)
if (nativeShape_) if (nativeShape_)
MarkShape(trc, &nativeShape_, "unboxed_layout_nativeShape"); MarkShape(trc, &nativeShape_, "unboxed_layout_nativeShape");
if (replacementNewGroup_)
MarkObjectGroup(trc, &replacementNewGroup_, "unboxed_layout_replacementNewGroup");
} }
size_t size_t
@ -172,18 +175,49 @@ UnboxedPlainObject::trace(JSTracer *trc, JSObject *obj)
/* static */ bool /* static */ bool
UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group) UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
{ {
AutoEnterAnalysis enter(cx);
UnboxedLayout &layout = group->unboxedLayout(); UnboxedLayout &layout = group->unboxedLayout();
Rooted<TaggedProto> proto(cx, group->proto());
MOZ_ASSERT(!layout.nativeGroup()); MOZ_ASSERT(!layout.nativeGroup());
// Immediately clear any new script on the group, as // Immediately clear any new script on the group. This is done by replacing
// rollbackPartiallyInitializedObjects() will be confused by the type // the existing new script with one for a replacement default new group.
// changes we make later on. // This is done so that the size of the replacment group's objects is the
group->clearNewScript(cx); // same as that for the unboxed group, so that we do not see polymorphic
// slot accesses later on for sites that see converted objects from this
// group and objects that were allocated using the replacement new group.
RootedObjectGroup replacementNewGroup(cx);
if (layout.newScript()) {
replacementNewGroup = ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_, proto);
if (!replacementNewGroup)
return false;
AutoEnterAnalysis enter(cx); PlainObject *templateObject = NewObjectWithGroup<PlainObject>(cx, replacementNewGroup,
cx->global(), layout.getAllocKind(),
MaybeSingletonObject);
if (!templateObject)
return false;
Rooted<TaggedProto> proto(cx, group->proto()); for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property &property = layout.properties()[i];
if (!templateObject->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
return false;
MOZ_ASSERT(templateObject->slotSpan() == i + 1);
MOZ_ASSERT(!templateObject->inDictionaryMode());
}
TypeNewScript *replacementNewScript =
TypeNewScript::makeNativeVersion(cx, layout.newScript(), templateObject);
if (!replacementNewScript)
return false;
replacementNewGroup->setNewScript(replacementNewScript);
gc::TraceTypeNewScript(replacementNewGroup);
group->clearNewScript(cx, replacementNewGroup);
}
size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind()); size_t nfixed = gc::GetGCKindSlots(layout.getAllocKind());
RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto, RootedShape shape(cx, EmptyShape::getInitialShape(cx, &PlainObject::class_, proto,
@ -224,6 +258,7 @@ UnboxedLayout::makeNativeGroup(JSContext *cx, ObjectGroup *group)
layout.nativeGroup_ = nativeGroup; layout.nativeGroup_ = nativeGroup;
layout.nativeShape_ = shape; layout.nativeShape_ = shape;
layout.replacementNewGroup_ = replacementNewGroup;
nativeGroup->setOriginalUnboxedGroup(group); nativeGroup->setOriginalUnboxedGroup(group);

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

@ -71,10 +71,18 @@ class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
HeapPtrObjectGroup nativeGroup_; HeapPtrObjectGroup nativeGroup_;
HeapPtrShape nativeShape_; HeapPtrShape nativeShape_;
// If nativeGroup is set and this object originally had a TypeNewScript,
// this points to the default 'new' group which replaced this one (and
// which might itself have been cleared since). This link is only needed to
// keep the replacement group from being GC'ed. If it were GC'ed and a new
// one regenerated later, that new group might have a different allocation
// kind from this group.
HeapPtrObjectGroup replacementNewGroup_;
public: public:
UnboxedLayout(const PropertyVector &properties, size_t size) UnboxedLayout(const PropertyVector &properties, size_t size)
: size_(size), newScript_(nullptr), traceList_(nullptr), : size_(size), newScript_(nullptr), traceList_(nullptr),
nativeGroup_(nullptr), nativeShape_(nullptr) nativeGroup_(nullptr), nativeShape_(nullptr), replacementNewGroup_(nullptr)
{ {
properties_.appendAll(properties); properties_.appendAll(properties);
} }