diff options
| author | Ulf Hermann <ulf.hermann@qt.io> | 2025-03-03 19:12:01 +0100 |
|---|---|---|
| committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2025-03-07 14:44:36 +0000 |
| commit | 58b564d9a4b9f78e4d87697ffe88f7524cf3e588 (patch) | |
| tree | 3d2bd1a239d320b28bd2cfd7f604b48ebbb8e567 | |
| parent | 1c488a7ff7b0bfed05c00f3a34dfe2eec1f272c4 (diff) | |
QtQml: Always load from existing compilation units first
If a compilation unit for the given URL already exists in the type
registry, we can safely use it since anything in the type registry is
shareable. An exception to this are requests with explicit inline source
code. Those cannot be cached at all.
Conversely, if we were to re-compile, that's not only wasteful but we
may end up with diverging type hierarchies and mismatched types.
Revert commit d0e16e3f614d73f6694863d2cfb1fbd661c34e3f since it has
become counter-productive now. We really want to have exactly one base
compilation unit for each type, and that should be universally
accessible. When d0e16e3f614d73f6694863d2cfb1fbd661c34e3f was written we
had separate base CUs for each engine, and we didn't want those to
accidentally find each other.
Realize that composite types have to be indexed by url(), not
finalUrl(), since url() includes any file selectors while finalUrl()
does not.
Fixes: QTBUG-134398
Change-Id: I130cdc27ebd8a2814e194478a27eef5bb7f79eb7
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
(cherry picked from commit 6468df7657f6af4de8727363c7f7d97b680b1867)
(cherry picked from commit d599310ae9142a76196ac5968a5639ad66c5de06)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
| -rw-r--r-- | src/qml/common/qv4compileddata.cpp | 2 | ||||
| -rw-r--r-- | src/qml/qml/qqmldatablob_p.h | 1 | ||||
| -rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 23 | ||||
| -rw-r--r-- | src/qml/qml/qqmlscriptblob.cpp | 22 | ||||
| -rw-r--r-- | src/qml/qml/qqmltypedata.cpp | 8 | ||||
| -rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 7 | ||||
| -rw-r--r-- | tests/auto/qml/qqmltypeloader/data/A.qml | 5 | ||||
| -rw-r--r-- | tests/auto/qml/qqmltypeloader/data/B.qml | 5 | ||||
| -rw-r--r-- | tests/auto/qml/qqmltypeloader/data/C.qml | 5 | ||||
| -rw-r--r-- | tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp | 62 |
10 files changed, 115 insertions, 25 deletions
diff --git a/src/qml/common/qv4compileddata.cpp b/src/qml/common/qv4compileddata.cpp index 80b806ec2d..3959c0c2a5 100644 --- a/src/qml/common/qv4compileddata.cpp +++ b/src/qml/common/qv4compileddata.cpp @@ -281,7 +281,7 @@ void CompiledData::CompilationUnit::finalizeCompositeType(const QQmlType &type) qmlType = type; } else { qmlType = QQmlMetaType::findCompositeType( - finalUrl(), this, (unitData()->flags & CompiledData::Unit::IsSingleton) + url(), this, (unitData()->flags & CompiledData::Unit::IsSingleton) ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton); } diff --git a/src/qml/qml/qqmldatablob_p.h b/src/qml/qml/qqmldatablob_p.h index 383ebcb918..f336155235 100644 --- a/src/qml/qml/qqmldatablob_p.h +++ b/src/qml/qml/qqmldatablob_p.h @@ -86,6 +86,7 @@ public: QDateTime sourceTimeStamp() const; bool exists() const; bool isEmpty() const; + bool isCacheable() const { return !hasInlineSourceCode; } bool isValid() const { return hasInlineSourceCode || !fileInfo.filePath().isEmpty(); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index da0adb9807..4dbbb423bf 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -169,15 +169,15 @@ static void addQQmlMetaTypeInterfaces(QQmlTypePrivate *priv, const QByteArray &c static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) { - // This is a procedurally registered composite type. It's evil. It doesn't get any metatypes - // because we never want to find it in the compositeTypes. Otherwise we might mix it up with an - // actually compiled version of the same type. - auto *d = new QQmlTypePrivate(QQmlType::CompositeType); data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); d->version = type.version; - d->extraData.compositeTypeData = QQmlTypeLoader::normalize(type.url); + + const QUrl normalized = QQmlTypeLoader::normalize(type.url); + d->extraData.compositeTypeData = normalized; + addQQmlMetaTypeInterfaces( + d, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(normalized)); return d; } @@ -186,10 +186,6 @@ static QQmlTypePrivate *createQQmlType( const QQmlPrivate::RegisterCompositeSingletonType &type, const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) { - // This is a procedurally registered composite singleton. It's evil. It doesn't get any - // metatypes because we never want to find it in the compositeTypes. Otherwise we might mix it - // up with an actually compiled version of the same type. - auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); @@ -197,6 +193,8 @@ static QQmlTypePrivate *createQQmlType( d->version = type.version; d->extraData.singletonTypeData->singletonInstanceInfo = siinfo; + addQQmlMetaTypeInterfaces( + d, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(siinfo->url)); return d; } @@ -696,13 +694,10 @@ QQmlType QQmlMetaType::findCompositeType( urlExists = false; } - if (const QtPrivate::QMetaTypeInterface *iface = urlExists - ? found.value()->typeId.iface() - : nullptr) { + if (urlExists) { if (compilationUnit.isNull()) return QQmlType(*found); - - const auto composite = data->compositeTypes.constFind(iface); + const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface()); if (composite == data->compositeTypes.constEnd() || composite.value() == compilationUnit) return QQmlType(*found); } diff --git a/src/qml/qml/qqmlscriptblob.cpp b/src/qml/qml/qqmlscriptblob.cpp index d6002e7039..773e3d452e 100644 --- a/src/qml/qml/qqmlscriptblob.cpp +++ b/src/qml/qml/qqmlscriptblob.cpp @@ -35,14 +35,22 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) { Q_ASSERT(isTypeLoaderThread()); - if (readCacheFile()) { - auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); - QString error; - if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { + if (data.isCacheable()) { + if (auto unit = QQmlMetaType::obtainCompilationUnit(url())) { initializeFromCompilationUnit(std::move(unit)); return; - } else { - qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() << "from disk cache:" << error; + } + + if (readCacheFile()) { + auto unit = QQml::makeRefPointer<QV4::CompiledData::CompilationUnit>(); + QString error; + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) { + initializeFromCompilationUnit(std::move(unit)); + return; + } else { + qCDebug(DBG_DISK_CACHE()) << "Error loading" << urlString() + << "from disk cache:" << error; + } } } @@ -167,7 +175,7 @@ void QQmlScriptBlob::done() m_scripts.clear(); if (auto cu = m_scriptData->compilationUnit()) { - cu->qmlType = QQmlMetaType::findCompositeType(finalUrl(), cu, QQmlMetaType::JavaScript); + cu->qmlType = QQmlMetaType::findCompositeType(url(), cu, QQmlMetaType::JavaScript); QQmlMetaType::registerInternalCompositeType(cu); } } diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 8c8237e33d..f15b233d84 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -81,6 +81,12 @@ bool QQmlTypeData::tryLoadFromDiskCache() { Q_ASSERT(isTypeLoaderThread()); + if (!m_backupSourceCode.isCacheable()) + return false; + + if (auto unit = QQmlMetaType::obtainCompilationUnit(url())) + return loadFromDiskCache(unit); + if (!readCacheFile()) return false; @@ -453,7 +459,7 @@ void QQmlTypeData::done() ? m_document.data()->isSingleton() : (m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton); m_qmlType = QQmlMetaType::findCompositeType( - finalUrl(), m_compiledData, isSingleton + url(), m_compiledData, isSingleton ? QQmlMetaType::Singleton : QQmlMetaType::NonSingleton); m_typeClassName = QByteArray(m_qmlType.typeId().name()).chopped(1); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 39a3fed4e3..67bf391707 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -4936,6 +4936,13 @@ void tst_qqmllanguage::compositeSingletonRemote() // Reads values from a Singleton accessed through selectors. void tst_qqmllanguage::compositeSingletonSelectors() { + // Poison the cache + QQmlEngine e1; + QQmlComponent component1(&e1, testFileUrl("singletonTest1.qml")); + QVERIFY2(component1.isReady(), qPrintable(component1.errorString())); + QScopedPointer<QObject> o1(component1.create()); + QVERIFY(!o1.isNull()); + QQmlEngine e2; QQmlFileSelector qmlSelector(&e2); qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); diff --git a/tests/auto/qml/qqmltypeloader/data/A.qml b/tests/auto/qml/qqmltypeloader/data/A.qml new file mode 100644 index 0000000000..2f24ea6794 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/A.qml @@ -0,0 +1,5 @@ +import QtQml + +QtObject { + property int five: 5 +} diff --git a/tests/auto/qml/qqmltypeloader/data/B.qml b/tests/auto/qml/qqmltypeloader/data/B.qml new file mode 100644 index 0000000000..d4e05a1089 --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/B.qml @@ -0,0 +1,5 @@ +import QtQml + +A { + property int six: 6 +} diff --git a/tests/auto/qml/qqmltypeloader/data/C.qml b/tests/auto/qml/qqmltypeloader/data/C.qml new file mode 100644 index 0000000000..ef7db3766c --- /dev/null +++ b/tests/auto/qml/qqmltypeloader/data/C.qml @@ -0,0 +1,5 @@ +import QtQml + +A { + property int one: 1 +} diff --git a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp index c50b7d93b9..6d23eaf55f 100644 --- a/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp +++ b/tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp @@ -10,11 +10,12 @@ #if QT_CONFIG(process) #include <QtCore/qprocess.h> #endif +#include <QtQml/private/qqmlcomponent_p.h> #include <QtQml/private/qqmlengine_p.h> -#include <QtQml/private/qqmltypedata_p.h> -#include <QtQml/private/qqmltypeloader_p.h> #include <QtQml/private/qqmlirbuilder_p.h> #include <QtQml/private/qqmlirloader_p.h> +#include <QtQml/private/qqmltypedata_p.h> +#include <QtQml/private/qqmltypeloader_p.h> #include <QtQuickTestUtils/private/testhttpserver_p.h> #include <QtQuickTestUtils/private/qmlutils_p.h> #include <QQmlComponent> @@ -51,6 +52,7 @@ private slots: void signalHandlersAreCompatible(); void loadTypeOnShutdown(); void floodTypeLoaderEventQueue(); + void retainQmlTypeAcrossEngines(); private: void checkSingleton(const QString & dataDirectory); @@ -866,6 +868,62 @@ void tst_QQMLTypeLoader::floodTypeLoaderEventQueue() } } +void tst_QQMLTypeLoader::retainQmlTypeAcrossEngines() +{ + QQmlEngine engine1; + QQmlComponent component1(&engine1, testFileUrl("B.qml")); + QVERIFY2(component1.isReady(), qPrintable(component1.errorString())); + + QQmlEngine engine2; + QQmlComponent component2(&engine2, testFileUrl("B.qml")); + QVERIFY2(component2.isReady(), qPrintable(component2.errorString())); + + QQmlEngine engine3; + QQmlComponent component3(&engine3, testFileUrl("C.qml")); + QVERIFY2(component3.isReady(), qPrintable(component3.errorString())); + + QQmlComponentPrivate *p1 = QQmlComponentPrivate::get(&component1); + QVERIFY(p1); + const auto cu1 = p1->compilationUnit; + QVERIFY(cu1); + + QQmlComponentPrivate *p2 = QQmlComponentPrivate::get(&component2); + QVERIFY(p2); + const auto cu2 = p2->compilationUnit; + QVERIFY(cu2); + + QQmlComponentPrivate *p3 = QQmlComponentPrivate::get(&component3); + QVERIFY(p3); + const auto cu3 = p3->compilationUnit; + QVERIFY(cu3); + + // The _executable_ CUs are all different + QVERIFY(cu1 != cu2); + QVERIFY(cu1 != cu3); + QVERIFY(cu2 != cu3); + + const auto base1 = cu1->baseCompilationUnit(); + const auto base2 = cu2->baseCompilationUnit(); + const auto base3 = cu3->baseCompilationUnit(); + + QCOMPARE(base1, base2); + QVERIFY(base1 != base3); + QVERIFY(base2 != base3); + + const QQmlType qmltype1 = base1->qmlType; + const QMetaObject *mo1 = qmltype1.typeId().metaObject(); + QVERIFY(mo1); + + const QQmlType qmltype3 = base3->qmlType; + const QMetaObject *mo3 = qmltype3.typeId().metaObject(); + QVERIFY(mo3); + + QVERIFY(mo1 != mo3); + + // The base classes are all the same. + QCOMPARE(mo1->superClass(), mo3->superClass()); +} + QTEST_MAIN(tst_QQMLTypeLoader) #include "tst_qqmltypeloader.moc" |
