aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-08-25 12:54:43 +0200
committerUlf Hermann <ulf.hermann@qt.io>2021-10-28 17:49:52 +0200
commit9b2ba45c1886ee88e856f49acb6b4e2ddb983421 (patch)
tree58ce88e083c81fc94c661784672667902483f124
parent2fb875e8e83366eddc5d156be10f409fd19ae922 (diff)
Allow ImmediatePropertyNames in addition to DeferredPropertyNames
If given, all properties but the ones mentioned in ImmediatePropertyNames are deferred. Also add some warnings related to DeferredPropertyNames being ignored in some cases. Finally, scanObject() can return false now. Therefore adapt its caller to take that into account. Task-number: QTBUG-95117 Change-Id: I1e696228de7ad3b495bf7791fdb014768aff4c96 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Maximilian Goldstein <max.goldstein@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp49
-rw-r--r--src/qml/qml/qqmltypecompiler_p.h1
-rw-r--r--tests/auto/qml/qqmlecmascript/data/ObjectWithId.qml3
-rw-r--r--tests/auto/qml/qqmlecmascript/data/brokenImmediateDeferred.qml2
-rw-r--r--tests/auto/qml/qqmlecmascript/data/brokenImmediateId.qml5
-rw-r--r--tests/auto/qml/qqmlecmascript/data/immediateDerived.qml7
-rw-r--r--tests/auto/qml/qqmlecmascript/data/immediateProperties.qml11
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.cpp3
-rw-r--r--tests/auto/qml/qqmlecmascript/testtypes.h52
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp56
10 files changed, 180 insertions, 9 deletions
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index ce074b5e0e..415a4eb7e9 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -1214,9 +1214,12 @@ QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingSca
bool QQmlDeferredAndCustomParserBindingScanner::scanObject()
{
- for (int i = 0; i < qmlObjects->size(); ++i)
- if (qmlObjects->at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
- scanObject(i);
+ for (int i = 0; i < qmlObjects->size(); ++i) {
+ if ((qmlObjects->at(i)->flags & QV4::CompiledData::Object::IsInlineComponentRoot)
+ && !scanObject(i)) {
+ return false;
+ }
+ }
return scanObject(/*root object*/0);
}
@@ -1253,12 +1256,25 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
QQmlPropertyResolver propertyResolver(propertyCache);
QStringList deferredPropertyNames;
+ QStringList immediatePropertyNames;
{
const QMetaObject *mo = propertyCache->firstCppMetaObject();
- const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
- if (namesIndex != -1) {
- QMetaClassInfo classInfo = mo->classInfo(namesIndex);
- deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
+ const int deferredNamesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
+ const int immediateNamesIndex = mo->indexOfClassInfo("ImmediatePropertyNames");
+ if (deferredNamesIndex != -1) {
+ if (immediateNamesIndex != -1) {
+ COMPILE_EXCEPTION(obj, tr("You cannot define both DeferredPropertyNames and "
+ "ImmediatePropertyNames on the same type."));
+ }
+ const QMetaClassInfo classInfo = mo->classInfo(deferredNamesIndex);
+ deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
+ } else if (immediateNamesIndex != -1) {
+ const QMetaClassInfo classInfo = mo->classInfo(immediateNamesIndex);
+ immediatePropertyNames = QString::fromUtf8(classInfo.value()).split(u',');
+
+ // If the property contains an empty string, all properties shall be deferred.
+ if (immediatePropertyNames.isEmpty())
+ immediatePropertyNames.append(QString());
}
}
@@ -1304,11 +1320,26 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex)
_seenObjectWithId |= seenSubObjectWithId;
}
- if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty
- && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
+ if (!immediatePropertyNames.isEmpty() && !immediatePropertyNames.contains(name)) {
+ if (seenSubObjectWithId) {
+ COMPILE_EXCEPTION(binding, tr("You cannot assign an id to an object assigned "
+ "to a deferred property."));
+ }
binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
+ } else if (!deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
+ if (seenSubObjectWithId) {
+ qWarning("Binding on %s is not deferred as requested by the DeferredPropertyNames "
+ "class info because one or more of its sub-objects contain an id.",
+ qPrintable(name));
+ } else if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+ qWarning("Binding on %s is not deferred as requested by the DeferredPropertyNames "
+ "class info because it constitutes a group property.", qPrintable(name));
+ } else {
+ binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
+ obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
+ }
}
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h
index 2fe1f5242e..2388073480 100644
--- a/src/qml/qml/qqmltypecompiler_p.h
+++ b/src/qml/qml/qqmltypecompiler_p.h
@@ -310,6 +310,7 @@ protected:
class QQmlDeferredAndCustomParserBindingScanner : public QQmlCompilePass
{
+ Q_DECLARE_TR_FUNCTIONS(QQmlDeferredAndCustomParserBindingScanner)
public:
QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler);
diff --git a/tests/auto/qml/qqmlecmascript/data/ObjectWithId.qml b/tests/auto/qml/qqmlecmascript/data/ObjectWithId.qml
new file mode 100644
index 0000000000..581e56b8ee
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/ObjectWithId.qml
@@ -0,0 +1,3 @@
+import Qt.test
+
+MyQmlObject { id: invisibleId }
diff --git a/tests/auto/qml/qqmlecmascript/data/brokenImmediateDeferred.qml b/tests/auto/qml/qqmlecmascript/data/brokenImmediateDeferred.qml
new file mode 100644
index 0000000000..852b75533c
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/brokenImmediateDeferred.qml
@@ -0,0 +1,2 @@
+import Qt.test
+BrokenImmediateDeferred {}
diff --git a/tests/auto/qml/qqmlecmascript/data/brokenImmediateId.qml b/tests/auto/qml/qqmlecmascript/data/brokenImmediateId.qml
new file mode 100644
index 0000000000..9a1566d68d
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/brokenImmediateId.qml
@@ -0,0 +1,5 @@
+import Qt.test
+
+MyImmediateObject {
+ objectProperty: MyQmlObject { id: wrong }
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/immediateDerived.qml b/tests/auto/qml/qqmlecmascript/data/immediateDerived.qml
new file mode 100644
index 0000000000..e1dd36e5d9
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/immediateDerived.qml
@@ -0,0 +1,7 @@
+import Qt.test
+
+DerivedFromImmediate {
+ objectName: "derived"
+ value: 11
+ value2: 20
+}
diff --git a/tests/auto/qml/qqmlecmascript/data/immediateProperties.qml b/tests/auto/qml/qqmlecmascript/data/immediateProperties.qml
new file mode 100644
index 0000000000..a1bb60ac99
--- /dev/null
+++ b/tests/auto/qml/qqmlecmascript/data/immediateProperties.qml
@@ -0,0 +1,11 @@
+import Qt.test
+
+MyImmediateObject {
+ id: root
+ objectName: "immediate"
+ value: 10
+ objectProperty: MyQmlObject {
+ value: root.value
+ }
+ objectProperty2: ObjectWithId {}
+}
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp
index 021a2d11ab..ebc59b88e7 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.cpp
+++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp
@@ -468,6 +468,9 @@ void registerTypes()
qmlRegisterSingletonType<MyInheritedQmlObject>("Test", 1, 0, "MyInheritedQmlObjectSingleton", inheritedQmlObject_provider);
qmlRegisterSingletonType<TestTypeCppSingleton>("Test", 1, 0, "TestTypeCppSingleton", testTypeCppProvider);
qmlRegisterType<MyDeferredObject>("Qt.test", 1,0, "MyDeferredObject");
+ qmlRegisterType<MyImmediateObject>("Qt.test", 1,0, "MyImmediateObject");
+ qmlRegisterType<DerivedFromImmediate>("Qt.test", 1,0, "DerivedFromImmediate");
+ qmlRegisterType<BrokenImmediateDeferred>("Qt.test", 1,0, "BrokenImmediateDeferred");
qmlRegisterType<DeferredChild>("Qt.test", 1,0, "DeferredChild");
qmlRegisterType<DeferredChildOverwrite>("Qt.test", 1, 0, "DeferredChildOverwrite");
qmlRegisterType<DeferredByParentChild>("Qt.test", 1,0, "DeferredByParentChild");
diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h
index 99fc6c96ab..6b3b46f721 100644
--- a/tests/auto/qml/qqmlecmascript/testtypes.h
+++ b/tests/auto/qml/qqmlecmascript/testtypes.h
@@ -409,6 +409,58 @@ private:
QObject *m_object2;
};
+class MyImmediateObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
+ Q_PROPERTY(QObject *objectProperty READ objectProperty WRITE setObjectProperty)
+ Q_PROPERTY(QObject *objectProperty2 READ objectProperty2 WRITE setObjectProperty2)
+ Q_CLASSINFO("ImmediatePropertyNames", "objectName")
+
+public:
+ int value() const { return m_value; }
+ void setValue(int v) { m_value = v; emit valueChanged(); }
+
+ QObject *objectProperty() const { return m_object; }
+ void setObjectProperty(QObject *obj) { m_object = obj; }
+
+ QObject *objectProperty2() const { return m_object2; }
+ void setObjectProperty2(QObject *obj) { m_object2 = obj; }
+
+signals:
+ void valueChanged();
+
+private:
+ int m_value = 0;
+ QObject *m_object = nullptr;
+ QObject *m_object2 = nullptr;
+};
+
+class BrokenImmediateDeferred : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int value MEMBER m_value)
+ Q_CLASSINFO("ImmediatePropertyNames", "objectName");
+ Q_CLASSINFO("DeferredPropertyNames", "value");
+public:
+ int m_value = 5;
+};
+
+class DerivedFromImmediate : public MyImmediateObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int value2 READ value2 WRITE setValue2 NOTIFY value2Changed)
+public:
+ int value2() const { return m_value2; }
+ void setValue2(int v) { m_value2 = v; emit value2Changed(); }
+
+signals:
+ void value2Changed();
+
+private:
+ int m_value2 = 0;
+};
+
class NonDeferredBased : public QObject
{
Q_OBJECT
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index 47d3aee5da..ad47c232f5 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -102,6 +102,7 @@ private slots:
void contextPropertiesTriggerReeval();
void objectPropertiesTriggerReeval();
void dependenciesWithFunctions();
+ void immediateProperties();
void deferredProperties();
void deferredPropertiesParent();
void deferredPropertiesOverwrite();
@@ -1208,6 +1209,61 @@ void tst_qqmlecmascript::dependenciesWithFunctions()
QVERIFY(object->property("success").toBool());
}
+void tst_qqmlecmascript::immediateProperties()
+{
+ QQmlEngine engine;
+ {
+ QQmlComponent component(&engine, testFileUrl("immediateProperties.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(obj);
+ MyImmediateObject *object = qobject_cast<MyImmediateObject *>(obj.data());
+ QVERIFY(object != nullptr);
+ QCOMPARE(object->objectName(), QStringLiteral("immediate"));
+ QCOMPARE(object->value(), 0);
+ QVERIFY(!object->objectProperty());
+ QVERIFY(!object->objectProperty2());
+ qmlExecuteDeferred(object);
+ QCOMPARE(object->value(), 10);
+ QVERIFY(object->objectProperty() != nullptr);
+ QVERIFY(object->objectProperty2() != nullptr);
+ MyQmlObject *qmlObject = qobject_cast<MyQmlObject *>(object->objectProperty());
+ QVERIFY(qmlObject != nullptr);
+ QCOMPARE(qmlObject->value(), 10);
+ object->setValue(19);
+ QCOMPARE(qmlObject->value(), 19);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("immediateDerived.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ QScopedPointer<QObject> o(component.create());
+ DerivedFromImmediate *derived = qobject_cast<DerivedFromImmediate *>(o.data());
+ QVERIFY(derived != nullptr);
+ QCOMPARE(derived->value(), 0);
+ QCOMPARE(derived->value2(), 0);
+ qmlExecuteDeferred(derived);
+ QCOMPARE(derived->value(), 11);
+ QCOMPARE(derived->value2(), 20);
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("brokenImmediateDeferred.qml"));
+ QVERIFY(component.isError());
+ QVERIFY(component.errorString().contains(
+ QStringLiteral("You cannot define both DeferredPropertyNames and "
+ "ImmediatePropertyNames on the same type.")));
+ }
+
+ {
+ QQmlComponent component(&engine, testFileUrl("brokenImmediateId.qml"));
+ QVERIFY(component.isError());
+ QVERIFY(component.errorString().contains(
+ QStringLiteral("You cannot assign an id to an object assigned "
+ "to a deferred property.")));
+ }
+}
+
void tst_qqmlecmascript::deferredProperties()
{
QQmlEngine engine;