diff options
Diffstat (limited to 'src/qml/jsruntime/qv4internalclass.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4internalclass.cpp | 170 |
1 files changed, 134 insertions, 36 deletions
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 70849775cb..904c6a5eaf 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -178,7 +178,7 @@ void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s) data->values.size = s; } -PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) +PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i) const { Q_ASSERT(data && i < size()); return PropertyKey::fromId(data->values.values[i].rawValue()); @@ -265,6 +265,13 @@ namespace Heap { void InternalClass::init(ExecutionEngine *engine) { +// InternalClass is automatically zeroed during allocation: +// prototype = nullptr; +// parent = nullptr; +// size = 0; +// numRedundantTransitions = 0; +// flags = 0; + Base::init(); new (&propertyTable) PropertyHash(); new (&nameMap) SharedInternalClassData<PropertyKey>(engine); @@ -273,13 +280,6 @@ void InternalClass::init(ExecutionEngine *engine) this->engine = engine; vtable = QV4::InternalClass::staticVTable(); -// prototype = nullptr; -// parent = nullptr; -// size = 0; - extensible = true; - isFrozen = false; - isSealed = false; - isUsedAsProto = false; protoId = engine->newProtoId(); // Also internal classes need an internal class pointer. Simply make it point to itself @@ -300,10 +300,8 @@ void InternalClass::init(Heap::InternalClass *other) prototype = other->prototype; parent = other; size = other->size; - extensible = other->extensible; - isSealed = other->isSealed; - isFrozen = other->isFrozen; - isUsedAsProto = other->isUsedAsProto; + numRedundantTransitions = other->numRedundantTransitions; + flags = other->flags; protoId = engine->newProtoId(); internalClass.set(engine, other->internalClass); @@ -365,7 +363,99 @@ static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) ++newClass->size; } -Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) +static PropertyAttributes attributesFromFlags(int flags) +{ + PropertyAttributes attributes; + attributes.m_all = uchar(flags); + return attributes; +} + +static Heap::InternalClass *cleanInternalClass(Heap::InternalClass *orig) +{ + if (++orig->numRedundantTransitions < Heap::InternalClass::MaxRedundantTransitions) + return orig; + + // We will generally add quite a few transitions here. We have 255 redundant ones. + // We can expect at least as many significant ones in addition. + std::vector<InternalClassTransition> transitions; + + Scope scope(orig->engine); + Scoped<QV4::InternalClass> child(scope, orig); + + { + quint8 remainingRedundantTransitions = orig->numRedundantTransitions; + QSet<PropertyKey> properties; + int structureChanges = 0; + + Scoped<QV4::InternalClass> parent(scope, orig->parent); + while (parent && remainingRedundantTransitions > 0) { + Q_ASSERT(child->d() != scope.engine->classes[ExecutionEngine::Class_Empty]); + const auto it = std::find_if( + parent->d()->transitions.begin(), parent->d()->transitions.end(), + [&child](const InternalClassTransition &t) { + return child->d() == t.lookup; + }); + Q_ASSERT(it != parent->d()->transitions.end()); + + if (it->flags & InternalClassTransition::StructureChange) { + // A structural change. Each kind of structural change has to be recorded only once. + if ((structureChanges & it->flags) != it->flags) { + transitions.push_back(*it); + structureChanges |= it->flags; + } else { + --remainingRedundantTransitions; + } + } else if (!properties.contains(it->id)) { + // We only need the final state of the property. + properties.insert(it->id); + + // Property removal creates _two_ redundant transitions. + // We don't have to replay either, but numRedundantTransitions only records one. + if (it->flags != 0) + transitions.push_back(*it); + } else { + --remainingRedundantTransitions; + } + + child = parent->d(); + parent = child->d()->parent; + Q_ASSERT(child->d() != parent->d()); + } + } + + for (auto it = transitions.rbegin(); it != transitions.rend(); ++it) { + switch (it->flags) { + case InternalClassTransition::NotExtensible: + child = child->d()->nonExtensible(); + continue; + case InternalClassTransition::VTableChange: + child = child->d()->changeVTable(it->vtable); + continue; + case InternalClassTransition::PrototypeChange: + child = child->d()->changePrototype(it->prototype); + continue; + case InternalClassTransition::ProtoClass: + child = child->d()->asProtoClass(); + continue; + case InternalClassTransition::Sealed: + child = child->d()->sealed(); + continue; + case InternalClassTransition::Frozen: + child = child->d()->frozen(); + continue; + default: + Q_ASSERT(it->flags != 0); + Q_ASSERT(it->flags < InternalClassTransition::StructureChange); + child = child->addMember(it->id, attributesFromFlags(it->flags)); + continue; + } + } + + return child->d(); +} + +Heap::InternalClass *InternalClass::changeMember( + PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { if (!data.isEmpty()) data.resolve(); @@ -381,7 +471,7 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert } if (data == propertyData.at(idx)) - return static_cast<Heap::InternalClass *>(this); + return this; Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); @@ -394,7 +484,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert Q_ASSERT(!propertyData.at(idx).isAccessor()); // add a dummy entry for the accessor - entry->setterIndex = newClass->size; + if (entry) + entry->setterIndex = newClass->size; e->setterIndex = newClass->size; addDummyEntry(newClass, *e); } @@ -403,7 +494,8 @@ Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, Propert t.lookup = newClass; Q_ASSERT(t.lookup); - return newClass; + + return cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) @@ -413,7 +505,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) if (proto) proto->setUsedAsProto(); Q_ASSERT(prototype != proto); - Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); + Q_ASSERT(!proto || proto->internalClass->isUsedAsProto()); Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange }; temp.prototype = proto; @@ -427,8 +519,7 @@ Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) newClass->prototype = proto; t.lookup = newClass; - - return newClass; + return prototype ? cleanInternalClass(newClass) : newClass; } Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) @@ -449,12 +540,14 @@ Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt) t.lookup = newClass; Q_ASSERT(t.lookup); Q_ASSERT(newClass->vtable); - return newClass; + return vtable == QV4::InternalClass::staticVTable() + ? newClass + : cleanInternalClass(newClass); } Heap::InternalClass *InternalClass::nonExtensible() { - if (!extensible) + if (!isExtensible()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible}; @@ -463,7 +556,7 @@ Heap::InternalClass *InternalClass::nonExtensible() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->extensible = false; + newClass->flags |= NotExtensible; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -500,7 +593,7 @@ Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAt Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry) { - Transition temp = { { identifier }, nullptr, (int)data.flags() }; + Transition temp = { { identifier }, nullptr, int(data.all()) }; Transition &t = lookupOrInsertTransition(temp); if (entry) { @@ -553,21 +646,23 @@ void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier) changeMember(object, identifier, Attr_Invalid); #ifndef QT_NO_DEBUG - // we didn't remove the data slot, just made it inaccessible - Q_ASSERT(object->internalClass()->size == oldClass->size); + // We didn't remove the data slot, just made it inaccessible. + // ... unless we've rebuilt the whole class. Then all the deleted properties are gone. + Q_ASSERT(object->internalClass()->numRedundantTransitions == 0 + || object->internalClass()->size == oldClass->size); #endif } Heap::InternalClass *InternalClass::sealed() { - if (isSealed) + if (isSealed()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isSealed); + Q_ASSERT(t.lookup && t.lookup->isSealed()); return t.lookup; } @@ -575,7 +670,7 @@ Heap::InternalClass *InternalClass::sealed() Scoped<QV4::InternalClass> ic(scope, engine->newClass(this)); Heap::InternalClass *s = ic->d(); - if (!isFrozen) { // freezing also makes all properties non-configurable + if (!isFrozen()) { // freezing also makes all properties non-configurable for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) @@ -584,7 +679,7 @@ Heap::InternalClass *InternalClass::sealed() s->propertyData.set(i, attrs); } } - s->isSealed = true; + s->flags |= Sealed; t.lookup = s; return s; @@ -592,14 +687,14 @@ Heap::InternalClass *InternalClass::sealed() Heap::InternalClass *InternalClass::frozen() { - if (isFrozen) + if (isFrozen()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen }; Transition &t = lookupOrInsertTransition(temp); if (t.lookup) { - Q_ASSERT(t.lookup && t.lookup->isFrozen); + Q_ASSERT(t.lookup && t.lookup->isFrozen()); return t.lookup; } @@ -616,7 +711,7 @@ Heap::InternalClass *InternalClass::frozen() attrs.setConfigurable(false); f->propertyData.set(i, attrs); } - f->isFrozen = true; + f->flags |= Frozen; t.lookup = f; return f; @@ -640,7 +735,7 @@ InternalClass *InternalClass::cryopreserved() bool InternalClass::isImplicitlyFrozen() const { - if (isFrozen) + if (isFrozen()) return true; for (uint i = 0; i < size; ++i) { @@ -656,7 +751,7 @@ bool InternalClass::isImplicitlyFrozen() const Heap::InternalClass *InternalClass::asProtoClass() { - if (isUsedAsProto) + if (isUsedAsProto()) return this; Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass }; @@ -665,7 +760,7 @@ Heap::InternalClass *InternalClass::asProtoClass() return t.lookup; Heap::InternalClass *newClass = engine->newClass(this); - newClass->isUsedAsProto = true; + newClass->flags |= UsedAsProto; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -685,7 +780,7 @@ static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic) void InternalClass::updateProtoUsage(Heap::Object *o) { - Q_ASSERT(isUsedAsProto); + Q_ASSERT(isUsedAsProto()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty); Q_ASSERT(!ic->prototype); @@ -698,6 +793,9 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) if (ic->prototype) ic->prototype->mark(stack); + if (ic->parent) + ic->parent->mark(stack); + ic->nameMap.mark(stack); } |