aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2025-03-03 19:12:01 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2025-03-07 14:44:36 +0000
commit58b564d9a4b9f78e4d87697ffe88f7524cf3e588 (patch)
tree3d2bd1a239d320b28bd2cfd7f604b48ebbb8e567
parent1c488a7ff7b0bfed05c00f3a34dfe2eec1f272c4 (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.cpp2
-rw-r--r--src/qml/qml/qqmldatablob_p.h1
-rw-r--r--src/qml/qml/qqmlmetatype.cpp23
-rw-r--r--src/qml/qml/qqmlscriptblob.cpp22
-rw-r--r--src/qml/qml/qqmltypedata.cpp8
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp7
-rw-r--r--tests/auto/qml/qqmltypeloader/data/A.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/B.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/data/C.qml5
-rw-r--r--tests/auto/qml/qqmltypeloader/tst_qqmltypeloader.cpp62
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"