From 4c3098ab106fae8f2ee256950cdbc8b8da29aa1b Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 29 Aug 2022 19:11:42 +0200 Subject: [PATCH] V4: Do not update proto usage before engine is fully initialized Updating the prototype usage is very expensive. We only need to do it once there are lookups. Before the engine is fully initialized there are no lookups. Change-Id: Ic919a1f8955718d417e7747ea72e009d443c42fd Reviewed-by: Fabian Kosmale --- src/qml/jsruntime/qv4engine.cpp | 4 ++++ src/qml/jsruntime/qv4enginebase_p.h | 3 ++- src/qml/jsruntime/qv4lookup.cpp | 33 +++++++++++++++++++++++++++++ src/qml/jsruntime/qv4object.cpp | 10 ++++++++- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index c94b24b263..4e8b29e33d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -867,6 +867,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) QV4::QObjectWrapper::initializeBindings(this); m_delayedCallQueue.init(this); + isInitialized = true; } ExecutionEngine::~ExecutionEngine() @@ -2182,8 +2183,11 @@ const QSet &ExecutionEngine::illegalNames() const void ExecutionEngine::setQmlEngine(QQmlEngine *engine) { + // Second stage of initialization. We're updating some more prototypes here. + isInitialized = false; m_qmlEngine = engine; initQmlGlobalObject(); + isInitialized = true; } static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object) diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index dc69529860..dd85f17717 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -44,7 +44,8 @@ struct Q_QML_EXPORT EngineBase { #endif quint8 isExecutingInRegExpJIT = false; - quint8 padding[3]; + quint8 isInitialized = false; + quint8 padding[2]; MemoryManager *memoryManager = nullptr; qint32 callDepth = 0; diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index ddd6f0174d..4e41fd60ce 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -38,6 +38,9 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + primitiveLookup.type = object.type(); switch (primitiveLookup.type) { case Value::Undefined_Type: @@ -83,6 +86,9 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Object *o = engine->globalObject; PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); protoLookup.protoId = o->internalClass()->protoId; @@ -220,6 +226,9 @@ ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Va ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); @@ -277,6 +286,9 @@ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEng ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); @@ -312,6 +324,9 @@ ReturnedValue Lookup::getterAccessor(Lookup *l, ExecutionEngine *engine, const V ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); @@ -328,6 +343,9 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + // we can safely cast to a QV4::Object here. If object is actually a string, // the internal class won't match Heap::Object *o = static_cast(object.heapObject()); @@ -381,6 +399,9 @@ ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, con ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) @@ -392,6 +413,9 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + if (object.type() == l->primitiveLookup.type && !object.isObject()) { Heap::Object *o = l->primitiveLookup.proto; if (l->primitiveLookup.protoId == o->internalClass->protoId) { @@ -423,6 +447,9 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *o = engine->globalObject->d(); if (l->protoLookup.protoId == o->internalClass->protoId) return l->protoLookup.data->asReturnedValue(); @@ -432,6 +459,9 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine) ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *o = engine->globalObject->d(); if (l->protoLookup.protoId == o->internalClass->protoId) { const Value *getter = l->protoLookup.data; @@ -551,6 +581,9 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Object *o = static_cast(object.managed()); if (o && o->internalClass()->protoId == l->insertionLookup.protoId) { o->setInternalClass(l->insertionLookup.newClass); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index aec04b167d..ef4a40f4f3 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -68,7 +68,9 @@ void Object::setInternalClass(Heap::InternalClass *ic) } } - if (ic->isUsedAsProto()) + // Before the engine is done initializing, we cannot have any lookups. + // Therefore, there is no point in updating the proto IDs. + if (ic->engine->isInitialized && ic->isUsedAsProto()) ic->updateProtoUsage(p); } @@ -734,6 +736,9 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Heap::Object *obj = object->d(); PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); if (name.isArrayIndex()) { @@ -769,6 +774,9 @@ ReturnedValue Object::virtualResolveLookupGetter(const Object *object, Execution bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) { + // Otherwise we cannot trust the protoIds + Q_ASSERT(engine->isInitialized); + Scope scope(engine); ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);