From 71f0bc8ce6244d4544dcd35a62ed8f163bb5b092 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 13 Nov 2017 13:31:23 +0100 Subject: Add a unique id to InternalClass that describes it's total state So far the InternalClass only did describe the state of the class itself, but it wouldn't change if some of the underlying objects in the prototype chain changed. This now fixes that and introduces a unique ID that completely describes the state of the object including all it's prototypes. This opens up for optimizing lookups down to one branch and a load, independent of the depth of the value inside the prototype chain. Change-Id: I0787e0e4710f2f6703b1d5e35996124b3db2d2da Reviewed-by: Erik Verbruggen --- src/qml/jsruntime/qv4internalclass.cpp | 62 ++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) (limited to 'src/qml/jsruntime/qv4internalclass.cpp') diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 603da1df7b..d439884ca2 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -112,6 +112,7 @@ InternalClass::InternalClass(ExecutionEngine *engine) , size(0) , extensible(true) { + id = engine->newInternalClassId(); } @@ -127,8 +128,9 @@ InternalClass::InternalClass(const QV4::InternalClass &other) , m_frozen(0) , size(other.size) , extensible(other.extensible) + , isUsedAsProto(other.isUsedAsProto) { - Q_ASSERT(extensible); + id = engine->newInternalClassId(); } static void insertHoleIntoPropertyData(Object *object, int idx) @@ -213,7 +215,10 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) { + if (proto) + proto->setUsedAsProto(); Q_ASSERT(prototype != proto); + Q_ASSERT(!proto || proto->internalClass->isUsedAsProto); Transition temp = { { nullptr }, 0, Transition::PrototypeChange }; temp.prototype = proto; @@ -237,6 +242,7 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) } t.lookup = newClass; + return newClass; } @@ -389,7 +395,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(t.lookup); } -uint QV4::InternalClass::find(const String *string) +uint InternalClass::find(const String *string) { engine->identifierTable->identifier(string); const Identifier *id = string->d()->identifier; @@ -449,6 +455,24 @@ InternalClass *InternalClass::propertiesFrozen() const return frozen; } +InternalClass *InternalClass::asProtoClass() +{ + if (isUsedAsProto) + return this; + + Transition temp = { { nullptr }, nullptr, Transition::ProtoClass }; + Transition &t = lookupOrInsertTransition(temp); + if (t.lookup) + return t.lookup; + + InternalClass *newClass = engine->newClass(*this); + newClass->isUsedAsProto = true; + + t.lookup = newClass; + Q_ASSERT(t.lookup); + return newClass; +} + void InternalClass::destroy() { std::vector destroyStack; @@ -478,6 +502,40 @@ void InternalClass::destroy() } } +void InternalClass::updateProtoUsage(Heap::Object *o) +{ + Q_ASSERT(isUsedAsProto); + InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; + Q_ASSERT(!ic->prototype); + + // only need to go two levels into the IC hierarchy, as prototype changes + // can only happen there + for (auto &t : ic->transitions) { + Q_ASSERT(t.lookup); + if (t.flags == InternalClassTransition::VTableChange) { + InternalClass *ic2 = t.lookup; + for (auto &t2 : ic2->transitions) { + if (t2.flags == InternalClassTransition::PrototypeChange && + t2.lookup->prototype == o) + ic2->updateInternalClassIdRecursive(); + } + } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) { + ic->updateInternalClassIdRecursive(); + } + } +} + +void InternalClass::updateInternalClassIdRecursive() +{ + id = engine->newInternalClassId(); + for (auto &t : transitions) { + Q_ASSERT(t.lookup); + t.lookup->updateInternalClassIdRecursive(); + } +} + + + void InternalClassPool::markObjects(MarkStack *markStack) { InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; -- cgit v1.2.3