From 4838687eaa63de6e4d6821f14cf865a13af9ebc1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 8 Mar 2018 16:04:26 +0100 Subject: Initialize all members of QQmlPropertyData This lead to quite a few valgrind warnings in test cases. Change-Id: Icef0fc5f93a68e4fe67e1ecd4755b456ad4778a9 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache_p.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 51a191a41f..b78a2ddd20 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -81,6 +81,7 @@ template class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData // to an invalid state on construction. +// ### We should be able to remove this split nowadays class QQmlPropertyRawData { public: @@ -273,20 +274,20 @@ public: private: Flags _flags; - qint16 _coreIndex; - quint16 _propType; + qint16 _coreIndex = 0; + quint16 _propType = 0; // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex; - qint16 _overrideIndex; + qint16 _notifyIndex = 0; + qint16 _overrideIndex = 0; - quint8 _revision; - quint8 _typeMinorVersion; - qint16 _metaObjectOffset; + quint8 _revision = 0; + quint8 _typeMinorVersion = 0; + qint16 _metaObjectOffset = 0; - QQmlPropertyCacheMethodArguments *_arguments; - StaticMetaCallFunction _staticMetaCallFunction; + QQmlPropertyCacheMethodArguments *_arguments = nullptr; + StaticMetaCallFunction _staticMetaCallFunction = nullptr; friend class QQmlPropertyData; friend class QQmlPropertyCache; -- cgit v1.2.3 From f514451cc2e3610e160b5dc8ccd1e390730ecc67 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2018 16:18:15 +0100 Subject: Fix unnecessary evaluation of dependent bindings Given two simple bindings in this order property int firstVar: secondVar property int secondVar: ... then the binding expression for "secondVar" ends up being evaluated twice at run-time. The first time happens when enabling the binding expression for "firstVar", which results in the engine detecting that there is a dependency onto another binding that has not been enabled yet. This is when QQmlData::flushPendingBinding(Impl) enables the expression for secondVar and does an initial evaluation. Afterwards the QQmlObjectCreator continues enabling the next binding in ::finalize(), which will end up evaluating secondVar a second time, unnecessarily. We can detect this case inside setEnabled and only call update() if we transition from disabled to enabled state. This should also cover the case of bindings created and assigned dynamically through QtQuick PropertyChanges / States, as those call setEnabled(false) before removing the binding (to replace it with something else) and setEnabled(true) when reverting the state (in QQmlPropertyPrivate::setBinding). Change-Id: I447432891eabff2c4393f5abfee1092992746fa0 Task-number: QTBUG-66945 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlbinding.cpp | 3 +- .../noDoubleEvaluationForFlushedBindings.2.qml | 9 ++++++ .../data/noDoubleEvaluationForFlushedBindings.qml | 9 ++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 33 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml create mode 100644 tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index ca3bff43a4..bc7fccb2c0 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -490,6 +490,7 @@ void QQmlBinding::refresh() void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { + const bool wasEnabled = enabledFlag(); setEnabledFlag(e); setNotifyOnValueChanged(e); @@ -499,7 +500,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) m_nextBinding.clearFlag2(); } - if (e) + if (e && !wasEnabled) update(flags); } diff --git a/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml new file mode 100644 index 0000000000..ff2f0f5a2c --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.2.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + property int secondVar: { + stats.increaseEvaluationCounter() + return 1 + } + property int firstVar: secondVar + 1 +} diff --git a/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml new file mode 100644 index 0000000000..0eb5e03642 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/noDoubleEvaluationForFlushedBindings.qml @@ -0,0 +1,9 @@ +import QtQml 2.0 + +QtObject { + property int firstVar: secondVar + 1 + property int secondVar: { + stats.increaseEvaluationCounter() + return 1 + } +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 5bb2b69565..403e8c5730 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -125,6 +125,8 @@ private slots: void dynamicObjectProperties(); void dynamicSignalsAndSlots(); void simpleBindings(); + void noDoubleEvaluationForFlushedBindings_data(); + void noDoubleEvaluationForFlushedBindings(); void autoComponentCreation(); void autoComponentCreationInGroupProperty(); void propertyValueSource(); @@ -1478,6 +1480,37 @@ void tst_qqmllanguage::simpleBindings() QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object)); } +class EvaluationCounter : public QObject +{ + Q_OBJECT +public: + int counter = 0; + Q_INVOKABLE void increaseEvaluationCounter() { ++counter; } +}; + +void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings_data() +{ + QTest::addColumn("fileName"); + QTest::newRow("order1") << QString("noDoubleEvaluationForFlushedBindings.qml"); + QTest::newRow("order2") << QString("noDoubleEvaluationForFlushedBindings.2.qml"); +} + +void tst_qqmllanguage::noDoubleEvaluationForFlushedBindings() +{ + QFETCH(QString, fileName); + QQmlEngine engine; + + EvaluationCounter stats; + engine.rootContext()->setContextProperty("stats", &stats); + + QQmlComponent component(&engine, testFileUrl(fileName)); + VERIFY_ERRORS(0); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + QCOMPARE(stats.counter, 1); +} + void tst_qqmllanguage::autoComponentCreation() { { -- cgit v1.2.3 From 2b740574449373fb8901d57667309778c32424dd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2018 16:02:50 +0100 Subject: Clean up memory leaks in tests A call to QQmlComponent::create() transfer ownership of the created object to the caller. Many tests forgot to delete the object or only deleted it manually if all tests passed. The simplest way to avoid leaks this way is to store the returned value in a QScopedPointer. Change-Id: I6173f440eddedd4f3eab5026f710602a263246c9 Reviewed-by: Lars Knoll --- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 468 ++++++++++------------- 1 file changed, 196 insertions(+), 272 deletions(-) diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 403e8c5730..b405181b14 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -374,9 +374,11 @@ void tst_qqmllanguage::insertedSemicolon() QQmlComponent component(&engine, testFileUrl(file)); + QScopedPointer object; + if(create) { - QObject *object = component.create(); - QVERIFY(!object); + object.reset(component.create()); + QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); @@ -608,9 +610,11 @@ void tst_qqmllanguage::errors() QQmlComponent component(&engine, testFileUrl(file)); + QScopedPointer object; + if (create) { - QObject *object = component.create(); - QVERIFY(!object); + object.reset(component.create()); + QVERIFY(object.isNull()); } VERIFY_ERRORS(errorFile.toLatin1().constData()); @@ -620,7 +624,7 @@ void tst_qqmllanguage::simpleObject() { QQmlComponent component(&engine, testFileUrl("simpleObject.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); } @@ -628,7 +632,7 @@ void tst_qqmllanguage::simpleContainer() { QQmlComponent component(&engine, testFileUrl("simpleContainer.qml")); VERIFY_ERRORS(0); - MyContainer *container= qobject_cast(component.create()); + QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(),2); } @@ -637,7 +641,7 @@ void tst_qqmllanguage::interfaceProperty() { QQmlComponent component(&engine, testFileUrl("interfaceProperty.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->interface()); QCOMPARE(object->interface()->id, 913); @@ -647,7 +651,7 @@ void tst_qqmllanguage::interfaceQList() { QQmlComponent component(&engine, testFileUrl("interfaceQList.qml")); VERIFY_ERRORS(0); - MyContainer *container= qobject_cast(component.create()); + QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getQListInterfaces()->count(), 2); for(int ii = 0; ii < 2; ++ii) @@ -658,7 +662,7 @@ void tst_qqmllanguage::assignObjectToSignal() { QQmlComponent component(&engine, testFileUrl("assignObjectToSignal.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); @@ -668,7 +672,7 @@ void tst_qqmllanguage::assignObjectToVariant() { QQmlComponent component(&engine, testFileUrl("assignObjectToVariant.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVariant v = object->property("a"); QVERIFY(v.userType() == qMetaTypeId()); @@ -678,7 +682,7 @@ void tst_qqmllanguage::assignLiteralSignalProperty() { QQmlComponent component(&engine, testFileUrl("assignLiteralSignalProperty.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->onLiteralSignal(), 10); } @@ -688,7 +692,7 @@ void tst_qqmllanguage::assignQmlComponent() { QQmlComponent component(&engine, testFileUrl("assignQmlComponent.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QObject *child = object->getChildren()->at(0); @@ -701,7 +705,7 @@ void tst_qqmllanguage::assignBasicTypes() { QQmlComponent component(&engine, testFileUrl("assignBasicTypes.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3); QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2); @@ -746,7 +750,7 @@ void tst_qqmllanguage::assignTypeExtremes() { QQmlComponent component(&engine, testFileUrl("assignTypeExtremes.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->uintProperty(), 0xEE6B2800); QCOMPARE(object->intProperty(), -0x77359400); @@ -757,7 +761,7 @@ void tst_qqmllanguage::assignCompositeToType() { QQmlComponent component(&engine, testFileUrl("assignCompositeToType.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); } @@ -766,7 +770,7 @@ void tst_qqmllanguage::assignLiteralToVariant() { QQmlComponent component(&engine, testFileUrl("assignLiteralToVariant.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(isJSNumberType(object->property("test1").userType())); @@ -794,8 +798,6 @@ void tst_qqmllanguage::assignLiteralToVariant() QCOMPARE(object->property("test10"), QVariant(bool(true))); QCOMPARE(object->property("test11"), QVariant(bool(false))); QVERIFY(object->property("test12") == QVariant(QVector4D(100, 100, 100, 100))); - - delete object; } // Test that literals are stored correctly in "var" properties @@ -805,7 +807,7 @@ void tst_qqmllanguage::assignLiteralToVar() { QQmlComponent component(&engine, testFileUrl("assignLiteralToVar.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(isJSNumberType(object->property("test1").userType())); @@ -845,15 +847,13 @@ void tst_qqmllanguage::assignLiteralToVar() QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100))); QCOMPARE(object->property("variantTest1Bound"), QVariant(9)); QCOMPARE(object->property("test1Bound"), QVariant(11)); - - delete object; } void tst_qqmllanguage::assignLiteralToJSValue() { QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); + QScopedPointer root(component.create()); QVERIFY(root != nullptr); { @@ -941,11 +941,11 @@ void tst_qqmllanguage::assignNullStrings() { QQmlComponent component(&engine, testFileUrl("assignNullStrings.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); - QMetaObject::invokeMethod(object, "assignNullStringsFromJs", Qt::DirectConnection); + QMetaObject::invokeMethod(object.data(), "assignNullStringsFromJs", Qt::DirectConnection); QVERIFY(object->stringProperty().isNull()); QVERIFY(object->byteArrayProperty().isNull()); } @@ -955,7 +955,7 @@ void tst_qqmllanguage::bindJSValueToVar() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); + QScopedPointer root(component.create()); QVERIFY(root != nullptr); QObject *object = root->findChild("varProperties"); @@ -1004,7 +1004,7 @@ void tst_qqmllanguage::bindJSValueToVariant() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); + QScopedPointer root(component.create()); QVERIFY(root != nullptr); QObject *object = root->findChild("variantProperties"); @@ -1053,7 +1053,7 @@ void tst_qqmllanguage::bindJSValueToType() QQmlComponent component(&engine, testFileUrl("assignLiteralToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); + QScopedPointer root(component.create()); QVERIFY(root != nullptr); { @@ -1088,7 +1088,7 @@ void tst_qqmllanguage::bindTypeToJSValue() QQmlComponent component(&engine, testFileUrl("bindTypeToJSValue.qml")); VERIFY_ERRORS(0); - QObject *root = component.create(); + QScopedPointer root(component.create()); QVERIFY(root != nullptr); { @@ -1227,7 +1227,7 @@ void tst_qqmllanguage::customParserTypes() { QQmlComponent component(&engine, testFileUrl("customParserTypes.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("count"), QVariant(2)); } @@ -1237,7 +1237,7 @@ void tst_qqmllanguage::rootAsQmlComponent() { QQmlComponent component(&engine, testFileUrl("rootAsQmlComponent.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->property("x"), QVariant(11)); QCOMPARE(object->getChildren()->count(), 2); @@ -1261,12 +1261,12 @@ void tst_qqmllanguage::inlineQmlComponents() { QQmlComponent component(&engine, testFileUrl("inlineQmlComponents.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 1); QQmlComponent *comp = qobject_cast(object->getChildren()->at(0)); QVERIFY(comp != nullptr); - MyQmlObject *compObject = qobject_cast(comp->create()); + QScopedPointer compObject(qobject_cast(comp->create())); QVERIFY(compObject != nullptr); QCOMPARE(compObject->value(), 11); } @@ -1277,7 +1277,7 @@ void tst_qqmllanguage::idProperty() { QQmlComponent component(&engine, testFileUrl("idProperty.qml")); VERIFY_ERRORS(0); - MyContainer *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->getChildren()->count(), 2); MyTypeObject *child = @@ -1308,14 +1308,14 @@ void tst_qqmllanguage::autoNotifyConnection() { QQmlComponent component(&engine, testFileUrl("autoNotifyConnection.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QMetaProperty prop = object->metaObject()->property(object->metaObject()->indexOfProperty("receivedNotify")); QVERIFY(prop.isValid()); - QCOMPARE(prop.read(object), QVariant::fromValue(false)); + QCOMPARE(prop.read(object.data()), QVariant::fromValue(false)); object->setPropertyWithNotify(1); - QCOMPARE(prop.read(object), QVariant::fromValue(true)); + QCOMPARE(prop.read(object.data()), QVariant::fromValue(true)); } // Tests that signals can be assigned to @@ -1323,7 +1323,7 @@ void tst_qqmllanguage::assignSignal() { QQmlComponent component(&engine, testFileUrl("assignSignal.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); @@ -1335,7 +1335,7 @@ void tst_qqmllanguage::assignSignalFunctionExpression() { QQmlComponent component(&engine, testFileUrl("assignSignalFunctionExpression.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QTest::ignoreMessage(QtWarningMsg, "MyQmlObject::basicSlot"); emit object->basicSignal(); @@ -1364,10 +1364,9 @@ void tst_qqmllanguage::overrideSignal() QQmlComponent component(&engine, testFileUrl(file)); if (errorFile.isEmpty()) { VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->property("success").toBool()); - delete object; } else { VERIFY_ERRORS(errorFile.toLatin1().constData()); } @@ -1378,8 +1377,7 @@ void tst_qqmllanguage::dynamicProperties() { QQmlComponent component(&engine, testFileUrl("dynamicProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != nullptr); + QScopedPointer object(component.create()); QCOMPARE(object->property("intProperty"), QVariant(10)); QCOMPARE(object->property("boolProperty"), QVariant(false)); QCOMPARE(object->property("doubleProperty"), QVariant(-10.1)); @@ -1396,15 +1394,13 @@ void tst_qqmllanguage::dynamicPropertiesNested() { QQmlComponent component(&engine, testFileUrl("dynamicPropertiesNested.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("super_a").toInt(), 11); // Overridden QCOMPARE(object->property("super_c").toInt(), 14); // Inherited QCOMPARE(object->property("a").toInt(), 13); // New QCOMPARE(object->property("b").toInt(), 12); // New - - delete object; } // Tests the creation and assignment to dynamic list properties @@ -1412,7 +1408,7 @@ void tst_qqmllanguage::listProperties() { QQmlComponent component(&engine, testFileUrl("listProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toInt(), 2); @@ -1432,7 +1428,7 @@ void tst_qqmllanguage::dynamicObjectProperties() { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("objectProperty"), qVariantFromValue((QObject*)nullptr)); @@ -1441,7 +1437,7 @@ void tst_qqmllanguage::dynamicObjectProperties() { QQmlComponent component(&engine, testFileUrl("dynamicObjectProperties.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->property("objectProperty") != qVariantFromValue((QObject*)nullptr)); @@ -1455,7 +1451,7 @@ void tst_qqmllanguage::dynamicSignalsAndSlots() QQmlComponent component(&engine, testFileUrl("dynamicSignalsAndSlots.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(object->metaObject()->indexOfMethod("signal1()") != -1); QVERIFY(object->metaObject()->indexOfMethod("signal2()") != -1); @@ -1463,7 +1459,7 @@ void tst_qqmllanguage::dynamicSignalsAndSlots() QVERIFY(object->metaObject()->indexOfMethod("slot2()") != -1); QCOMPARE(object->property("test").toInt(), 0); - QMetaObject::invokeMethod(object, "slot3", Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); + QMetaObject::invokeMethod(object.data(), "slot3", Qt::DirectConnection, Q_ARG(QVariant, QVariant(10))); QCOMPARE(object->property("test").toInt(), 10); } @@ -1471,13 +1467,13 @@ void tst_qqmllanguage::simpleBindings() { QQmlComponent component(&engine, testFileUrl("simpleBindings.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value1"), QVariant(10)); QCOMPARE(object->property("value2"), QVariant(10)); QCOMPARE(object->property("value3"), QVariant(21)); QCOMPARE(object->property("value4"), QVariant(10)); - QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object)); + QCOMPARE(object->property("objectProperty"), QVariant::fromValue(object.data())); } class EvaluationCounter : public QObject @@ -1516,20 +1512,20 @@ void tst_qqmllanguage::autoComponentCreation() { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); - MyTypeObject *child = qobject_cast(object->componentProperty()->create()); + QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } { QQmlComponent component(&engine, testFileUrl("autoComponentCreation.2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); - MyTypeObject *child = qobject_cast(object->componentProperty()->create()); + QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } @@ -1539,10 +1535,10 @@ void tst_qqmllanguage::autoComponentCreationInGroupProperty() { QQmlComponent component(&engine, testFileUrl("autoComponentCreationInGroupProperties.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(object->componentProperty() != nullptr); - MyTypeObject *child = qobject_cast(object->componentProperty()->create()); + QScopedPointer child(qobject_cast(object->componentProperty()->create())); QVERIFY(child != nullptr); QCOMPARE(child->realProperty(), qreal(9)); } @@ -1552,7 +1548,7 @@ void tst_qqmllanguage::propertyValueSource() { QQmlComponent component(&engine, testFileUrl("propertyValueSource.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QList valueSources; @@ -1566,14 +1562,14 @@ void tst_qqmllanguage::propertyValueSource() MyPropertyValueSource *valueSource = qobject_cast(valueSources.at(0)); QVERIFY(valueSource != nullptr); - QCOMPARE(valueSource->prop.object(), qobject_cast(object)); + QCOMPARE(valueSource->prop.object(), qobject_cast(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } { QQmlComponent component(&engine, testFileUrl("propertyValueSource.2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QList valueSources; @@ -1587,7 +1583,7 @@ void tst_qqmllanguage::propertyValueSource() MyPropertyValueSource *valueSource = qobject_cast(valueSources.at(0)); QVERIFY(valueSource != nullptr); - QCOMPARE(valueSource->prop.object(), qobject_cast(object)); + QCOMPARE(valueSource->prop.object(), qobject_cast(object.data())); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); } } @@ -1596,9 +1592,9 @@ void tst_qqmllanguage::attachedProperties() { QQmlComponent component(&engine, testFileUrl("attachedProperties.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - QObject *attached = qmlAttachedPropertiesObject(object); + QObject *attached = qmlAttachedPropertiesObject(object.data()); QVERIFY(attached != nullptr); QCOMPARE(attached->property("value"), QVariant(10)); QCOMPARE(attached->property("value2"), QVariant(13)); @@ -1609,7 +1605,7 @@ void tst_qqmllanguage::dynamicObjects() { QQmlComponent component(&engine, testFileUrl("dynamicObject.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); } @@ -1618,7 +1614,7 @@ void tst_qqmllanguage::customVariantTypes() { QQmlComponent component(&engine, testFileUrl("customVariantTypes.qml")); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->customType().a, 10); } @@ -1632,7 +1628,7 @@ void tst_qqmllanguage::valueTypes() QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); QTest::ignoreMessage(QtWarningMsg, qPrintable(message)); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); @@ -1663,20 +1659,17 @@ void tst_qqmllanguage::cppnamespace() { QQmlComponent component(&engine, testFileUrl("cppnamespace.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("intProperty").toInt(), (int)MyNamespace::MyOtherNSEnum::OtherKey2); - - delete object; } { QQmlComponent component(&engine, testFileUrl("cppnamespace.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - delete object; } } @@ -1686,7 +1679,7 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias @@ -1698,15 +1691,13 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("valueAlias", QVariant(19)); QCOMPARE(object->property("valueAlias").toInt(), 19); QCOMPARE(object->property("value").toInt(), 19); - - delete object; } // Complex object alias { QQmlComponent component(&engine, testFileUrl("alias.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias @@ -1717,21 +1708,19 @@ void tst_qqmllanguage::aliasProperties() // Write through alias MyQmlObject *v2 = new MyQmlObject(); - v2->setParent(object); + v2->setParent(object.data()); object->setProperty("aliasObject", qVariantFromValue(v2)); MyQmlObject *v3 = qvariant_cast(object->property("aliasObject")); QVERIFY(v3 != nullptr); QCOMPARE(v3, v2); - - delete object; } // Nested aliases { QQmlComponent component(&engine, testFileUrl("alias.3.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 1892); @@ -1744,27 +1733,23 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("value2", QVariant(8080)); QCOMPARE(object->property("value").toInt(), 8080); QCOMPARE(object->property("value2").toInt(), 8080); - - delete object; } // Enum aliases { QQmlComponent component(&engine, testFileUrl("alias.4.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("enumAlias").toInt(), 1); - - delete object; } // Id aliases { QQmlComponent component(&engine, testFileUrl("alias.5.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVariant v = object->property("otherAlias"); @@ -1778,15 +1763,13 @@ void tst_qqmllanguage::aliasProperties() QCOMPARE(v.userType(), qMetaTypeId()); o = qvariant_cast(v); QVERIFY(!o); - - delete object; } // Nested aliases - this used to cause a crash { QQmlComponent component(&engine, testFileUrl("alias.6.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("a").toInt(), 1923); @@ -1798,7 +1781,7 @@ void tst_qqmllanguage::aliasProperties() QQmlComponent component(&engine, testFileUrl("alias.7.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QObject *object1 = qvariant_cast(object->property("object")); @@ -1811,10 +1794,10 @@ void tst_qqmllanguage::aliasProperties() delete object1; - QObject *alias2 = object; // "Random" start value + QObject *alias2 = object.data(); // "Random" start value int status = -1; void *a[] = { &alias2, nullptr, &status }; - QMetaObject::metacall(object, QMetaObject::ReadProperty, + QMetaObject::metacall(object.data(), QMetaObject::ReadProperty, object->metaObject()->indexOfProperty("aliasedObject"), a); QVERIFY(!alias2); } @@ -1823,24 +1806,20 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.8.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); - - delete object; } // Complex composite type { QQmlComponent component(&engine, testFileUrl("alias.9.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("value").toInt(), 10); - - delete object; } // Valuetype alias @@ -1848,7 +1827,7 @@ void tst_qqmllanguage::aliasProperties() { QQmlComponent component(&engine, testFileUrl("alias.10.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias @@ -1860,15 +1839,13 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("valueAlias", QVariant(QRect(3, 3, 4, 9))); QCOMPARE(object->property("valueAlias").toRect(), QRect(3, 3, 4, 9)); QCOMPARE(object->property("rectProperty").toRect(), QRect(3, 3, 4, 9)); - - delete object; } // Valuetype sub-alias { QQmlComponent component(&engine, testFileUrl("alias.11.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); // Read through alias @@ -1880,8 +1857,6 @@ void tst_qqmllanguage::aliasProperties() object->setProperty("aliasProperty", QVariant(4)); QCOMPARE(object->property("aliasProperty").toInt(), 4); QCOMPARE(object->property("rectProperty").toRect(), QRect(4, 8, 102, 111)); - - delete object; } // Nested aliases with a qml file @@ -1946,10 +1921,9 @@ void tst_qqmllanguage::aliasPropertiesAndSignals() { QQmlComponent component(&engine, testFileUrl("aliasPropertiesAndSignals.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("test").toBool(), true); - delete o; } // Test that the root element in a composite type can be a Component @@ -1957,7 +1931,7 @@ void tst_qqmllanguage::componentCompositeType() { QQmlComponent component(&engine, testFileUrl("componentCompositeType.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); } @@ -1991,11 +1965,9 @@ void tst_qqmllanguage::i18n() QFETCH(QString, stringProperty); QQmlComponent component(&engine, testFileUrl(file)); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->stringProperty(), stringProperty); - - delete object; } // Check that the Component::onCompleted attached property works @@ -2006,7 +1978,7 @@ void tst_qqmllanguage::onCompleted() QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 6 10"); QTest::ignoreMessage(QtDebugMsg, "Completed 10 11"); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); } @@ -2015,12 +1987,11 @@ void tst_qqmllanguage::onDestruction() { QQmlComponent component(&engine, testFileUrl("onDestruction.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 6 10"); QTest::ignoreMessage(QtDebugMsg, "Destruction 10 11"); - delete object; } // Check that assignments to QQmlScriptString properties work @@ -2030,7 +2001,7 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QVERIFY(!object->scriptProperty().isEmpty()); QCOMPARE(object->scriptProperty().stringLiteral(), QString()); @@ -2041,21 +2012,21 @@ void tst_qqmllanguage::scriptString() const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(object->scriptProperty()); QVERIFY(scriptPrivate != nullptr); QCOMPARE(scriptPrivate->script, QString("foo + bar")); - QCOMPARE(scriptPrivate->scope, qobject_cast(object)); - QCOMPARE(scriptPrivate->context, qmlContext(object)); + QCOMPARE(scriptPrivate->scope, qobject_cast(object.data())); + QCOMPARE(scriptPrivate->context, qmlContext(object.data())); QVERIFY(object->grouped() != nullptr); const QQmlScriptStringPrivate *groupedPrivate = QQmlScriptStringPrivate::get(object->grouped()->script()); QCOMPARE(groupedPrivate->script, QString("console.log(1921)")); - QCOMPARE(groupedPrivate->scope, qobject_cast(object)); - QCOMPARE(groupedPrivate->context, qmlContext(object)); + QCOMPARE(groupedPrivate->scope, qobject_cast(object.data())); + QCOMPARE(groupedPrivate->context, qmlContext(object.data())); } { QQmlComponent component(&engine, testFileUrl("scriptString2.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().stringLiteral(), QString("hello\\n\\\"world\\\"")); } @@ -2064,7 +2035,7 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString3.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().numberLiteral(&ok), qreal(12.345)); @@ -2076,7 +2047,7 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString4.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); bool ok; QCOMPARE(object->scriptProperty().booleanLiteral(&ok), true); @@ -2087,7 +2058,7 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString5.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isNullLiteral(), true); } @@ -2096,7 +2067,7 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString6.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->scriptProperty().isUndefinedLiteral(), true); } @@ -2104,12 +2075,12 @@ void tst_qqmllanguage::scriptString() QQmlComponent component(&engine, testFileUrl("scriptString7.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); { - QQmlExpression expr(ss, /*context*/nullptr, object); + QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } @@ -2130,9 +2101,9 @@ void tst_qqmllanguage::scriptStringJs() QQmlComponent component(&engine, testFileUrl("scriptStringJs.qml")); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); - QQmlContext *context = QQmlEngine::contextForObject(object); + QQmlContext *context = QQmlEngine::contextForObject(object.data()); QVERIFY(context != nullptr); bool ok; @@ -2144,7 +2115,7 @@ void tst_qqmllanguage::scriptStringJs() QVERIFY(object->scriptProperty().numberLiteral(&ok) == 0.0 && !ok); QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); - QJSValue inst = engine.newQObject(object); + QJSValue inst = engine.newQObject(object.data()); QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); func.callWithInstance(inst, QJSValueList() << "test a \"string "); @@ -2234,7 +2205,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() QQmlComponent component(&engine, url); VERIFY_ERRORS(0); - MyTypeObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QQmlScriptString ss = object->scriptProperty(); QVERIFY(!ss.isEmpty()); @@ -2246,11 +2217,11 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() const QQmlScriptStringPrivate *scriptPrivate = QQmlScriptStringPrivate::get(ss); QVERIFY(scriptPrivate != nullptr); QVERIFY(scriptPrivate->script.isEmpty()); - QCOMPARE(scriptPrivate->scope, qobject_cast(object)); - QCOMPARE(scriptPrivate->context, qmlContext(object)); + QCOMPARE(scriptPrivate->scope, qobject_cast(object.data())); + QCOMPARE(scriptPrivate->context, qmlContext(object.data())); { - QQmlExpression expr(ss, /*context*/nullptr, object); + QQmlExpression expr(ss, /*context*/nullptr, object.data()); QCOMPARE(expr.evaluate().toInt(), int(100)); } } @@ -2261,22 +2232,22 @@ void tst_qqmllanguage::scriptStringComparison() { QQmlComponent component1(&engine, testFileUrl("scriptString.qml")); QVERIFY(!component1.isError() && component1.errors().isEmpty()); - MyTypeObject *object1 = qobject_cast(component1.create()); + QScopedPointer object1(qobject_cast(component1.create())); QVERIFY(object1 != nullptr); QQmlComponent component2(&engine, testFileUrl("scriptString2.qml")); QVERIFY(!component2.isError() && component2.errors().isEmpty()); - MyTypeObject *object2 = qobject_cast(component2.create()); + QScopedPointer object2(qobject_cast(component2.create())); QVERIFY(object2 != nullptr); QQmlComponent component3(&engine, testFileUrl("scriptString3.qml")); QVERIFY(!component3.isError() && component3.errors().isEmpty()); - MyTypeObject *object3 = qobject_cast(component3.create()); + QScopedPointer object3(qobject_cast(component3.create())); QVERIFY(object3 != nullptr); //QJSValue inst1 = engine.newQObject(object1); - QJSValue inst2 = engine.newQObject(object2); - QJSValue inst3 = engine.newQObject(object3); + QJSValue inst2 = engine.newQObject(object2.data()); + QJSValue inst3 = engine.newQObject(object3.data()); QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); const QString s = "hello\\n\\\"world\\\""; @@ -2331,7 +2302,7 @@ void tst_qqmllanguage::scriptStringComparison() // While this are two instances of the same object they are still considered different // because the (none literal) script string may access variables which have different // values in both instances and hence evaluated to different results. - MyTypeObject *object1_2 = qobject_cast(component1.create()); + QScopedPointer object1_2(qobject_cast(component1.create())); QVERIFY(object1_2 != nullptr); QVERIFY(object1->scriptProperty() != object1_2->scriptProperty()); } @@ -2343,7 +2314,7 @@ void tst_qqmllanguage::defaultPropertyListOrder() QQmlComponent component(&engine, testFileUrl("defaultPropertyListOrder.qml")); VERIFY_ERRORS(0); - MyContainer *container = qobject_cast(component.create()); + QScopedPointer container(qobject_cast(component.create())); QVERIFY(container != nullptr); QCOMPARE(container->getChildren()->count(), 6); @@ -2364,15 +2335,13 @@ void tst_qqmllanguage::declaredPropertyValues() void tst_qqmllanguage::dontDoubleCallClassBegin() { QQmlComponent component(&engine, testFileUrl("dontDoubleCallClassBegin.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o); MyParserStatus *o2 = qobject_cast(qvariant_cast(o->property("object"))); QVERIFY(o2); QCOMPARE(o2->classBeginCount(), 1); QCOMPARE(o2->componentCompleteCount(), 1); - - delete o; } void tst_qqmllanguage::reservedWords_data() @@ -2472,10 +2441,9 @@ void tst_qqmllanguage::testType(const QString& qml, const QString& type, const Q QCOMPARE(actualerror.left(partialMatch ? expectederror.length(): -1),expectederror); } else { VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(QString(object->metaObject()->className()), type); - delete object; } engine.setImportPathList(defaultImportPathList); @@ -2486,10 +2454,9 @@ void tst_qqmllanguage::inlineAssignmentsOverrideBindings() { QQmlComponent component(&engine, testFileUrl("inlineAssignmentsOverrideBindings.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 11); - delete o; } // QTBUG-19354 @@ -2920,10 +2887,9 @@ void tst_qqmllanguage::importsPath() QTRY_VERIFY(component.isReady()); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toString(), value); - delete object; engine.setImportPathList(defaultImportPathList); } @@ -3131,10 +3097,9 @@ void tst_qqmllanguage::importJs() } if (performTest) { - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("test").toBool(),true); - delete object; } engine.setImportPathList(defaultImportPathList); @@ -3183,21 +3148,21 @@ void tst_qqmllanguage::qmlAttachedPropertiesObjectMethod() { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.1.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - QCOMPARE(qmlAttachedPropertiesObject(object, false), (QObject *)nullptr); - QVERIFY(qmlAttachedPropertiesObject(object, true) != nullptr); + QCOMPARE(qmlAttachedPropertiesObject(object.data(), false), (QObject *)nullptr); + QVERIFY(qmlAttachedPropertiesObject(object.data(), true) != nullptr); } { QQmlComponent component(&engine, testFileUrl("qmlAttachedPropertiesObjectMethod.2.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - QVERIFY(qmlAttachedPropertiesObject(object, false) != nullptr); - QVERIFY(qmlAttachedPropertiesObject(object, true) != nullptr); + QVERIFY(qmlAttachedPropertiesObject(object.data(), false) != nullptr); + QVERIFY(qmlAttachedPropertiesObject(object.data(), true) != nullptr); } } @@ -3218,12 +3183,10 @@ void tst_qqmllanguage::customOnProperty() QQmlComponent component(&engine, testFileUrl("customOnProperty.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("on").toInt(), 10); - - delete object; } // QTBUG-12601 @@ -3232,12 +3195,10 @@ void tst_qqmllanguage::variantNotify() QQmlComponent component(&engine, testFileUrl("variantNotify.qml")); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("notifyCount").toInt(), 1); - - delete object; } void tst_qqmllanguage::revisions() @@ -3246,38 +3207,32 @@ void tst_qqmllanguage::revisions() QQmlComponent component(&engine, testFileUrl("revisions11.qml")); VERIFY_ERRORS(0); - MyRevisionedClass *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop2(), 10.0); - - delete object; } { QQmlEngine myEngine; QQmlComponent component(&myEngine, testFileUrl("revisionssub11.qml")); VERIFY_ERRORS(0); - MyRevisionedSubclass *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); QCOMPARE(object->prop3(), 10.0); QCOMPARE(object->prop4(), 10.0); - - delete object; } { QQmlComponent component(&engine, testFileUrl("versionedbase.qml")); VERIFY_ERRORS(0); - MySubclass *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->prop1(), 10.0); QCOMPARE(object->prop2(), 10.0); - - delete object; } } @@ -3323,8 +3278,8 @@ void tst_qqmllanguage::subclassedUncreateableRevision() QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); - QCOMPARE(obj, static_cast(nullptr)); + QScopedPointer obj(c.create()); + QCOMPARE(obj.data(), static_cast(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyUncreateableBaseClass")); } @@ -3335,17 +3290,16 @@ void tst_qqmllanguage::subclassedUncreateableRevision() if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); + QScopedPointer obj(c.create()); if (!shouldWork) { - QCOMPARE(obj, static_cast(nullptr)); + QCOMPARE(obj.data(), static_cast(nullptr)); return; } QVERIFY(obj); - MyUncreateableBaseClass *base = qobject_cast(obj); + MyUncreateableBaseClass *base = qobject_cast(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); - delete obj; } void tst_qqmllanguage::subclassedExtendedUncreateableRevision_data() @@ -3379,8 +3333,8 @@ void tst_qqmllanguage::subclassedExtendedUncreateableRevision() QQmlComponent c(&engine); QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); - QCOMPARE(obj, static_cast(nullptr)); + QScopedPointer obj(c.create()); + QCOMPARE(obj.data(), static_cast(nullptr)); QCOMPARE(c.errors().count(), 1); QCOMPARE(c.errors().first().description(), QString("Cannot create MyExtendedUncreateableBaseClass")); } @@ -3391,17 +3345,16 @@ void tst_qqmllanguage::subclassedExtendedUncreateableRevision() if (!shouldWork) QTest::ignoreMessage(QtWarningMsg, "QQmlComponent: Component is not ready"); c.setData(qml.toUtf8(), QUrl::fromLocalFile(QDir::currentPath())); - QObject *obj = c.create(); + QScopedPointer obj(c.create()); if (!shouldWork) { - QCOMPARE(obj, static_cast(nullptr)); + QCOMPARE(obj.data(), static_cast(nullptr)); return; } QVERIFY(obj); - MyExtendedUncreateableBaseClass *base = qobject_cast(obj); + MyExtendedUncreateableBaseClass *base = qobject_cast(obj.data()); QVERIFY(base); QCOMPARE(base->property(prop.toLatin1()).toBool(), true); - delete obj; } void tst_qqmllanguage::uncreatableTypesAsProperties() @@ -3462,12 +3415,10 @@ void tst_qqmllanguage::aliasPropertyChangeSignals() QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); - - delete o; } // QTCREATORBUG-2769 @@ -3475,12 +3426,10 @@ void tst_qqmllanguage::aliasPropertyChangeSignals() QQmlComponent component(&engine, testFileUrl("aliasPropertyChangeSignals.2.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toBool(), true); - - delete o; } } @@ -3491,24 +3440,20 @@ void tst_qqmllanguage::propertyInit() QQmlComponent component(&engine, testFileUrl("propertyInit.1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 1); - - delete o; } { QQmlComponent component(&engine, testFileUrl("propertyInit.2.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test").toInt(), 123); - - delete o; } } @@ -3518,17 +3463,16 @@ void tst_qqmllanguage::registrationOrder() { QQmlComponent component(&engine, testFileUrl("registrationOrder.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->metaObject(), &MyVersion2Class::staticMetaObject); - delete o; } void tst_qqmllanguage::readonly() { QQmlComponent component(&engine, testFileUrl("readonly.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("test1").toInt(), 10); @@ -3554,8 +3498,6 @@ void tst_qqmllanguage::readonly() QCOMPARE(o->property("test1").toInt(), 10); QCOMPARE(o->property("test2").toInt(), 22); QCOMPARE(o->property("test3").toInt(), 2); - - delete o; } void tst_qqmllanguage::readonlyObjectProperties() @@ -3580,7 +3522,7 @@ void tst_qqmllanguage::receivers() { QQmlComponent component(&engine, testFileUrl("receivers.qml")); - MyReceiversTestObject *o = qobject_cast(component.create()); + QScopedPointer o(qobject_cast(component.create())); QVERIFY(o != nullptr); QCOMPARE(o->mySignalCount(), 1); QCOMPARE(o->propChangedCount(), 2); @@ -3589,8 +3531,6 @@ void tst_qqmllanguage::receivers() QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::mySignal))); QVERIFY(o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::propChanged))); QVERIFY(!o->isSignalConnected(QMetaMethod::fromSignal(&MyReceiversTestObject::myUnconnectedSignal))); - - delete o; } void tst_qqmllanguage::registeredCompositeType() @@ -3598,10 +3538,8 @@ void tst_qqmllanguage::registeredCompositeType() QQmlComponent component(&engine, testFileUrl("registeredCompositeType.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - - delete o; } // QTBUG-43582 @@ -3610,14 +3548,12 @@ void tst_qqmllanguage::registeredCompositeTypeWithEnum() QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithEnum.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("enumValue0").toInt(), static_cast(MyCompositeBaseType::EnumValue0)); QCOMPARE(o->property("enumValue42").toInt(), static_cast(MyCompositeBaseType::EnumValue42)); QCOMPARE(o->property("enumValue15").toInt(), static_cast(MyCompositeBaseType::ScopedCompositeEnum::EnumValue15)); - - delete o; } // QTBUG-43581 @@ -3626,12 +3562,10 @@ void tst_qqmllanguage::registeredCompositeTypeWithAttachedProperty() QQmlComponent component(&engine, testFileUrl("registeredCompositeTypeWithAttachedProperty.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QCOMPARE(o->property("attachedProperty").toString(), QStringLiteral("test")); - - delete o; } // QTBUG-18268 @@ -3644,15 +3578,14 @@ void tst_qqmllanguage::remoteLoadCrash() while (component.isLoading()) QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); - QObject *o = component.create(); - delete o; + QScopedPointer o(component.create()); } void tst_qqmllanguage::signalWithDefaultArg() { QQmlComponent component(&engine, testFileUrl("signalWithDefaultArg.qml")); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->property("signalCount").toInt(), 0); @@ -3667,15 +3600,13 @@ void tst_qqmllanguage::signalWithDefaultArg() QCOMPARE(object->property("signalArg").toInt(), 15); - QMetaObject::invokeMethod(object, "emitNoArgSignal"); + QMetaObject::invokeMethod(object.data(), "emitNoArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 3); QCOMPARE(object->property("signalArg").toInt(), 5); - QMetaObject::invokeMethod(object, "emitArgSignal"); + QMetaObject::invokeMethod(object.data(), "emitArgSignal"); QCOMPARE(object->property("signalCount").toInt(), 4); QCOMPARE(object->property("signalArg").toInt(), 22); - - delete object; } void tst_qqmllanguage::signalParameterTypes() @@ -3683,19 +3614,17 @@ void tst_qqmllanguage::signalParameterTypes() // bound signal handlers { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.1.qml")); - QObject *obj = component.create(); + QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); - delete obj; } // dynamic signal connections { QQmlComponent component(&engine, testFileUrl("signalParameterTypes.2.qml")); - QObject *obj = component.create(); + QScopedPointer obj(component.create()); QVERIFY(obj != nullptr); QVERIFY(obj->property("success").toBool()); - delete obj; } } @@ -3708,7 +3637,7 @@ void tst_qqmllanguage::globalEnums() QQmlComponent component(&engine, testFileUrl("globalEnums.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); MyEnum1Class *enum1Class = o->findChild(QString::fromLatin1("enum1Class")); @@ -3734,7 +3663,7 @@ void tst_qqmllanguage::globalEnums() QSignalSpy signalA(enum2Class, SIGNAL(valueAChanged(MyEnum1Class::EnumA))); QSignalSpy signalB(enum2Class, SIGNAL(valueBChanged(MyEnum2Class::EnumB))); - QMetaObject::invokeMethod(o, "setEnumValues"); + QMetaObject::invokeMethod(o.data(), "setEnumValues"); QVERIFY(enum1Class->getValue() == MyEnum1Class::A_13); QVERIFY(enum2Class->getValueA() == MyEnum1Class::A_11); @@ -3753,8 +3682,6 @@ void tst_qqmllanguage::globalEnums() QVERIFY(enum2Class->property("dValue") == 2); QVERIFY(enum2Class->property("eValue") == 14); QVERIFY(enum2Class->property("e2Value") == 76); - - delete o; } void tst_qqmllanguage::lowercaseEnumRuntime_data() @@ -3800,7 +3727,7 @@ void tst_qqmllanguage::scopedEnum() { QQmlComponent component(&engine, testFileUrl("scopedEnum.qml")); - MyTypeObject *o = qobject_cast(component.create()); + QScopedPointer o(qobject_cast(component.create())); QVERIFY(o != nullptr); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal1); @@ -3808,7 +3735,7 @@ void tst_qqmllanguage::scopedEnum() QCOMPARE(o->property("listValue").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal3); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal1); - QMetaObject::invokeMethod(o, "assignNewValue"); + QMetaObject::invokeMethod(o.data(), "assignNewValue"); QCOMPARE(o->scopedEnum(), MyTypeObject::MyScopedEnum::ScopedVal2); QCOMPARE(o->property("noScope").toInt(), (int)MyTypeObject::MyScopedEnum::ScopedVal2); } @@ -3817,7 +3744,7 @@ void tst_qqmllanguage::qmlEnums() { { QQmlComponent component(&engine, testFileUrl("TypeWithEnum.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("enumValue").toInt(), 1); QCOMPARE(o->property("enumValue2").toInt(), 2); @@ -3832,7 +3759,7 @@ void tst_qqmllanguage::qmlEnums() { QQmlComponent component(&engine, testFileUrl("usingTypeWithEnum.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o); QCOMPARE(o->property("enumValue").toInt(), 1); QCOMPARE(o->property("enumValue2").toInt(), 0); @@ -3877,10 +3804,9 @@ void tst_qqmllanguage::literals() QQmlComponent component(&engine, testFile("literals.qml")); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property(property.toLatin1()), value); - delete object; } void tst_qqmllanguage::objectDeletionNotify_data() @@ -3899,19 +3825,17 @@ void tst_qqmllanguage::objectDeletionNotify() QQmlComponent component(&engine, testFile(file)); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("success").toBool(), true); - QMetaObject::invokeMethod(object, "destroyObject"); + QMetaObject::invokeMethod(object.data(), "destroyObject"); // Process the deletion event QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); QCoreApplication::processEvents(); QCOMPARE(object->property("success").toBool(), true); - - delete object; } void tst_qqmllanguage::scopedProperties() @@ -3945,7 +3869,7 @@ void tst_qqmllanguage::implicitImportsLast() QQmlComponent component(&engine, testFile("localOrderTest.qml")); VERIFY_ERRORS(0); - QObject *object = qobject_cast(component.create()); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QVERIFY(QString(object->metaObject()->className()).startsWith(QLatin1String("QQuickMouseArea"))); QObject* object2 = object->property("item").value(); @@ -3965,10 +3889,10 @@ void tst_qqmllanguage::getSingletonInstance(QQmlEngine& engine, const char* file QQmlComponent component(&engine, testFile(fileName)); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - getSingletonInstance(object, propertyName, result); + getSingletonInstance(object.data(), propertyName, result); } void tst_qqmllanguage::getSingletonInstance(QObject* o, const char* propertyName, QObject** result /* out */) @@ -4005,10 +3929,10 @@ void tst_qqmllanguage::compositeSingletonProperties() { QQmlComponent component(&engine, testFile("singletonTest1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); } // Checks that the addresses of the composite singletons used in the same @@ -4059,15 +3983,15 @@ void tst_qqmllanguage::compositeSingletonQualifiedNamespace() { QQmlComponent component(&engine, testFile("singletonTest5.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file (without namespace!) QObject *s1 = nullptr; - getSingletonInstance(o, "singletonInstance", &s1); + getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; @@ -4084,16 +4008,16 @@ void tst_qqmllanguage::compositeSingletonModule() QQmlComponent component(&engine, testFile("singletonTest6.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 125, "value2", -55); - verifyCompositeSingletonPropertyValues(o, "value3", 125, "value4", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 125, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 125, "value4", -55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; - getSingletonInstance(o, "singletonInstance", &s1); + getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; @@ -4110,16 +4034,16 @@ void tst_qqmllanguage::compositeSingletonModuleVersioned() QQmlComponent component(&engine, testFile("singletonTest7.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55); - verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; - getSingletonInstance(o, "singletonInstance", &s1); + getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; @@ -4136,16 +4060,16 @@ void tst_qqmllanguage::compositeSingletonModuleQualified() QQmlComponent component(&engine, testFile("singletonTest8.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 225, "value2", 55); - verifyCompositeSingletonPropertyValues(o, "value3", 225, "value4", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 225, "value2", 55); + verifyCompositeSingletonPropertyValues(o.data(), "value3", 225, "value4", 55); // lets verify that the singleton instance we are using is the same // when loaded through another file QObject *s1 = nullptr; - getSingletonInstance(o, "singletonInstance", &s1); + getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); QObject* s2 = nullptr; @@ -4175,10 +4099,10 @@ void tst_qqmllanguage::compositeSingletonDynamicSignal() { QQmlComponent component(&engine, testFile("singletonTest11.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", -55); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", -55); } // Use qmlRegisterType to register a qml composite type with pragma Singleton defined in it. @@ -4224,10 +4148,10 @@ void tst_qqmllanguage::compositeSingletonRemote() QCoreApplication::processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents, 50); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 525, "value2", 355); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 525, "value2", 355); } // Load a composite singleton type and a javascript file that has .pragma library @@ -4237,14 +4161,14 @@ void tst_qqmllanguage::compositeSingletonJavaScriptPragma() { QQmlComponent component(&engine, testFile("singletonTest16.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); // The value1 that is read from the SingletonType was changed from 125 to 99 // in compositeSingletonDynamicSignal() above. As the type is a singleton and // the engine has not been destroyed, we just retrieve the old instance and // the value is still 99. - verifyCompositeSingletonPropertyValues(o, "value1", 99, "value2", 333); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 99, "value2", 333); } // Reads values from a Singleton accessed through selectors. @@ -4255,10 +4179,10 @@ void tst_qqmllanguage::compositeSingletonSelectors() qmlSelector.setExtraSelectors(QStringList() << "basicSelector"); QQmlComponent component(&e2, testFile("singletonTest1.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 625, "value2", 455); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 625, "value2", 455); } // Reads values from a Singleton that was registered through the C++ API: @@ -4267,10 +4191,10 @@ void tst_qqmllanguage::compositeSingletonRegistered() { QQmlComponent component(&engine, testFile("singletonTest17.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - verifyCompositeSingletonPropertyValues(o, "value1", 925, "value2", 755); + verifyCompositeSingletonPropertyValues(o.data(), "value1", 925, "value2", 755); } void tst_qqmllanguage::compositeSingletonCircular() @@ -4280,7 +4204,7 @@ void tst_qqmllanguage::compositeSingletonCircular() QQmlTestMessageHandler messageHandler; - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); // ensure we aren't hitting the recursion warning @@ -4635,7 +4559,7 @@ void tst_qqmllanguage::noChildEvents() QQmlComponent component(&engine); component.setData("import QtQml 2.0; import Test 1.0; MyQmlObject { property QtObject child: QtObject {} }", QUrl()); VERIFY_ERRORS(0); - MyQmlObject *object = qobject_cast(component.create()); + QScopedPointer object(qobject_cast(component.create())); QVERIFY(object != nullptr); QCOMPARE(object->childAddedEventCount(), 0); } @@ -4655,10 +4579,10 @@ void tst_qqmllanguage::deleteSingletons() QQmlEngine tmpEngine; QQmlComponent component(&tmpEngine, testFile("singletonTest5.qml")); VERIFY_ERRORS(0); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); QObject *s1 = nullptr; - getSingletonInstance(o, "singletonInstance", &s1); + getSingletonInstance(o.data(), "singletonInstance", &s1); QVERIFY(s1 != nullptr); singleton = s1; QVERIFY(singleton.data() != nullptr); @@ -4682,7 +4606,7 @@ void tst_qqmllanguage::arrayBuffer() QFETCH(QString, file); QQmlComponent component(&engine, testFile(file)); VERIFY_ERRORS(0); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); QCOMPARE(object->property("ok").toBool(), true); } -- cgit v1.2.3 From aa94c6c0469b0595f483f13ac88459f0035deef9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 22 Feb 2018 12:14:34 +0100 Subject: Fix issue with bindings to aliases that cannot yet be resolved When an alias points to a child object which has not yet been initialized, it's id won't have been registered yet, so setting up a binding to it will result in a crash. The fix is: when setting a binding target fails, and its target property is an alias, queue them until all bindings have been set up, and try again. Task-number: QTBUG-57041 Change-Id: I4dc5a6d25c0a32fed9fd952c955e2006c76be45a Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 11 +- src/qml/qml/qqmlbinding_p.h | 4 +- src/qml/qml/qqmlobjectcreator.cpp | 135 ++++++++++++++--------- src/qml/qml/qqmlobjectcreator_p.h | 3 + src/qml/qml/qqmlproperty.cpp | 1 + tests/auto/qml/qqmllanguage/data/alias.16.qml | 15 +++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 10 ++ 7 files changed, 122 insertions(+), 57 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/alias.16.qml diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index bc7fccb2c0..c314833304 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -515,13 +515,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop) setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) +bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; if (!object) { m_targetIndex = QQmlPropertyIndex(); - return; + return false; } int coreIndex = core.coreIndex(); @@ -531,9 +531,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const int aValueTypeIndex; if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + // can't resolve id (yet) m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } if (valueTypeIndex == -1) valueTypeIndex = aValueTypeIndex; @@ -542,7 +543,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const if (!data || !data->propertyCache) { m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); Q_ASSERT(propertyData); @@ -558,6 +559,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); data->propertyCache->addref(); } + + return true; } void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 19ec3f5d4f..b67b02975f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -72,6 +72,8 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: + typedef QExplicitlySharedDataPointer Ptr; + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, const QString &url = QString(), quint16 lineNumber = 0); @@ -82,7 +84,7 @@ public: ~QQmlBinding() override; void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); + bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 36e56a01f8..90f3beb40b 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -780,7 +780,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) qSwap(_currentList, savedList); } -bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); @@ -802,7 +802,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType() == qMetaTypeId()) { + if (bindingProperty && bindingProperty->propType() == qMetaTypeId()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -815,7 +815,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); return true; } @@ -826,7 +826,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - if (!property) // ### error + if (!bindingProperty) // ### error return true; if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { @@ -838,20 +838,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = nullptr; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType())) { - valueType = QQmlValueTypeFactory::valueType(property->propType()); + if (QQmlValueTypeFactory::isValueType(bindingProperty->propType())) { + valueType = QQmlValueTypeFactory::valueType(bindingProperty->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex()); + valueType->read(_qobject, bindingProperty->coreIndex()); groupObject = valueType; - valueTypeProperty = property; + valueTypeProperty = bindingProperty; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -864,21 +864,21 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, bindingProperty->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, runtimeFunction, currentQmlContext()); @@ -890,34 +890,44 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlBinding *qmlBinding; - const QQmlPropertyData *prop = property; + QQmlBinding::Ptr qmlBinding; + const QQmlPropertyData *targetProperty = bindingProperty; const QQmlPropertyData *subprop = nullptr; if (_valueTypeProperty) { - prop = _valueTypeProperty; - subprop = property; + targetProperty = _valueTypeProperty; + subprop = bindingProperty; } if (binding->containsTranslations()) { qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, currentQmlContext()); + qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, context, currentQmlContext()); } - qmlBinding->setTarget(_bindingTarget, *prop, subprop); - sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); + auto bindingTarget = _bindingTarget; + auto valueTypeProperty = _valueTypeProperty; + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) + return false; + + sharedState->allCreatedBindings.push(qmlBinding); - if (property->isAlias()) { - QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); - } else { - qmlBinding->addToObject(); + if (bindingProperty->isAlias()) { + QQmlPropertyPrivate::setBinding(qmlBinding.data(), QQmlPropertyPrivate::DontEnable); + } else { + qmlBinding->addToObject(); - if (!_valueTypeProperty) { - QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); - Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); + if (!valueTypeProperty) { + QQmlData *targetDeclarativeData = QQmlData::get(bindingTarget); + Q_ASSERT(targetDeclarativeData); + targetDeclarativeData->setPendingBindingBit(bindingTarget, bindingProperty->coreIndex()); + } } - } + + return true; + }; + if (!assignBinding(sharedState.data())) + pendingAliasBindings.push_back(assignBinding); } return true; } @@ -934,9 +944,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vs->setTarget(prop); return true; } @@ -946,8 +956,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlPropertyIndex propertyIndex; - if (property->isAlias()) { - QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); + if (bindingProperty->isAlias()) { + QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); @@ -964,9 +974,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } @@ -982,8 +992,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // Assigning object to signal property? if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - if (!property->isFunction()) { - recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(property->name(_qobject))); + if (!bindingProperty->isFunction()) { + recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject))); return false; } QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); @@ -992,7 +1002,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); + QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -1001,7 +1011,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, bindingProperty->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -1010,32 +1020,32 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { + if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType() == QMetaType::QVariant) { - if (property->isVarProperty()) { + } else if (bindingProperty->propType() == QMetaType::QVariant) { + if (bindingProperty->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } - } else if (property->isQList()) { + } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); void *itemToAdd = createdSubObject; const char *iid = nullptr; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); + int listItemType = QQmlEnginePrivate::get(engine)->listType(bindingProperty->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -1051,17 +1061,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } return true; } - if (property->isQList()) { + if (bindingProperty->isQList()) { recordError(binding->location, tr("Cannot assign primitives to lists")); return false; } - setPropertyValue(property, binding); + setPropertyValue(bindingProperty, binding); return true; } @@ -1274,12 +1284,33 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + bool ok = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + if (ok) { + if (isContextObject && !pendingAliasBindings.empty()) { + bool processedAtLeastOneBinding = false; + do { + processedAtLeastOneBinding = false; + for (std::vector::iterator it = pendingAliasBindings.begin(); + it != pendingAliasBindings.end(); ) { + if ((*it)(sharedState.data())) { + it = pendingAliasBindings.erase(it); + processedAtLeastOneBinding = true; + } else { + ++it; + } + } + } while (processedAtLeastOneBinding && pendingAliasBindings.empty()); + Q_ASSERT(pendingAliasBindings.empty()); + } + } else { + // an error occurred, so we can't setup the pending alias bindings + pendingAliasBindings.clear(); + } qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); - return result ? instance : nullptr; + return ok ? instance : nullptr; } QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 399a5f6d4a..67a5bdd827 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -161,6 +161,9 @@ private: QV4::QmlContext *_qmlContext; friend struct QQmlObjectCreatorRecursionWatcher; + + typedef std::function PendingAliasBinding; + std::vector pendingAliasBindings; }; struct QQmlObjectCreatorRecursionWatcher diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ebf296b29d..c4487f91a3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -877,6 +877,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); + Q_ASSERT(binding->targetObject()); QObject *object = binding->targetObject(); const QQmlPropertyIndex index = binding->targetPropertyIndex(); diff --git a/tests/auto/qml/qqmllanguage/data/alias.16.qml b/tests/auto/qml/qqmllanguage/data/alias.16.qml new file mode 100644 index 0000000000..4637aec58f --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/alias.16.qml @@ -0,0 +1,15 @@ +import QtQuick 2.0 +import QtQuick.Window 2.0 + +Window { + visible: true + + property alias list: repeater.model + + list: ["A", "B", "C"] + + Repeater { + id: repeater + } +} + diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index b405181b14..8bc631fbdd 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -1914,6 +1914,16 @@ void tst_qqmllanguage::aliasProperties() QCOMPARE(subItem->property("y").toInt(), 1); } + + // Alias to sub-object with binding (QTBUG-57041) + { + // This is shold *not* crash. + QQmlComponent component(&engine, testFileUrl("alias.16.qml")); + VERIFY_ERRORS(0); + + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + } } // QTBUG-13374 Test that alias properties and signals can coexist -- cgit v1.2.3 From 727ddee130271242e20a7a2d3a66d49b3e2c0d0c Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Mar 2018 11:21:11 +0100 Subject: Fix failing assertion when loading QML/JS on Integrity With the QV4::Moth::BytecodeGenerator::Jump type we are relying on the compiler doing a return value optimization. That however is not required by the C++11 standard and the GHS compiler does indeed not do that here, resulting in a ~Jump destructor call in the following sequence _before_ link() is called: Jump generateJump() { ...; return Jump(...); } ... generateJump().link(); The destructor however verifies that link() was called, which fails. Fix this by making Jump a move-only type, which the compiler will issue if it doesn't perform a return value optimization. Task-number: QTBUG-66917 Change-Id: I97cc9a5d7f97d61e573ad8bc309cf48ab18eb25d Reviewed-by: Kimmo Ollila Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4bytecodegenerator_p.h | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 3b3c766bfe..e69f2cd310 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -102,14 +102,19 @@ public: Jump(BytecodeGenerator *generator, int instruction) : generator(generator), index(instruction) - {} + { Q_ASSERT(generator && index != -1); } + ~Jump() { - Q_ASSERT(generator->instructions[index].linkedLabel != -1); + Q_ASSERT(index == -1 || generator->instructions[index].linkedLabel != -1); // make sure link() got called } + Jump(Jump &&j) { + std::swap(generator, j.generator); + std::swap(index, j.index); + } - BytecodeGenerator *generator; - int index; + BytecodeGenerator *generator = nullptr; + int index = -1; void link() { link(generator->label()); @@ -119,6 +124,12 @@ public: Q_ASSERT(generator->instructions[index].linkedLabel == -1); generator->instructions[index].linkedLabel = l.index; } + + private: + // make this type move-only: + Q_DISABLE_COPY(Jump) + // we never move-assign this type anywhere, so disable it: + Jump &operator=(Jump &&) = delete; }; struct ExceptionHandler : public Label { -- cgit v1.2.3 From 4c689ad93d06bcfff5dfc4fdc41c69767aa04bee Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2018 16:11:27 +0100 Subject: Fix failing ch15.9.5.43-0-8 after US summer time change Eddy fixed the issue in the testsuite itself and this change bumps the submodule to include the fix. (It also brings in the .gitignore fix, which was omitted from an earlier merge by accident). Task-number: QTBUG-67010 Change-Id: I006a2c8babb135187eeb5c296b616e7c1208cd4c Reviewed-by: Edward Welbourne --- .gitmodules | 1 + tests/auto/qml/ecmascripttests/test262 | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index c9561f396d..e6e5650150 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "tests/auto/qml/ecmascripttests/test262"] path = tests/auto/qml/ecmascripttests/test262 url = ../qtdeclarative-testsuites.git + branch = snapshot-20150317-8f6a508-based diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 index d60c4ed97e..40b4f28e98 160000 --- a/tests/auto/qml/ecmascripttests/test262 +++ b/tests/auto/qml/ecmascripttests/test262 @@ -1 +1 @@ -Subproject commit d60c4ed97e69639bc5bc1db43a98828debf80c8a +Subproject commit 40b4f28e98c416a092e26aa17489bf94ccb8bf4f -- cgit v1.2.3 From a8ff8f76317c394094dd6237bbb78de51e1d9844 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2018 16:06:11 +0100 Subject: Fix ABI/API symbol tagging The findclasslist.pl perl script that produces the linker version script got confused by the "struct name" that was part of a macro and thought that the class "name" in the *_p.h was supposed to be annotated with the private API tag, resulting in a "*4name*" mask in the linker script, which in turn made lots of public symbols "private" that had name in it, such as QQmlProperty::name(). Fixing the indentation works around it and conforms to coding style. Change-Id: I0c66a6bb1d49941d6ec6dd89d9433d9b6ae0c639 Done-with: Thiago Macieira Task-number: QTBUG-67004 Reviewed-by: Thiago Macieira --- src/qml/memory/qv4mmdefs_p.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 3e2bae46c2..8a53492822 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -323,23 +323,23 @@ struct MarkStack { #define DECLARE_HEAP_OBJECT_BASE(name, base) \ -struct name##OffsetStruct { \ - name##Members(name, HEAP_OBJECT_OFFSET_MEMBER_EXPANSION) \ -}; \ -struct name##SizeStruct : base, name##OffsetStruct {}; \ -struct name##Data { \ - typedef base SuperClass; \ - static Q_CONSTEXPR size_t baseOffset = sizeof(name##SizeStruct) - sizeof(name##OffsetStruct); \ - name##Members(name, HEAP_OBJECT_MEMBER_EXPANSION) \ -}; \ -Q_STATIC_ASSERT(sizeof(name##SizeStruct) == sizeof(name##Data) + name##Data::baseOffset); \ + struct name##OffsetStruct { \ + name##Members(name, HEAP_OBJECT_OFFSET_MEMBER_EXPANSION) \ + }; \ + struct name##SizeStruct : base, name##OffsetStruct {}; \ + struct name##Data { \ + typedef base SuperClass; \ + static Q_CONSTEXPR size_t baseOffset = sizeof(name##SizeStruct) - sizeof(name##OffsetStruct); \ + name##Members(name, HEAP_OBJECT_MEMBER_EXPANSION) \ + }; \ + Q_STATIC_ASSERT(sizeof(name##SizeStruct) == sizeof(name##Data) + name##Data::baseOffset); \ #define DECLARE_HEAP_OBJECT(name, base) \ -DECLARE_HEAP_OBJECT_BASE(name, base) \ -struct name : base, name##Data + DECLARE_HEAP_OBJECT_BASE(name, base) \ + struct name : base, name##Data #define DECLARE_EXPORTED_HEAP_OBJECT(name, base) \ -DECLARE_HEAP_OBJECT_BASE(name, base) \ -struct Q_QML_EXPORT name : base, name##Data + DECLARE_HEAP_OBJECT_BASE(name, base) \ + struct Q_QML_EXPORT name : base, name##Data #define DECLARE_MARKOBJECTS(class) \ static void markObjects(Heap::Base *b, MarkStack *stack) { \ -- cgit v1.2.3 From f0f632c7c1043f9c8097b4677f0a9eae01f916d2 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sun, 11 Mar 2018 23:15:38 +0000 Subject: Fix non-integer scale factors with Text native rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-67007 Change-Id: Id32cc14ea1bc258cfc139a859ccae014f5893563 Reviewed-by: Morten Johan Sørvig --- src/quick/scenegraph/qsgdefaultglyphnode_p.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index dc473a6640..ce706d76f7 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -71,9 +71,9 @@ static inline QVector4D qsg_premultiply(const QVector4D &c, float globalOpacity) return QVector4D(c.x() * o, c.y() * o, c.z() * o, o); } -static inline int qsg_device_pixel_ratio(QOpenGLContext *ctx) +static inline qreal qsg_device_pixel_ratio(QOpenGLContext *ctx) { - int devicePixelRatio = 1; + qreal devicePixelRatio = 1; if (ctx->surface()->surfaceClass() == QSurface::Window) { QWindow *w = static_cast(ctx->surface()); if (QQuickWindow *qw = qobject_cast(w)) -- cgit v1.2.3 From a726f68cf0bf903466bbc75da4a2a429424c4877 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 13 Mar 2018 11:23:15 +0100 Subject: Fix typos, punctuation, and grammar in sprites.qdoc Change-Id: Ie3c24958e96c54cd91cd4a752638c11764338028 Reviewed-by: Venugopal Shivashankar --- src/quick/doc/src/concepts/effects/sprites.qdoc | 60 ++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/quick/doc/src/concepts/effects/sprites.qdoc b/src/quick/doc/src/concepts/effects/sprites.qdoc index cb4d7e2ac6..004db90eb3 100644 --- a/src/quick/doc/src/concepts/effects/sprites.qdoc +++ b/src/quick/doc/src/concepts/effects/sprites.qdoc @@ -52,38 +52,38 @@ This allows you to easily insert a transitional animation between two different \image spriteenginegraph.png As an example, consider the above diagram which illustrates the sprites for a hypothetical 2D -platform game character. The character starts by displaying the standing state. From this state, -barring external input, he will transition to either the waiting animation, the walking animation, -or play the standing animation again. Because the weights for those transitions are one, zero and three -respectively, he has a one in four chance of playing the waiting animation when the standing animation -finishes, and a three in four chance of playing the standing animation again. This allows for a character +platform game character. The character starts by displaying the \e standing state. From this state, +barring external input, he will transition to either the \e waiting animation, the \e walking animation, +or play the \e standing animation again. Because the weights for those transitions are one, zero and three +respectively, he has a one in four chance of playing the \e waiting animation when the \e standing animation +finishes, and a three in four chance of playing the \e standing animation again. This allows for a character who has a slightly animated and variable behavior while waiting. -Because there is a zero weight transition to the walking animation, the standing animation will not normally -transition there. But if you set the goal animation to be the walking animation, it would play the walking -animation when it finished the standing animation. If it was previously in the waiting animation, it would -finish playing that, then play the standing animation, then play the walking animation. It would then continue to -play the walking animation until the goal animation is unset, at which point it would switch to the standing -animation after finishing the walking animation. - -If you set the goal state then to the jumping animation, it would finish the walking animation before -playing the jumping animation. Because the jumping animation does not transition to other states, it will still -keep playing the jumping animation until the state is forced to change. In this example, you could set it back to -walking and change to goal animation to walking or to nothing (which would lead it to play the standing animation -after the walking animation). Note that by forcibly setting the animation, you can start playing the animation +Because there is a zero weight transition to the \e walking animation, the \e standing animation will not normally +transition there. But if you set the goal animation to be the \e walking animation, it would play the \e walking +animation when it finished the \e standing animation. If it was previously in the \e waiting animation, it would +finish playing that, then play the \e standing animation, then play the \e walking animation. It would then continue to +play the \e walking animation until the goal animation is unset, at which point it would switch to the \e standing +animation after finishing the \e walking animation. + +If you then set the goal state to the \e jumping animation, it would finish the \e walking animation before +playing the \e jumping animation. Because the \e jumping animation does not transition to other states, it will still +keep playing the \e jumping animation until the state is forced to change. In this example, you could set it back to +\e walking and change the goal animation to \e walking or to nothing (which would lead it to play the \e standing animation +after the \e walking animation). Note that by forcibly setting the animation, you can start playing the animation immediately. \section2 Input Format -The file formats accepted by the sprite engine is the same as the file formats accepted by other QML types, -such as \l Image. In order to animate the image however, the sprite engine requires the image file to contain +The file formats accepted by the sprite engine are the same as the file formats accepted by other QML types, +such as \l Image. In order to animate the image, however, the sprite engine requires the image file to contain all of the frames of the animation. They should be arranged in a contiguous line, which may wrap from the right edge of the file to a lower row starting from the left edge of the file (and which is placed directly below the previous row). \image spritecutting.png -As an example, take the above image. For now just consider the black numbers, and assume the squares are 40x40 pixels. +As an example, take the above image. For now, just consider the black numbers, and assume the squares are 40x40 pixels. Normally, the image is read from the top-left corner. If you specified the frame size as 40x40 pixels, and a frame count of 8, then it would read in the frames as they are numbered. The frame in the top left would be the first frame, the frame in the top right would be the fifth frame, and then it would wrap to the next row (at pixel location 0,40 in the file) to read @@ -97,9 +97,9 @@ The first 120x40 of the image will not be used, as it starts reading 40x40 block When it reaches the end of the file at 160,0, it then starts to read the next row from 0,40. The blue numbers show the frame numbers if you tried to load two frames of that size, starting from 40,40. Note -that it is possible to load multiple sprites out of the one image file. The red, blue and black numbers can all +that it is possible to load multiple sprites from one image file. The red, blue and black numbers can all be loaded as separate animations to the same sprite engine. The following code loads the animations as per the image. -It also specifies that animations are to played at 20 frames per second. +It also specifies that animations are to be played at 20 frames per second. \code Sprite { @@ -131,17 +131,17 @@ Sprite { } \endcode -Frames within one animation must be the same size, however multiple animations within the same file -do not. Sprites without a frameCount specified assume that they take the entire file, and you must specify +Frames within one animation must be the same size. However, multiple animations within the same file +do not. Sprites without a \l {Sprite::}{frameCount} specified assume that they take the entire file, and you must specify the frame size. Sprites without a frame size assume that they are square and take the entire file without wrapping, and you must specify a frame count. -The sprite engine internally copies and cuts up images to fit in an easier to read internal format, which leads +The sprite engine internally copies and cuts up images to fit in an easier-to-read internal format, which leads to some graphics memory limitations. Because it requires all the sprites for a single engine to be in the same texture, attempting to load many different animations can run into texture memory limits on embedded devices. In these situations, a warning will be output to the console containing the maximum texture size. -There are several tools to help turn a set of images into sprite sheets, here are some examples: +There are several tools to help turn a set of images into sprite sheets. Here are some examples: \list \li Photoshop plugin: \l http://www.johnwordsworth.com/projects/photoshop-sprite-sheet-generator-script \li Gimp plugin: \l http://registry.gimp.org/node/20943 @@ -150,23 +150,23 @@ There are several tools to help turn a set of images into sprite sheets, here ar \section2 QML Types Using the Sprite Engine -Sprites for the sprite engine can be defined using the \l Sprite type. This type includes the input parameters +Sprites for the sprite engine can be defined using the \l Sprite type. This type includes the input parameters, as well as the length of the animation and weighted transitions to other animations. It is purely a data class, and does not render anything. \l SpriteSequence is a type which uses a sprite engine to draw the sprites defined in it. It is a single and self-contained sprite engine, and does not interact with other sprite engines. \l Sprite types can be shared between -sprite engine using types, but this is not done automatically. So if you have defined a sprite in one \l SpriteSequence +sprite engine-using types, but this is not done automatically. So, if you have defined a sprite in one \l SpriteSequence you will need to redefine it (or reference the same \l Sprite type) in the sprites property of another \l SpriteSequence in order to transition to that animation. Additionally, \l ImageParticle can use \l Sprite types to define sprites for each particle. This is again a single -sprite engine per type. This works similarly to SpriteSequence, but it also has the parametrized variability provided +sprite engine per type. This works similarly to \c SpriteSequence, but it also has the parameterized variability provided by the \l ImageParticle type. \section1 AnimatedSprite Type -For use-cases which do not need to transition between animations, consider the AnimatedSprite type. +For use-cases which do not need to transition between animations, consider the \l AnimatedSprite type. This type displays sprite animations with the same input format, but only one at a time. It also provides more fine-grained manual control, as there is no sprite engine managing the timing and transitions behind the scenes. -- cgit v1.2.3 From 44e37e7c213d4f472450b0d4ca673eb7c7e57122 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Thu, 8 Mar 2018 14:28:09 +0100 Subject: Doc: Add Qt Quick usage guidelines Task-number: QTBUG-15757 Change-Id: I9193ed459ced63cceb819a66f5a8c76042f455b6 Reviewed-by: Mitch Curtis --- src/qml/doc/src/external-resources.qdoc | 29 +++ .../doc/src/guidelines/qtquick-bestpractices.qdoc | 204 +++++++++++++++++++++ .../doc/src/guidelines/qtquick-guidelines.qdoc | 50 +++++ .../src/guidelines/qtquick-toolsnutilities.qdoc | 113 ++++++++++++ src/quick/doc/src/qtquick.qdoc | 1 + 5 files changed, 397 insertions(+) create mode 100644 src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc create mode 100644 src/quick/doc/src/guidelines/qtquick-guidelines.qdoc create mode 100644 src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index 717e983517..c6c922c171 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -44,3 +44,32 @@ \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date \title Mozilla Developer Network Date Reference */ +/*! + \externalpage hhttps://www.froglogic.com/squish/gui-testing + \title Squish +*/ +/*! + \externalpage http://doc.qt.io/GammaRay + \title GammaRay +*/ +/*! + \externalpage http://doc.qt.io/QtQmlLive + \title QmlLive +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/creator-debugging-qml.html + \title QML Debugger +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/creator-qml-performance-monitor.html + \title QML Profiler +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/index.html + \title Qt Creator Manual +*/ +/*! + \externalpage http://doc.qt.io/qtcreator/creator-editor-external.html + \title Qt Creator: Integrating 3rd Party Tools +*/ + diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc new file mode 100644 index 0000000000..6327ea67e6 --- /dev/null +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtquick-bestpractices.html +\title Qt Quick Best Practices +\brief Lists the practices that works best for Qt Quick + +Besides all the benefits that Qt Quick offers, it can be challenging in certain +situations. For example, a Qt Quick application with a large codebase can be +painful to maintain if not organized well. The following sections elaborate +on some of the best practices that will help you get better results. + +\section1 Custom UI Controls + +A fluid and modern UI is key for any application's success in today's world, and +that's where QML makes so much sense for a designer or developer. Qt offers the +most basic UI controls that are necessary to a create fluid and modern-looking +UI. It is recommended to browse this list of UI controls before creating your +own custom UI control. + +Besides these basic UI controls offered by Qt Quick itself, a rich set of UI +controls are also available with Qt Quick Controls 2. They cater to the most +common use cases without any change, and offer a lot more possibilities with their +customization options. In particular, Qt Quick Controls 2 provides styling +options that align with the latest UI design trends. If these UI controls do not +satisfy to your application's needs, only then it is recommended to create a +custom control. + + +\section2 Related Information +\list +\li \l{Qt Quick Controls 2} +\li \l{Qt Quick} +\endlist + +\section1 Keep it Short and Simple or "KiSS" + +QML being a declarative language, a lot of the details are worked out by the underlying +engine. So it is important for any QML application, especially one with a +larger codebase, to have its code organized in smaller and simpler \c .qml files. + +\omit +need a few snippet or example applications that showcase this. +\endomit + +\section2 Related Information +\list + \li \l{QML Coding Conventions} +\endlist + +\section1 Bundle Application Resources + +Most applications depend on resources such as images and icons to provide a +rich user experience. It can often be a challenge to make these resources +available to the application regardless of the target OS. Most popular OS-es +employ stricter security policies that restrict access to the file system, +making it harder to load these resources. As an alternative, Qt offers its own +resource system that is built into the application binary, enabling access to +the application's resources regardless of the target OS. + +It is recommended to bundle your application's resources (including the +\c .qml files) into a resource file (\c.qrc). For example, the following entry +in the qmake project file ensures that the resources are built into the +application binary, making them available when needed: + +\badcode + RESOURCES += resources.qrc +\endcode + +If your application depends on a limited number of resources, you could list +them directly in the project file. + +\badcode + RESOURCES += a.qml b.png +\endcode + +In such a case, qmake creates the \c qmake_intermediate.qrc build artifact, +which you could rename and use the \c{RESOURCES += resource-set.qrc} entry +instead. + +You could go a step further by using one \c .qrc file for each resource type. +For example, list the \c .qml files in \c files.qrc, images in +\c images.qrc, fonts in \c fonts.qrc, and so on. That way, you need not +recompile the QML files when you, for example, add an image to the list in +\c images.qrc. + +\section2 Related Information +\list + \li \l{The Qt Resource System} +\endlist + +\section1 Application UI and Business Logic + +One of the key goals that most application developers want to achieve is to +create a maintainable application. One of the ways to achieve this goal is +to separate the UI from the business logic. The following are a few reasons +why application's UI should be in QML: + +\list + \li QML is a declarative language, which suits best for defining UIs. + \li It's easier to embed JavaScript in QML to respond to events, for example. + \li QML is faster to code as it is not strongly typed. +\endlist + +On the other hand, C++ being a strongly typed language, suits best for defining +business logic. Typically, such code performs tasks such as complex calculations +or larger data processing, which can be faster with C++ than with QML. + +Qt offers various approaches to integrate QML and C++ code in an application. +In most cases, the C++ part (business logic) provides the data model to the QML +part (UI), which presents it in a readable form. It is often a challenge to +decide when to use this approach. It is recommended to use QML +if the data model is static, simple, and small, as C++ could be overkill. +Use C++ if the application depends on a dynamic, large, and complex data model. + +\omit +examples snippets of simpler and complex data models. +\endomit + +\section2 Interaction Path + +Although Qt enables you to manipulate QML from C++, it is not a recommended +approach, as debugging such code can be painful. The QML engine works out +a lot of the details on the fly, so manipulating the QML items from C++ could +lead to unexpected results. This approach also makes the C++ code rely on the +QML code to provide certain properties or objects, making it difficult to +refactor the QML code without breaking the C++ code. Moreover, such C++ code +cannot reused with other QML code. To avoid all of these hassles and have a +maintainable application, it is recommended to always use the C++ to QML path +and not the other way around. + +\section2 Related Information +\list +\li \l{Integrating QML and C++} +\li \l{Chat Tutorial Example} +\endlist + +\section1 Qt Quick Layouts + +Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout. +Unlike its alternative, the item positioners, the Qt Quick Layouts can also +resize its children on window resize. Although Qt Quick Layouts are often +the desired choice for most use cases, the following \e dos and \e{don'ts} +must be considered while using them: + +\section2 Dos + +\list + \li Use anchors or the item's width and height properties to specify the size + of the layout against its parent. + \li Use the \l Layout attached property to set the size and alignment + attributes of the layout's immediate children. +\endlist + +\section2 Don'ts + +\list + \li Do not rely on anchors to specify the preferred size of an item in a layout. + Instead, use \c Layout.preferredWidth and \c Layout.preferredHeight. + \li Do not define preferred sizes for items that provide implicitWidth and + implicitHeight, unless their implicit sizes are not satisfactory. + \li Do not mix anchors and layouts in ways that cause conflicts. For example, + do not apply anchor constraints to a layout's immediate children. + + \snippet qml/windowconstraints.qml rowlayout +\endlist + +\note Layouts and anchors are both types of objects that take more memory and +instantiation time. Avoid using them (especially in list and table delegates, +and styles for controls) when simple bindings to x, y, width, and height +properties are enough. + +\section2 Related Information + +\list + \li \l{Item Positioners} + \li \l{Qt Quick Layouts Overview} +\endlist +*/ diff --git a/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc b/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc new file mode 100644 index 0000000000..b8432fd9ca --- /dev/null +++ b/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtquick-guidelines.html +\title Qt Quick Guidelines +\brief Provides best practices and conventions for application developers + +Qt Quick has been the enabler that has helped developers achieve UI design +goals faster and relieve from the monotony of imperative-coding. It is one of +the very few UI design technologies that can be modified easily either using +a text editor or a graphical designer tool. + +As an application developer, it is important to understand the limitations +of any technology such as Qt Quick. This topic provides you the necessary insight +into Qt Quick, and how it affects your application. It also provides pointers to best +practices that you should try to follow, and the list of tools that helps you +understand and improve your application. + +\list +\li \l{Qt Quick Best practices}{Best Practices} +\li \l{Qt Quick Tools and Utilities} +\li \l{Performance Considerations And Suggestions}{Performance improvement tips} +\li \l{Qt Quick Scene Graph} +\endlist +*/ diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc new file mode 100644 index 0000000000..9729b16392 --- /dev/null +++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtquick-tools-and-utilities.html +\title Qt Quick Tools and Utilities +\brief Lists the tools and utilities that enhance developer experience + +Qt offers several tools and utilities to enable a rich developer experience, +especially for Qt Quick developers. The following sections provide a brief +introduction to those tools and utilities, and provide links to further +information about them. + +\section1 Qt Quick Designer + +The Qt Quick Designer enables designing Qt Quick-based UIs using simple +drag-n-drop gestures that most designers are familiar with. It offers UI +elements from the Qt Quick and Qt Quick Controls 2 modules, as well as +integration for custom UI elements. + +The following is a list of example applications that use UIs created by +the Qt Quick Designer: + +\list + \li \l{Qt Quick Controls 2 - Contacts List} + \li \l{Qt Quick Controls 2 - Flat Style} +\endlist + +\section2 QML Debugger and Profiler + +Being a declarative language, a piece of QML code provides minimal details +about the entities defined. In such a scenario, the QML debugger is a very +useful utility that enables: +\list + \li debugging JavaScript functions, + \li executing JavaScript expressions, + \li and inspecting QML properties. +\endlist + +Besides this, a QML profiler enables you to get necessary diagnostic information, +allowing you to analyze the application code for performance issues. For +example, too much JavaScript in each frame, long-running C++ functions, and +so on. + +\section2 Related Information +\list + \li \l{QML Debugger} + \li \l{QML Profiler} +\endlist + +\section2 QmlLive, GammaRay, and Squish + +QmlLive is a 3rd party tool that offers a QML runtime capable of rendering +changes to the code in realtime. It avoids the need to rebuild the +application after every code change and install it on the target device. +You can also extend it to build a custom runtime that suits your needs. + +GammaRay is a useful utility that provides diagnostic information +about your application. It is similar to the QML Profiler described in the +earlier section, but offers a lot more. For example, the number of items or +QObjects created, function calls made, time taken by each function call, +property value introspection at runtime, and so on. Such information is very +handy, especially while debugging QML applications. + +Squish is a well-known testing tool that automates UI testing by recording +your actions or running scripts. Once the tests are setup, UI tests are a lot +easier to run. + +\section2 Related Information +\list + \li \l{QmlLive} + \li \l{GammaRay} + \li \l{Squish} +\endlist + +\section1 Qt Creator + +The Qt Creator IDE is the key tool that enhances the overall developer experience of +working with Qt Quick. Its auto-completion and debugging features make working +with Qt Quick easier. Besides this, most of the tools and utilities +mentioned in the earlier sections are integrated into it, with the possibility of +integrating 3rd party tools such as QmlLive and GammaRay. + +\section2 Related Information +\list +\li \l{Qt Creator Manual} +\li \l{Qt Creator: Integrating 3rd Party Tools} +\endlist +*/ diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc index ece66cb589..4d8da5ed5a 100644 --- a/src/quick/doc/src/qtquick.qdoc +++ b/src/quick/doc/src/qtquick.qdoc @@ -118,6 +118,7 @@ Additional Qt Quick information: \li \l{Qt Quick Test QML Types}{Tests} - contains types for writing unit test for a QML application \endlist \li \l{Qt Quick Examples and Tutorials} +\li \l{Qt Quick Guidelines} \endlist Further information for writing QML applications: -- cgit v1.2.3 From 1b996c138f6afae7ccf9d9ec87036768fd8ef329 Mon Sep 17 00:00:00 2001 From: Nils Jeisecke Date: Fri, 2 Mar 2018 17:34:23 +0100 Subject: tests: add autotest for Q_GADGET derived template class This tests the effectivity of the qtbase fixes for QTBUG-66744. Task-number: QTBUG-66744 Change-Id: I5bb041082ae4ce6cb91076c3f1279ac7bdcae4f0 Reviewed-by: Simon Hausmann --- .../auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp index c252bba001..a456facd2f 100644 --- a/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp +++ b/tests/auto/qml/qqmlvaluetypes/tst_qqmlvaluetypes.cpp @@ -89,6 +89,7 @@ private slots: void customValueType(); void customValueTypeInQml(); void gadgetInheritance(); + void gadgetTemplateInheritance(); void toStringConversion(); void enumerableProperties(); void enumProperties(); @@ -1613,6 +1614,19 @@ public: Q_INVOKABLE void functionInDerivedGadget(int value) { m_derivedProperty = value; } }; +// QTBUG-66744: we want a Q_GADGET giving us generic type safety in C++ and property access in Qml +template +struct DerivedTypedGadget : public BaseGadget +{ + // cannot use Q_GADGET here +public: + DerivedTypedGadget() {} +}; + +class DerivedTypedGadgetDummyType {}; + +Q_DECLARE_METATYPE(DerivedTypedGadget) + class TypeWithCustomValueType : public QObject { Q_OBJECT @@ -1657,6 +1671,21 @@ void tst_qqmlvaluetypes::gadgetInheritance() QCOMPARE(value.property("baseProperty").toInt(), 42); } +void tst_qqmlvaluetypes::gadgetTemplateInheritance() +{ + QJSEngine engine; + + QJSValue value = engine.toScriptValue(DerivedTypedGadget()); + + QCOMPARE(value.property("baseProperty").toInt(), 0); + value.setProperty("baseProperty", 10); + QCOMPARE(value.property("baseProperty").toInt(), 10); + + QJSValue method = value.property("functionInBaseGadget"); + method.call(QJSValueList() << QJSValue(42)); + QCOMPARE(value.property("baseProperty").toInt(), 42); +} + struct StringLessGadget { Q_GADGET }; -- cgit v1.2.3 From b54679fcd0c183f3e85fc6ab89033b6f8d510e45 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 13 Mar 2018 21:57:00 +0100 Subject: Fix a crash in Object.proto.propertyIsEnumerable If the property being queried is an array index, we would call ArrayData::getProperty with a the Property pointer being null. We correctly handle this for named properties, but didn't here. Change-Id: Iba98a13f276432f273545c87cfc998fe64f45c51 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata_p.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 9356670b6d..b2573b4491 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -304,9 +304,11 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) } *attrs = attributes(index); - p->value = *(Index{ this, mapped }); - if (attrs->isAccessor()) - p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + if (p) { + p->value = *(Index{ this, mapped }); + if (attrs->isAccessor()) + p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + } return true; } -- cgit v1.2.3 From 5b1538d1c0b408bb54786336f2e59c208686edb2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2018 10:20:09 +0100 Subject: Fix use of CONFIG+=qtquickcompiler with spaces in paths Add strategic calls to system_quote and shell_quote and correct the output list iteration to not use $$list, as that will end up splitting on spaces in the path. Task-number: QTBUG-67011 Change-Id: I31dbee537e2052ac7b802ee3509a74c9db3b8beb Reviewed-by: Lars Knoll Reviewed-by: Oswald Buddenhagen --- tools/qmlcachegen/qtquickcompiler.prf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf index 75e474ba70..1e87a2d7e7 100644 --- a/tools/qmlcachegen/qtquickcompiler.prf +++ b/tools/qmlcachegen/qtquickcompiler.prf @@ -22,21 +22,21 @@ QMLCACHE_RESOURCE_FILES = for(res, RESOURCES) { absRes = $$absolute_path($$res, $$_PRO_FILE_PWD_) - rccContents = $$system($$QMAKE_RCC_DEP -list $$absRes,lines) + rccContents = $$system($$QMAKE_RCC_DEP -list $$system_quote($$absRes),lines) contains(rccContents,.*\\.js$)|contains(rccContents,.*\\.qml$) { new_resource = $$qmlCacheResourceFileOutputName($$res) mkpath($$dirname(new_resource)) - remaining_files = $$system($$QML_CACHEGEN_FILTER -filter-resource-file -o $$new_resource $$absRes,lines) + remaining_files = $$system($$QML_CACHEGEN_FILTER -filter-resource-file -o $$system_quote($$new_resource) $$system_quote($$absRes),lines) !isEmpty(remaining_files) { NEWRESOURCES += $$new_resource - QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$absRes=$$new_resource + QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes=$$new_resource) } else { - QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$absRes + QMLCACHE_LOADER_FLAGS += --resource-file-mapping=$$shell_quote($$absRes) } QMLCACHE_RESOURCE_FILES += $$absRes - for(candidate, $$list($$rccContents)) { + for(candidate, rccContents) { contains(candidate,.*\\.js$)|contains(candidate,.*\\.qml$) { QMLCACHE_FILES += $$candidate } @@ -50,7 +50,7 @@ RESOURCES = $$NEWRESOURCES QMLCACHE_RESOURCE_FLAGS = for(res, QMLCACHE_RESOURCE_FILES) { - QMLCACHE_RESOURCE_FLAGS += --resource=$$res + QMLCACHE_RESOURCE_FLAGS += --resource=$$shell_quote($$res) } defineReplace(qmlCacheOutputName) { -- cgit v1.2.3 From b1243b8c9ea0add1b7548428c8b0fcb8ee3ac71a Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 14 Mar 2018 09:42:03 +0100 Subject: Scale up SVG images if source size is larger than original size Since SVG images can be scaled up without any loss of quality then we should allow this to happen even though it is not done for other image formats. This restores the 5.9.x behavior for SVG images. Additionally the manual test is updated to showcase an embedded image inside a SVG one to indicate this is continuing to work as before too. Task-number: QTBUG-67019 Change-Id: Ia719899937f8146e8fab50aa85adf18e2f79aa98 Reviewed-by: Evangelos Foutras Reviewed-by: Shawn Rutledge --- src/quick/util/qquickimageprovider.cpp | 9 ++++++--- tests/auto/quick/qquickimage/tst_qquickimage.cpp | 6 ++++++ tests/manual/imagehandler/embeddedimage.svg | 10 ++++++++++ tests/manual/imagehandler/heart.png | Bin 0 -> 15194 bytes tests/manual/imagehandler/main.qml | 6 +++++- 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 tests/manual/imagehandler/embeddedimage.svg create mode 100644 tests/manual/imagehandler/heart.png diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index 1a13f6395a..2bb2af4fc0 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -680,15 +680,18 @@ QSize QQuickImageProviderWithOptions::loadSize(const QSize &originalSize, const return res; const bool preserveAspectCropOrFit = options.preserveAspectRatioCrop() || options.preserveAspectRatioFit(); + const bool formatIsSvg = (format == "svg" || format == "svgz"); - if (!preserveAspectCropOrFit && (format == "svg" || format == "svgz") && !requestedSize.isEmpty()) + if (!preserveAspectCropOrFit && formatIsSvg && !requestedSize.isEmpty()) return requestedSize; qreal ratio = 0.0; - if (requestedSize.width() && (preserveAspectCropOrFit || requestedSize.width() < originalSize.width())) { + if (requestedSize.width() && (preserveAspectCropOrFit || formatIsSvg || + requestedSize.width() < originalSize.width())) { ratio = qreal(requestedSize.width()) / originalSize.width(); } - if (requestedSize.height() && (preserveAspectCropOrFit || requestedSize.height() < originalSize.height())) { + if (requestedSize.height() && (preserveAspectCropOrFit || formatIsSvg || + requestedSize.height() < originalSize.height())) { qreal hr = qreal(requestedSize.height()) / originalSize.height(); if (ratio == 0.0) ratio = hr; diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 3613ba6d87..d1f46a3912 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -445,6 +445,12 @@ void tst_qquickimage::svg() // Due to aspect ratio calculations we can't get a precise // check for all setups, so we allow a small margin of error QVERIFY(qAbs(obj->height() - 141) < 1); + + // Setting it to a size bigger than the actual file, SVG formats + // can scale up although other image formats cannot + obj->setSourceSize(QSize(800,0)); + QCOMPARE(obj->width(), 800.0); + QVERIFY(qAbs(obj->height() - 1131) < 1); delete obj; } diff --git a/tests/manual/imagehandler/embeddedimage.svg b/tests/manual/imagehandler/embeddedimage.svg new file mode 100644 index 0000000000..f952640822 --- /dev/null +++ b/tests/manual/imagehandler/embeddedimage.svg @@ -0,0 +1,10 @@ + + + + This document has a reference to an external image + + + External image + + diff --git a/tests/manual/imagehandler/heart.png b/tests/manual/imagehandler/heart.png new file mode 100644 index 0000000000..deaec18274 Binary files /dev/null and b/tests/manual/imagehandler/heart.png differ diff --git a/tests/manual/imagehandler/main.qml b/tests/manual/imagehandler/main.qml index 55e5b89cae..ec474e62ce 100644 --- a/tests/manual/imagehandler/main.qml +++ b/tests/manual/imagehandler/main.qml @@ -41,6 +41,10 @@ Window { width: parent.width sourceSize.height: height sourceSize.width: width + MouseArea { + anchors.fill: parent + onClicked: svgImage.source = "embeddedimage.svg" + } } ListModel { id: imageFillModeModel @@ -57,7 +61,7 @@ Window { height: 75 anchors.bottom: parent.bottom Text { - text: "Click the options below to change the fill mode" + text: "Click the options below to change the fill mode.
Click the image to change the used image." font.pointSize: 16 } -- cgit v1.2.3 From 08197a25c081f734b93065f6c7c7ce570b155758 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 14 Mar 2018 11:09:38 +0100 Subject: Add Q_UNUSED(engine) to fix build on iOS Change-Id: I47e84ee2c3f36dae9354e54b68ac60001703bf3d Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4regexp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index d99536829b..e10493b879 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -111,6 +111,8 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, bool ig JSC::JSGlobalData dummy(internalClass->engine->regExpAllocator); JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode); } +#else + Q_UNUSED(engine) #endif if (hasValidJITCode()) { valid = true; -- cgit v1.2.3 From 936570a3b3440ed2508bacb9b9916381cb864ffb Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 6 Mar 2018 13:52:33 +0100 Subject: QQuickWindow: skip mouse synthesis for touch events from trackpads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A trackpad is primarily a mouse-emulating device, which can also recognize gestures, and furthermore can send raw touches iff it is asked to. So, every touch event from a trackpad is likely to be followed by a mouse or gesture event (especially when the MouseEmulation capability confirms that it is able to do that). Therefore mouse event synthesis is redundant. In this bug scenario, MultiPointTouchArea enables touch events within its rectangular bounds. A Flickable is on top. As you flick with two fingers on the trackpad, macOS sends touch events with two touch points (because MPTA enabled touch events) and also a series of QWheelEvents as it recognizes the flick gesture. The Flickable receives mouse events sythesized from one touch point, and reacts as if you were dragging with one finger on a touchscreen, while it simultaneously also reacts to the QWheelEvents. Meanwhile the remaining touchpoint falls through to the MPTA underneath; so the user ends up interacting with both at the same time and making the Flickable jump around besides. This patch just fixes the jumpiness in Flickable: it will no longer receive the synth-mouse events, so it will not be dragged, and will react only to the wheel events. But MPTA still gets the touches. Task-number: QTBUG-66329 Change-Id: I6f535a2c9e47bcb284eaf9ae1fdaa39f8b510af9 Reviewed-by: Jan Arve Sæther --- src/quick/items/qquickevents_p_p.h | 1 + src/quick/items/qquickwindow.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index c4f0b60d92..2a1e9dc184 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -597,6 +597,7 @@ public: Area = QTouchDevice::Area, Pressure = QTouchDevice::Pressure, Velocity = QTouchDevice::Velocity, + MouseEmulation = QTouchDevice::MouseEmulation, // some bits reserved in case we need more of QTouchDevice::Capabilities Scroll = 0x0100, // mouse has a wheel, or there is OS-level scroll gesture recognition (dubious?) Hover = 0x0200, diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index aefdaea2b7..48eba6a7a0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -654,6 +654,12 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve Q_Q(QQuickWindow); auto device = pointerEvent->device(); + // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant + if (device->type() == QQuickPointerDevice::TouchPad && device->capabilities().testFlag(QQuickPointerDevice::MouseEmulation)) { + qCDebug(DBG_TOUCH_TARGET) << "skipping delivery of synth-mouse event from" << device; + return false; + } + // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here. Q_ASSERT(pointerEvent->asPointerTouchEvent()); QScopedPointer event(pointerEvent->asPointerTouchEvent()->touchEventForItem(item)); @@ -2831,7 +2837,11 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. bool acceptsTouchEvents = false; #endif - if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { + auto device = pte->device(); + if (device->type() == QQuickPointerDevice::TouchPad && + device->capabilities().testFlag(QQuickPointerDevice::MouseEmulation)) { + qCDebug(DBG_TOUCH_TARGET) << "skipping filtering of synth-mouse event from" << device; + } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { // get a touch event customized for delivery to filteringParent QScopedPointer filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); if (filteringParentTouchEvent) { -- cgit v1.2.3 From 62dd8b20055fd5ccec1dc2eb33523aae2e6ad7b9 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 12 Mar 2018 11:05:08 +0100 Subject: Improve SpriteSequence's documentation Change-Id: I7e9e09ba3b6c0ec3c0b4bed75b24ed87fbe9660f Reviewed-by: Venugopal Shivashankar --- src/quick/items/qquickspritesequence.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/quick/items/qquickspritesequence.cpp b/src/quick/items/qquickspritesequence.cpp index 0a39c09ebc..72761ab82b 100644 --- a/src/quick/items/qquickspritesequence.cpp +++ b/src/quick/items/qquickspritesequence.cpp @@ -73,38 +73,38 @@ QT_BEGIN_NAMESPACE Whether the sprite is animating or not. - Default is true + Default is \c true. */ /*! \qmlproperty bool QtQuick::SpriteSequence::interpolate - If true, interpolation will occur between sprite frames to make the + If \c true, interpolation will occur between sprite frames to make the animation appear smoother. - Default is true. + Default is \c true. */ /*! \qmlproperty string QtQuick::SpriteSequence::currentSprite - The name of the Sprite which is currently animating. + The name of the \l Sprite that is currently animating. */ /*! \qmlproperty string QtQuick::SpriteSequence::goalSprite - The name of the Sprite which the animation should move to. + The name of the \l Sprite that the animation should move to. - Sprite states have defined durations and transitions between them, setting goalState - will cause it to disregard any path weightings (including 0) and head down the path - which will reach the goalState quickest (fewest animations). It will pass through + Sprite states have defined durations and transitions between them; setting \c goalSprite + will cause it to disregard any path weightings (including \c 0) and head down the path + that will reach the \c goalSprite quickest (fewest animations). It will pass through intermediate states on that path, and animate them for their duration. - If it is possible to return to the goalState from the starting point of the goalState - it will continue to do so until goalState is set to "" or an unreachable state. + If it is possible to return to the \c goalSprite from the starting point of the \c goalSprite, + it will continue to do so until \c goalSprite is set to \c "" or an unreachable state. */ /*! \qmlmethod QtQuick::SpriteSequence::jumpTo(string sprite) - This function causes the SpriteSequence to jump to the specified sprite immediately, intermediate - sprites are not played. The \a sprite argument is the name of the sprite you wish to jump to. + This function causes the SpriteSequence to jump to the specified \a sprite immediately; + intermediate sprites are not played. */ /*! \qmlproperty list QtQuick::SpriteSequence::sprites -- cgit v1.2.3 From 928a5d40377853aafbb2860feccf6d65bf81417b Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 12 Mar 2018 10:35:43 +0100 Subject: Improve AnimatedSprite's documentation Change-Id: Icc86afe67fd5a432798ce3173da51275bed1bf64 Reviewed-by: Shawn Rutledge Reviewed-by: Venugopal Shivashankar --- src/quick/items/qquickanimatedsprite.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 741e4607e5..190c48ac88 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -152,10 +152,10 @@ QT_BEGIN_NAMESPACE /*! \qmlproperty qreal QtQuick::AnimatedSprite::frameRate - Frames per second to show in the animation. Values equal to or below 0 are invalid. + Frames per second to show in the animation. Values less than or equal to \c 0 are invalid. - If frameRate is valid then it will be used to calculate the duration of the frames. - If not, and frameDuration is valid , then frameDuration will be used. + If \c frameRate is valid, it will be used to calculate the duration of the frames. + If not, and \l frameDuration is valid, \c frameDuration will be used. Changing this parameter will restart the animation. */ @@ -163,10 +163,10 @@ QT_BEGIN_NAMESPACE /*! \qmlproperty int QtQuick::AnimatedSprite::frameDuration - Duration of each frame of the animation in milliseconds. Values equal to or below 0 are invalid. + Duration of each frame of the animation in milliseconds. Values less than or equal to \c 0 are invalid. - If frameRate is valid then it will be used to calculate the duration of the frames. - If not, and frameDuration is valid, then frameDuration will be used. + If frameRate is valid, it will be used to calculate the duration of the frames. + If not, and \l frameDuration is valid, \c frameDuration will be used. Changing this parameter will restart the animation. */ @@ -218,21 +218,21 @@ QT_BEGIN_NAMESPACE /*! \qmlproperty bool QtQuick::AnimatedSprite::reverse - If true, then the animation will be played in reverse. + If \c true, the animation will be played in reverse. - Default is false. + Default is \c false. */ /*! \qmlproperty bool QtQuick::AnimatedSprite::frameSync - If true, then the animation will have no duration. Instead, the animation will advance + If \c true, the animation will have no duration. Instead, the animation will advance one frame each time a frame is rendered to the screen. This synchronizes it with the painting rate as opposed to elapsed time. If frameSync is set to true, it overrides both frameRate and frameDuration. - Default is false. + Default is \c false. Changing this parameter will restart the animation. */ @@ -242,9 +242,9 @@ QT_BEGIN_NAMESPACE After playing the animation this many times, the animation will automatically stop. Negative values are invalid. - If this is set to AnimatedSprite.Infinite the animation will not stop playing on its own. + If this is set to \c AnimatedSprite.Infinite the animation will not stop playing on its own. - Default is AnimatedSprite.Infinite + Default is \c AnimatedSprite.Infinite */ /*! @@ -252,13 +252,13 @@ QT_BEGIN_NAMESPACE When paused, the current frame can be advanced manually. - Default is false. + Default is \c false. */ /*! \qmlproperty int QtQuick::AnimatedSprite::currentFrame - When paused, the current frame can be advanced manually by setting this property or calling advance(). + When paused, the current frame can be advanced manually by setting this property or calling \l advance(). */ @@ -452,7 +452,7 @@ void QQuickAnimatedSprite::maybeUpdate() \qmlmethod int QtQuick::AnimatedSprite::pause() Pauses the sprite animation. This does nothing if - \l paused is true. + \l paused is \c true. \sa resume() */ @@ -471,7 +471,7 @@ void QQuickAnimatedSprite::pause() /*! \qmlmethod int QtQuick::AnimatedSprite::resume() - Resumes the sprite animation if \l paused is true; + Resumes the sprite animation if \l paused is \c true; otherwise, this does nothing. \sa pause() -- cgit v1.2.3 From 3339bf866a68ba129e88fd7d27a2abbea0d1c6ba Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Mar 2018 15:02:45 +0100 Subject: When deactivating a loader, do not immediately clear its context In 2eb2d6386da304cd1164264ae0bff685c796d89c, deactivating/clearing the loader would now prevent any subsequent bindings from being evaluated. The problem there was that the item created by the loader wouldn't have a parent item (among things) anymore, so references to it in the bindings would result in errors. The way to prevent it was done by invalidating the context of the item, which in turn would detach it from the root context. This is a problem if objects in the root context are referenced after deactivating/clearing the loader: onSomethingChanged: { loader.source = "" objectInRootContext.doIt() } This would result in a ReferenceError when resolving objectInRootContext and break the behavior present before the fix mentioned above. The correct way is to recursively clear the context set on all bindings, but leave everything in place. This way, no subsequent bindings will be evaluated, but the currently "running" scripts will still be able to reach the root context. Task-number: QTBUG-66822 Change-Id: Ic9c2ab0a752093a26967da4783cb4c29cf83d2ca Reviewed-by: Simon Hausmann Reviewed-by: Michael Brasser --- src/qml/qml/qqmlcontext.cpp | 8 +++ src/qml/qml/qqmlcontext_p.h | 1 + src/quick/items/qquickloader.cpp | 4 +- tests/auto/quick/qquickloader/data/rootContext.qml | 55 +++++++++++++++++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 61 ++++++++++++++++++++++ 5 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquickloader/data/rootContext.qml diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 6e43bc735f..5dd3278b4c 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -593,6 +593,14 @@ void QQmlContextData::invalidate() parent = nullptr; } +void QQmlContextData::clearContextRecursively() +{ + clearContext(); + + for (auto ctxIt = childContexts; ctxIt; ctxIt = ctxIt->nextChild) + ctxIt->clearContextRecursively(); +} + void QQmlContextData::clearContext() { emitDestruction(); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index ff36d6c9a8..5dfee48848 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -115,6 +115,7 @@ public: QQmlContextData(QQmlContext *); void emitDestruction(); void clearContext(); + void clearContextRecursively(); void invalidate(); inline bool isValid() const { diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 34f30e81a3..6960e16bd9 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -102,7 +102,7 @@ void QQuickLoaderPrivate::clear() // this we may get transient errors from use of 'parent', for example. QQmlContext *context = qmlContext(object); if (context) - QQmlContextData::get(context)->invalidate(); + QQmlContextData::get(context)->clearContextRecursively(); if (loadingFromSource && component) { // disconnect since we deleteLater @@ -363,7 +363,7 @@ void QQuickLoader::setActive(bool newVal) // this we may get transient errors from use of 'parent', for example. QQmlContext *context = qmlContext(d->object); if (context) - QQmlContextData::get(context)->invalidate(); + QQmlContextData::get(context)->clearContextRecursively(); if (d->item) { QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); diff --git a/tests/auto/quick/qquickloader/data/rootContext.qml b/tests/auto/quick/qquickloader/data/rootContext.qml new file mode 100644 index 0000000000..277db43d57 --- /dev/null +++ b/tests/auto/quick/qquickloader/data/rootContext.qml @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the manual tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: 360 + height: 360 + + property alias loader: loader + + Loader { + id: loader + } + + property Component component: Item { + property bool trigger: false + onTriggerChanged: { + objectInRootContext.doIt() // make sure we can resolve objectInRootContext + loader.active = false + objectInRootContext.doIt() // make sure we can STILL resolve objectInRootContext + anotherProperty = true // see if we can trigger subsequent signal handlers (we shouldn't) + } + property bool anotherProperty: false + onAnotherPropertyChanged: { + // this should never be executed + objectInRootContext.doIt() + } + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 30fc52f95a..7a176661e8 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -123,6 +124,8 @@ private slots: void bindings(); void parentErrors(); + + void rootContext(); }; Q_DECLARE_METATYPE(QList) @@ -1327,6 +1330,64 @@ void tst_QQuickLoader::parentErrors() QVERIFY2(warningsSpy.isEmpty(), qPrintable(failureMessage)); } +class ObjectInRootContext: public QObject +{ + Q_OBJECT + +public: + int didIt = 0; + +public slots: + void doIt() { + didIt += 1; + } +}; + +void tst_QQuickLoader::rootContext() +{ + QQmlEngine engine; + ObjectInRootContext objectInRootContext; + engine.rootContext()->setContextProperty("objectInRootContext", &objectInRootContext); + + QQmlComponent component(&engine, testFileUrl("rootContext.qml")); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QQuickLoader *loader = object->property("loader").value(); + QVERIFY(loader); + + QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList))); + + // Give the loader a component + loader->setSourceComponent(object->property("component").value()); + QTRY_VERIFY(loader->active()); + QTRY_VERIFY(loader->item()); + + QString failureMessage; + if (!warningsSpy.isEmpty()) { + QDebug stream(&failureMessage); + stream << warningsSpy.first().first().value>(); + } + QVERIFY2(warningsSpy.isEmpty(), qPrintable(failureMessage)); + QCOMPARE(objectInRootContext.didIt, 0); + + // Deactivate the loader, which deletes the item. + // Check that a) there are no errors, and b) the objectInRootContext can still be resolved even + // after deactivating the loader. If it cannot, a ReferenceError for objectInRootContext is + // generated (and the 'doIt' counter in objectInRootContext will be 1 for the call before + // the deactivation). + loader->item()->setProperty("trigger", true); + QTRY_VERIFY(!loader->active()); + QTRY_VERIFY(!loader->item()); + + if (!warningsSpy.isEmpty()) { + QDebug stream(&failureMessage); + stream << warningsSpy.first().first().value>(); + } + QVERIFY2(warningsSpy.isEmpty(), qPrintable(failureMessage)); + QCOMPARE(objectInRootContext.didIt, 2); +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" -- cgit v1.2.3 From d7b361bc33992ed61310b709df476cc4fa9f67e5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2018 17:15:41 +0100 Subject: Reduce the overhead of type loader locking Allow pulling the shared mutex out of the QQmlThread for the type loader so that the lock and unlock calls can be inlined. We do a lot more of those now. Task-number: QTBUG-41465 Change-Id: I42f3d17feb08863f51b003b061d89f49c5a6d574 Reviewed-by: Michael Brasser --- src/qml/qml/ftw/qqmlthread.cpp | 6 ++++++ src/qml/qml/ftw/qqmlthread_p.h | 2 ++ src/qml/qml/qqmltypeloader.cpp | 16 ++++------------ src/qml/qml/qqmltypeloader_p.h | 5 +++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index 78b477b472..96313d7627 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -57,6 +57,7 @@ public: void run() override; + inline QMutex &mutex() { return _mutex; } inline void lock() { _mutex.lock(); } inline void unlock() { _mutex.unlock(); } inline void wait() { _wait.wait(&_mutex); } @@ -263,6 +264,11 @@ bool QQmlThread::isShutdown() const return d->m_shutdown; } +QMutex &QQmlThread::mutex() +{ + return d->mutex(); +} + void QQmlThread::lock() { d->lock(); diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h index 0ed12a2972..b5c580fe8b 100644 --- a/src/qml/qml/ftw/qqmlthread_p.h +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE class QThread; +class QMutex; class QQmlThreadPrivate; class QQmlThread @@ -71,6 +72,7 @@ public: void shutdown(); bool isShutdown() const; + QMutex &mutex(); void lock(); void unlock(); void wakeOne(); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 61208d1c4a..5b954605e0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -983,16 +983,6 @@ void QQmlTypeLoader::setProfiler(QQmlProfiler *profiler) } #endif -void QQmlTypeLoader::lock() -{ - m_thread->lock(); -} - -void QQmlTypeLoader::unlock() -{ - m_thread->unlock(); -} - struct PlainLoader { void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const { @@ -1632,8 +1622,10 @@ bool QQmlTypeLoaderQmldirContent::designerSupported() const Constructs a new type loader that uses the given \a engine. */ QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) - : m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)), - m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) + : m_engine(engine) + , m_thread(new QQmlTypeLoaderThread(this)) + , m_mutex(m_thread->mutex()) + , m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) { } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 713f707387..df79d13f1c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -313,8 +313,8 @@ public: bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; - void lock(); - void unlock(); + void lock() { m_mutex.lock(); } + void unlock() { m_mutex.unlock(); } void load(QQmlDataBlob *, Mode = PreferSynchronous); void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous); @@ -381,6 +381,7 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; + QMutex &m_mutex; #if QT_CONFIG(qml_debug) QScopedPointer m_profiler; -- cgit v1.2.3 From ee89a8c052db0fa3dffe3e01c4c0309cf9ec80d0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 16 Mar 2018 11:15:38 +0100 Subject: Fix lookup of enums declared in QML singletons Given the following expression var x = MySingleton.MyEnumValue where MySingleton is a QML (composite) singleton and MyEnumValue comes from a QML declared enum, we had code in place up to (and including) 5.10 to attempt to optimize that expression to a enum constant at compile time. In 5.10 that optimization does not exist anymore. In <= 5.10 we would also skip the optimization under certain circumstances (too many statementes, etc.). The fallback that is in place for handling this at run-time tried to be smart by avoiding the QQmlContextWrapper::get lookup and return straight a reference to the singleton as QObject. That works for regular property lookups, but it fails when trying to look up something like an enum, that isn't a meta-object property. Change-Id: I1819b9d8ae06a3f595e067bf5b018c4065be76bb Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 6 ------ src/qml/compiler/qv4compileddata_p.h | 2 +- src/qml/compiler/qv4instr_moth.cpp | 4 ---- src/qml/compiler/qv4instr_moth_p.h | 6 ++---- src/qml/jit/qv4jit.cpp | 11 ----------- src/qml/jit/qv4jit_p.h | 1 - src/qml/jsruntime/qv4engine.cpp | 21 --------------------- src/qml/jsruntime/qv4engine_p.h | 1 - src/qml/jsruntime/qv4runtime.cpp | 7 ------- src/qml/jsruntime/qv4runtimeapi_p.h | 1 - src/qml/jsruntime/qv4vme_moth.cpp | 4 ---- .../lib/org/qtproject/MixedModule/SingletonType.qml | 3 +++ .../qml/qqmllanguage/data/usingTypeWithEnum.qml | 2 ++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 4 ++++ 14 files changed, 12 insertions(+), 61 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 237cd9bf3b..a9d86b24f5 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -2213,12 +2213,6 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot); return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex))); } else if (r.type.isValid()) { - if (r.type.isCompositeSingleton()) { - Instruction::LoadQmlSingleton load; - load.name = registerString(name); - bytecodeGenerator->addInstruction(load); - return Reference::fromAccumulator(this); - } return Reference::fromName(this, name); } else { Q_ASSERT(r.importNamespace); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 2c0320f7f1..f1776f5772 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x17 +#define QV4_DATA_STRUCTURE_VERSION 0x18 class QIODevice; class QQmlPropertyCache; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 34953d52ce..450fa50528 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -634,10 +634,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadQmlImportedScripts) d << dumpRegister(result, nFormals); MOTH_END_INSTR(LoadQmlImportedScripts) - - MOTH_BEGIN_INSTR(LoadQmlSingleton) - d << name; - MOTH_END_INSTR(LoadQmlSingleton) } } diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 2d1428bd19..7dd639c94c 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -174,7 +174,6 @@ QT_BEGIN_NAMESPACE #define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) #define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) -#define INSTR_LoadQmlSingleton(op) INSTRUCTION(op, LoadQmlSingleton, 1, name) #define FOR_EACH_MOTH_INSTR(F) \ @@ -290,9 +289,8 @@ QT_BEGIN_NAMESPACE F(Mod) \ F(Sub) \ F(LoadQmlContext) \ - F(LoadQmlImportedScripts) \ - F(LoadQmlSingleton) -#define MOTH_NUM_INSTRUCTIONS() (static_cast(Moth::Instr::Type::LoadQmlSingleton) + 1) + F(LoadQmlImportedScripts) +#define MOTH_NUM_INSTRUCTIONS() (static_cast(Moth::Instr::Type::LoadQmlImportedScripts) + 1) #if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) // icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp index 5dc98a591a..bc46c0ca1d 100644 --- a/src/qml/jit/qv4jit.cpp +++ b/src/qml/jit/qv4jit.cpp @@ -917,14 +917,6 @@ void BaselineJIT::generate_LoadQmlImportedScripts(int result) as->storeReg(result); } -void BaselineJIT::generate_LoadQmlSingleton(int name) -{ - as->prepareCallWithArgCount(2); - as->passInt32AsArg(name, 1); - as->passEngineAsArg(0); - JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlSingleton, Assembler::ResultInAccumulator); -} - void BaselineJIT::startInstruction(Instr::Type /*instr*/) { if (hasLabel()) @@ -1328,9 +1320,6 @@ void BaselineJIT::collectLabelsInBytecode() MOTH_BEGIN_INSTR(LoadQmlImportedScripts) MOTH_END_INSTR(LoadQmlImportedScripts) - - MOTH_BEGIN_INSTR(LoadQmlSingleton) - MOTH_END_INSTR(LoadQmlSingleton) } } #undef MOTH_BEGIN_INSTR diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h index c17ab4ff6e..5aebf78a8d 100644 --- a/src/qml/jit/qv4jit_p.h +++ b/src/qml/jit/qv4jit_p.h @@ -240,7 +240,6 @@ public: void generate_Sub(int lhs) override; void generate_LoadQmlContext(int result) override; void generate_LoadQmlImportedScripts(int result) override; - void generate_LoadQmlSingleton(int name) override; void startInstruction(Moth::Instr::Type instr) override; void endInstruction(Moth::Instr::Type instr) override; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5f59e1e809..5521633db7 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -758,27 +758,6 @@ QObject *ExecutionEngine::qmlScopeObject() const return ctx->qml()->scopeObject; } -ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name) -{ - QQmlContextData *ctx = callingQmlContext(); - if (!ctx->imports) - return Encode::undefined(); - // Search for attached properties, enums and imported scripts - QQmlTypeNameCache::Result r = ctx->imports->query(name); - - Q_ASSERT(r.isValid()); - Q_ASSERT(r.type.isValid()); - Q_ASSERT(r.type.isSingleton()); - - QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); - QQmlEngine *e = qmlEngine(); - siinfo->init(e); - - if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) - return QV4::QObjectWrapper::wrap(this, qobjectSingleton); - return QJSValuePrivate::convertedToValue(this, siinfo->scriptApi(e)); -} - QQmlContextData *ExecutionEngine::callingQmlContext() const { Heap::QmlContext *ctx = qmlContext(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 5edf89f720..c7fb743088 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -441,7 +441,6 @@ public: Heap::QmlContext *qmlContext() const; QObject *qmlScopeObject() const; - ReturnedValue qmlSingletonWrapper(String *name); QQmlContextData *callingQmlContext() const; diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 04cad8ddb7..2506777e76 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1326,13 +1326,6 @@ ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine) return context->importedScripts.value(); } -QV4::ReturnedValue Runtime::method_loadQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) -{ - Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return engine->qmlSingletonWrapper(name); -} - ReturnedValue Runtime::method_uMinus(const Value &value) { TRACE1(value); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 2956a4a463..91232256a9 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -184,7 +184,6 @@ struct ExceptionCheck { /* qml */ \ F(ReturnedValue, loadQmlContext, (NoThrowEngine *engine)) \ F(ReturnedValue, loadQmlImportedScripts, (NoThrowEngine *engine)) \ - F(ReturnedValue, loadQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index feeeee527a..e73365e9b1 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -1369,10 +1369,6 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) - MOTH_BEGIN_INSTR(LoadQmlSingleton) - acc = Runtime::method_loadQmlSingleton(static_cast(engine), name); - MOTH_END_INSTR(LoadQmlSingleton) - catchException: Q_ASSERT(engine->hasException); if (!exceptionHandler) { diff --git a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml index 7763c783f1..2913ceca08 100644 --- a/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml +++ b/tests/auto/qml/qqmllanguage/data/lib/org/qtproject/MixedModule/SingletonType.qml @@ -2,4 +2,7 @@ import QtQuick 2.0 pragma Singleton Item { + enum EnumInSingleton { + EnumValue = 42 + } } diff --git a/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml index 2509fc0df1..43e54bbf1d 100644 --- a/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml +++ b/tests/auto/qml/qqmllanguage/data/usingTypeWithEnum.qml @@ -1,8 +1,10 @@ import QtQuick 2.0 +import org.qtproject.MixedModule 1.0 QtObject { property int enumValue: TypeWithEnum.EnumValue2 property int enumValue2: -1 property int scopedEnumValue: TypeWithEnum.MyEnum.EnumValue3 + property int enumValueFromSingleton: { var x = SingletonType.EnumValue; return x; } Component.onCompleted: enumValue2 = TypeWithEnum.EnumValue1 } diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8bc631fbdd..f1f35f9fd4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -3752,6 +3752,9 @@ void tst_qqmllanguage::scopedEnum() void tst_qqmllanguage::qmlEnums() { + QQmlEngine engine; + engine.setImportPathList(QStringList(defaultImportPathList) << testFile("lib")); + { QQmlComponent component(&engine, testFileUrl("TypeWithEnum.qml")); QScopedPointer o(component.create()); @@ -3774,6 +3777,7 @@ void tst_qqmllanguage::qmlEnums() QCOMPARE(o->property("enumValue").toInt(), 1); QCOMPARE(o->property("enumValue2").toInt(), 0); QCOMPARE(o->property("scopedEnumValue").toInt(), 2); + QCOMPARE(o->property("enumValueFromSingleton").toInt(), 42); } } -- cgit v1.2.3 From 80b5f8c2f448be574e731e5d371c38b84578f5c3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2018 14:46:00 +0100 Subject: Add a test that verifies the this object in signal handlers Ask expected, this passes currently. The this object is set to the scope object in QQmlJavaScriptExpression::evaluate, which QQmlBoundSignalExpression::evaluate calls. Task-number: QTBUG-66942 Change-Id: I16a709768f9c798910377a52b5e882bb6d554a5f Reviewed-by: Ulf Hermann --- .../qml/qqmlecmascript/data/signalHandlers.qml | 13 +++++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 25 +++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml index cd68fb9b82..14326bb9e6 100644 --- a/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml +++ b/tests/auto/qml/qqmlecmascript/data/signalHandlers.qml @@ -102,4 +102,17 @@ QtObject { }) return testSuccess } + + property QtObject subObject: QtObject { + id: subObject + property int value + property bool ok: false + onValueChanged: this.ok = true + } + + function testThisInSignalHandler() { + subObject.ok = false + subObject.value = subObject.value + 1 + return subObject.ok + } } diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 5ff959515e..31cf40be16 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -6278,41 +6278,40 @@ void tst_qqmlecmascript::includeRemoteSuccess() void tst_qqmlecmascript::signalHandlers() { QQmlComponent component(&engine, testFileUrl("signalHandlers.qml")); - QObject *o = component.create(); + QScopedPointer o(component.create()); QVERIFY(o != nullptr); - QCOMPARE(o->property("count").toInt(), 0); - QMetaObject::invokeMethod(o, "testSignalCall"); + QMetaObject::invokeMethod(o.data(), "testSignalCall"); QCOMPARE(o->property("count").toInt(), 1); - QMetaObject::invokeMethod(o, "testSignalHandlerCall"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerCall"); QCOMPARE(o->property("count").toInt(), 1); QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function")); QCOMPARE(o->property("funcCount").toInt(), 0); - QMetaObject::invokeMethod(o, "testSignalConnection"); + QMetaObject::invokeMethod(o.data(), "testSignalConnection"); QCOMPARE(o->property("funcCount").toInt(), 1); - QMetaObject::invokeMethod(o, "testSignalHandlerConnection"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerConnection"); QCOMPARE(o->property("funcCount").toInt(), 2); - QMetaObject::invokeMethod(o, "testSignalDefined"); + QMetaObject::invokeMethod(o.data(), "testSignalDefined"); QCOMPARE(o->property("definedResult").toBool(), true); - QMetaObject::invokeMethod(o, "testSignalHandlerDefined"); + QMetaObject::invokeMethod(o.data(), "testSignalHandlerDefined"); QCOMPARE(o->property("definedHandlerResult").toBool(), true); QVariant result; - QMetaObject::invokeMethod(o, "testConnectionOnAlias", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testConnectionOnAlias", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(o, "testAliasSignalHandler", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testAliasSignalHandler", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - QMetaObject::invokeMethod(o, "testSignalWithClosureArgument", Q_RETURN_ARG(QVariant, result)); + QMetaObject::invokeMethod(o.data(), "testSignalWithClosureArgument", Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result.toBool(), true); + QMetaObject::invokeMethod(o.data(), "testThisInSignalHandler", Q_RETURN_ARG(QVariant, result)); QCOMPARE(result.toBool(), true); - - delete o; } void tst_qqmlecmascript::qtbug_37351() -- cgit v1.2.3 From 61447075954aab99b3abc9c78294e5966ae3b6ce Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 16 Mar 2018 14:51:15 +0100 Subject: Pass "this" object when evaluating debug jobs We have to explicitly specify the "this" object on QV4::Function::call, otherwise it will assume undefined or the QML global object. Task-number: QTBUG-66942 Change-Id: I1af7742b4fee1b49e9760a413834daf3edb15d74 Reviewed-by: Simon Hausmann --- .../qmltooling/qmldbg_debugger/qv4debugjob.cpp | 10 +++++-- .../qqmlnativedebugservice.cpp | 10 +++++-- src/qml/jsruntime/qv4script.cpp | 7 +++-- src/qml/jsruntime/qv4script_p.h | 2 +- .../qml/debugger/qv4debugger/tst_qv4debugger.cpp | 32 ++++++++++++++++++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index 5b049ab521..7950d21612 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -112,8 +112,14 @@ void JavaScriptJob::run() script.inheritContext = true; script.parse(); QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = script.run(); + if (!scope.engine->hasException) { + if (frame) { + QV4::ScopedValue thisObject(scope, frame->thisObject()); + result = script.run(thisObject); + } else { + result = script.run(); + } + } if (scope.engine->hasException) { result = scope.engine->catchException(); resultIsException = true; diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index 718975275a..e17fe92983 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -260,8 +260,14 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression) // That is a side-effect of inheritContext. script.inheritContext = true; script.parse(); - if (!m_engine->hasException) - return script.run(); + if (!m_engine->hasException) { + if (m_engine->currentStackFrame) { + QV4::ScopedValue thisObject(scope, m_engine->currentStackFrame->thisObject()); + script.run(thisObject); + } else { + script.run(); + } + } m_runningJob = false; return QV4::Encode::undefined(); diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index bb6608bec0..267c93952d 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -136,7 +136,7 @@ void Script::parse() } } -ReturnedValue Script::run() +ReturnedValue Script::run(const QV4::Value *thisObject) { if (!parsed) parse(); @@ -149,10 +149,11 @@ ReturnedValue Script::run() if (qmlContext.isUndefined()) { TemporaryAssignment savedGlobalCode(engine->globalCode, vmFunction); - return vmFunction->call(engine->globalObject, nullptr, 0, context); + return vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0, + context); } else { Scoped qml(valueScope, qmlContext.value()); - return vmFunction->call(nullptr, nullptr, 0, qml); + return vmFunction->call(thisObject, nullptr, 0, qml); } } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 24291b9aa6..cb03c6b064 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -93,7 +93,7 @@ struct Q_QML_EXPORT Script { bool parseAsBinding; void parse(); - ReturnedValue run(); + ReturnedValue run(const QV4::Value *thisObject = nullptr); Function *function(); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 5dd62da15a..4ce0f9fd89 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -316,6 +316,8 @@ private slots: void lastLineOfConditional_data(); void lastLineOfConditional(); + + void readThis(); private: QV4Debugger *debugger() const { @@ -865,6 +867,36 @@ void tst_qv4debugger::lastLineOfConditional() QCOMPARE(secondState.lineNumber, lastLine); } +void tst_qv4debugger::readThis() +{ + m_debuggerAgent->m_captureContextInfo = true; + QString script = + "var x = function() {\n" + " return this.a;\n" + "}.apply({a : 5}, []);\n"; + + TestAgent::ExpressionRequest request; + request.expression = "this"; + request.frameNr = 0; + request.context = -1; // no extra context + m_debuggerAgent->m_expressionRequests << request; + + debugger()->addBreakPoint("applyThis", 2); + evaluateJavaScript(script, "applyThis"); + QVERIFY(m_debuggerAgent->m_wasPaused); + + QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 1); + QJsonObject result0 = m_debuggerAgent->m_expressionResults[0]; + QCOMPARE(result0.value("type").toString(), QStringLiteral("object")); + QCOMPARE(result0.value("value").toInt(), 1); + QJsonArray properties = result0.value("properties").toArray(); + QCOMPARE(properties.size(), 1); + QJsonObject a = properties.first().toObject(); + QCOMPARE(a.value("name").toString(), QStringLiteral("a")); + QCOMPARE(a.value("type").toString(), QStringLiteral("number")); + QCOMPARE(a.value("value").toInt(), 5); +} + void tst_qv4debugger::redundancy_data() { QTest::addColumn("redundantRefs"); -- cgit v1.2.3 From d5c7229339a916b2f1f004ec4ea9de89995c003d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 16 Mar 2018 17:27:38 +0100 Subject: Restore the QV4_WRITE_PERF_MAP feature We want to be able to generate perf map files for JITed code. Task-number: QTBUG-67056 Change-Id: I56899e1dbf184083d94efe926d21fca4f9ea1e18 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4assembler.cpp | 47 ++++++++++++++++++--- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 68 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 186e5952da..72b057b2bc 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include +#include #include "qv4engine_p.h" #include "qv4assembler_p.h" @@ -1367,6 +1368,17 @@ static void printDisassembledOutputWithCalls(QByteArray processedOutput, qDebug("%s", processedOutput.constData()); } +static QByteArray functionName(Function *function) +{ + QByteArray name = function->name()->toQString().toUtf8(); + if (name.isEmpty()) { + name = QByteArray::number(reinterpret_cast(function), 16); + name.prepend("QV4::Function(0x"); + name.append(')'); + } + return name; +} + void Assembler::link(Function *function) { for (const auto &jumpTarget : pasm()->patches) @@ -1388,12 +1400,7 @@ void Assembler::link(Function *function) buf.open(QIODevice::WriteOnly); WTF::setDataFile(new QIODevicePrintStream(&buf)); - QByteArray name = function->name()->toQString().toUtf8(); - if (name.isEmpty()) { - name = QByteArray::number(quintptr(function), 16); - name.prepend("QV4::Function(0x"); - name.append(')'); - } + QByteArray name = functionName(function); codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data()); WTF::setDataFile(stderr); @@ -1404,6 +1411,34 @@ void Assembler::link(Function *function) function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef); function->jittedCode = reinterpret_cast(function->codeRef->code().executableAddress()); + +#if defined(Q_OS_LINUX) + // This implements writing of JIT'd addresses so that perf can find the + // symbol names. + // + // Perf expects the mapping to be in a certain place and have certain + // content, for more information, see: + // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt + static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); + if (doProfile) { + static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map") + .arg(QCoreApplication::applicationPid())); + static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly); + if (!isOpen) { + qWarning("QV4::JIT::Assembler: Cannot write perf map file."); + doProfile = false; + } else { + perfMapFile.write(QByteArray::number(reinterpret_cast( + codeRef.code().executableAddress()), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(QByteArray::number(static_cast(codeRef.size()), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(functionName(function)); + perfMapFile.putChar('\n'); + perfMapFile.flush(); + } + } +#endif } void Assembler::addLabel(int offset) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 8f3011001b..8ffaa96569 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -202,6 +202,7 @@ private slots: void malformedExpression(); void scriptScopes(); + void perfMapFile(); signals: void testSignal(); @@ -4130,6 +4131,73 @@ void tst_QJSEngine::scriptScopes() QCOMPARE(use.toInt(), 42); } +static const char *perfMapKey = "QV4_PROFILE_WRITE_PERF_MAP"; +static const char *jitCallKey = "QV4_JIT_CALL_THRESHOLD"; + +struct EnvironmentModifier { + const bool hasPerfMap = false; + const bool hasJitCall = false; + const QByteArray perfMap; + const QByteArray jitCall; + + EnvironmentModifier() : + hasPerfMap(qEnvironmentVariableIsSet(perfMapKey)), + hasJitCall(qEnvironmentVariableIsSet(jitCallKey)), + perfMap(qgetenv(perfMapKey)), + jitCall(qgetenv(jitCallKey)) + { + qputenv(perfMapKey, "1"); + qputenv(jitCallKey, "0"); + } + + ~EnvironmentModifier() + { + if (hasPerfMap) + qputenv(perfMapKey, perfMap); + else + qunsetenv(perfMapKey); + + if (hasJitCall) + qputenv(jitCallKey, jitCall); + else + qunsetenv(jitCallKey); + } +}; + +void tst_QJSEngine::perfMapFile() +{ +#if !defined(Q_OS_LINUX) + QSKIP("perf map files are only generated on linux"); +#else + EnvironmentModifier modifier; + Q_UNUSED(modifier); + QJSEngine engine; + QJSValue def = engine.evaluate("'use strict'; function foo() { return 42 }"); + QVERIFY(!def.isError()); + QJSValue use = engine.evaluate("'use strict'; foo()"); + QVERIFY(use.isNumber()); + QFile file(QString::fromLatin1("/tmp/perf-%1.map").arg(QCoreApplication::applicationPid())); + QVERIFY(file.exists()); + QVERIFY(file.open(QIODevice::ReadOnly)); + QList functions; + while (!file.atEnd()) { + const QByteArray contents = file.readLine(); + QVERIFY(contents.endsWith('\n')); + QList fields = contents.split(' '); + QCOMPARE(fields.length(), 3); + bool ok = false; + const qulonglong address = fields[0].toULongLong(&ok, 16); + QVERIFY(ok); + QVERIFY(address > 0); + const ulong size = fields[1].toULong(&ok, 16); + QVERIFY(ok); + QVERIFY(size > 0); + functions.append(fields[2]); + } + QVERIFY(functions.contains("foo\n")); +#endif +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3 From a3ad52526f79c1528f170c8affe5af00b68ca61d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2018 17:08:21 +0100 Subject: Fix crash when calling QQmlEngine::clearComponentCache() We must protect various resources in the type loader with our existing lock. The QQmlTypeLoaderQmldirContent is now value based, so that we can release the lock on the shared cache early. Copying it involves adjusting the refcount of the QHash and QString instances in the QQmlDirParser. The safety of this was verified with a TSAN build and the example supplied in the task. It crashed reliably with TASN errors first and with this patch it runs without errors. Task-number: QTBUG-41465 Change-Id: I616843c4b8bdfd65d1277d4faa8cb884d8e77df8 Reviewed-by: Lars Knoll --- src/qml/qml/qqmldirparser_p.h | 2 -- src/qml/qml/qqmlengine.cpp | 2 ++ src/qml/qml/qqmlimport.cpp | 62 +++++++++++++++++++++--------------------- src/qml/qml/qqmlimport_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 55 ++++++++++++++++++++----------------- src/qml/qml/qqmltypeloader_p.h | 9 ++++-- 6 files changed, 71 insertions(+), 61 deletions(-) diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 95370398ad..820c40238d 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qml/qqmldirparser_p.h @@ -63,8 +63,6 @@ class QQmlError; class QQmlEngine; class Q_QML_PRIVATE_EXPORT QQmlDirParser { - Q_DISABLE_COPY(QQmlDirParser) - public: QQmlDirParser(); ~QQmlDirParser(); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4054d2f0be..49f25e89fe 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1104,7 +1104,9 @@ QQmlEngine::~QQmlEngine() void QQmlEngine::clearComponentCache() { Q_D(QQmlEngine); + d->typeLoader.lock(); d->typeLoader.clearCache(); + d->typeLoader.unlock(); } /*! diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 8e6cbcbd7e..005db4248e 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -318,17 +318,17 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList *errors); + QQmlTypeLoaderQmldirContent *qmldir, QList *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); @@ -668,14 +668,14 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; - qmlDirComponents = qmldir->components(); + qmlDirComponents = qmldir.components(); - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already for (QList::const_iterator it = nameSpace->imports.constBegin(); @@ -1068,26 +1068,26 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList *errors) { - Q_ASSERT(qmldir); + Q_ASSERT(qmldir.hasContent()); if (qmlImportTrace()) qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: " << "loaded " << qmldirFilePath; - if (designerSupportRequired && !qmldir->designerSupported()) { + if (designerSupportRequired && !qmldir.designerSupported()) { if (errors) { QQmlError error; - error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir->typeNamespace())); + error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir.typeNamespace())); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } return false; } - int qmldirPluginCount = qmldir->plugins().count(); + int qmldirPluginCount = qmldir.plugins().count(); if (qmldirPluginCount == 0) return true; @@ -1098,7 +1098,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, // listed plugin inside qmldir. And for this reason, mixing dynamic and static plugins inside a // single module is not recommended. - QString typeNamespace = qmldir->typeNamespace(); + QString typeNamespace = qmldir.typeNamespace(); QString qmldirPath = qmldirFilePath; int slash = qmldirPath.lastIndexOf(Slash); if (slash > 0) @@ -1108,7 +1108,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, int staticPluginsFound = 0; #if defined(QT_SHARED) - const auto qmldirPlugins = qmldir->plugins(); + const auto qmldirPlugins = qmldir.plugins(); for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) { QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); if (!resolvedFilePath.isEmpty()) { @@ -1174,7 +1174,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, if (qmldirPluginCount > 1 && staticPluginsFound > 0) error.setDescription(QQmlImportDatabase::tr("could not resolve all plugins for module \"%1\"").arg(uri)); else - error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir->plugins()[dynamicPluginsFound].name)); + error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir.plugins()[dynamicPluginsFound].name)); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } @@ -1187,17 +1187,17 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList *errors) + QQmlTypeLoaderQmldirContent *qmldir, QList *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); *qmldir = typeLoader->qmldirContent(qmldirIdentifier); - if (*qmldir) { + if ((*qmldir).hasContent()) { // Ensure that parsing was successful - if ((*qmldir)->hasError()) { + if ((*qmldir).hasError()) { QUrl url = QUrl::fromLocalFile(qmldirIdentifier); - const QList qmldirErrors = (*qmldir)->errors(uri); + const QList qmldirErrors = (*qmldir).errors(uri); for (int i = 0; i < qmldirErrors.size(); ++i) { QQmlError error = qmldirErrors.at(i); error.setUrl(url); @@ -1323,14 +1323,14 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList *errors) { int lowest_min = INT_MAX; int highest_min = INT_MIN; typedef QQmlDirComponents::const_iterator ConstIterator; - const QQmlDirComponents &components = qmldir->components(); + const QQmlDirComponents &components = qmldir.components(); ConstIterator cend = components.constEnd(); for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) { @@ -1354,7 +1354,7 @@ bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent } typedef QList::const_iterator SConstIterator; - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); SConstIterator send = scripts.constEnd(); for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) { @@ -1446,14 +1446,14 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) @@ -1471,7 +1471,7 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri)); errors->prepend(error); return false; - } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) { + } else if ((vmaj >= 0) && (vmin >= 0) && qmldir.hasContent()) { // Verify that the qmldir content is valid for this version if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors)) return false; @@ -1565,12 +1565,12 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors)) @@ -1589,14 +1589,14 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & Q_ASSERT(nameSpace); if (QQmlImportInstance *import = nameSpace->findImport(uri)) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { + if (qmldir.hasContent()) { int vmaj = import->majversion; int vmin = import->minversion; - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) { diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index b70bb5253c..2437979ef8 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -85,7 +85,7 @@ struct QQmlImportInstance QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList *errors); static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5b954605e0..2b778b0b63 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1370,8 +1370,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1418,8 +1418,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1584,6 +1584,7 @@ QString QQmlTypeLoaderQmldirContent::typeNamespace() const void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { + m_hasContent = true; m_location = location; m_parser.parse(content); } @@ -1808,6 +1809,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) int lastSlash = path.lastIndexOf(QLatin1Char('/')); QString dirPath(path.left(lastSlash)); + LockHolder holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache *entry = exists ? new QCache : nullptr; @@ -1871,6 +1873,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) --length; QString dirPath(path.left(length)); + LockHolder holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache *files = exists ? new QCache : nullptr; @@ -1889,8 +1892,10 @@ Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQm It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent QQmlTypeLoader::qmldirContent(const QString &filePathIn) { + LockHolder holder(this); + QString filePath; // Try to guess if filePathIn is already a URL. This is necessarily fragile, because @@ -1904,39 +1909,39 @@ const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString & filePath = filePathIn; } else { filePath = QQmlFile::urlToLocalFileOrQrc(url); - if (filePath.isEmpty()) // Can't load the remote here, but should be cached - return *(m_importQmlDirCache.value(filePathIn)); + if (filePath.isEmpty()) { // Can't load the remote here, but should be cached + if (auto entry = m_importQmlDirCache.value(filePathIn)) + return **entry; + else + return QQmlTypeLoaderQmldirContent(); + } } - QQmlTypeLoaderQmldirContent *qmldir; QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); - if (!val) { - qmldir = new QQmlTypeLoaderQmldirContent; + if (val) + return **val; + QQmlTypeLoaderQmldirContent *qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"")) - QFile file(filePath); - if (!QQml_isFileCaseCorrect(filePath)) { - ERROR(CASE_MISMATCH_ERROR.arg(filePath)); - } else if (file.open(QFile::ReadOnly)) { - QByteArray data = file.readAll(); - qmldir->setContent(filePath, QString::fromUtf8(data)); - } else { - ERROR(NOT_READABLE_ERROR.arg(filePath)); - } + QFile file(filePath); + if (!QQml_isFileCaseCorrect(filePath)) { + ERROR(CASE_MISMATCH_ERROR.arg(filePath)); + } else if (file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + qmldir->setContent(filePath, QString::fromUtf8(data)); + } else { + ERROR(NOT_READABLE_ERROR.arg(filePath)); + } #undef ERROR #undef NOT_READABLE_ERROR #undef CASE_MISMATCH_ERROR - m_importQmlDirCache.insert(filePath, qmldir); - } else { - qmldir = *val; - } - - return qmldir; + m_importQmlDirCache.insert(filePath, qmldir); + return *qmldir; } void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index df79d13f1c..4665d342c1 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -228,12 +228,16 @@ class QQmlTypeLoaderQmldirContent { private: friend class QQmlTypeLoader; - QQmlTypeLoaderQmldirContent(); void setContent(const QString &location, const QString &content); void setError(const QQmlError &); public: + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; + + bool hasContent() const { return m_hasContent; } bool hasError() const; QList errors(const QString &uri) const; @@ -250,6 +254,7 @@ public: private: QQmlDirParser m_parser; QString m_location; + bool m_hasContent = false; }; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader @@ -304,7 +309,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); -- cgit v1.2.3 From 958e412a25523cc031564faae81c569aa6c3b01f Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 19 Mar 2018 10:47:24 +0100 Subject: QML debugger: Don't crash when creating objects on engine destruction You can create further objects while the QML engine is being destroyed. The debug service is not interested in those because they will be rather short lived anyway. Task-number: QTBUG-62458 Change-Id: If5395ef058268e0e956d159bc636495da1c0c98f Reviewed-by: Simon Hausmann --- .../qmldbg_debugger/qqmlenginedebugservice.cpp | 3 ++- .../tst_qqmlenginedebugservice.cpp | 24 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 288ad243ce..236109d041 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -812,7 +812,8 @@ void QQmlEngineDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) void QQmlEngineDebugServiceImpl::objectCreated(QJSEngine *engine, QObject *object) { Q_ASSERT(engine); - Q_ASSERT(m_engines.contains(engine)); + if (!m_engines.contains(engine)) + return; int engineId = QQmlDebugService::idForObject(engine); int objectId = QQmlDebugService::idForObject(object); diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index d01d9a6791..89217e7556 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -141,6 +141,7 @@ private slots: void queryObjectWithNonStreamableTypes(); void asynchronousCreate(); void invalidContexts(); + void createObjectOnDestruction(); }; QmlDebugObjectReference tst_QQmlEngineDebugService::findRootObject( @@ -1345,6 +1346,29 @@ void tst_QQmlEngineDebugService::invalidContexts() QCOMPARE(m_dbg->rootContext().contexts.count(), 0); } +void tst_QQmlEngineDebugService::createObjectOnDestruction() +{ + QSignalSpy spy(m_dbg, SIGNAL(newObject(int))); + { + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQml 2.0;" + "QtObject {" + "property Component x:" + "Qt.createQmlObject('import QtQml 2.0; Component { QtObject { } }'," + "this, 'x.qml');" + "Component.onDestruction: x.createObject(this, {});" + "}", QUrl::fromLocalFile("x.qml")); + QVERIFY(component.isReady()); + QVERIFY(component.create()); + QTRY_COMPARE(spy.count(), 2); + } + // Doesn't crash and doesn't give us another signal for the object created on destruction. + QTest::qWait(500); + QCOMPARE(spy.count(), 2); +} + int main(int argc, char *argv[]) { int _argc = argc + 1; -- cgit v1.2.3 From e1d32c80665c7d90a21138b26cb74dbfc86a63ba Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2018 13:52:07 +0100 Subject: Fix CONFIG+=qtquickcompiler with Q_CLEANUP_RESOURCE As we provide the init resources wrapper, we must also provide the cleanup wrapper. Change-Id: I7e45ae48ba955e70ffd8e253d4d2c15d0a50dabe Task-number: QTBUG-67087 Reviewed-by: Lars Knoll --- tools/qmlcachegen/generateloader.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp index 1a0b987c64..a2e673d15a 100644 --- a/tools/qmlcachegen/generateloader.cpp +++ b/tools/qmlcachegen/generateloader.cpp @@ -358,15 +358,23 @@ bool generateLoader(const QStringList &compiledFiles, const QString &outputFileN originalResourceFile.truncate(mappingSplit); } - const QString function = QLatin1String("qInitResources_") + qtResourceNameForFile(originalResourceFile); + const QString suffix = qtResourceNameForFile(originalResourceFile); + const QString initFunction = QLatin1String("qInitResources_") + suffix; - stream << QStringLiteral("int QT_MANGLE_NAMESPACE(%1)() {\n").arg(function); + stream << QStringLiteral("int QT_MANGLE_NAMESPACE(%1)() {\n").arg(initFunction); stream << " ::unitRegistry();\n"; if (!newResourceFile.isEmpty()) stream << " Q_INIT_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n"; stream << " return 1;\n"; stream << "}\n"; - stream << "Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(" << function << "));\n"; + stream << "Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(" << initFunction << "));\n"; + + const QString cleanupFunction = QLatin1String("qCleanupResources_") + suffix; + stream << QStringLiteral("int QT_MANGLE_NAMESPACE(%1)() {\n").arg(cleanupFunction); + if (!newResourceFile.isEmpty()) + stream << " Q_CLEANUP_RESOURCE(" << qtResourceNameForFile(newResourceFile) << ");\n"; + stream << " return 1;\n"; + stream << "}\n"; } } -- cgit v1.2.3 From f495d4b660107536d0a67ba48e88550278f13893 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 19 Mar 2018 15:07:28 +0100 Subject: Fix out of bounds reads in Array.concat In some cases, when our simple array data had an offset and data would wrap around, ArrayData::append would write out of bounds data into the new array, leading to crashes. Task-number: QTBUG-51581 Change-Id: I55172542ef0b94d263cfc9a17d7ca49ec6c3a565 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata.cpp | 2 +- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index 30c8527f21..b9c0e12305 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -617,7 +617,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) uint toCopy = n; uint chunk = toCopy; if (chunk > os->values.alloc - os->offset) - chunk -= os->values.alloc - os->offset; + chunk = os->values.alloc - os->offset; obj->arrayPut(oldSize, os->values.data() + os->offset, chunk); toCopy -= chunk; if (toCopy) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 8ffaa96569..8a017fd573 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -128,6 +128,7 @@ private slots: void JSONparse(); void arraySort(); void lookupOnDisappearingProperty(); + void arrayConcat(); void qRegExpInport_data(); void qRegExpInport(); @@ -3009,6 +3010,19 @@ void tst_QJSEngine::lookupOnDisappearingProperty() QVERIFY(func.call(QJSValueList()<< o).isUndefined()); } +void tst_QJSEngine::arrayConcat() +{ + QJSEngine eng; + QJSValue v = eng.evaluate("var x = [1, 2, 3, 4, 5, 6];" + "var y = [];" + "for (var i = 0; i < 5; ++i)" + " x.shift();" + "for (var i = 10; i < 13; ++i)" + " x.push(i);" + "x.toString();"); + QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12")); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() -- cgit v1.2.3 From f01edde1f282b0de2112ddea31853d876d3c5727 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 13:51:14 +0100 Subject: Fix grammar in "Qt Quick Best Practices" Change-Id: I3ba63110a3674675f44feca51ba98128844e1904 Reviewed-by: Simon Hausmann --- src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 6327ea67e6..82b5a10b60 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -28,7 +28,7 @@ /*! \page qtquick-bestpractices.html \title Qt Quick Best Practices -\brief Lists the practices that works best for Qt Quick +\brief Lists the practices that work best for Qt Quick Besides all the benefits that Qt Quick offers, it can be challenging in certain situations. For example, a Qt Quick application with a large codebase can be @@ -48,7 +48,7 @@ controls are also available with Qt Quick Controls 2. They cater to the most common use cases without any change, and offer a lot more possibilities with their customization options. In particular, Qt Quick Controls 2 provides styling options that align with the latest UI design trends. If these UI controls do not -satisfy to your application's needs, only then it is recommended to create a +satisfy your application's needs, only then it is recommended to create a custom control. -- cgit v1.2.3 From a07d2f21a4d445fae7a7cc130e45a8fd72997713 Mon Sep 17 00:00:00 2001 From: Gatis Paeglis Date: Mon, 19 Mar 2018 12:01:06 +0100 Subject: tests: un-blacklist tst_qquickitem::ignoreButtonPressNotInAcceptedMouseButtons() This change reverts 8740f35e69 and 8ef46910fc. The qtestlib limitation was fixed by b3e91b66b9175c1c3ff5f73f3ac231f74f9bf932 Task-number: QTBUG-63957 Change-Id: I2e12b1eff25c5ea8005db0893477a9732f24d211 Reviewed-by: Liang Qi --- tests/auto/quick/qquickitem/BLACKLIST | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/auto/quick/qquickitem/BLACKLIST b/tests/auto/quick/qquickitem/BLACKLIST index f00b061356..d94a3ef102 100644 --- a/tests/auto/quick/qquickitem/BLACKLIST +++ b/tests/auto/quick/qquickitem/BLACKLIST @@ -1,4 +1,2 @@ [contains:hollow square: testing points inside] xcb -[ignoreButtonPressNotInAcceptedMouseButtons] -* -- cgit v1.2.3 From 1de4d413c9dd5c8ad5859f944d9ded7d8778f777 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2018 17:01:21 +0100 Subject: Fix assigning objects to QJSValue properties We support simple object bindings such as someProperty: Rectangle { ... } when the type of "someProperty" is QVariant, but we produce an error when it's QJSValue. There is no good reason for that, and the fix for QTBUG-67118 requires this. Change-Id: Ia5dc88749bcba0b5c781a6ab2b4a9fb92299e0ac Reviewed-by: Mitch Curtis Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertyvalidator.cpp | 2 +- src/qml/qml/qqmlobjectcreator.cpp | 12 ++++++++++++ tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml | 1 + tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 00bb694ef4..ffd3b5975a 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -653,7 +653,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (property->propType() == QMetaType::QVariant) { + } else if (property->propType() == QMetaType::QVariant || property->propType() == qMetaTypeId()) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 90f3beb40b..7051fb51da 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,6 +55,7 @@ #include #include #include +#include QT_USE_NAMESPACE @@ -1039,6 +1040,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper argv[0] = &value; QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } + } else if (bindingProperty->propType() == qMetaTypeId()) { + QV4::Scope scope(v4); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); + if (bindingProperty->isVarProperty()) { + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); + } else { + QJSValue value; + QJSValuePrivate::setValue(&value, v4, wrappedObject); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); + } } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); diff --git a/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml b/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml index fce248a381..69b0096a5e 100644 --- a/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml +++ b/tests/auto/qml/qqmllanguage/data/assignLiteralToJSValue.qml @@ -30,6 +30,7 @@ QtObject { MyQmlObject { id: testObj22; objectName: "test22"; qjsvalue: null }, MyQmlObject { id: testObj1Bound; objectName: "test1Bound"; qjsvalue: testObj1.qjsvalue + 4 }, // 1 + 4 + 4 = 9 MyQmlObject { id: testObj20Bound; objectName: "test20Bound"; qjsvalue: testObj20.qjsvalue(testObj1Bound.qjsvalue) }, // 9 * 3 = 27 + MyQmlObject { id: testObj23; objectName: "test23"; qjsvalue: QtObject { objectName: "blah" } }, QtObject { id: varProperties objectName: "varProperties" diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index f1f35f9fd4..18b1718ddf 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -934,6 +934,11 @@ void tst_qqmllanguage::assignLiteralToJSValue() QJSValue value = object->qjsvalue(); QVERIFY(value.isNumber()); QCOMPARE(value.toNumber(), qreal(27)); + } { + MyQmlObject *object = root->findChild("test23"); + QJSValue value = object->qjsvalue(); + QVERIFY(value.isQObject()); + QCOMPARE(value.toQObject()->objectName(), "blah"); } } -- cgit v1.2.3 From a4adaee3a8c6a1048bbfe8286fef9009150c781c Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 14:27:32 +0100 Subject: Doc: reorganize "Bundle Application Resources" section Start off with the "direct" syntax, as that's the most straight-forward. Then, explain why it can be inefficient and introduce the concept of separate .qrc files. Change-Id: I63c2c3e188db04ed58e816f7e69ab98a42196ff1 Reviewed-by: hjk Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 48 ++++++++++++++-------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 82b5a10b60..20611f5b04 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -80,34 +80,46 @@ rich user experience. It can often be a challenge to make these resources available to the application regardless of the target OS. Most popular OS-es employ stricter security policies that restrict access to the file system, making it harder to load these resources. As an alternative, Qt offers its own -resource system that is built into the application binary, enabling access to -the application's resources regardless of the target OS. +\l {The Qt Resource System}{resource system} that is built into the +application binary, enabling access to the application's resources regardless +of the target OS. -It is recommended to bundle your application's resources (including the -\c .qml files) into a resource file (\c.qrc). For example, the following entry -in the qmake project file ensures that the resources are built into the -application binary, making them available when needed: +For example, the following entry in the qmake project file ensures that the +resources are built into the application binary, making them available when +needed: \badcode - RESOURCES += resources.qrc + RESOURCES += a.qml b.png \endcode -If your application depends on a limited number of resources, you could list -them directly in the project file. +It's also possible to use a +\l {files(pattern[, recursive=false])}{wildcard syntax} to select several files +at once: \badcode - RESOURCES += a.qml b.png + RESOURCES += $$files(*.qml) $$files(*.png) +\endcode + +This approach is convenient for applications that depend on a limited number +of resources. However, whenever a new file is added to \c RESOURCES using this +approach, it causes \e all of the other files in \c RESOURCES to be recompiled +as well. This can be inefficient, especially for large sets of files. +In this case, a better approach is to separate each type of resource into its +own \l {Resource Collection Files (.qrc)}{.qrc} file. For example, the snippet +above could be changed like so: + +\badcode + RESOURCES += qml.qrc images.qrc \endcode -In such a case, qmake creates the \c qmake_intermediate.qrc build artifact, -which you could rename and use the \c{RESOURCES += resource-set.qrc} entry -instead. +Now, whenever a QML file is changed in qml.qrc, only the QML files have to be +recompiled. -You could go a step further by using one \c .qrc file for each resource type. -For example, list the \c .qml files in \c files.qrc, images in -\c images.qrc, fonts in \c fonts.qrc, and so on. That way, you need not -recompile the QML files when you, for example, add an image to the list in -\c images.qrc. +If you want to conveniently switch from using the "direct" syntax to a .qrc +file, look for \c qmake_intermediate.qrc (a build artifact created by qmake) +in your project's build directory and rename it to, for example, +\c resources.qrc. Then, replace the old \c RESOURCES entry with +\c {RESOURCES += resources.qrc}. \section2 Related Information \list -- cgit v1.2.3 From 0f719a8fa5a91945e698af4f7295256653ef78e5 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 14:39:23 +0100 Subject: Doc: simplify "Qt Quick Best Practices" introduction paragraph We don't need to mention specific challenges in the introduction. Change-Id: I03eec5fe543fbf0b1859850424eba8cfa9f134a5 Reviewed-by: J-P Nurmi Reviewed-by: Simon Hausmann --- src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 20611f5b04..20ea8ec48c 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -30,10 +30,10 @@ \title Qt Quick Best Practices \brief Lists the practices that work best for Qt Quick -Besides all the benefits that Qt Quick offers, it can be challenging in certain -situations. For example, a Qt Quick application with a large codebase can be -painful to maintain if not organized well. The following sections elaborate -on some of the best practices that will help you get better results. +Despite all of the benefits that Qt Quick offers, it can be challenging in +certain situations. The following sections elaborate on some of the best +practices that will help you get better results when developing Qt Quick +applications. \section1 Custom UI Controls -- cgit v1.2.3 From 971292128b292052ef935da67a5d04fb5a3753f4 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 15:46:28 +0100 Subject: Doc: improve "Separate UI from Logic" section Change-Id: I0d36e009b4551e45e5e7fda6c95fc3fbfabfe1a5 Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 48 ++++++++++++++-------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 20ea8ec48c..9d411a581f 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -126,33 +126,47 @@ in your project's build directory and rename it to, for example, \li \l{The Qt Resource System} \endlist -\section1 Application UI and Business Logic +\section1 Separate UI from Logic One of the key goals that most application developers want to achieve is to create a maintainable application. One of the ways to achieve this goal is -to separate the UI from the business logic. The following are a few reasons -why application's UI should be in QML: +to separate the user interface from the business logic. The following are a few +reasons why an application's UI should be written in QML: \list - \li QML is a declarative language, which suits best for defining UIs. - \li It's easier to embed JavaScript in QML to respond to events, for example. - \li QML is faster to code as it is not strongly typed. + \li Declarative languages are strongly suited for defining UIs. + \li QML code is simpler to write, as it is less verbose than C++, and is not + strongly typed. This also results in it being an excellent language to + prototype in, a quality that is vital when collaborating with designers, + for example. + \li JavaScript can easily be used in QML to respond to events. \endlist -On the other hand, C++ being a strongly typed language, suits best for defining -business logic. Typically, such code performs tasks such as complex calculations -or larger data processing, which can be faster with C++ than with QML. +Being a strongly typed language, C++ is best suited for an application's logic. +Typically, such code performs tasks such as complex calculations +or data processing, which are faster in C++ than QML. Qt offers various approaches to integrate QML and C++ code in an application. -In most cases, the C++ part (business logic) provides the data model to the QML -part (UI), which presents it in a readable form. It is often a challenge to -decide when to use this approach. It is recommended to use QML -if the data model is static, simple, and small, as C++ could be overkill. -Use C++ if the application depends on a dynamic, large, and complex data model. +A typical use case is displaying a list of data in a user interface. +If the data set is static, simple, and/or small, a model written in QML can be +sufficient. -\omit -examples snippets of simpler and complex data models. -\endomit +The following snippet demonstrates examples of models written in QML: + +\qml + model: ListModel { + ListElement { name: "Item 1" } + ListElement { name: "Item 2" } + ListElement { name: "Item 3" } + } + + model: [ "Item 1", "Item 2", "Item 3" ] + + model: 10 +\endqml + +Use \l {QAbstractItemModel Subclass}{C++} for dynamic data sets that are large +or frequently modified. \section2 Interaction Path -- cgit v1.2.3 From 22b13921f8067f8a93164875a4ad59bed85b0400 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 19 Mar 2018 13:14:13 +0100 Subject: Handle function expressions as signal handlers There are two ways to use function expressions on the right-hand side of bindings: property var somethingPressed somethingPressed: function() { /* ..press something else.. */ } signal buttonPressed onButtonPressed: function() { /* ..handle buttonPress.. */ } In the former case, it declares a property that holds a function. So on initialization, the right-hand side of the binding returns a closure that gets assigned to the property 'somethingPressed'. In the latter case, the signal handler is explicitly marked as a function for clarity. So, the handler should not be returning the closure, but the handler should *be* the closure. In general, it is not possible to detect if the left-hand side is a property or a signal handler when generating QML cache files ahead of time. So for this case, we mark the function as only returning a closure. Then when instantiating the object, we check if it is a signal handler, and if the handler is marked as only returning a closure. If so, we set that closure to be the signal handler. Task-number: QTBUG-57043 Task-number: QTBUG-50328 Change-Id: I3008ddd847e30b7d0adef07344a326f84d85f1ba Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 8 ++++ src/qml/compiler/qv4compileddata_p.h | 2 +- src/qml/compiler/qv4compiler.cpp | 4 +- src/qml/compiler/qv4compilercontext_p.h | 1 + src/qml/jsruntime/qv4function_p.h | 7 +++ src/qml/qml/qqmlobjectcreator.cpp | 8 ++++ src/qml/types/qqmlconnections.cpp | 14 ++++-- tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 64 ++++++++++++++++++++++++++ 8 files changed, 103 insertions(+), 5 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index bc4ca5d6f4..a262908960 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2075,6 +2075,14 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function + // When a user writes the following QML signal binding: + // onSignal: function() { doSomethingUsefull } + // we will generate a binding function that just returns the closure. However, that's not useful + // at all, because if the onSignal is a signal handler, the user is actually making it explicit + // that the binding is a function, so we should execute that. However, we don't know that during + // AOT compilation, so mark the surrounding function as only-returning-a-closure. + _context->returnsClosure = cast(ast) && cast(cast(ast)->expression); + BytecodeGenerator bytecode(_context->line, _module->debugMode); BytecodeGenerator *savedBytecodeGenerator; savedBytecodeGenerator = bytecodeGenerator; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index f1776f5772..e6c4225bbd 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -225,7 +225,7 @@ struct Function quint32_le localsOffset; quint32_le nLineNumbers; quint32_le lineNumberOffset; - quint32_le nInnerFunctions; + quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers quint32_le nRegisters; Location location; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index f2e1f4a0de..ccc909c199 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -305,6 +305,9 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->flags |= CompiledData::Function::IsStrict; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; + function->nestedFunctionIndex = + irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first())) + : std::numeric_limits::max(); function->nFormals = irFunction->arguments.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); @@ -317,7 +320,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->lineNumberOffset = currentOffset; currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nInnerFunctions = irFunction->nestedContexts.size(); function->nRegisters = irFunction->registerCount; diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index a78a66db52..8fabf41c40 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -144,6 +144,7 @@ struct Context { bool usesThis = false; bool hasTry = false; bool hasWith = false; + bool returnsClosure = false; mutable bool argumentsCanEscape = false; enum UsesArgumentsObject { diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 4c8c790ca7..59a94e5dde 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -105,6 +105,13 @@ struct Q_QML_EXPORT Function { { return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } + + Function *nestedFunction() const + { + if (compiledFunction->nestedFunctionIndex == std::numeric_limits::max()) + return nullptr; + return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; + } }; } diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7051fb51da..7c36f59035 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -879,6 +879,14 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + + // When a user writes the following: + // onSignal: function() { doSomethingUsefull } + // then do not run the binding that returns the closure, but run the closure + // instead. + if (auto closure = runtimeFunction->nestedFunction()) + runtimeFunction = closure; + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index a43562a7b8..d1a7aa9b6f 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -288,9 +288,17 @@ void QQmlConnections::connectSignals() new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this)); signal->setEnabled(d->enabled); - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : nullptr; + auto f = d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = f->nestedFunction()) + f = closure; + + QQmlBoundSignalExpression *expression = + ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) + : nullptr; signal->takeExpression(expression); d->boundsignals += signal; } else { diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 2f5ff0022e..e36edca699 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -47,6 +47,7 @@ private slots: void signalHandlerParameters(); void errorOnArgumentsInSignalHandler(); void aheadOfTimeCompilation(); + void functionExpressions(); void workerScripts(); }; @@ -292,6 +293,69 @@ void tst_qmlcachegen::workerScripts() QTRY_VERIFY(obj->property("success").toBool()); } +void tst_qmlcachegen::functionExpressions() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile( + "test.qml", + "import QtQuick 2.0\n" + "Item {\n" + " id: di\n" + " \n" + " property var f\n" + " property bool f_called: false\n" + " f : function() { f_called = true }\n" + " \n" + " signal g\n" + " property bool g_handler_called: false\n" + " onG: function() { g_handler_called = true }\n" + " \n" + " signal h(int i)\n" + " property bool h_connections_handler_called: false\n" + " Connections {\n" + " target: di\n" + " onH: function(magic) { h_connections_handler_called = (magic == 42)\n }\n" + " }\n" + " \n" + " function runTest() { \n" + " f()\n" + " g()\n" + " h(42)\n" + " }\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + QCOMPARE(obj->property("f_called").toBool(), false); + QCOMPARE(obj->property("g_handler_called").toBool(), false); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), false); + + QMetaObject::invokeMethod(obj.data(), "runTest"); + + QCOMPARE(obj->property("f_called").toBool(), true); + QCOMPARE(obj->property("g_handler_called").toBool(), true); + QCOMPARE(obj->property("h_connections_handler_called").toBool(), true); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" -- cgit v1.2.3 From 64e90f393146dadb382d154e7d67a9109ab2492a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Mar 2018 16:40:38 +0100 Subject: Fix QML data structure version checking for ahead-of-time generated files We must also do version checking for QML and JS files that were compiled ahead of time and are embedded in resources. If the lookup for the original source code fails, then we must generate an appropriate error message. As an upside we get better error reporting when trying to load an empty file and Qt.include() now reports the error message in the statusText field. The error reporting for imported scripts was not changed as importing an empty script is (oddly) allowed. Task-number: QTBUG-66986 Change-Id: Ie0ef81af371a51ecf8c66ae7954d43f5cc6c12de Reviewed-by: Erik Verbruggen --- src/qml/compiler/qv4compilationunitmapper.cpp | 32 --------------- src/qml/compiler/qv4compilationunitmapper_p.h | 2 - src/qml/compiler/qv4compilationunitmapper_unix.cpp | 2 +- src/qml/compiler/qv4compilationunitmapper_win.cpp | 2 +- src/qml/compiler/qv4compileddata.cpp | 38 ++++++++++++++++++ src/qml/compiler/qv4compileddata_p.h | 2 + src/qml/jsruntime/qv4include.cpp | 10 +++-- src/qml/jsruntime/qv4include_p.h | 3 +- src/qml/jsruntime/qv4script.cpp | 17 ++++++-- src/qml/jsruntime/qv4script_p.h | 2 +- src/qml/qml/qqmlmetatype.cpp | 35 +++++++++++++++- src/qml/qml/qqmlmetatype_p.h | 12 +++++- src/qml/qml/qqmltypeloader.cpp | 38 ++++++++++++++---- src/qml/qml/qqmltypeloader_p.h | 5 +++ src/qml/types/qquickworkerscript.cpp | 6 ++- tests/auto/qml/qmlcachegen/qmlcachegen.pro | 2 + tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 46 ++++++++++++++++++++++ tests/auto/qml/qmlcachegen/versionchecks.qml | 4 ++ .../qml/qqmlecmascript/data/include_callback.js | 2 +- tests/auto/qml/qqmllanguage/data/empty.errors.txt | 3 +- 20 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 tests/auto/qml/qmlcachegen/versionchecks.qml diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index d94f7ac238..350f6f9485 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -59,36 +59,4 @@ CompilationUnitMapper::~CompilationUnitMapper() close(); } -bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString) -{ - if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) { - *errorString = QStringLiteral("Magic bytes in the header do not match"); - return false; - } - - if (header->version != quint32(QV4_DATA_STRUCTURE_VERSION)) { - *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(header->version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); - return false; - } - - if (header->qtVersion != quint32(QT_VERSION)) { - *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(header->qtVersion, 0, 16).arg(QT_VERSION, 0, 16); - return false; - } - - if (header->sourceTimeStamp) { - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!sourceTimeStamp.isValid()) - sourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - - if (sourceTimeStamp.isValid() && sourceTimeStamp.toMSecsSinceEpoch() != header->sourceTimeStamp) { - *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); - return false; - } - } - - return true; -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h index b24f98df7c..80f914c141 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -72,8 +72,6 @@ public: void close(); private: - static bool verifyHeader(const QV4::CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString); - #if defined(Q_OS_UNIX) size_t length; #endif diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp index 38dabc41cf..8348613888 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -73,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!verifyHeader(&header, sourceTimeStamp, errorString)) + if (!header.verifyHeader(sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index d7a93ae233..8b000021f8 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -87,7 +87,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!verifyHeader(&header, sourceTimeStamp, errorString)) + if (!header.verifyHeader(sourceTimeStamp, errorString)) return nullptr; const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index cc11b250f3..6220c1bc11 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -755,6 +755,44 @@ void Unit::generateChecksum() #endif } +bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const +{ +#ifndef V4_BOOTSTRAP + if (strncmp(magic, CompiledData::magic_str, sizeof(magic))) { + *errorString = QStringLiteral("Magic bytes in the header do not match"); + return false; + } + + if (version != quint32(QV4_DATA_STRUCTURE_VERSION)) { + *errorString = QString::fromUtf8("V4 data structure version mismatch. Found %1 expected %2").arg(version, 0, 16).arg(QV4_DATA_STRUCTURE_VERSION, 0, 16); + return false; + } + + if (qtVersion != quint32(QT_VERSION)) { + *errorString = QString::fromUtf8("Qt version mismatch. Found %1 expected %2").arg(qtVersion, 0, 16).arg(QT_VERSION, 0, 16); + return false; + } + + if (sourceTimeStamp) { + // Files from the resource system do not have any time stamps, so fall back to the application + // executable. + if (!expectedSourceTimeStamp.isValid()) + expectedSourceTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + + if (expectedSourceTimeStamp.isValid() && expectedSourceTimeStamp.toMSecsSinceEpoch() != sourceTimeStamp) { + *errorString = QStringLiteral("QML source file has a different time stamp than cached file."); + return false; + } + } + + return true; +#else + Q_UNUSED(expectedSourceTimeStamp) + Q_UNUSED(errorString) + return false; +#endif +} + } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index e6c4225bbd..49360bc3f1 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -727,6 +727,8 @@ struct Unit quint32_le padding; + bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const; + const Import *importAt(int idx) const { return reinterpret_cast((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index aaf5e3b857..d0d66c9b9a 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -92,7 +92,8 @@ QV4Include::~QV4Include() #endif } -QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status) +QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status status, + const QString &statusText) { QV4::Scope scope(v4); @@ -105,6 +106,8 @@ QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status stat o->put((s = v4->newString(QStringLiteral("NETWORK_ERROR"))), (v = QV4::Primitive::fromInt32(NetworkError))); o->put((s = v4->newString(QStringLiteral("EXCEPTION"))), (v = QV4::Primitive::fromInt32(Exception))); o->put((s = v4->newString(QStringLiteral("status"))), (v = QV4::Primitive::fromInt32(status))); + if (!statusText.isEmpty()) + o->put((s = v4->newString(QStringLiteral("statusText"))), (v = v4->newString(statusText))); return o.asReturnedValue(); } @@ -227,7 +230,8 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons } else { QScopedPointer script; - script.reset(QV4::Script::createFromFileOrCache(scope.engine, qmlcontext, localFile, url)); + QString error; + script.reset(QV4::Script::createFromFileOrCache(scope.engine, qmlcontext, localFile, url, &error)); if (!script.isNull()) { script->parse(); @@ -242,7 +246,7 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons result = resultValue(scope.engine, Ok); } } else { - result = resultValue(scope.engine, NetworkError); + result = resultValue(scope.engine, NetworkError, error); } callback(callbackFunction, result); diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 8015722afc..70ccfbf223 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -88,7 +88,8 @@ private: QV4::ReturnedValue result(); - static QV4::ReturnedValue resultValue(QV4::ExecutionEngine *v4, Status status = Loading); + static QV4::ReturnedValue resultValue(QV4::ExecutionEngine *v4, Status status = Loading, + const QString &statusText = QString()); static void callback(const QV4::Value &callback, const QV4::Value &status); QV4::ExecutionEngine *v4; diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 267c93952d..b4d9e11716 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -223,17 +223,28 @@ QQmlRefPointer Script::precompile(QV4::Compi return cg.generateCompilationUnit(/*generate unit data*/false); } -Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl) +Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error) { - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl)) { + if (error) + error->clear(); + + QQmlMetaType::CachedUnitLookupError cacheError = QQmlMetaType::CachedUnitLookupError::NoError; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(originalUrl, &cacheError)) { QQmlRefPointer jsUnit; jsUnit.adopt(new QV4::CompiledData::CompilationUnit(cachedUnit)); return new QV4::Script(engine, qmlContext, jsUnit); } QFile f(fileName); - if (!f.open(QIODevice::ReadOnly)) + if (!f.open(QIODevice::ReadOnly)) { + if (error) { + if (cacheError == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + *error = originalUrl.toString() + QString::fromUtf8(" was compiled ahead of time with an incompatible version of Qt and the original source code cannot be found. Please recompile"); + else + *error = QString::fromUtf8("Error opening source file %1: %2").arg(originalUrl.toString()).arg(f.errorString()); + } return nullptr; + } QByteArray data = f.readAll(); QString sourceCode = QString::fromUtf8(data); diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index cb03c6b064..b4ac150044 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -101,7 +101,7 @@ struct Q_QML_EXPORT Script { QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QString &fileName, const QString &finalUrl, const QString &source, QList *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr); - static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl); + static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); }; diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 7754f0fddc..8fda7f6f77 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,8 @@ #include #include "qqmlcomponent.h" +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + QT_BEGIN_NAMESPACE struct QQmlMetaTypeData @@ -2539,18 +2542,46 @@ QList QQmlMetaType::qmlSingletonTypes() return retn; } -const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri) +const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { - if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) + if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { + QString error; + if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; + if (status) + *status = CachedUnitLookupError::VersionMismatch; + return nullptr; + } + if (status) + *status = CachedUnitLookupError::NoError; return unit->qmlData; + } } + + if (status) + *status = CachedUnitLookupError::NoUnitFound; + return nullptr; } +void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.prepend(handler); +} + +void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.removeAll(handler); +} + /*! Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 07bef526ba..cd7afc8a01 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -133,7 +133,17 @@ public: static QList parentFunctions(); - static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri); + enum class CachedUnitLookupError { + NoError, + NoUnitFound, + VersionMismatch + }; + + static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status); + + // used by tst_qqmlcachegen.cpp + static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); + static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static bool namespaceContainsRegistrations(const QString &, int majorVersion); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2b778b0b63..5572fdad44 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1259,6 +1259,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); + d.hasInlineSourceCode = true; setData(blob, d); } @@ -1670,9 +1671,11 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) typeData = new QQmlTypeData(url, this); // TODO: if (compiledData == 0), is it safe to omit this insertion? m_typeCache.insert(url, typeData); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url())) { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(typeData, cachedUnit, mode); } else { + typeData->setCachedUnitStatus(error); QQmlTypeLoader::load(typeData, mode); } } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) { @@ -1727,9 +1730,11 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url) scriptBlob = new QQmlScriptBlob(url, this); m_scriptCache.insert(url, scriptBlob); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url())) { + QQmlMetaType::CachedUnitLookupError error; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(scriptBlob, cachedUnit); } else { + scriptBlob->setCachedUnitStatus(error); QQmlTypeLoader::load(scriptBlob); } } @@ -2428,8 +2433,13 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) if (isError()) return; - if (!m_backupSourceCode.exists()) { - setError(QQmlTypeLoader::tr("No such file or directory")); + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); return; } @@ -2981,6 +2991,13 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) } } + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } QmlIR::Document irUnit(isDebugging()); @@ -3178,7 +3195,7 @@ void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { error->clear(); - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return inlineSourceCode; QFile f(fileInfo.absoluteFilePath()); @@ -3205,7 +3222,7 @@ QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return QDateTime(); QDateTime timeStamp = fileInfo.lastModified(); @@ -3220,11 +3237,18 @@ QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const bool QQmlDataBlob::SourceCodeData::exists() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return true; return fileInfo.exists(); } +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 4665d342c1..5988632547 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -140,11 +140,13 @@ public: QString readAll(QString *error) const; QDateTime sourceTimeStamp() const; bool exists() const; + bool isEmpty() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; QString inlineSourceCode; QFileInfo fileInfo; + bool hasInlineSourceCode = false; }; protected: @@ -271,6 +273,8 @@ public: const QQmlImports &imports() const { return m_importCache; } + void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + protected: bool addImport(const QV4::CompiledData::Import *import, QList *errors); @@ -293,6 +297,7 @@ public: QQmlImports m_importCache; QHash m_unresolvedImports; QList m_qmldirs; + QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; QQmlTypeLoader(QQmlEngine *); diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 80c2d3a4bc..ef92e4108d 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -397,9 +397,11 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) QV4::Scoped qmlContext(scope, getWorker(script)); Q_ASSERT(!!qmlContext); - program.reset(QV4::Script::createFromFileOrCache(v4, qmlContext, fileName, url)); + QString error; + program.reset(QV4::Script::createFromFileOrCache(v4, qmlContext, fileName, url, &error)); if (program.isNull()) { - qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + if (!error.isEmpty()) + qWarning().nospace() << error; return; } diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index a8b72224ba..bad912c781 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -8,4 +8,6 @@ workerscripts_test.files = worker.js worker.qml workerscripts_test.prefix = /workerscripts RESOURCES += workerscripts_test +RESOURCES += versionchecks.qml + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index e36edca699..1c05005c90 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include class tst_qmlcachegen: public QObject @@ -48,6 +49,7 @@ private slots: void errorOnArgumentsInSignalHandler(); void aheadOfTimeCompilation(); void functionExpressions(); + void versionChecksForAheadOfTimeUnits(); void workerScripts(); }; @@ -280,6 +282,50 @@ void tst_qmlcachegen::aheadOfTimeCompilation() QCOMPARE(result.toInt(), 42); } +static QQmlPrivate::CachedQmlUnit *temporaryModifiedCachedUnit = nullptr; + +void tst_qmlcachegen::versionChecksForAheadOfTimeUnits() +{ + QVERIFY(QFile::exists(":/versionchecks.qml")); + QCOMPARE(QFileInfo(":/versionchecks.qml").size(), 0); + + Q_ASSERT(!temporaryModifiedCachedUnit); + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + const QV4::CompiledData::Unit *originalUnit = QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error); + QVERIFY(originalUnit); + QV4::CompiledData::Unit *tweakedUnit = (QV4::CompiledData::Unit *)malloc(originalUnit->unitSize); + memcpy(reinterpret_cast(tweakedUnit), reinterpret_cast(originalUnit), originalUnit->unitSize); + tweakedUnit->version = QV4_DATA_STRUCTURE_VERSION - 1; + temporaryModifiedCachedUnit = new QQmlPrivate::CachedQmlUnit{tweakedUnit, nullptr, nullptr}; + + auto testHandler = [](const QUrl &url) -> const QQmlPrivate::CachedQmlUnit * { + if (url == QUrl("qrc:/versionchecks.qml")) + return temporaryModifiedCachedUnit; + return nullptr; + }; + QQmlMetaType::prependCachedUnitLookupFunction(testHandler); + + { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + QVERIFY(!QQmlMetaType::findCachedCompilationUnit(QUrl("qrc:/versionchecks.qml"), &error)); + QCOMPARE(error, QQmlMetaType::CachedUnitLookupError::VersionMismatch); + } + + { + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc:/versionchecks.qml")); + QCOMPARE(component.status(), QQmlComponent::Error); + QCOMPARE(component.errorString(), QString("qrc:/versionchecks.qml:-1 File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile\n")); + } + + Q_ASSERT(temporaryModifiedCachedUnit); + free(const_cast(temporaryModifiedCachedUnit->qmlData)); + delete temporaryModifiedCachedUnit; + temporaryModifiedCachedUnit = nullptr; + + QQmlMetaType::removeCachedUnitLookupFunction(testHandler); +} + void tst_qmlcachegen::workerScripts() { QVERIFY(QFile::exists(":/workerscripts/worker.js")); diff --git a/tests/auto/qml/qmlcachegen/versionchecks.qml b/tests/auto/qml/qmlcachegen/versionchecks.qml new file mode 100644 index 0000000000..77d67e7da4 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/versionchecks.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property bool ok: true +} diff --git a/tests/auto/qml/qqmlecmascript/data/include_callback.js b/tests/auto/qml/qqmlecmascript/data/include_callback.js index ea19eba300..7f3195bb1f 100644 --- a/tests/auto/qml/qqmlecmascript/data/include_callback.js +++ b/tests/auto/qml/qqmlecmascript/data/include_callback.js @@ -1,6 +1,6 @@ function go() { var a = Qt.include("missing.js", function(o) { test2 = o.status == o.NETWORK_ERROR }); - test1 = a.status == a.NETWORK_ERROR + test1 = (a.status == a.NETWORK_ERROR) && (a.statusText.indexOf("Error opening source file") != -1); var b = Qt.include("blank.js", function(o) { test4 = o.status == o.OK }); test3 = b.status == b.OK diff --git a/tests/auto/qml/qqmllanguage/data/empty.errors.txt b/tests/auto/qml/qqmllanguage/data/empty.errors.txt index 620db2bbba..ba685d78ae 100644 --- a/tests/auto/qml/qqmllanguage/data/empty.errors.txt +++ b/tests/auto/qml/qqmllanguage/data/empty.errors.txt @@ -1,2 +1 @@ -1:1:Expected token `numeric literal' -1:1:Expected a qualified name id +-1:-1:File is empty -- cgit v1.2.3 From 80592dcf0df3bce6177e8e051e88af7b9e6b3f6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2018 16:33:01 +0100 Subject: Tighten QML cache version checking Don't just include the "compile hash" of QtQml in the dependencies hash of QML files but use a dedicated field in the data structure, that we will also fill in when generating cache files ahead of time. This ensures that AOT generated cache files are considered invalid even when switching between different sha1s of declarative. Task-number: QTBUG-66986 Change-Id: I3d8ee103fd1a33a5b4c4576b3a2703fcd09712dd Reviewed-by: Erik Verbruggen --- src/qml/compiler/compiler.pri | 2 -- src/qml/compiler/qv4compileddata.cpp | 45 +++++++++--------------------------- src/qml/compiler/qv4compileddata_p.h | 6 +++-- src/qml/compiler/qv4compiler.cpp | 4 ++++ src/qml/qml.pro | 3 ++- 5 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 2ca0c39acc..95096db51d 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -40,8 +40,6 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp - -qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl } gcc { diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6220c1bc11..8dcc068a06 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -70,18 +70,14 @@ #include -#if defined(QT_BUILD_INTERNAL) -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) -#include -#endif -#endif - QT_BEGIN_NAMESPACE namespace QV4 { namespace CompiledData { +static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version"); + #if !defined(V4_BOOTSTRAP) static QString cacheFilePath(const QUrl &url) { @@ -686,32 +682,6 @@ void ResolvedTypeReference::doDynamicTypeCheck() isFullyDynamicType = qtTypeInherits(mo); } -static QByteArray ownLibraryChecksum() -{ - static QByteArray libraryChecksum; - static bool checksumInitialized = false; - if (checksumInitialized) - return libraryChecksum; - checksumInitialized = true; -#if defined(QT_BUILD_INTERNAL) && !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen) - // This is a bit of a hack to make development easier. When hacking on the code generator - // the cache files may end up being re-used. To avoid that we also add the checksum of - // the QtQml library. - Dl_info libInfo; - if (dladdr(reinterpret_cast(&ownLibraryChecksum), &libInfo) != 0) { - QFile library(QFile::decodeName(libInfo.dli_fname)); - if (library.open(QIODevice::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(&library); - libraryChecksum = hash.result(); - } - } -#else - libraryChecksum = QByteArray(QML_COMPILE_HASH); -#endif - return libraryChecksum; -} - bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const { for (auto it = constBegin(), end = constEnd(); it != end; ++it) { @@ -719,8 +689,6 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e return false; } - hash->addData(ownLibraryChecksum()); - return true; } @@ -785,6 +753,15 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) } } +#if defined(QML_COMPILE_HASH) + if (qstrcmp(QML_COMPILE_HASH, libraryVersionHash) != 0) { + *errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match"); + return false; + } +#else +#error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files" +#endif + return true; #else Q_UNUSED(expectedSourceTimeStamp) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 49360bc3f1..1df9d6794f 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x18 +#define QV4_DATA_STRUCTURE_VERSION 0x19 class QIODevice; class QQmlPropertyCache; @@ -688,6 +688,8 @@ struct Unit quint32_le unitSize; // Size of the Unit and any depending data. // END DO NOT CHANGE THESE FIELDS EVER + char libraryVersionHash[48]; + char md5Checksum[16]; // checksum of all bytes following this field. void generateChecksum(); @@ -793,7 +795,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 144, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 192, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index ccc909c199..c9e535c93f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -48,6 +48,9 @@ #include #include +// generated by qmake: +#include "qml_compile_hash_p.h" + QV4::Compiler::StringTableGenerator::StringTableGenerator() { clear(); @@ -396,6 +399,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.flags |= module->unitFlags; unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; + qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH); memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); diff --git a/src/qml/qml.pro b/src/qml/qml.pro index f75bfa0313..2137877427 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -36,7 +36,8 @@ DEFINES += QT_NO_FOREACH } compile_hash_contents = \ "// Generated file, DO NOT EDIT" \ - "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" + "$${LITERAL_HASH}define QML_COMPILE_HASH \"$$QML_COMPILE_HASH\"" \ + "$${LITERAL_HASH}define QML_COMPILE_HASH_LENGTH $$str_size($$QML_COMPILE_HASH)" write_file("$$OUT_PWD/qml_compile_hash_p.h", compile_hash_contents)|error() } -- cgit v1.2.3 From 1953f3cac57acc9ee28809a9bee1962aeebbc35e Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 16:09:42 +0100 Subject: Remove qtquick-guidelines, move contents into qtquick-bestpractices This page doesn't offer much on its own, and qtquick-bestpractices.qdoc is already starting to gather useful guidelines, so move its contents there. Having everything related to best practices/guidelines on one page means less clicking between these "overview" pages, which I think is a bit of a problem currently. Change-Id: I18316dc177a6a7eb5a031e178cd0aed31dfa63ae Reviewed-by: J-P Nurmi Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 15 +++++++ .../doc/src/guidelines/qtquick-guidelines.qdoc | 50 ---------------------- 2 files changed, 15 insertions(+), 50 deletions(-) delete mode 100644 src/quick/doc/src/guidelines/qtquick-guidelines.qdoc diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 9d411a581f..ca9bfc9ebb 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -227,4 +227,19 @@ properties are enough. \li \l{Item Positioners} \li \l{Qt Quick Layouts Overview} \endlist + +\section1 Performance + +For information on performance in QML and Qt Quick, +see \l {Performance Considerations And Suggestions}. + +\section1 Tools and Utilities + +For information on useful tools and utilies that make working with QML and +Qt Quick easier, see \l {Qt Quick Tools and Utilities}. + +\section1 Scene Graph + +For information on Qt Quick's scene graph, see \l {Qt Quick Scene Graph}. + */ diff --git a/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc b/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc deleted file mode 100644 index b8432fd9ca..0000000000 --- a/src/quick/doc/src/guidelines/qtquick-guidelines.qdoc +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! -\page qtquick-guidelines.html -\title Qt Quick Guidelines -\brief Provides best practices and conventions for application developers - -Qt Quick has been the enabler that has helped developers achieve UI design -goals faster and relieve from the monotony of imperative-coding. It is one of -the very few UI design technologies that can be modified easily either using -a text editor or a graphical designer tool. - -As an application developer, it is important to understand the limitations -of any technology such as Qt Quick. This topic provides you the necessary insight -into Qt Quick, and how it affects your application. It also provides pointers to best -practices that you should try to follow, and the list of tools that helps you -understand and improve your application. - -\list -\li \l{Qt Quick Best practices}{Best Practices} -\li \l{Qt Quick Tools and Utilities} -\li \l{Performance Considerations And Suggestions}{Performance improvement tips} -\li \l{Qt Quick Scene Graph} -\endlist -*/ -- cgit v1.2.3 From 2b0e0bee0c6153a8912778de5ff1cabd728d98d8 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 14 Mar 2018 16:00:02 +0100 Subject: Rename best practices page to "Best Practices for QML and Qt Quick" Qt Quick is most often used via its QML types, and since some of the tips on this page are not specific to Qt Quick but apply to QML in general, it makes sense to list them both on the same page for convenience and completeness. Change-Id: I6d61b98ffd7e52dc28b33ef00a78dd745f39820a Reviewed-by: Frederik Gladhorn Reviewed-by: Simon Hausmann --- src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index ca9bfc9ebb..ea78c88028 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -27,12 +27,12 @@ /*! \page qtquick-bestpractices.html -\title Qt Quick Best Practices -\brief Lists the practices that work best for Qt Quick +\title Best Practices for QML and Qt Quick +\brief Lists best practices for working with QML and Qt Quick -Despite all of the benefits that Qt Quick offers, it can be challenging in -certain situations. The following sections elaborate on some of the best -practices that will help you get better results when developing Qt Quick +Despite all of the benefits that QML and Qt Quick offer, they can be +challenging in certain situations. The following sections elaborate on some of +the best practices that will help you get better results when developing applications. \section1 Custom UI Controls -- cgit v1.2.3 From cc62a35bbebc9d069c9dbaeb703dfd6afea548e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Vr=C3=A1til?= Date: Tue, 20 Mar 2018 18:14:20 +0100 Subject: Fix QQmlListModel crash when appending an empty array in debug mode Calling QQmlListModel::append() with an empty JS array triggers an assert in QAbstractItemModel::beginInsertRows() because it's called with negative "last" parameter. Change-Id: I202da260d79f2e6677c663c5785ff754c715fef8 Reviewed-by: Simon Hausmann --- src/qml/types/qqmllistmodel.cpp | 23 +++++++++++----------- tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 22 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index d4fc02cd3e..c4e33d572d 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2351,21 +2351,22 @@ void QQmlListModel::append(QQmlV4Function *args) QV4::ScopedObject argObject(scope); int objectArrayLength = objectArray->getLength(); + if (objectArrayLength > 0) { + int index = count(); + emitItemsAboutToBeInserted(index, objectArrayLength); - int index = count(); - emitItemsAboutToBeInserted(index, objectArrayLength); + for (int i=0 ; i < objectArrayLength ; ++i) { + argObject = objectArray->getIndexed(i); - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->getIndexed(i); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject); + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); + } else { + m_listModel->append(argObject); + } } - } - emitItemsInserted(); + emitItemsInserted(); + } } else if (argObject) { int index; diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 4618ea4071..9fdc54f067 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -125,6 +125,7 @@ private slots: void bindingsOnGetResult(); void stringifyModelEntry(); void qobjectTrackerForDynamicModelObjects(); + void crash_append_empty_array(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -1534,6 +1535,27 @@ void tst_qqmllistmodel::qobjectTrackerForDynamicModelObjects() QVERIFY(!ddata->jsWrapper.isNullOrUndefined()); } +void tst_qqmllistmodel::crash_append_empty_array() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"" + " }\n" + "}\n", QUrl()); + QScopedPointer scene(component.create()); + QQmlListModel *model = scene->findChild("testModel"); + QSignalSpy spy(model, &QQmlListModel::rowsAboutToBeInserted); + QQmlExpression expr(engine.rootContext(), model, "append(new Array())"); + expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + QCOMPARE(spy.count(), 0); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" -- cgit v1.2.3 From 8f4452fc72cd7a175ae5c89ffffe3a6ca6979392 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 20 Mar 2018 12:35:26 +0100 Subject: Doc: mention alternative syntax for resource files This syntax allows creating distinct .qrc files without having to create them manually. Change-Id: Iab7c76fd162bb7f39b42fb983f85d74fce3036d4 Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 50 +++++++++++++++------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index ea78c88028..f842f1b6aa 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -84,20 +84,36 @@ making it harder to load these resources. As an alternative, Qt offers its own application binary, enabling access to the application's resources regardless of the target OS. -For example, the following entry in the qmake project file ensures that the -resources are built into the application binary, making them available when -needed: +For example, consider the following project directory structure: \badcode - RESOURCES += a.qml b.png +project +├── images +│ ├── image1.png +│ └── image2.png +├── project.pro +└── qml + └── main.qml \endcode -It's also possible to use a +The following entry in \c project.pro ensures that the resources are built into +the application binary, making them available when needed: + +\badcode + RESOURCES += \ + qml/main.qml \ + images/image1.png \ + images/image2.png +\endcode + +A more convenient approach is to use the \l {files(pattern[, recursive=false])}{wildcard syntax} to select several files at once: \badcode - RESOURCES += $$files(*.qml) $$files(*.png) + RESOURCES += \ + $$files(qml/*.qml) \ + $$files(images/*.png) \endcode This approach is convenient for applications that depend on a limited number @@ -106,20 +122,24 @@ approach, it causes \e all of the other files in \c RESOURCES to be recompiled as well. This can be inefficient, especially for large sets of files. In this case, a better approach is to separate each type of resource into its own \l {Resource Collection Files (.qrc)}{.qrc} file. For example, the snippet -above could be changed like so: +above could be changed to the following: \badcode - RESOURCES += qml.qrc images.qrc + qml.files = $$files(*.qml) + qml.prefix = /qml + RESOURCES += qml + + images.files = $$files(*.png) + images.prefix = /images + RESOURCES += images \endcode -Now, whenever a QML file is changed in qml.qrc, only the QML files have to be -recompiled. +Now, whenever a QML file is changed, only the QML files have to be recompiled. -If you want to conveniently switch from using the "direct" syntax to a .qrc -file, look for \c qmake_intermediate.qrc (a build artifact created by qmake) -in your project's build directory and rename it to, for example, -\c resources.qrc. Then, replace the old \c RESOURCES entry with -\c {RESOURCES += resources.qrc}. +Sometimes it can be necessary to have more control over the path for a +specific file managed by the resource system. For example, if we wanted to give +\c image2.png an alias, we would need to switch to an explicit \c .qrc file. +\l {Creating Resource Files} explains how to do this in detail. \section2 Related Information \list -- cgit v1.2.3 From 1a816cd8dcc3499351ce6dfb6ad3bdfb34c31ede Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 20 Mar 2018 14:48:03 +0100 Subject: Doc: provide an example of the C++ - QML interaction path Change-Id: Ib65bb9edbcbd1172cc620243b078c9691d961828 Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 126 +++++++++++++++++++-- 1 file changed, 115 insertions(+), 11 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index f842f1b6aa..7d4f564a6f 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -188,17 +188,121 @@ The following snippet demonstrates examples of models written in QML: Use \l {QAbstractItemModel Subclass}{C++} for dynamic data sets that are large or frequently modified. -\section2 Interaction Path - -Although Qt enables you to manipulate QML from C++, it is not a recommended -approach, as debugging such code can be painful. The QML engine works out -a lot of the details on the fly, so manipulating the QML items from C++ could -lead to unexpected results. This approach also makes the C++ code rely on the -QML code to provide certain properties or objects, making it difficult to -refactor the QML code without breaking the C++ code. Moreover, such C++ code -cannot reused with other QML code. To avoid all of these hassles and have a -maintainable application, it is recommended to always use the C++ to QML path -and not the other way around. +\section2 Interacting with QML from C++ + +Although Qt enables you to manipulate QML from C++, it is not recommended to do +so. To explain why, let's take a look at a simplified example. Suppose we were +writing the UI for a settings page: + +\qml + import QtQuick 2.11 + import QtQuick.Controls 2.4 + + Page { + Button { + text: qsTr("Restore default settings") + } + } +\endqml + +We want the button to do something in C++ when it is clicked. We know objects +in QML can emit change signals just like they can in C++, so we give the button +an \l [QML]{QtObject::}{objectName} so that we can find it from C++: + +\qml + Button { + objectName: "restoreDefaultsButton" + text: qsTr("Restore default settings") + } +\endqml + +Then, in C++, we find that object and connect to its change signal: + +\code + #include + #include + #include + + class Backend : public QObject + { + Q_OBJECT + + public: + Backend() {} + + public slots: + void restoreDefaults() { + settings.setValue("loadLastProject", QVariant(false)); + } + + private: + QSettings settings; + }; + + int main(int argc, char *argv[]) + { + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + Backend backend; + + QObject *rootObject = engine.rootObjects().first(); + QObject *restoreDefaultsButton = rootObject->findChild("restoreDefaultsButton"); + QObject::connect(restoreDefaultsButton, SIGNAL(clicked()), + &backend, SLOT(restoreDefaults())); + + return app.exec(); + } + + #include "main.moc" +\endcode + +The problem with this approach is that the C++ logic layer depends on the QML +presentation layer. If we were to refactor the QML in such a way that the +\c objectName changes, or some other change breaks the ability for the C++ +to find the QML object, our workflow becomes much more complicated and tedious. + +Refactoring QML is a lot easier than refactoring C++, so in order to make +maintenance pain-free, we should strive to keep C++ types unaware of QML as +much as possible. This can be achieved by exposing the C++ types to QML: + +\code + int main(int argc, char *argv[]) + { + QGuiApplication app(argc, argv); + + Backend backend; + + QQmlApplicationEngine engine; + engine.rootContext()->setContextProperty("backend", &backend); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) + return -1; + + return app.exec(); + } +\endcode + +The QML then calls the C++ slot directly: + +\qml + import QtQuick 2.11 + import QtQuick.Controls 2.4 + + Page { + Button { + text: qsTr("Restore default settings") + onClicked: backend.restoreDefaults() + } + } +\endqml + +With this approach, the C++ remains unchanged in the event that the QML needs +to be refactored in the future. \section2 Related Information \list -- cgit v1.2.3 From dc4d6293f9473c0f03c570430d08867d2d01c6e2 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 20 Mar 2018 08:35:10 -0500 Subject: Handle function expressions in SignalTransition Extend 22b13921f8067f8a93164875a4ad59bed85b0400 to SignalTransition. Change-Id: Ic7d03353efaa7ba894b913e5b0bc193d648d21df Task-number: QTBUG-50328 Reviewed-by: Simon Hausmann --- src/imports/statemachine/signaltransition.cpp | 14 +++++++++++--- .../auto/qmltest/statemachine/tst_triggeredArguments2.qml | 1 - 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 63a969c0e8..0d35e3064b 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -176,9 +176,17 @@ void SignalTransition::connectTriggered() QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex()); int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod); - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target, signalIndex, - ctxtdata, this, m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]) : nullptr; + auto f = m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = f->nestedFunction()) + f = closure; + + QQmlBoundSignalExpression *expression = + ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) + : nullptr; if (expression) expression->setNotifyOnValueChanged(false); m_signalExpression = expression; diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml index 73bc653404..f23cc14152 100644 --- a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml +++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml @@ -67,7 +67,6 @@ TestCase { // Emit the signalTrans.signal testCase.mysignal("test1", true, 2) - expectFail("", "QTBUG-50328") compare(testCase.mystr, "test1") compare(testCase.mybool, true) compare(testCase.myint, 2) -- cgit v1.2.3 From 21301c1dbb00f4a2cd991e520423ed039b297ffb Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 20 Mar 2018 09:44:30 -0500 Subject: Simplify handling of function expressions as signal handlers Change-Id: I4bfa05b4619c248119c78d05e64270e6627f6065 Reviewed-by: Simon Hausmann --- src/imports/statemachine/signaltransition.cpp | 7 ------- src/qml/qml/qqmlboundsignal.cpp | 12 ++++++++++++ src/qml/qml/qqmlobjectcreator.cpp | 8 -------- src/qml/types/qqmlconnections.cpp | 7 ------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index 0d35e3064b..ab625788bb 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -177,13 +177,6 @@ void SignalTransition::connectTriggered() int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod); auto f = m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - - // If the function is marked as having a nested function, then the user wrote: - // onSomeSignal: function() { /*....*/ } - // So take that nested function: - if (auto closure = f->nestedFunction()) - f = closure; - QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) : nullptr; diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 060706ac50..d5117c8cec 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -110,6 +110,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, m_index(index), m_target(target) { + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = function->nestedFunction()) + function = closure; + setupFunction(scope, function); init(ctxt, scopeObject); } @@ -122,6 +128,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = runtimeFunction->nestedFunction()) + runtimeFunction = closure; + QV4::ExecutionEngine *engine = ctxt->engine->handle(); QList signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames(); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7c36f59035..7051fb51da 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -879,14 +879,6 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - - // When a user writes the following: - // onSignal: function() { doSomethingUsefull } - // then do not run the binding that returns the closure, but run the closure - // instead. - if (auto closure = runtimeFunction->nestedFunction()) - runtimeFunction = closure; - int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index d1a7aa9b6f..2ae3df6ebb 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -289,13 +289,6 @@ void QQmlConnections::connectSignals() signal->setEnabled(d->enabled); auto f = d->compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - - // If the function is marked as having a nested function, then the user wrote: - // onSomeSignal: function() { /*....*/ } - // So take that nested function: - if (auto closure = f->nestedFunction()) - f = closure; - QQmlBoundSignalExpression *expression = ctxtdata ? new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f) : nullptr; -- cgit v1.2.3 From 01a40e1f920b58f00d52ff4542f6ef9c606a9b03 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 19 Mar 2018 15:01:43 +0100 Subject: Do not look up objects in deferred properties If a property is a deferred property then skip it if searching for qobjects. This is not enough to avoid all asserts, but not reading deferred properties does make sense in any case. Task-number: QTBUG-67152 Change-Id: I495051745a5daf458909ff6c4cb5210597774ded Reviewed-by: J-P Nurmi --- src/quick/designer/qquickdesignercustomobjectdata.cpp | 12 ++++++++++++ src/quick/designer/qquickdesignersupportitems.cpp | 13 +++++++++++++ src/quick/designer/qquickdesignersupportproperties.cpp | 11 ++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp index 59e086b5a3..daa9486f02 100644 --- a/src/quick/designer/qquickdesignercustomobjectdata.cpp +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -148,7 +148,19 @@ void QQuickDesignerCustomObjectData::populateResetHashes() const QQuickDesignerSupport::PropertyNameList propertyNameList = QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object()); + const QMetaObject *mo = object()->metaObject(); + QStringList deferredPropertyNames; + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) { + + if (deferredPropertyNames.contains(propertyName)) + continue; + QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object())); QQmlAbstractBinding::Ptr binding = QQmlAbstractBinding::Ptr(QQmlPropertyPrivate::binding(property)); diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 9fadbb2122..82474827aa 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -94,11 +94,24 @@ static void allSubObjects(QObject *object, QObjectList &objectList) objectList.append(object); + const QMetaObject *mo = object->metaObject(); + + QStringList deferredPropertyNames; + const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = mo->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + for (int index = QObject::staticMetaObject.propertyOffset(); index < object->metaObject()->propertyCount(); index++) { + QMetaProperty metaProperty = object->metaObject()->property(index); + if (deferredPropertyNames.contains(metaProperty.name())) + continue; + // search recursive in property objects if (metaProperty.isReadable() && metaProperty.isWritable() diff --git a/src/quick/designer/qquickdesignersupportproperties.cpp b/src/quick/designer/qquickdesignersupportproperties.cpp index 674f811f8f..c746f55daa 100644 --- a/src/quick/designer/qquickdesignersupportproperties.cpp +++ b/src/quick/designer/qquickdesignersupportproperties.cpp @@ -202,11 +202,20 @@ QQuickDesignerSupport::PropertyNameList QQuickDesignerSupportProperties::allProp const QMetaObject *metaObject = object->metaObject(); + + QStringList deferredPropertyNames; + const int namesIndex = metaObject->indexOfClassInfo("DeferredPropertyNames"); + if (namesIndex != -1) { + QMetaClassInfo classInfo = metaObject->classInfo(namesIndex); + deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + } + for (int index = 0; index < metaObject->propertyCount(); ++index) { QMetaProperty metaProperty = metaObject->property(index); QQmlProperty declarativeProperty(object, QString::fromUtf8(metaProperty.name())); if (declarativeProperty.isValid() && declarativeProperty.propertyTypeCategory() == QQmlProperty::Object) { - if (declarativeProperty.name() != QLatin1String("parent")) { + if (declarativeProperty.name() != QLatin1String("parent") + && !deferredPropertyNames.contains(declarativeProperty.name())) { QObject *childObject = QQmlMetaType::toQObject(declarativeProperty.read()); if (childObject) propertyNameList.append(allPropertyNames(childObject, -- cgit v1.2.3 From 8629682663adb0de5f91d2bd545b5d68e6afb7cd Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 18 Dec 2017 17:40:48 +0100 Subject: Add a feature for DelegateModel Change-Id: Ia24767b33a20bd70096bbb8b4f27729c788eb331 Reviewed-by: Oswald Buddenhagen --- src/qml/configure.json | 9 ++++++++- src/qml/qml/qqmlengine.cpp | 4 ++++ src/qml/types/qqmldelegatemodel_p.h | 2 ++ src/qml/types/qqmldelegatemodel_p_p.h | 2 ++ src/qml/types/qqmlinstantiator.cpp | 27 ++++++++++++++++++++++----- src/qml/types/qqmlinstantiator_p_p.h | 4 ++++ src/qml/types/qqmlmodelsmodule.cpp | 4 ++++ src/qml/types/types.pri | 12 +++++++++--- src/qml/util/qqmladaptormodel_p.h | 2 ++ src/qml/util/util.pri | 10 ++++++++-- src/quick/configure.json | 8 +++++++- 11 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/qml/configure.json b/src/qml/configure.json index b744ea6948..681cecea99 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -39,6 +39,12 @@ "features.xmlstreamwriter" ], "output": [ "privateFeature" ] + }, + "qml-delegate-model": { + "label": "QML delegate model", + "purpose": "Provides the DelegateModel QML type.", + "section": "QML", + "output": [ "privateFeature" ] } }, @@ -47,7 +53,8 @@ "section": "Qt QML", "entries": [ "qml-network", - "qml-debug" + "qml-debug", + "qml-delegate-model" ] } ] diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 49f25e89fe..ebd354d003 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -88,7 +88,9 @@ #include #include #include +#if QT_CONFIG(qml_delegate_model) #include +#endif #include #include #include @@ -240,8 +242,10 @@ void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility qmlRegisterType(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType(uri, versionMajor, versionMinor, "Package"); +#if QT_CONFIG(qml_delegate_model) qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataModel"); qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataGroup"); +#endif qmlRegisterType(uri, versionMajor, versionMinor, "VisualItemModel"); } diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index b894df8f82..b0786cd088 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -62,6 +62,8 @@ #include #include +QT_REQUIRE_CONFIG(qml_delegate_model); + QT_BEGIN_NAMESPACE class QQmlChangeSet; diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 68b987a5fa..18980cfd7c 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -60,6 +60,8 @@ // We mean it. // +QT_REQUIRE_CONFIG(qml_delegate_model); + QT_BEGIN_NAMESPACE typedef QQmlListCompositor Compositor; diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp index 213bef7879..030758fa3b 100644 --- a/src/qml/types/qqmlinstantiator.cpp +++ b/src/qml/types/qqmlinstantiator.cpp @@ -44,7 +44,9 @@ #include #include #include +#if QT_CONFIG(qml_delegate_model) #include +#endif QT_BEGIN_NAMESPACE @@ -53,7 +55,9 @@ QQmlInstantiatorPrivate::QQmlInstantiatorPrivate() , effectiveReset(false) , active(true) , async(false) +#if QT_CONFIG(qml_delegate_model) , ownModel(false) +#endif , requestedIndex(-1) , model(QVariant(1)) , instanceModel(nullptr) @@ -198,6 +202,7 @@ void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bo q->countChanged(); } +#if QT_CONFIG(qml_delegate_model) void QQmlInstantiatorPrivate::makeModel() { Q_Q(QQmlInstantiator); @@ -209,6 +214,7 @@ void QQmlInstantiatorPrivate::makeModel() if (componentComplete) delegateModel->componentComplete(); } +#endif /*! @@ -349,6 +355,7 @@ void QQmlInstantiator::setDelegate(QQmlComponent* c) d->delegate = c; emit delegateChanged(); +#if QT_CONFIG(qml_delegate_model) if (!d->ownModel) return; @@ -356,6 +363,7 @@ void QQmlInstantiator::setDelegate(QQmlComponent* c) dModel->setDelegate(c); if (d->componentComplete) d->regenerate(); +#endif } /*! @@ -398,12 +406,15 @@ void QQmlInstantiator::setModel(const QVariant &v) QObject *object = qvariant_cast(v); QQmlInstanceModel *vim = nullptr; if (object && (vim = qobject_cast(object))) { +#if QT_CONFIG(qml_delegate_model) if (d->ownModel) { delete d->instanceModel; prevModel = nullptr; d->ownModel = false; } +#endif d->instanceModel = vim; +#if QT_CONFIG(qml_delegate_model) } else if (v != QVariant(0)){ if (!d->ownModel) d->makeModel(); @@ -413,6 +424,7 @@ void QQmlInstantiator::setModel(const QVariant &v) dataModel->setModel(v); d->effectiveReset = false; } +#endif } if (d->instanceModel != prevModel) { @@ -423,10 +435,12 @@ void QQmlInstantiator::setModel(const QVariant &v) //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); } - connect(d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); + if (d->instanceModel) { + connect(d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); + connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); + //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); + } } d->regenerate(); @@ -476,10 +490,13 @@ void QQmlInstantiator::componentComplete() { Q_D(QQmlInstantiator); d->componentComplete = true; +#if QT_CONFIG(qml_delegate_model) if (d->ownModel) { static_cast(d->instanceModel)->componentComplete(); d->regenerate(); - } else { + } else +#endif + { QVariant realModel = d->model; d->model = QVariant(0); setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0 diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qml/types/qqmlinstantiator_p_p.h index 9edaecf7a8..a5a4d1a32d 100644 --- a/src/qml/types/qqmlinstantiator_p_p.h +++ b/src/qml/types/qqmlinstantiator_p_p.h @@ -69,7 +69,9 @@ public: void clear(); void regenerate(); +#if QT_CONFIG(qml_delegate_model) void makeModel(); +#endif void _q_createdItem(int, QObject *); void _q_modelUpdated(const QQmlChangeSet &, bool); QObject *modelObject(int index, bool async); @@ -78,7 +80,9 @@ public: bool effectiveReset:1; bool active:1; bool async:1; +#if QT_CONFIG(qml_delegate_model) bool ownModel:1; +#endif int requestedIndex; QVariant model; QQmlInstanceModel *instanceModel; diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index c36e26a525..e217b63c6f 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -40,7 +40,9 @@ #include "qqmlmodelsmodule_p.h" #include #include +#if QT_CONFIG(qml_delegate_model) #include +#endif #include QT_BEGIN_NAMESPACE @@ -51,8 +53,10 @@ void QQmlModelsModule::defineModule() qmlRegisterType(uri, 2, 1, "ListElement"); qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); +#if QT_CONFIG(qml_delegate_model) qmlRegisterType(uri, 2, 1, "DelegateModel"); qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); +#endif qmlRegisterType(uri, 2, 1, "ObjectModel"); qmlRegisterType(uri, 2, 3, "ObjectModel"); diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index e85ab5982b..8bcbd6e544 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,7 +1,6 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ - $$PWD/qqmldelegatemodel.cpp \ $$PWD/qqmllistmodel.cpp \ $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlmodelsmodule.cpp \ @@ -14,8 +13,6 @@ SOURCES += \ HEADERS += \ $$PWD/qqmlbind_p.h \ $$PWD/qqmlconnections_p.h \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ $$PWD/qqmllistmodel_p.h \ $$PWD/qqmllistmodel_p_p.h \ $$PWD/qqmllistmodelworkeragent_p.h \ @@ -27,6 +24,15 @@ HEADERS += \ $$PWD/qqmlinstantiator_p.h \ $$PWD/qqmlinstantiator_p_p.h +qtConfig(qml-delegate-model) { + SOURCES += \ + $$PWD/qqmldelegatemodel.cpp + + HEADERS += \ + $$PWD/qqmldelegatemodel_p.h \ + $$PWD/qqmldelegatemodel_p_p.h +} + qtConfig(animation) { SOURCES += \ $$PWD/qqmltimer.cpp diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index 8f773efa20..b152a886a5 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -57,6 +57,8 @@ #include +QT_REQUIRE_CONFIG(qml_delegate_model); + QT_BEGIN_NAMESPACE class QQmlEngine; diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri index a9c5ffe9b7..bebb271f1b 100644 --- a/src/qml/util/util.pri +++ b/src/qml/util/util.pri @@ -2,12 +2,18 @@ SOURCES += \ $$PWD/qqmlchangeset.cpp \ $$PWD/qqmllistaccessor.cpp \ $$PWD/qqmllistcompositor.cpp \ - $$PWD/qqmladaptormodel.cpp \ $$PWD/qqmlpropertymap.cpp HEADERS += \ $$PWD/qqmlchangeset_p.h \ $$PWD/qqmllistaccessor_p.h \ $$PWD/qqmllistcompositor_p.h \ - $$PWD/qqmladaptormodel_p.h \ $$PWD/qqmlpropertymap.h + +qtConfig(qml-delegate-model) { + SOURCES += \ + $$PWD/qqmladaptormodel.cpp + + HEADERS += \ + $$PWD/qqmladaptormodel_p.h +} diff --git a/src/quick/configure.json b/src/quick/configure.json index 7004ea7f7b..b77ea863b3 100644 --- a/src/quick/configure.json +++ b/src/quick/configure.json @@ -79,6 +79,7 @@ "label": "GridView item", "purpose": "Provides the GridView item.", "section": "Qt Quick", + "condition": "features.qml-delegate-model", "output": [ "privateFeature" ] @@ -101,6 +102,7 @@ "label": "ListView item", "purpose": "Provides the ListView item.", "section": "Qt Quick", + "condition": "features.qml-delegate-model", "output": [ "privateFeature" ] @@ -126,7 +128,10 @@ "label": "PathView item", "purpose": "Provides the PathView item.", "section": "Qt Quick", - "condition": "features.quick-path", + "condition": [ + "features.qml-delegate-model", + "features.quick-path" + ], "output": [ "privateFeature" ] @@ -143,6 +148,7 @@ "label": "Repeater item", "purpose": "Provides the Repeater item.", "section": "Qt Quick", + "condition": "features.qml-delegate-model", "output": [ "privateFeature" ] -- cgit v1.2.3 From 214fbaa57b73296a0a191b5ff2b1fbc8bf0aaa7a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 26 Feb 2018 09:16:06 +0100 Subject: add qmlRegisterModule to all QML plugins (QUIP 99) Now it should always be possible to do import QtQuick.Module x.m where x is the module's major version and m is Qt's minor version. [ChangeLog][QtQuick][Important Behavior Changes] In Qt 5.11 and newer versions, QML plugin modules are available with the same minor version as the Qt release minor version number. For example it's possible to import QtQuick.Window 2.11 or import QtQuick.Layouts 1.11 even though there haven't been any API changes in these modules for Qt 5.11, and the maximum possible import version will automatically increment in future Qt versions. This is intended to reduce confusion. Change-Id: I0d28ed04d186bcdd5acde95b8ed0b66c1c4697e3 Reviewed-by: J-P Nurmi --- src/imports/layouts/plugin.cpp | 3 +++ src/imports/localstorage/plugin.cpp | 3 +++ src/imports/models/plugin.cpp | 4 ++++ src/imports/particles/plugin.cpp | 4 ++++ src/imports/statemachine/plugin.cpp | 3 +++ src/imports/testlib/main.cpp | 3 +++ src/imports/window/plugin.cpp | 3 +++ src/imports/xmllistmodel/plugin.cpp | 3 +++ src/qml/qml/qqmlengine.cpp | 6 ++++++ 9 files changed, 32 insertions(+) diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp index 25d5bacc90..c805c9fb43 100644 --- a/src/imports/layouts/plugin.cpp +++ b/src/imports/layouts/plugin.cpp @@ -75,6 +75,9 @@ public: qmlRegisterUncreatableType(uri, 1, 2, "Layout", QStringLiteral("Do not create objects of type Layout")); qmlRegisterRevision(uri, 1, 1); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 1, QT_VERSION_MINOR); } }; //![class decl] diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 88121df66c..fb71fcaff6 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -820,6 +820,9 @@ public: { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.LocalStorage")); qmlRegisterSingletonType(uri, 2, 0, "LocalStorage", module_api_factory); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); } }; diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp index 2f8a9713d2..fb1d42e85e 100644 --- a/src/imports/models/plugin.cpp +++ b/src/imports/models/plugin.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include +#include #include @@ -83,6 +84,9 @@ public: Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.Models")); Q_UNUSED(uri); QQmlModelsModule::defineModule(); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); } }; //![class decl] diff --git a/src/imports/particles/plugin.cpp b/src/imports/particles/plugin.cpp index a04e115976..d548f26599 100644 --- a/src/imports/particles/plugin.cpp +++ b/src/imports/particles/plugin.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include +#include #include @@ -62,6 +63,9 @@ public: Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Particles")); Q_UNUSED(uri); QQuickParticlesModule::defineModule(); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); } }; //![class decl] diff --git a/src/imports/statemachine/plugin.cpp b/src/imports/statemachine/plugin.cpp index 1357743126..93ced6e280 100644 --- a/src/imports/statemachine/plugin.cpp +++ b/src/imports/statemachine/plugin.cpp @@ -75,6 +75,9 @@ public: qmlRegisterCustomType(uri, 1, 0, "SignalTransition", new SignalTransitionParser); qmlRegisterType(uri, 1, 0, "TimeoutTransition"); qmlProtectModule(uri, 1); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 1, QT_VERSION_MINOR); } }; diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 45e9bd2cf6..443229bee9 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -160,6 +160,9 @@ public: qmlRegisterType(uri,1,2,"TestEvent"); qmlRegisterType(uri,1,0,"TestUtil"); qmlRegisterType(); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 1, QT_VERSION_MINOR); } }; diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp index 4e6eedf326..d8d21ce27e 100644 --- a/src/imports/window/plugin.cpp +++ b/src/imports/window/plugin.cpp @@ -78,6 +78,9 @@ public: Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Window")); Q_UNUSED(uri); QQuickWindowModule::defineModule(); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); } }; //![class decl] diff --git a/src/imports/xmllistmodel/plugin.cpp b/src/imports/xmllistmodel/plugin.cpp index dc6a02918b..82e11eeeb3 100644 --- a/src/imports/xmllistmodel/plugin.cpp +++ b/src/imports/xmllistmodel/plugin.cpp @@ -63,6 +63,9 @@ public: Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.XmlListModel")); qmlRegisterType(uri,2,0,"XmlListModel"); qmlRegisterType(uri,2,0,"XmlRole"); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule(uri, 2, QT_VERSION_MINOR); } }; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ebd354d003..54a7c5130d 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -257,6 +257,9 @@ void QQmlEnginePrivate::defineQtQuick2Module() // register the QtQuick2 types which are implemented in the QtQml module. registerQtQuick2Types("QtQuick",2,0); qmlRegisterUncreatableType("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); } bool QQmlEnginePrivate::designerMode() @@ -956,6 +959,9 @@ void QQmlEnginePrivate::init() registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. qmlRegisterUncreatableType("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward + qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); + QQmlData::init(); baseModulesUninitialized = false; } -- cgit v1.2.3 From bb9dd912baae3aa537d5775d0829bed3baa5877d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Br=C3=BCning?= Date: Thu, 16 Nov 2017 15:54:58 +0100 Subject: Correct logging of swap time in software render loop The conversion from nanoseconds to milliseconds wrongly divided the swap time by 10 million instead of 1 million. Change-Id: Id2b1594fbf7abafabfeae790c7083ad1cf4064a0 Reviewed-by: Andy Nichols --- src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 423f5f7321..b400473128 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -195,7 +195,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) int(polishTime / 1000000), int((syncTime - polishTime) / 1000000), int((renderTime - syncTime) / 1000000), - int((swapTime - renderTime) / 10000000), + int((swapTime - renderTime) / 1000000), int(lastFrameTime.msecsTo(QTime::currentTime()))); lastFrameTime = QTime::currentTime(); } -- cgit v1.2.3 From 641d50eeb0272544e3eaff6c10bdefcdc544106a Mon Sep 17 00:00:00 2001 From: Martin Smith Date: Tue, 6 Mar 2018 15:21:38 +0100 Subject: doc: Fix qtquick.qdocconf to include stuff for qtquickwidgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A fake module header is added for qtquick named QtQuick_pch.h, and qtquick.qdocconf is modified to use this fake module header instead of using QtQuick. The fake module header includes QtQuick and QtQuickWidgets. Change-Id: I2a3bd4ad32bdad878f8e7049cf71c393517547a0 Reviewed-by: Topi Reiniö --- src/quick/QtQuick_pch.h | 45 ++++++++++++++++++++++++++++++++++++++++++ src/quick/doc/qtquick.qdocconf | 4 ++++ 2 files changed, 49 insertions(+) create mode 100644 src/quick/QtQuick_pch.h diff --git a/src/quick/QtQuick_pch.h b/src/quick/QtQuick_pch.h new file mode 100644 index 0000000000..5d66631e7f --- /dev/null +++ b/src/quick/QtQuick_pch.h @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifdef Q_CLANG_QDOC +#ifndef QTQUICK_PCH +#define QTQUICK_PCH +#include +#include +#endif +#endif diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index 7ce0dfcf09..b6ddb7bf8b 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -5,6 +5,10 @@ description = Qt Quick Reference Documentation version = $QT_VERSION examplesinstallpath = quick +moduleheader = QtQuick_pch.h + +includepaths = -I $QT_PLUGIN_PATH/../include/QtQuick \ + -I $QT_PLUGIN_PATH/../include/QtQuickWidgets qhp.projects = QtQuick -- cgit v1.2.3 From 3d67ffa29191799014111accb9ff5d2f737f659c Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Thu, 15 Mar 2018 14:40:51 +0100 Subject: Add additional check for support JIT compilation on ARM platform JIT compilation is require hardware with FPU double precision. On ARM platform we can check it via __ARM_FP (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0774g/chr1383660321827.html) Change-Id: I8f3a00e639cebe65d874cb085d97aa8f1cc18a4f Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4global_p.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 7f82a02ae0..9b13d4e341 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -108,6 +108,11 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } //# define V4_ENABLE_JIT #endif +// check FPU with double precision on ARM platform +#if (defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)) && defined(V4_ENABLE_JIT) && defined(__ARM_FP) && (__ARM_FP <= 0x04) +# undef V4_ENABLE_JIT +#endif + // Black list some platforms #if defined(V4_ENABLE_JIT) #if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -- cgit v1.2.3 From 106631254f1c4d322d2347477725f6d0ed28faac Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 22 Mar 2018 12:10:33 +0100 Subject: Fix isInt32 for -0.0 Because no, that can't be represented as an 32bit integer. Change-Id: I83e5e74fdfbd9b13ac04a49311619d8939c7b093 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4value_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 97ed13cd91..a5ee6b5373 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -339,7 +339,7 @@ public: if (isDouble()) { double d = doubleValue(); int i = (int)d; - if (i == d) { + if (i == d && !(d == 0 && std::signbit(d))) { setInt_32(i); return true; } -- cgit v1.2.3 From 9a2cfd515534c336e867a9d33a3d99b6c6ebd1a1 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 9 Mar 2018 13:04:53 +0100 Subject: Add test for jump strict-not-equal undefined on 32bit Commit 86702c3be53fda404ebe331207f9062675c952e0 and e2218f8b5c527a6da52ae4dc8a381b3ff68d3cd0 were submitted directly to the 5.9 and 5.6 branches. As the problem does not exist per-se in 5.11 there is no fix required. This commit only adds the test coverage for this issue to avoid regressing. Change-Id: Ic5f5d56f52f3855d20755f771804025f8a93acd9 Task-number: QTBUG-66832 Reviewed-by: Erik Verbruggen --- .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 31cf40be16..e0b8127dfb 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -351,6 +351,7 @@ private slots: void shadowedFunctionName(); void anotherNaN(); void callPropertyOnUndefined(); + void jumpStrictNotEqualUndefined(); private: // static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); @@ -8479,6 +8480,26 @@ void tst_qqmlecmascript::callPropertyOnUndefined() QVERIFY(!v.isError()); // well, more importantly: this shouldn't fail on an assert. } +void tst_qqmlecmascript::jumpStrictNotEqualUndefined() +{ + QJSEngine engine; + QJSValue v = engine.evaluate(QString::fromLatin1( + "var ok = 0\n" + "var foo = 0\n" + "if (foo !== void 1)\n" + " ++ok;\n" + "else\n" + " --ok;\n" + "if (foo === void 1)\n" + " --ok;\n" + "else\n" + " ++ok;\n" + "ok\n" + )); + QVERIFY(!v.isError()); + QCOMPARE(v.toInt(), 2); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" -- cgit v1.2.3 From cc6eef647302426ae6ef0759b9f59fd14e57bd1e Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 26 Mar 2018 10:50:07 +0200 Subject: Quick/DesignerSupport: Use QByteArrayList when checking property lists Fix warnings like designer/qquickdesignersupportitems.cpp:112:63: warning: 'QString::QString(const char*)' is deprecated: Use fromUtf8, QStringLiteral, or QLatin1String [-Wdeprecated-declarations] Amends 01a40e1f920b58f00d52ff4542f6ef9c606a9b03. Change-Id: I0e8167f8eacc0d901f79fb5dd8b2772ab29c55a8 Reviewed-by: Thomas Hartmann --- src/quick/designer/qquickdesignercustomobjectdata.cpp | 4 ++-- src/quick/designer/qquickdesignersupportitems.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/designer/qquickdesignercustomobjectdata.cpp b/src/quick/designer/qquickdesignercustomobjectdata.cpp index daa9486f02..8989de711e 100644 --- a/src/quick/designer/qquickdesignercustomobjectdata.cpp +++ b/src/quick/designer/qquickdesignercustomobjectdata.cpp @@ -149,11 +149,11 @@ void QQuickDesignerCustomObjectData::populateResetHashes() QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object()); const QMetaObject *mo = object()->metaObject(); - QStringList deferredPropertyNames; + QByteArrayList deferredPropertyNames; const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); if (namesIndex != -1) { QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + deferredPropertyNames = QByteArray(classInfo.value()).split(','); } for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) { diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp index 82474827aa..1c338fa79d 100644 --- a/src/quick/designer/qquickdesignersupportitems.cpp +++ b/src/quick/designer/qquickdesignersupportitems.cpp @@ -96,11 +96,11 @@ static void allSubObjects(QObject *object, QObjectList &objectList) const QMetaObject *mo = object->metaObject(); - QStringList deferredPropertyNames; + QByteArrayList deferredPropertyNames; const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); if (namesIndex != -1) { QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); + deferredPropertyNames = QByteArray(classInfo.value()).split(','); } for (int index = QObject::staticMetaObject.propertyOffset(); -- cgit v1.2.3 From 2ffd1a4f6baee12724de390606f6ea4ffd319def Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 27 Mar 2018 11:04:42 +0200 Subject: QML Tooling: Remember global "enabled" flag in profiler service If profiling has been enabled for all engines, that now also holds for engines to be registered in the future. This way the client doesn't need to employ the EngineControl service just to figure out when the first engine registers. Change-Id: I44a441bb6ead25abdadac1ae3e4edf9b0a9659f5 Reviewed-by: Simon Hausmann --- .../qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp | 12 +++++++++++- src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index eb6fbce101..e391abab1e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -59,7 +59,7 @@ Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter) QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) : QQmlConfigurableDebugService(1, parent), - m_waitingForStop(false), m_useMessageTypes(false) + m_waitingForStop(false), m_useMessageTypes(false), m_globalEnabled(false), m_globalFeatures(0) { m_timer.start(); QQmlAbstractProfilerAdapter *quickAdapter = @@ -137,6 +137,10 @@ void QQmlProfilerServiceImpl::engineAdded(QJSEngine *engine) "QML profilers have to be added from the engine thread"); QMutexLocker lock(&m_configMutex); + + if (m_globalEnabled) + startProfiling(engine, m_globalFeatures); + const auto range = qAsConst(m_engineProfilers).equal_range(engine); for (auto it = range.first; it != range.second; ++it) (*it)->stopWaiting(); @@ -254,6 +258,9 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features if (startedAny) d << idForObject(engine); } else { + m_globalEnabled = true; + m_globalFeatures = features; + QSet engines; for (QMultiHash::iterator i(m_engineProfilers.begin()); i != m_engineProfilers.end(); ++i) { @@ -292,6 +299,9 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine) QList stopping; QList reporting; + if (engine == nullptr) + m_globalEnabled = false; + bool stillRunning = false; for (QMultiHash::iterator i(m_engineProfilers.begin()); i != m_engineProfilers.end(); ++i) { diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h index a5e060a1ab..a8c3047421 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h @@ -116,6 +116,9 @@ private: bool m_waitingForStop; bool m_useMessageTypes; + bool m_globalEnabled; + quint64 m_globalFeatures; + QList m_globalProfilers; QMultiHash m_engineProfilers; QList m_stoppingEngines; -- cgit v1.2.3 From fe4aaf9cc11467aa5cab0d9d1d2706b283719b0a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 27 Mar 2018 10:04:26 +0200 Subject: QML Tooling: Wait for QML trace to be started in QML profiler test If we don't wait for anything else, we need to wait for at least the "trace started" message before we disable profiling. Otherwise we might disable the profiling before the first engine registers, which will give us no trace at all. Change-Id: I9fb64e82bd05c60640ffbbd2ece5e99edfa97f4a Reviewed-by: Simon Hausmann --- .../auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index cd8e4216f3..87c123b85d 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -525,6 +525,8 @@ void tst_QQmlProfilerService::connect() if (!traceEnabled) m_client->client->setRecording(true); + + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); @@ -643,6 +645,7 @@ void tst_QQmlProfilerService::controlFromJS() { QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess); + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); -- cgit v1.2.3 From 2f0d959bfb324b7e7f0c79b26819700e66b4c44a Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 26 Mar 2018 13:42:08 +0200 Subject: Use a separate process for testing perf.map file The environment change was too fragile. If the JIT ran before the relevant test function was executed, it would set the doProfile flag to false, and never re-evaluate the environment variable. The qmljs binary is only available for private tests, and the test didn't quite fit into qjsengine anyway. Therefore a new test for the QV4Assembler class that genertes the map files is added. Change-Id: Ice0c18daaee9f0f4f0f15eba0261bcc01aa4b105 Reviewed-by: Simon Hausmann --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 68 ------------------ tests/auto/qml/qml.pro | 1 + tests/auto/qml/qv4assembler/qv4assembler.pro | 7 ++ tests/auto/qml/qv4assembler/tst_qv4assembler.cpp | 91 ++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 tests/auto/qml/qv4assembler/qv4assembler.pro create mode 100644 tests/auto/qml/qv4assembler/tst_qv4assembler.cpp diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 8a017fd573..0455895c14 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -203,7 +203,6 @@ private slots: void malformedExpression(); void scriptScopes(); - void perfMapFile(); signals: void testSignal(); @@ -4145,73 +4144,6 @@ void tst_QJSEngine::scriptScopes() QCOMPARE(use.toInt(), 42); } -static const char *perfMapKey = "QV4_PROFILE_WRITE_PERF_MAP"; -static const char *jitCallKey = "QV4_JIT_CALL_THRESHOLD"; - -struct EnvironmentModifier { - const bool hasPerfMap = false; - const bool hasJitCall = false; - const QByteArray perfMap; - const QByteArray jitCall; - - EnvironmentModifier() : - hasPerfMap(qEnvironmentVariableIsSet(perfMapKey)), - hasJitCall(qEnvironmentVariableIsSet(jitCallKey)), - perfMap(qgetenv(perfMapKey)), - jitCall(qgetenv(jitCallKey)) - { - qputenv(perfMapKey, "1"); - qputenv(jitCallKey, "0"); - } - - ~EnvironmentModifier() - { - if (hasPerfMap) - qputenv(perfMapKey, perfMap); - else - qunsetenv(perfMapKey); - - if (hasJitCall) - qputenv(jitCallKey, jitCall); - else - qunsetenv(jitCallKey); - } -}; - -void tst_QJSEngine::perfMapFile() -{ -#if !defined(Q_OS_LINUX) - QSKIP("perf map files are only generated on linux"); -#else - EnvironmentModifier modifier; - Q_UNUSED(modifier); - QJSEngine engine; - QJSValue def = engine.evaluate("'use strict'; function foo() { return 42 }"); - QVERIFY(!def.isError()); - QJSValue use = engine.evaluate("'use strict'; foo()"); - QVERIFY(use.isNumber()); - QFile file(QString::fromLatin1("/tmp/perf-%1.map").arg(QCoreApplication::applicationPid())); - QVERIFY(file.exists()); - QVERIFY(file.open(QIODevice::ReadOnly)); - QList functions; - while (!file.atEnd()) { - const QByteArray contents = file.readLine(); - QVERIFY(contents.endsWith('\n')); - QList fields = contents.split(' '); - QCOMPARE(fields.length(), 3); - bool ok = false; - const qulonglong address = fields[0].toULongLong(&ok, 16); - QVERIFY(ok); - QVERIFY(address > 0); - const ulong size = fields[1].toULong(&ok, 16); - QVERIFY(ok); - QVERIFY(size > 0); - functions.append(fields[2]); - } - QVERIFY(functions.contains("foo\n")); -#endif -} - QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 9557393a2e..3433b56864 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -69,6 +69,7 @@ PRIVATETESTS += \ qqmltranslation \ qqmlimport \ qqmlobjectmodel \ + qv4assembler \ qv4mm \ ecmascripttests \ bindingdependencyapi diff --git a/tests/auto/qml/qv4assembler/qv4assembler.pro b/tests/auto/qml/qv4assembler/qv4assembler.pro new file mode 100644 index 0000000000..afd7586ac7 --- /dev/null +++ b/tests/auto/qml/qv4assembler/qv4assembler.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qv4assembler +macos:CONFIG -= app_bundle + +SOURCES += tst_qv4assembler.cpp + +QT += qml-private testlib diff --git a/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp new file mode 100644 index 0000000000..9bd1afa256 --- /dev/null +++ b/tests/auto/qml/qv4assembler/tst_qv4assembler.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +class tst_QV4Assembler : public QObject +{ + Q_OBJECT + +private slots: + void perfMapFile(); +}; + +void tst_QV4Assembler::perfMapFile() +{ +#if !defined(Q_OS_LINUX) + QSKIP("perf map files are only generated on linux"); +#else + const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs"; + QProcess process; + + QTemporaryFile infile; + QVERIFY(infile.open()); + infile.write("'use strict'; function foo() { return 42 }; foo();"); + infile.close(); + + QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); + environment.insert("QV4_PROFILE_WRITE_PERF_MAP", "1"); + environment.insert("QV4_JIT_CALL_THRESHOLD", "0"); + + process.setProcessEnvironment(environment); + process.start(qmljs, QStringList({infile.fileName()})); + QVERIFY(process.waitForStarted()); + const qint64 pid = process.processId(); + QVERIFY(pid != 0); + QVERIFY(process.waitForFinished()); + QCOMPARE(process.exitCode(), 0); + + QFile file(QString::fromLatin1("/tmp/perf-%1.map").arg(pid)); + QVERIFY(file.exists()); + QVERIFY(file.open(QIODevice::ReadOnly)); + QList functions; + while (!file.atEnd()) { + const QByteArray contents = file.readLine(); + QVERIFY(contents.endsWith('\n')); + QList fields = contents.split(' '); + QCOMPARE(fields.length(), 3); + bool ok = false; + const qulonglong address = fields[0].toULongLong(&ok, 16); + QVERIFY(ok); + QVERIFY(address > 0); + const ulong size = fields[1].toULong(&ok, 16); + QVERIFY(ok); + QVERIFY(size > 0); + functions.append(fields[2]); + } + QVERIFY(functions.contains("foo\n")); +#endif +} + +QTEST_MAIN(tst_QV4Assembler) + +#include "tst_qv4assembler.moc" + -- cgit v1.2.3 From 8756ab658ac283850d275dfd315b55904fb02259 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 27 Mar 2018 10:13:31 +0200 Subject: QML Tooling: Don't send StartTrace if we haven't started any profilers If the client requested the trace to be started before any engines were registered, this would result in a StartTrace with an empty list of engines, which would never be matched by an EndTrace. This is rather confusing. It's better to send nothing at all. Change-Id: I8728628bd7c9fe068f7275c499440f95542dcc11 Reviewed-by: Simon Hausmann --- src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index e391abab1e..7014249c83 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -281,9 +281,8 @@ void QQmlProfilerServiceImpl::startProfiling(QJSEngine *engine, quint64 features } emit startFlushTimer(); + emit messageToClient(name(), d.data()); } - - emit messageToClient(name(), d.data()); } /*! -- cgit v1.2.3 From 01cb3ff9d191a02cf59b1904ed8eaa79fa7e5c9a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 26 Mar 2018 17:07:27 +0200 Subject: QQuickPointerHandler: make classBegin and componentComplete protected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are public in QQmlParserStatus but don't need to be public here. Also de-inline the default implementations, because this class will be public C++ API eventually. Change-Id: Ic7dfbec853e3d20f45b361401f710dedb5eae416 Reviewed-by: Jan Arve Sæther --- src/quick/handlers/qquickpointerhandler.cpp | 10 +++++++++- src/quick/handlers/qquickpointerhandler_p.h | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp index 959030b8fe..e5b1dc8985 100644 --- a/src/quick/handlers/qquickpointerhandler.cpp +++ b/src/quick/handlers/qquickpointerhandler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -245,6 +245,14 @@ void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission) emit grabPermissionChanged(); } +void QQuickPointerHandler::classBegin() +{ +} + +void QQuickPointerHandler::componentComplete() +{ +} + /*! \internal Acquire or give up the exclusive grab of the given \a point, according to diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h index 9ea6a8b5e2..e2bcce8fc9 100644 --- a/src/quick/handlers/qquickpointerhandler_p.h +++ b/src/quick/handlers/qquickpointerhandler_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -106,9 +106,6 @@ public: GrabPermissions grabPermissions() const { return static_cast(m_grabPermissions); } void setGrabPermissions(GrabPermissions grabPermissions); - void classBegin() override { } - void componentComplete() override { } - Q_SIGNALS: void enabledChanged(); void activeChanged(); @@ -118,6 +115,9 @@ Q_SIGNALS: void canceled(QQuickEventPoint *point); protected: + void classBegin() override; + void componentComplete() override; + QQuickPointerEvent *currentEvent() { return m_currentEvent; } virtual bool wantsPointerEvent(QQuickPointerEvent *event); virtual void handlePointerEventImpl(QQuickPointerEvent *event); -- cgit v1.2.3 From fa1a117ea6e2a3ea0931d441d1dc8917e2145f73 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Tue, 27 Mar 2018 16:30:27 +0200 Subject: Yarr: Add Q_FALLTHROUGH to silence GCC 7 warning Change-Id: I258b5ea84a96f5269b241c9b0cce53202c19c61a Reviewed-by: Friedemann Kleint --- src/3rdparty/masm/yarr/YarrInterpreter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.cpp b/src/3rdparty/masm/yarr/YarrInterpreter.cpp index 4a789f6f28..16fc183cad 100644 --- a/src/3rdparty/masm/yarr/YarrInterpreter.cpp +++ b/src/3rdparty/masm/yarr/YarrInterpreter.cpp @@ -731,6 +731,7 @@ public: context->term -= term.atom.parenthesesWidth; return false; } + Q_FALLTHROUGH(); case QuantifierNonGreedy: if (backTrack->begin == notFound) { backTrack->begin = input.getPos(); -- cgit v1.2.3 From 1acd77ad525f40888333fe3a6e53789cdd5b5afa Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 27 Mar 2018 16:10:09 +0200 Subject: Qml Profiler: Store and send URL of compile events Task-number: QTBUG-67354 Change-Id: I7251095570d5ba8d0a62d854cfcbc339b2455747 Reviewed-by: Simon Hausmann --- src/qml/debugger/qqmlprofiler_p.h | 2 +- .../tst_qqmlprofilerservice.cpp | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index deb4d107d6..287c53ea05 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -190,7 +190,7 @@ public: } RefLocation(QQmlDataBlob *ref) - : Location(QQmlSourceLocation()), locationType(Compiling), sent(false) + : Location(QQmlSourceLocation(), ref->url()), locationType(Compiling), sent(false) { blob = ref; blob->addref(); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 87c123b85d..f2b44a4d95 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -221,6 +221,7 @@ private slots: void flushInterval(); void translationBinding(); void memory(); + void compile(); private: bool m_recordFromStart = true; @@ -752,6 +753,41 @@ void tst_QQmlProfilerService::memory() QVERIFY(smallItems > 5); } +void tst_QQmlProfilerService::compile() +{ + connect(true, "test.qml"); + + QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + m_client->client->setRecording(false); + + checkTraceReceived(); + checkJsHeap(); + + QQmlProfilerDefinitions::Message rangeStage = QQmlProfilerDefinitions::MaximumMessage; + for (auto message : m_client->qmlMessages) { + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.rangeType() == QQmlProfilerDefinitions::Compiling) { + switch (rangeStage) { + case QQmlProfilerDefinitions::MaximumMessage: + QCOMPARE(message.rangeStage(), QQmlProfilerDefinitions::RangeStart); + break; + case QQmlProfilerDefinitions::RangeStart: + QCOMPARE(message.rangeStage(), QQmlProfilerDefinitions::RangeEnd); + break; + default: + QFAIL("Wrong range stage"); + } + rangeStage = message.rangeStage(); + QCOMPARE(type.message(), QQmlProfilerDefinitions::MaximumMessage); + QCOMPARE(type.location().filename(), testFileUrl("test.qml").toString()); + QCOMPARE(type.location().line(), 0); + QCOMPARE(type.location().column(), 0); + } + } + + QCOMPARE(rangeStage, QQmlProfilerDefinitions::RangeEnd); +} + QTEST_MAIN(tst_QQmlProfilerService) #include "tst_qqmlprofilerservice.moc" -- cgit v1.2.3 From ae966127fd7c14669dc202a6d30dc8cf56164f94 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2018 10:17:56 +0200 Subject: Improve QtQuickCompiler cmake tests Perform not only a build test when running the cmake tests, also run the resulting testlib based binary to verify the toolchain end to end. Change-Id: Ic962edf10b810b36f3feb68df55e7230841865d6 Reviewed-by: Kevin Funk --- tests/auto/cmake/CMakeLists.txt | 1 + tests/auto/cmake/qtquickcompiler/CMakeLists.txt | 5 ++++- tests/auto/cmake/qtquickcompiler/main.cpp | 29 +++++++++++++++++++++---- tests/auto/cmake/qtquickcompiler/main.qml | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 4d4d8e3db7..f304a99705 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -25,4 +25,5 @@ add_test(qtquickcompiler ${CMAKE_CTEST_COMMAND} --build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-project qqc_test --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST} + --test-command qqc_test ) diff --git a/tests/auto/cmake/qtquickcompiler/CMakeLists.txt b/tests/auto/cmake/qtquickcompiler/CMakeLists.txt index 6dee1e25dc..ded79d87ed 100644 --- a/tests/auto/cmake/qtquickcompiler/CMakeLists.txt +++ b/tests/auto/cmake/qtquickcompiler/CMakeLists.txt @@ -4,11 +4,14 @@ project(qqc_test) find_package(Qt5Qml 5.0.0 REQUIRED) find_package(Qt5Gui 5.0.0 REQUIRED) +find_package(Qt5Test 5.0.0 REQUIRED) find_package(Qt5QuickCompiler) set(CMAKE_CXXFLAGS "${CMAKE_CXXFLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") qtquick_compiler_add_resources(RESOURCES qqc_test.qrc) +set(CMAKE_AUTOMOC ON) + add_executable(qqc_test "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp" ${RESOURCES}) -target_link_libraries(qqc_test Qt5::Gui Qt5::Qml) +target_link_libraries(qqc_test Qt5::Gui Qt5::Qml Qt5::Test) diff --git a/tests/auto/cmake/qtquickcompiler/main.cpp b/tests/auto/cmake/qtquickcompiler/main.cpp index 47fc709c0a..c357ef60e6 100644 --- a/tests/auto/cmake/qtquickcompiler/main.cpp +++ b/tests/auto/cmake/qtquickcompiler/main.cpp @@ -1,9 +1,30 @@ -#include +#include #include +#include -int main(int argc, char **argv) +class tst_QQC : public QObject { - QGuiApplication app(argc, argv); - return app.exec(); + Q_OBJECT +private slots: + void packaging(); +}; + +void tst_QQC::packaging() +{ + QVERIFY(QFile::exists(":/main.qml")); + QCOMPARE(QFileInfo(":/main.qml").size(), 0); + QVERIFY(QFile::exists(":/main.cpp")); + QVERIFY(QFileInfo(":/main.cpp").size() > 0); + + + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc:/main.qml")); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("success").toInt(), 42); } + +QTEST_MAIN(tst_QQC) + +#include "main.moc" diff --git a/tests/auto/cmake/qtquickcompiler/main.qml b/tests/auto/cmake/qtquickcompiler/main.qml index 1f146d89c3..0836808dc2 100644 --- a/tests/auto/cmake/qtquickcompiler/main.qml +++ b/tests/auto/cmake/qtquickcompiler/main.qml @@ -1,4 +1,4 @@ import QtQml 2.0 QtObject { - property bool success: 42 + property int success: 42 } -- cgit v1.2.3 From 3ae0e1504e94e5c531fa5e919cc8e9f503a5bd34 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2018 10:38:35 +0200 Subject: qtquick_compiler_add_resources() with spaces in paths Ensure a quoted path when constructing the command line for rcc -list. Change-Id: I43e31015e3de58f65f46d2e594cb362d71b123ed Task-number: QTBUG-57758 Reviewed-by: Kevin Funk --- tests/auto/cmake/qtquickcompiler/CMakeLists.txt | 2 +- tests/auto/cmake/qtquickcompiler/main.qml | 4 ---- tests/auto/cmake/qtquickcompiler/qqc_test.qrc | 6 ------ tests/auto/cmake/qtquickcompiler/resources with space/main.qml | 4 ++++ tests/auto/cmake/qtquickcompiler/resources with space/qqc_test.qrc | 6 ++++++ tools/qmlcachegen/Qt5QuickCompilerConfig.cmake | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 tests/auto/cmake/qtquickcompiler/main.qml delete mode 100644 tests/auto/cmake/qtquickcompiler/qqc_test.qrc create mode 100644 tests/auto/cmake/qtquickcompiler/resources with space/main.qml create mode 100644 tests/auto/cmake/qtquickcompiler/resources with space/qqc_test.qrc diff --git a/tests/auto/cmake/qtquickcompiler/CMakeLists.txt b/tests/auto/cmake/qtquickcompiler/CMakeLists.txt index ded79d87ed..4e46544767 100644 --- a/tests/auto/cmake/qtquickcompiler/CMakeLists.txt +++ b/tests/auto/cmake/qtquickcompiler/CMakeLists.txt @@ -9,7 +9,7 @@ find_package(Qt5QuickCompiler) set(CMAKE_CXXFLAGS "${CMAKE_CXXFLAGS} ${Qt5Core_EXECUTABLE_COMPILE_FLAGS}") -qtquick_compiler_add_resources(RESOURCES qqc_test.qrc) +qtquick_compiler_add_resources(RESOURCES "resources with space/qqc_test.qrc") set(CMAKE_AUTOMOC ON) diff --git a/tests/auto/cmake/qtquickcompiler/main.qml b/tests/auto/cmake/qtquickcompiler/main.qml deleted file mode 100644 index 0836808dc2..0000000000 --- a/tests/auto/cmake/qtquickcompiler/main.qml +++ /dev/null @@ -1,4 +0,0 @@ -import QtQml 2.0 -QtObject { - property int success: 42 -} diff --git a/tests/auto/cmake/qtquickcompiler/qqc_test.qrc b/tests/auto/cmake/qtquickcompiler/qqc_test.qrc deleted file mode 100644 index f128b7004b..0000000000 --- a/tests/auto/cmake/qtquickcompiler/qqc_test.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - -./main.qml -./main.cpp - - diff --git a/tests/auto/cmake/qtquickcompiler/resources with space/main.qml b/tests/auto/cmake/qtquickcompiler/resources with space/main.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/cmake/qtquickcompiler/resources with space/main.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/cmake/qtquickcompiler/resources with space/qqc_test.qrc b/tests/auto/cmake/qtquickcompiler/resources with space/qqc_test.qrc new file mode 100644 index 0000000000..63d8d1f37e --- /dev/null +++ b/tests/auto/cmake/qtquickcompiler/resources with space/qqc_test.qrc @@ -0,0 +1,6 @@ + + +./main.qml +../main.cpp + + diff --git a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake index 6fe1662995..56cb3fb55e 100644 --- a/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake +++ b/tools/qmlcachegen/Qt5QuickCompilerConfig.cmake @@ -44,7 +44,7 @@ function(QTQUICK_COMPILER_ADD_RESOURCES outfiles) set(rcc_file_with_compilation_units) - exec_program(${rcc_path} ARGS -list ${input_resource} OUTPUT_VARIABLE rcc_contents) + exec_program(${rcc_path} ARGS -list \"${input_resource}\" OUTPUT_VARIABLE rcc_contents) string(REGEX REPLACE "[\r\n]+" ";" rcc_contents ${rcc_contents}) foreach(it ${rcc_contents}) get_filename_component(extension ${it} EXT) -- cgit v1.2.3 From e09a02fac0d1ecbffeebcb2012ecc659c8986a4e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2018 13:24:43 +0200 Subject: Fix CONFIG+=qtquickcompiler with paths that contain spaces Map spaces to underscores as well. Task-number: QTBUG-54683 Change-Id: Id73c086a2845111623df631c06733ba2b42249e0 Reviewed-by: Andy Shaw --- tests/auto/qml/qmlcachegen/qmlcachegen.pro | 2 ++ tests/auto/qml/qmlcachegen/trickypaths.qml | 4 ++++ tests/auto/qml/qmlcachegen/trickypaths.qrc | 5 +++++ tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 14 ++++++++++++++ tools/qmlcachegen/generateloader.cpp | 1 + 5 files changed, 26 insertions(+) create mode 100644 tests/auto/qml/qmlcachegen/trickypaths.qml create mode 100644 tests/auto/qml/qmlcachegen/trickypaths.qrc diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro index bad912c781..a2f963e8c3 100644 --- a/tests/auto/qml/qmlcachegen/qmlcachegen.pro +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -10,4 +10,6 @@ RESOURCES += workerscripts_test RESOURCES += versionchecks.qml +RESOURCES += trickypaths.qrc + QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qml b/tests/auto/qml/qmlcachegen/trickypaths.qml new file mode 100644 index 0000000000..0836808dc2 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 +QtObject { + property int success: 42 +} diff --git a/tests/auto/qml/qmlcachegen/trickypaths.qrc b/tests/auto/qml/qmlcachegen/trickypaths.qrc new file mode 100644 index 0000000000..271cf6571e --- /dev/null +++ b/tests/auto/qml/qmlcachegen/trickypaths.qrc @@ -0,0 +1,5 @@ + + +trickypaths.qml + + diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index 1c05005c90..5c1692f086 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -52,6 +52,8 @@ private slots: void versionChecksForAheadOfTimeUnits(); void workerScripts(); + + void trickyPaths(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -402,6 +404,18 @@ void tst_qmlcachegen::functionExpressions() QCOMPARE(obj->property("h_connections_handler_called").toBool(), true); } +void tst_qmlcachegen::trickyPaths() +{ + QString pathWithSpaces(QStringLiteral(":/directory with spaces/file name with spaces.qml")); + QVERIFY2(QFile::exists(pathWithSpaces), qPrintable(pathWithSpaces)); + QCOMPARE(QFileInfo(pathWithSpaces).size(), 0); + QQmlEngine engine; + QQmlComponent component(&engine, QUrl("qrc" + pathWithSpaces)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("success").toInt(), 42); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" diff --git a/tools/qmlcachegen/generateloader.cpp b/tools/qmlcachegen/generateloader.cpp index a2e673d15a..96528a9477 100644 --- a/tools/qmlcachegen/generateloader.cpp +++ b/tools/qmlcachegen/generateloader.cpp @@ -51,6 +51,7 @@ QString symbolNamespaceForPath(const QString &relativePath) symbol.replace(QLatin1Char('.'), QLatin1Char('_')); symbol.replace(QLatin1Char('+'), QLatin1Char('_')); symbol.replace(QLatin1Char('-'), QLatin1Char('_')); + symbol.replace(QLatin1Char(' '), QLatin1Char('_')); return symbol; } -- cgit v1.2.3 From 27bf1eafbf6b3b16e0256feff4f6fce3f4fddc72 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2018 12:44:25 +0200 Subject: Fix QQmlExpression::hasError()/error() on syntax errors Record errors that happen during QV4::Script::parse() time in the same way as we record errors during binding evaluation, in order to correctly set the error state of QQmlExpression. This also removes dead code about setting line, description, etc. which is taken care of by ExecutionEngine::catchExceptionAsQmlError. Task-number: QTBUG-67240 Change-Id: I2d586e16803d0883cdd2d1d262b4c67202c00562 Reviewed-by: Michael Brasser --- src/qml/qml/qqmlexpression.cpp | 5 +++++ src/qml/qml/qqmljavascriptexpression.cpp | 14 +++++--------- tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp | 6 +++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 59cc9bb09f..27d3acb9b7 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -252,6 +252,11 @@ QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; + if (hasError()) { + if (isUndefined) + *isUndefined = true; + return QV4::Encode::undefined(); + } } return evaluate(isUndefined); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 74148e3ca4..3daa107b64 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -451,15 +451,11 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * QV4::Script script(v4, qmlContext, code, filename, line); script.parse(); if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - if (error.description().isEmpty()) - error.setDescription(QLatin1String("Exception occurred during function evaluation")); - if (error.line() == -1) - error.setLine(line); - if (error.url().isEmpty()) - error.setUrl(QUrl::fromLocalFile(filename)); - error.setObject(qmlScope); - ep->warning(error); + QQmlDelayedError *error = delayedError(); + error->catchJavaScriptException(v4); + error->setErrorObject(qmlScope); + if (!error->addError(ep)) + ep->warning(error); return; } setupFunction(qmlContext, script.vmFunction); diff --git a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp index e1ad1e8c5f..1decc04ad2 100644 --- a/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp +++ b/tests/auto/qml/qqmlexpression/tst_qqmlexpression.cpp @@ -101,8 +101,12 @@ void tst_qqmlexpression::syntaxError() { QQmlEngine engine; QQmlExpression expression(engine.rootContext(), nullptr, "asd asd"); - QVariant v = expression.evaluate(); + bool isUndefined = false; + QVariant v = expression.evaluate(&isUndefined); QCOMPARE(v, QVariant()); + QVERIFY(expression.hasError()); + QCOMPARE(expression.error().description(), "SyntaxError: Expected token `;'"); + QVERIFY(isUndefined); } void tst_qqmlexpression::exception() -- cgit v1.2.3 From e696a6b7bff04d1581cf59b4d96ecb5508f54169 Mon Sep 17 00:00:00 2001 From: Topi Reinio Date: Tue, 27 Mar 2018 11:17:02 +0200 Subject: Doc: Drop the use of QT_PLUGIN_PATH in documentation config This caused build issues in certain configurations. We don't actually need it, as we can rely on the base Qt include path instead. Also move the custom header under src/quick/doc and rename it. Task-number: QTBUG-67342 Change-Id: Id75e140f9d286f6a47c2ecc039bfb52b13a92608 Reviewed-by: Simon Hausmann Reviewed-by: Martin Smith --- src/quick/QtQuick_pch.h | 45 ------------------------------------------ src/quick/doc/QtQuickDoc | 2 ++ src/quick/doc/qtquick.qdocconf | 6 +++--- 3 files changed, 5 insertions(+), 48 deletions(-) delete mode 100644 src/quick/QtQuick_pch.h create mode 100644 src/quick/doc/QtQuickDoc diff --git a/src/quick/QtQuick_pch.h b/src/quick/QtQuick_pch.h deleted file mode 100644 index 5d66631e7f..0000000000 --- a/src/quick/QtQuick_pch.h +++ /dev/null @@ -1,45 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifdef Q_CLANG_QDOC -#ifndef QTQUICK_PCH -#define QTQUICK_PCH -#include -#include -#endif -#endif diff --git a/src/quick/doc/QtQuickDoc b/src/quick/doc/QtQuickDoc new file mode 100644 index 0000000000..6c151f2ebd --- /dev/null +++ b/src/quick/doc/QtQuickDoc @@ -0,0 +1,2 @@ +#include +#include diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index b6ddb7bf8b..c137d1d2c8 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -5,10 +5,10 @@ description = Qt Quick Reference Documentation version = $QT_VERSION examplesinstallpath = quick -moduleheader = QtQuick_pch.h -includepaths = -I $QT_PLUGIN_PATH/../include/QtQuick \ - -I $QT_PLUGIN_PATH/../include/QtQuickWidgets +# Custom module header that pulls in also QtQuickWidgets +moduleheader = QtQuickDoc +includepaths = -I . qhp.projects = QtQuick -- cgit v1.2.3 From 2d48082e322ae1600985491fe15528dd48a5c604 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 28 Mar 2018 12:01:34 +0200 Subject: QmlProfiler test: Keep recording until we receive the needed events We can apparently catch the engine at a point where it has already registered but not compiled anything. Change-Id: I09cd7fefe731a61a6f2095e125516ecf57c602cb Reviewed-by: Simon Hausmann --- .../qqmlprofilerservice/tst_qqmlprofilerservice.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index f2b44a4d95..562804bc45 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -753,11 +753,24 @@ void tst_QQmlProfilerService::memory() QVERIFY(smallItems > 5); } +static bool hasCompileEvents(const QVector &types) +{ + for (const QQmlProfilerEventType &type : types) { + if (type.message() == QQmlProfilerDefinitions::MaximumMessage + && type.rangeType() == QQmlProfilerDefinitions::Compiling) + return true; + } + return false; +} + void tst_QQmlProfilerService::compile() { - connect(true, "test.qml"); + // Flush interval so that we actually get the events before we stop recording. + connect(true, "test.qml", true, 100); - QTRY_VERIFY(m_client->numLoadedEventTypes() > 0); + // We need to check specifically for compile events as we can otherwise stop recording after the + // StartTrace has arrived, but before it compiles anything. + QTRY_VERIFY(hasCompileEvents(m_client->types)); m_client->client->setRecording(false); checkTraceReceived(); -- cgit v1.2.3 From 9159a7c3db4a0625a2cbe690df5d22f8db1616d1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 27 Mar 2018 13:29:52 +0200 Subject: Fix debug-and-release builds with CONFIG += qtquickcompiler Allow for QMLCACHE_DIR to be pre-defined when modified with debug or release suffix via exclusive_builds_post.prf. Task-number: QTBUG-66675 Change-Id: I007fd8359a860e4c7c2b3efdd90a678ddaad72c3 Reviewed-by: Oswald Buddenhagen --- tools/qmlcachegen/qtquickcompiler.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlcachegen/qtquickcompiler.prf b/tools/qmlcachegen/qtquickcompiler.prf index 1e87a2d7e7..9fa982ca0f 100644 --- a/tools/qmlcachegen/qtquickcompiler.prf +++ b/tools/qmlcachegen/qtquickcompiler.prf @@ -2,7 +2,7 @@ qtPrepareTool(QML_CACHEGEN, qmlcachegen, _FILTER) qtPrepareTool(QMAKE_RCC, rcc, _DEP) -QMLCACHE_DIR = .qmlcache +isEmpty(QMLCACHE_DIR): QMLCACHE_DIR = . defineReplace(qmlCacheResourceFileOutputName) { name = $$relative_path($$1, $$_PRO_FILE_PWD_) -- cgit v1.2.3 From 1053b89c4f79c95084368d92dc5af02579e00804 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 26 Mar 2018 10:19:33 +0200 Subject: qmltest: Use correct Testlib header Fix warning: QtTest/qtest_global.h:4:4: warning: #warning Header is deprecated. Please include instead. [-Wcpp] introduced by qtbase/4db38197c2bac8bcee9d08a0e20ed2c8d127b8f2. Change-Id: Icb5b17d8680f9dc02f43fa9cf06f516cbef3de87 Reviewed-by: Ulf Hermann --- src/qmltest/qtestoptions_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h index 02609a6189..68fe09f793 100644 --- a/src/qmltest/qtestoptions_p.h +++ b/src/qmltest/qtestoptions_p.h @@ -51,7 +51,7 @@ // We mean it. // -#include +#include #include #include -- cgit v1.2.3 From d2ed475b84d872181b872472c742297476c062c2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 3 Apr 2018 13:43:41 +0200 Subject: Examples: remove bogus return Change-Id: I653974c80c6767d9af95b9157a73c09dbdeb76cc Reviewed-by: Mitch Curtis --- examples/qml/referenceexamples/properties/birthdayparty.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/qml/referenceexamples/properties/birthdayparty.cpp b/examples/qml/referenceexamples/properties/birthdayparty.cpp index 8a5e9e553e..717c3e0f71 100644 --- a/examples/qml/referenceexamples/properties/birthdayparty.cpp +++ b/examples/qml/referenceexamples/properties/birthdayparty.cpp @@ -90,7 +90,7 @@ Person *BirthdayParty::guest(int index) const } void BirthdayParty::clearGuests() { - return m_guests.clear(); + m_guests.clear(); } // ![0] -- cgit v1.2.3 From faa36b9084994e2f9ffa85241a1e247da79d3fd0 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 3 Apr 2018 15:02:35 +0200 Subject: Doc: add a flowchart to aid choosing the correct C++ => QML integration http://doc.qt.io/qt-5/qtqml-cppintegration-overview.html is currently a scary wall of text. A flowchart is something that is very easy to follow, making it much easier for users to choose the correct C++ => QML integration method for their situation. Change-Id: If684126395054c69e4583844aa0d7c0ff525c7a1 Reviewed-by: J-P Nurmi Reviewed-by: Venugopal Shivashankar --- src/qml/doc/images/cpp-qml-integration-flowchart.odg | Bin 0 -> 16080 bytes src/qml/doc/images/cpp-qml-integration-flowchart.png | Bin 0 -> 113781 bytes src/qml/doc/src/cppintegration/topic.qdoc | 6 ++++++ 3 files changed, 6 insertions(+) create mode 100644 src/qml/doc/images/cpp-qml-integration-flowchart.odg create mode 100644 src/qml/doc/images/cpp-qml-integration-flowchart.png diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.odg b/src/qml/doc/images/cpp-qml-integration-flowchart.odg new file mode 100644 index 0000000000..8482bd4bf4 Binary files /dev/null and b/src/qml/doc/images/cpp-qml-integration-flowchart.odg differ diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.png b/src/qml/doc/images/cpp-qml-integration-flowchart.png new file mode 100644 index 0000000000..56a33205fd Binary files /dev/null and b/src/qml/doc/images/cpp-qml-integration-flowchart.png differ diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index 6b6e308edf..fbb654378d 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -143,6 +143,12 @@ with a QML module that can then be imported and used by QML code in other applic \l{qtqml-modules-cppplugins.html}{Providing Types and Functionality in a C++ Plugin} for more information. +\section1 Choosing the Correct Integration Method Between C++ and QML + +To quickly determine which integration method is appropriate for your situation, the following +flowchart can be used: + +\image cpp-qml-integration-flowchart \section1 Exposing Attributes of C++ Classes to QML -- cgit v1.2.3 From d18583a804a7afe292f08b77f60773be21b1a8e2 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Tue, 20 Mar 2018 10:25:03 +0100 Subject: Doc: Add a section on scalable application UI Also fixed a grammar issue and a few broken links. Change-Id: I807da06536d6a9101e67fd73858cbbfe90d00663 Reviewed-by: J-P Nurmi Reviewed-by: Mitch Curtis --- src/qml/doc/src/external-resources.qdoc | 5 +-- .../images/qtquickcontrols2-gallery-welcome.png | Bin 0 -> 20138 bytes .../doc/src/guidelines/qtquick-bestpractices.qdoc | 47 ++++++++++++++++++++- .../src/guidelines/qtquick-toolsnutilities.qdoc | 2 +- 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 src/quick/doc/images/qtquickcontrols2-gallery-welcome.png diff --git a/src/qml/doc/src/external-resources.qdoc b/src/qml/doc/src/external-resources.qdoc index c6c922c171..d26288ee6e 100644 --- a/src/qml/doc/src/external-resources.qdoc +++ b/src/qml/doc/src/external-resources.qdoc @@ -69,7 +69,6 @@ \title Qt Creator Manual */ /*! - \externalpage http://doc.qt.io/qtcreator/creator-editor-external.html - \title Qt Creator: Integrating 3rd Party Tools + \externalpage https://fontawesome.com/ + \title Font Awesome */ - diff --git a/src/quick/doc/images/qtquickcontrols2-gallery-welcome.png b/src/quick/doc/images/qtquickcontrols2-gallery-welcome.png new file mode 100644 index 0000000000..e69e7e46a7 Binary files /dev/null and b/src/quick/doc/images/qtquickcontrols2-gallery-welcome.png differ diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 7d4f564a6f..ff3bd625da 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -39,7 +39,7 @@ applications. A fluid and modern UI is key for any application's success in today's world, and that's where QML makes so much sense for a designer or developer. Qt offers the -most basic UI controls that are necessary to a create fluid and modern-looking +most basic UI controls that are necessary to create a fluid and modern-looking UI. It is recommended to browse this list of UI controls before creating your own custom UI control. @@ -307,7 +307,7 @@ to be refactored in the future. \section2 Related Information \list \li \l{Integrating QML and C++} -\li \l{Chat Tutorial Example} +\li \l{Qt Quick Controls 2 - Chat Tutorial}{Chat application tutorial} \endlist \section1 Qt Quick Layouts @@ -366,4 +366,47 @@ Qt Quick easier, see \l {Qt Quick Tools and Utilities}. For information on Qt Quick's scene graph, see \l {Qt Quick Scene Graph}. +\section1 Scalable User Interfaces + +As display resolutions improve, a scalable application UI becomes more and +more important. One of the approaches to achieve this is to maintain several +copies of the UI for different screen resolutions, and load the appropriate one +depending on the available resolution. Although this works pretty +well, it adds to the maintenance overhead. + +Qt offers a better solution to this problem and recommends the application +developers to follow these tips: + +\list + \li Use anchors or the Qt Quick Layouts module to lay out the visual items. + \li Do not specify explicit width and height for a visual item. + \li Provide UI resources such as images and icons for each display resolution + that your application supports. The Qt Quick Controls 2 gallery example + demonstrates this well by providing the \c qt-logo.png for \c @2x, \c @3x, + and \c @4x resolutions, enabling the application to cater to high + resolution displays. Qt automatically chooses the appropriate + image that is suitable for the given display, provided the high DPI scaling + feature is explicitly enabled. + \li Use SVG images for small icons. While larger SVGs can be slow to render, + small ones work well. Vector images avoid the need to provide several + versions of an image, as is necessary with bitmap images. + \li Use font-based icons, such as Font Awesome. These scale to any display + resolution, and also allow colorization. The + Qt Quick Controls 2 Text Editor example demonstrates this well. +\endlist + +With this in place, your application's UI should scale depending +on the display resolution on offer. + +\image qtquickcontrols2-gallery-welcome.png + +\section2 Related Information + +\list + \li \l{Qt Quick Controls2 - Gallery Example}{Gallery example} + \li \l{Qt Quick Controls 2 - Text Editor}{Text Editor example} + \li \l{Font Awesome} + \li \l{Scalability} + \li \l{High DPI Displays} +\endlist */ diff --git a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc index 9729b16392..5f00abc193 100644 --- a/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-toolsnutilities.qdoc @@ -46,7 +46,7 @@ The following is a list of example applications that use UIs created by the Qt Quick Designer: \list - \li \l{Qt Quick Controls 2 - Contacts List} + \li \l{Qt Quick Controls 2 - Contact List} \li \l{Qt Quick Controls 2 - Flat Style} \endlist -- cgit v1.2.3 From 271e0c1e6e90bb3efd327ebee318c9f0ac95fd71 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Fri, 23 Mar 2018 09:35:35 +0100 Subject: Doc: correct Sprite's detailed description Task-number: QTBUG-58641 Change-Id: Ib339746d459328bdac8fa0eeff80300df99ff482 Reviewed-by: Venugopal Shivashankar --- src/quick/items/qquicksprite.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp index 99b1b1f430..6b8567439b 100644 --- a/src/quick/items/qquicksprite.cpp +++ b/src/quick/items/qquicksprite.cpp @@ -51,8 +51,8 @@ QT_BEGIN_NAMESPACE \ingroup qtquick-visual-utility \brief Specifies sprite animations - QQuickSprite renders sprites of one or more frames and animates them. The sprites - can be in the middle of an image file, or split along multiple rows, as long as they form + Sprite defines a series of one or more frames to be animated and rendered by SpriteSequence. + The sprites can be in the middle of an image file, or split along multiple rows, as long as they form a contiguous line wrapping to the next row of the file from the left edge of the file. For full details, see the \l{Sprite Animations} overview. -- cgit v1.2.3 From 20a2b1e11e9b3bfc86c24e3e283cddc4b85233ab Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 3 Apr 2018 16:11:37 +0200 Subject: Link to C++ => QML flowchart in QML registration function docs Change-Id: I684bf2c461be5ebc78df06c816a0717d5958e0de Reviewed-by: Venugopal Shivashankar --- src/qml/doc/src/qmlfunctions.qdoc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index ede213b84a..82d85a1472 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -105,6 +105,8 @@ than the actual version of the library. Indeed, it is normal for the new library to allow QML written to previous versions to continue to work, even if more advanced versions of some of its types are available. + + \sa {Choosing the Correct Integration Method Between C++ and QML} */ /*! @@ -141,7 +143,8 @@ Returns the QML type id. - \sa qmlRegisterTypeNotAvailable() + \sa qmlRegisterTypeNotAvailable(), + {Choosing the Correct Integration Method Between C++ and QML} */ /*! @@ -266,7 +269,8 @@ Without this, a generic "Game is not a type" message would be given. - \sa qmlRegisterUncreatableType() + \sa qmlRegisterUncreatableType(), + {Choosing the Correct Integration Method Between C++ and QML} */ /*! @@ -279,6 +283,8 @@ system. Returns the QML type id. + + \sa {Choosing the Correct Integration Method Between C++ and QML} */ /*! @@ -346,7 +352,9 @@ property int someValue: ExampleApi.MyApi.someProperty } \endqml - */ + + \sa {Choosing the Correct Integration Method Between C++ and QML} +*/ /*! \fn template QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create = true) @@ -486,7 +494,8 @@ } \endcode - */ + \sa {Choosing the Correct Integration Method Between C++ and QML} +*/ /*! \fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) -- cgit v1.2.3 From 67c9f735ceb148dba482ca7174b5f3bd8de7892f Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Tue, 3 Apr 2018 16:07:50 +0200 Subject: Improve documentation for qmlRegisterType() Explain when it should be used. Task-number: QTBUG-67332 Change-Id: I759a192778a0370831f44b871e58c5ee49d3fe3c Reviewed-by: Venugopal Shivashankar --- src/qml/doc/src/qmlfunctions.qdoc | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 82d85a1472..34dce8f4da 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -282,6 +282,74 @@ system. Instances of this type cannot be created from the QML system. + This function should be used when the type will not be referenced by name. + Specifically, it has to be used for C++ types that are used as the left-hand + side of a property binding. + + For example, consider the following two classes: + + \code + class Bar : public QObject + { + Q_OBJECT + Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged) + + public: + Bar() {} + + QString baz() const { return mBaz; } + + void setBaz(const QString &baz) + { + if (baz == mBaz) + return; + + mBaz = baz; + emit bazChanged(); + } + + signals: + void bazChanged(); + + private: + QString mBaz; + }; + + class Foo : public QObject + { + Q_OBJECT + Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL) + + public: + Foo() {} + + Bar *bar() { return &mBar; } + + private: + Bar mBar; + }; + \endcode + + In QML, we assign a string to the \c baz property of \c bar: + + \code + Foo { + bar.baz: "abc" + Component.onCompleted: print(bar.baz) + } + \endcode + + For the QML engine to know that the \c Bar type has a \c baz property, + we have to make \c Bar known: + + \code + qmlRegisterType("App", 1, 0, "Foo"); + qmlRegisterType(); + \code + + As the \c Foo type is instantiated in QML, it must be registered + with the version of \l qmlRegisterType() that takes an import URI. + Returns the QML type id. \sa {Choosing the Correct Integration Method Between C++ and QML} -- cgit v1.2.3 From a22297cd1387413f7c5d7aeb74935905489aae34 Mon Sep 17 00:00:00 2001 From: Venugopal Shivashankar Date: Mon, 26 Mar 2018 13:28:44 +0200 Subject: Doc: Change the section title to avoid a linking conflict Otherwise, all links to the Qt Quick Layouts module page will lead here. Change-Id: I04c14ca9a28ebf0e19e489229352ba4d11f24859 Reviewed-by: Mitch Curtis --- src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index ff3bd625da..218ebbe54d 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -310,7 +310,7 @@ to be refactored in the future. \li \l{Qt Quick Controls 2 - Chat Tutorial}{Chat application tutorial} \endlist -\section1 Qt Quick Layouts +\section1 Using Qt Quick Layouts Qt offers Qt Quick Layouts to arrange Qt Quick items visually in a layout. Unlike its alternative, the item positioners, the Qt Quick Layouts can also -- cgit v1.2.3 From b780aeb0ce30b040b9057f1e2b447b08b24b45cb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 17 Jan 2018 15:44:18 +0100 Subject: Allow for QQmlData::set*Bit and clear*Bit to be inlined Change-Id: I216adf12e7ec402f3ccb4f846165171c9833f23b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmldata_p.h | 57 ++++++++++++++++++++++++++++++--- src/qml/qml/qqmlengine.cpp | 79 ++++++++++------------------------------------ 2 files changed, 70 insertions(+), 66 deletions(-) diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 20b96d2c4b..59fefde893 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -208,12 +208,12 @@ public: QQmlData**prevContextObject; inline bool hasBindingBit(int) const; - void clearBindingBit(int); - void setBindingBit(QObject *obj, int); + inline void setBindingBit(QObject *obj, int); + inline void clearBindingBit(int); inline bool hasPendingBindingBit(int index) const; - void setPendingBindingBit(QObject *obj, int); - void clearPendingBindingBit(int); + inline void setPendingBindingBit(QObject *obj, int); + inline void clearPendingBindingBit(int); quint16 lineNumber; quint16 columnNumber; @@ -304,6 +304,27 @@ private: const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; return bits[offset] & bitFlagForBit(bit); } + + Q_ALWAYS_INLINE void clearBit(int bit) + { + uint offset = QQmlData::offsetForBit(bit); + if (bindingBitsArraySize > offset) { + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + bits[offset] &= ~QQmlData::bitFlagForBit(bit); + } + } + + Q_ALWAYS_INLINE void setBit(QObject *obj, int bit) + { + uint offset = QQmlData::offsetForBit(bit); + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + if (Q_UNLIKELY(bindingBitsArraySize <= offset)) + bits = growBits(obj, bit); + bits[offset] |= QQmlData::bitFlagForBit(bit); + } + + Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit); + Q_DISABLE_COPY(QQmlData); }; @@ -356,6 +377,20 @@ bool QQmlData::hasBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2); } +void QQmlData::setBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2); +} + +void QQmlData::clearBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2); +} + bool QQmlData::hasPendingBindingBit(int coreIndex) const { Q_ASSERT(coreIndex >= 0); @@ -364,6 +399,20 @@ bool QQmlData::hasPendingBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2 + 1); } +void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2 + 1); +} + +void QQmlData::clearPendingBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2 + 1); +} + void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) { QQmlData *data = QQmlData::get(o, false); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 54a7c5130d..613f9b4fe5 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -111,12 +111,6 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE -typedef QQmlData::BindingBitsType BindingBitsType; -enum { - BitsPerType = QQmlData::BitsPerType, - InlineBindingArraySize = QQmlData::InlineBindingArraySize -}; - void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); @@ -1896,66 +1890,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) } } -static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) -{ - uint offset = QQmlData::offsetForBit(bit); - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - if (Q_UNLIKELY(data->bindingBitsArraySize <= offset)) { - int props = QQmlMetaObject(obj).propertyCount(); - Q_ASSERT(bit < 2 * props); - - uint arraySize = (2 * static_cast(props) + BitsPerType - 1) / BitsPerType; - Q_ASSERT(arraySize > InlineBindingArraySize && arraySize > data->bindingBitsArraySize); - - BindingBitsType *newBits = static_cast(malloc(arraySize*sizeof(BindingBitsType))); - memcpy(newBits, bits, data->bindingBitsArraySize * sizeof(BindingBitsType)); - memset(newBits + data->bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - data->bindingBitsArraySize)); - - if (data->bindingBitsArraySize > InlineBindingArraySize) - free(bits); - data->bindingBits = newBits; - bits = newBits; - data->bindingBitsArraySize = arraySize; - } - Q_ASSERT(offset < data->bindingBitsArraySize); - bits[offset] |= QQmlData::bitFlagForBit(bit); -} - -static void QQmlData_clearBit(QQmlData *data, int bit) -{ - uint offset = QQmlData::offsetForBit(bit); - if (data->bindingBitsArraySize > offset) { - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - bits[offset] &= ~QQmlData::bitFlagForBit(bit); - } -} - -void QQmlData::clearBindingBit(int coreIndex) +QQmlData::BindingBitsType *QQmlData::growBits(QObject *obj, int bit) { - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2); -} + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + int props = QQmlMetaObject(obj).propertyCount(); + Q_ASSERT(bit < 2 * props); + Q_UNUSED(bit); // .. for Q_NO_DEBUG mode when the assert above expands to empty -void QQmlData::setBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2); -} + uint arraySize = (2 * static_cast(props) + BitsPerType - 1) / BitsPerType; + Q_ASSERT(arraySize > 1); + Q_ASSERT(arraySize <= 0xffff); // max for bindingBitsArraySize -void QQmlData::clearPendingBindingBit(int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2 + 1); -} + BindingBitsType *newBits = static_cast(malloc(arraySize*sizeof(BindingBitsType))); + memcpy(newBits, bits, bindingBitsArraySize * sizeof(BindingBitsType)); + memset(newBits + bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - bindingBitsArraySize)); -void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2 + 1); + if (bindingBitsArraySize > InlineBindingArraySize) + free(bits); + bindingBits = newBits; + bits = newBits; + bindingBitsArraySize = arraySize; + return bits; } QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) -- cgit v1.2.3 From d7ada80e5c134a0d1a13539a46e378b7ea6dcf6d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 6 Apr 2018 12:14:17 +0200 Subject: QQmlDebuggingEnabler test: Relax output string condition Apparently the "qml: " prefix to strings output via console.log() is not reliable. We can also get "qml - ", as seen in the test log. Also, increase the timeouts. The test log indicates that they are too small. Change-Id: Icb0da329e52273f9300504047b79b1ad41c02892 Task-number: QTBUG-67505 Reviewed-by: Mitch Curtis --- .../qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp index a76740a3f9..52e7f85e52 100644 --- a/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp +++ b/tests/auto/qml/debugger/qqmldebuggingenabler/qqmldebuggingenabler/tst_qqmldebuggingenabler.cpp @@ -126,8 +126,10 @@ void tst_QQmlDebuggingEnabler::qmlscene() } QCOMPARE(m_process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(m_process->output().contains(QLatin1String("qml: Component.onCompleted"))); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains( + QLatin1String("Component.onCompleted")), 15000); + } } void tst_QQmlDebuggingEnabler::custom_data() @@ -171,8 +173,10 @@ void tst_QQmlDebuggingEnabler::custom() } QCOMPARE(m_process->state(), QLatin1String("running")); - if (!blockMode) - QTRY_VERIFY(m_process->output().contains(QLatin1String("QQmlEngine created"))); + if (!blockMode) { + QTRY_VERIFY_WITH_TIMEOUT(m_process->output().contains(QLatin1String("QQmlEngine created")), + 15000); + } } QTEST_MAIN(tst_QQmlDebuggingEnabler) -- cgit v1.2.3 From d868bb4f3e4b0424fd4a2989ff1c82692b0f014c Mon Sep 17 00:00:00 2001 From: Nils Jeisecke Date: Wed, 4 Apr 2018 13:23:00 +0200 Subject: Fix Flickable mouse wheel handling on macOS On macOS a special movementEnding timer was added to the wheelEvent handling to fix QTBUG-63026. This has introduced a regression with wrong vData/hData.moving flags: When the timer fires before the Qt::ScrollEnd phase is reached, movementEnding is invoked early (can be reproduced with very slow scrolling using the Magic Mouse). In this case movementEnding sets vData.moving = false but not vMoved = false because scrollingPhase is still true. This will prevent any further invocation of movementStarting from inside the drag method (it expects a change in vMoved) so once this situation has occurred the "moving" flags will be out of sync. Visible effect: If a ListView has a currentItem set with setCurrentIndex, its viewportMoved method will no longer correctly set the moveReason to "Mouse" because the check depends on "moving" flag as an indicator for mouse interaction. This results in the view permanently jumping back to the current item on any scroll operation because the moveReason will be stuck at "SetIndex". The fix is to ignore the timer event if scrollingPhase is still true. Task-number: QTBUG-67460 Change-Id: I7cf02b8c625b7baf249ad26c4e0c3df874a18eae Reviewed-by: Richard Moe Gustavsen Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 8cb64377cc..9c775f7e93 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1649,10 +1649,12 @@ void QQuickFlickable::timerEvent(QTimerEvent *event) } } else if (event->timerId() == d->movementEndingTimer.timerId()) { d->movementEndingTimer.stop(); - d->pressed = false; - d->stealMouse = false; - if (!d->velocityTimeline.isActive() && !d->timeline.isActive()) - movementEnding(true, true); + if (!d->scrollingPhase) { + d->pressed = false; + d->stealMouse = false; + if (!d->velocityTimeline.isActive() && !d->timeline.isActive()) + movementEnding(true, true); + } } } -- cgit v1.2.3 From 55400f1f194e5abc368c25313caccd5818166fb8 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 21 Mar 2018 09:13:18 +0100 Subject: Doc: Improve the "Interacting with QML from C++" section Change-Id: I8930314179514d091a39640551f2816a23cbebc8 Reviewed-by: Simon Hausmann --- .../doc/src/guidelines/qtquick-bestpractices.qdoc | 24 ++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc index 218ebbe54d..e70791d1c9 100644 --- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc +++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc @@ -191,8 +191,11 @@ or frequently modified. \section2 Interacting with QML from C++ Although Qt enables you to manipulate QML from C++, it is not recommended to do -so. To explain why, let's take a look at a simplified example. Suppose we were -writing the UI for a settings page: +so. To explain why, let's take a look at a simplified example. + +\section3 Pulling References from QML + +Suppose we were writing the UI for a settings page: \qml import QtQuick 2.11 @@ -261,14 +264,18 @@ Then, in C++, we find that object and connect to its change signal: #include "main.moc" \endcode -The problem with this approach is that the C++ logic layer depends on the QML +With this approach, references to objects are "pulled" from QML. +The problem with this is that the C++ logic layer depends on the QML presentation layer. If we were to refactor the QML in such a way that the \c objectName changes, or some other change breaks the ability for the C++ to find the QML object, our workflow becomes much more complicated and tedious. +\section3 Pushing References to QML + Refactoring QML is a lot easier than refactoring C++, so in order to make maintenance pain-free, we should strive to keep C++ types unaware of QML as -much as possible. This can be achieved by exposing the C++ types to QML: +much as possible. This can be achieved by "pushing" references to C++ types +into QML: \code int main(int argc, char *argv[]) @@ -304,6 +311,15 @@ The QML then calls the C++ slot directly: With this approach, the C++ remains unchanged in the event that the QML needs to be refactored in the future. +In the example above, we set a context property on the root context to expose +the C++ object to QML. This means that the property is available to every +component loaded by the engine. Context properties are useful for objects that +must be available as soon as the QML is loaded and cannot be instantiated in +QML. + +\l {Integrating QML and C++} demonstrates an alternative approach where QML is +made aware of a C++ type so that it can instantiate it itself. + \section2 Related Information \list \li \l{Integrating QML and C++} -- cgit v1.2.3 From bdf0771a695f144dcd91f0f9a6cc6b81c5763d87 Mon Sep 17 00:00:00 2001 From: Paul Wicking Date: Wed, 21 Mar 2018 12:28:00 +0100 Subject: Doc: Remove erroneous example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current documentation is not current anymore. Removed faulty example and added link to relevant page for more information. Task-number: QTBUG-67082 Change-Id: Ic2d29ab358b227289480d9986679676048a35821 Reviewed-by: Topi Reiniö Reviewed-by: Simon Hausmann --- src/qml/doc/src/javascript/hostenvironment.qdoc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index d11e96df2b..eb40f10065 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -154,10 +154,11 @@ This restriction exists as the QML environment is not yet fully established. To run code after the environment setup has completed, see \l {JavaScript in Application Startup Code}. -\li The value of \c this is currently undefined in QML in the majority of contexts. +\li The value of \c this is undefined in QML in the majority of contexts. The \c this keyword is supported when binding properties from JavaScript. -In all other situations, the value of +In QML binding expressions, QML signal handlers, and QML declared functions, +\c this refers to the scope object. In all other situations, the value of \c this is undefined in QML. To refer to a specific object, provide an \c id. For example: @@ -168,20 +169,17 @@ Item { function mouseAreaClicked(area) { console.log("Clicked in area at: " + area.x + ", " + area.y); } - // This will not work because this is undefined + // This will pass area to the function MouseArea { - height: 50; width: 200 - onClicked: mouseAreaClicked(this) - } - // This will pass area2 to the function - MouseArea { - id: area2 + id: area y: 50; height: 50; width: 200 - onClicked: mouseAreaClicked(area2) + onClicked: mouseAreaClicked(area) } } \endqml +\sa {Scope and Naming Resolution} + \endlist -- cgit v1.2.3 From 839f09c65523fb5c419b62e078f72bb39285449a Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 28 Mar 2018 00:24:56 +0100 Subject: Avoid marking hidden windows as updatePending in Gui render loop Since eeb320bbd8763f3e72f79369cc3908e999a0da3c the GL context only deletes textures when all windows with pending updates have finished rendering. renderWindow will not process any window that is not visible. This leaves a logic bug that we can have the updatePending flag set but never cleared. If we have two windows, this leaves the other window still updating normally, but lastDirtyWindow will always be false and we never call endSync. This results in an effective memory leak of all textures. This patch resets the flag on hide() a move that can be considered safe given the show() method will reset this flag anyway. Change-Id: Iab0171716e27e31077a66b5e36a00bf28a2e7a8c Reviewed-by: Kai Uwe Broulik Reviewed-by: Qt CI Bot Reviewed-by: Dominik Holland Reviewed-by: Aleix Pol Reviewed-by: Andy Nichols --- src/quick/scenegraph/qsgrenderloop.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 60f3538662..2eaed497ef 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -305,6 +305,8 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); cd->fireAboutToStop(); + if (m_windows.contains(window)) + m_windows[window].updatePending = false; } void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) @@ -494,7 +496,8 @@ QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) { - if (!m_windows.contains(window)) + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (!cd->isRenderable() || !m_windows.contains(window)) return; m_windows[window].updatePending = true; -- cgit v1.2.3 From d1a5adb5d6d6d64bf648cbeda70663751c8b982d Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 9 Apr 2018 13:45:57 +0200 Subject: Improve QQuickItemGrabResult::image() docs It can return a null image. An "empty" image is confusing, as there's no function in QImage with that name, requiring the user to consult the docs. Change-Id: Iaa805510804162fe3a7df00394b4a9ad61ac1d20 Reviewed-by: J-P Nurmi --- src/quick/items/qquickitemgrabresult.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp index 003fde8c9e..b45cb09c4b 100644 --- a/src/quick/items/qquickitemgrabresult.cpp +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -139,7 +139,7 @@ public: * This property holds the pixel results from a grab. * * If the grab is not yet complete or if it failed, - * an empty image is returned. + * a null image is returned (\c {image.isNull()} will return \c true). */ /*! -- cgit v1.2.3 From 740462a87480e1b347d833321cdcf316840c1c13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2018 15:31:29 +0200 Subject: Fix crash when QML engine warning handler feeds errors back into QML When a QQmlEngine warning handler that's called during component instantiation results in subsequent component instantiations, either via the signal or via a Qt message handler like in the bug report, then we might end up modifying the linked list of errored bindings before returning from the QQmlEnginePrivate::warning() call. The easy fix is to extract the QQmlError, unlink the delayed error from the linked list and then deliver the error to the QQmlEngine. Change-Id: I6b7be61b57b35636282595937046ff76091144a3 Task-number: QTBUG-53293 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcomponent.cpp | 3 +-- src/qml/qml/qqmlengine.cpp | 13 ------------- src/qml/qml/qqmlengine_p.h | 2 -- src/qml/qml/qqmlincubator.cpp | 12 ++++-------- src/qml/qml/qqmljavascriptexpression.cpp | 2 +- src/qml/qml/qqmljavascriptexpression_p.h | 16 +++++++++------- 6 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 3174bbecd3..fe4768db15 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -929,8 +929,7 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS if (0 == enginePriv->inProgressCreations) { while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 613f9b4fe5..7e11177caa 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1996,11 +1996,6 @@ void QQmlEnginePrivate::warning(const QList &errors) dumpwarning(errors); } -void QQmlEnginePrivate::warning(QQmlDelayedError *error) -{ - warning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error) { if (engine) @@ -2017,14 +2012,6 @@ void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList &erro dumpwarning(error); } -void QQmlEnginePrivate::warning(QQmlEngine *engine, QQmlDelayedError *error) -{ - if (engine) - QQmlEnginePrivate::get(engine)->warning(error); - else - dumpwarning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error) { if (engine) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index d6110c6699..da52e01793 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -231,10 +231,8 @@ public: void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList &); - void warning(QQmlDelayedError *); static void warning(QQmlEngine *, const QQmlError &); static void warning(QQmlEngine *, const QList &); - static void warning(QQmlEngine *, QQmlDelayedError *); static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList &); diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 4546a4423f..df168960c6 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -367,10 +367,8 @@ finishIncubate: enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } else if (!creator.isNull()) { vmeGuard.guard(creator.data()); @@ -575,10 +573,8 @@ void QQmlIncubator::clear() enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 3daa107b64..40cf1417d0 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -455,7 +455,7 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * error->catchJavaScriptException(v4); error->setErrorObject(qmlScope); if (!error->addError(ep)) - ep->warning(error); + ep->warning(error->error()); return; } setupFunction(qmlContext, script.vmFunction); diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index a028850074..bff8866011 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -63,16 +63,18 @@ class QQmlDelayedError { public: inline QQmlDelayedError() : nextError(nullptr), prevError(nullptr) {} - inline ~QQmlDelayedError() { removeError(); } + inline ~QQmlDelayedError() { (void)removeError(); } bool addError(QQmlEnginePrivate *); - inline void removeError() { - if (!prevError) return; - if (nextError) nextError->prevError = prevError; - *prevError = nextError; - nextError = nullptr; - prevError = nullptr; + Q_REQUIRED_RESULT inline QQmlError removeError() { + if (prevError) { + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = nullptr; + prevError = nullptr; + } + return m_error; } inline bool isValid() const { return m_error.isValid(); } -- cgit v1.2.3 From ccad6b577016c8a0986f56b2656471896b5817ea Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 3 Apr 2018 12:37:04 +0200 Subject: clear() the loader before destroying it This prevents bindings in children being evaluated and removes spurious qml errors on the command line. Amends 2eb2d6386da304cd1164264ae0bff685c796d89c. Task-number: QTBUG-63729 Change-Id: I88b85ed40c6b8c5fbb422831055942cc0f4ee424 Reviewed-by: Simon Hausmann --- src/quick/items/qquickloader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 6960e16bd9..cd48896e58 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -311,10 +311,7 @@ QQuickLoader::QQuickLoader(QQuickItem *parent) QQuickLoader::~QQuickLoader() { Q_D(QQuickLoader); - if (d->item) { - QQuickItemPrivate *p = QQuickItemPrivate::get(d->item); - p->removeItemChangeListener(d, watchedChanges); - } + d->clear(); } /*! -- cgit v1.2.3 From 51b73e0bb68812d78315af032546750d04656c02 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2018 16:49:19 +0200 Subject: Fix XMLHttpRequest when used with QQmlEngine::evaluate Our XHR implementation insists on a valid QQmlContext when processing callbacks. This is to protect against callbacks being triggered after dynamic QML contexts such as delegates have been destroyed. Unfortunately those checks are too strict and make it impossible to use XHR from within plain JS scripts (where v4->callingQmlContext() will return a null pointer). Dispatching the callbacks in functions that are directly called from QML/JS is safe and something we can do unconditionally. This applies to the callbacks triggered from abort() and open() for example. When we're called from QNetworkAccessManager we should enforce the continued existence of a QML context only if it was present at send() time. Task-number: QTBUG-67337 Change-Id: I8235f6ef407adc3eaeeff4eee72238ba6750afb2 Reviewed-by: Michael Brasser Reviewed-by: Valery Kotov Reviewed-by: Lars Knoll --- src/qml/qml/qqmlxmlhttprequest.cpp | 62 +++++++++++----------- .../qml/qqmlxmlhttprequest/data/noqmlcontext.js | 11 ++++ .../qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp | 26 +++++++++ 3 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 5673acec89..567d83f3ee 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1019,7 +1019,7 @@ public: Opened = 1, HeadersReceived = 2, Loading = 3, Done = 4 }; - QQmlXMLHttpRequest(QNetworkAccessManager *manager); + QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4); virtual ~QQmlXMLHttpRequest(); bool sendFlag() const; @@ -1028,9 +1028,9 @@ public: int replyStatus() const; QString replyStatusText() const; - ReturnedValue open(Object *thisObject, QQmlContextData *context, const QString &, const QUrl &, LoadType); + ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType); ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &); - ReturnedValue abort(Object *thisObject, QQmlContextData *context); + ReturnedValue abort(Object *thisObject); void addHeader(const QString &, const QString &); QString header(const QString &name) const; @@ -1078,9 +1078,10 @@ private: PersistentValue m_thisObject; QQmlContextDataRef m_qmlContext; + bool m_wasConstructedWithQmlContext = true; - static void dispatchCallback(Object *thisObj, QQmlContextData *context); - void dispatchCallback(); + static void dispatchCallbackNow(Object *thisObj); + void dispatchCallbackSafely(); int m_status; QString m_statusText; @@ -1096,12 +1097,13 @@ private: QV4::PersistentValue m_parsedDocument; }; -QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager) +QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4) : m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(nullptr), m_network(nullptr), m_nam(manager) , m_responseType() , m_parsedDocument() { + m_wasConstructedWithQmlContext = v4->callingQmlContext() != nullptr; } QQmlXMLHttpRequest::~QQmlXMLHttpRequest() @@ -1134,7 +1136,7 @@ QString QQmlXMLHttpRequest::replyStatusText() const return m_statusText; } -ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *context, const QString &method, const QUrl &url, LoadType loadType) +ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, const QString &method, const QUrl &url, LoadType loadType) { destroyNetwork(); m_sendFlag = false; @@ -1145,7 +1147,7 @@ ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *cont m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad); m_state = Opened; m_addedHeaders.clear(); - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); return Encode::undefined(); } @@ -1297,7 +1299,7 @@ ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *cont return Encode::undefined(); } -ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *context) +ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject) { destroyNetwork(); m_responseEntityBody = QByteArray(); @@ -1310,7 +1312,7 @@ ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *con m_state = Done; m_sendFlag = false; - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); } m_state = Unsent; @@ -1329,7 +1331,7 @@ void QQmlXMLHttpRequest::readyRead() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } bool wasEmpty = m_responseEntityBody.isEmpty(); @@ -1337,7 +1339,7 @@ void QQmlXMLHttpRequest::readyRead() if (wasEmpty && !m_responseEntityBody.isEmpty()) m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } static const char *errorToString(QNetworkReply::NetworkError error) @@ -1380,14 +1382,14 @@ void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error) error == QNetworkReply::ServiceUnavailableError || error == QNetworkReply::UnknownServerError) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } else { m_errorFlag = true; m_responseEntityBody = QByteArray(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); } #define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 @@ -1419,7 +1421,7 @@ void QQmlXMLHttpRequest::finished() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } m_responseEntityBody.append(m_network->readAll()); readEncoding(); @@ -1436,11 +1438,11 @@ void QQmlXMLHttpRequest::finished() destroyNetwork(); if (m_state < Loading) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); m_thisObject.clear(); m_qmlContext.setContextData(nullptr); @@ -1557,17 +1559,10 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const return m_responseEntityBody; } -void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *context) +void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj) { Q_ASSERT(thisObj); - if (!context) - // if the calling context object is no longer valid, then it has been - // deleted explicitly (e.g., by a Loader deleting the itemContext when - // the source is changed). We do nothing in this case, as the evaluation - // cannot succeed. - return; - QV4::Scope scope(thisObj->engine()); ScopedString s(scope, scope.engine->newString(QStringLiteral("onreadystatechange"))); ScopedFunctionObject callback(scope, thisObj->get(s)); @@ -1585,9 +1580,16 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont } } -void QQmlXMLHttpRequest::dispatchCallback() +void QQmlXMLHttpRequest::dispatchCallbackSafely() { - dispatchCallback(m_thisObject.as(), m_qmlContext.contextData()); + if (m_wasConstructedWithQmlContext && !m_qmlContext.contextData()) + // if the calling context object is no longer valid, then it has been + // deleted explicitly (e.g., by a Loader deleting the itemContext when + // the source is changed). We do nothing in this case, as the evaluation + // cannot succeed. + return; + + dispatchCallbackNow(m_thisObject.as()); } void QQmlXMLHttpRequest::destroyNetwork() @@ -1640,7 +1642,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast(f); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); Scoped w(scope, scope.engine->memoryManager->allocObject(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); @@ -1778,7 +1780,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + return r->open(w, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -1860,7 +1862,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const FunctionObject *b, cons V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return r->abort(w, scope.engine->callingQmlContext()); + return r->abort(w); } ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) diff --git a/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js new file mode 100644 index 0000000000..adb7269310 --- /dev/null +++ b/tests/auto/qml/qqmlxmlhttprequest/data/noqmlcontext.js @@ -0,0 +1,11 @@ +(function(url, resultCollector) { + var x = new XMLHttpRequest; + x.open("GET", url); + x.setRequestHeader("Accept-Language","en-US"); + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + resultCollector.responseText = x.responseText + } + } + x.send() +}) diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 59716acc0d..ecce6515ed 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -108,6 +108,8 @@ private slots: void text(); void cdata(); + void noQmlContext(); + // Crashes // void outstanding_request_at_shutdown(); @@ -1243,6 +1245,30 @@ void tst_qqmlxmlhttprequest::cdata() QCOMPARE(object->property("status").toInt(), 200); } +void tst_qqmlxmlhttprequest::noQmlContext() +{ + TestHTTPServer server; + QVERIFY2(server.listen(), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("open_network.expect"), + testFileUrl("open_network.reply"), + testFileUrl("testdocument.html"))); + QUrl url = server.urlString(QStringLiteral("/testdocument.html")); + + QQmlEngine engine; + + QFile f(testFile("noqmlcontext.js")); + QVERIFY(f.open(QIODevice::ReadOnly)); + QString script = QString::fromUtf8(f.readAll()); + QJSValue testFunction = engine.evaluate(script); + QVERIFY(testFunction.isCallable()); + + QJSValue resultCollector = engine.newObject(); + + testFunction.call(QJSValueList() << url.toString() << resultCollector); + + QTRY_COMPARE(resultCollector.property("responseText").toString(), "QML Rocks!\n"); + } + void tst_qqmlxmlhttprequest::stateChangeCallingContext() { #ifdef Q_OS_WIN -- cgit v1.2.3 From f2633df1be25acdf1a5444bbd301955ae3412297 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 10 Apr 2018 15:23:30 +0200 Subject: Fix leak of compiler contexts Repeatedly entering a context that was entered before would result in leaking the previously entered context. This can happen when compiling child objects in a QML file, which is done recursively. So before compiling the bindings/function in the child object, first the global context and then the QML context are entered. The fix is to re-use the global context, as it's the same anyway for all objects in the same module. And we can remove entering the QML context, because nothing is in there, and we don't put anything in it ever. Change-Id: Ib1c4259d2dec22df46e96edb65bc3d377e52e671 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 4 +--- src/qml/compiler/qv4compilercontext.cpp | 2 ++ src/qml/compiler/qv4compilerscanfunctions.cpp | 21 ++++++++++++++------- src/qml/compiler/qv4compilerscanfunctions_p.h | 7 ++++--- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index a9d86b24f5..4a1b27d7aa 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1809,8 +1809,7 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList runtimeFunctionIndices(functions.size()); QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode); - scan.enterEnvironment(nullptr, QV4::Compiler::QmlBinding); - scan.enterQmlScope(qmlRoot, QStringLiteral("context scope")); + scan.enterGlobalEnvironment(QV4::Compiler::QmlBinding); for (const CompiledFunctionOrExpression &f : functions) { Q_ASSERT(f.node != qmlRoot); QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast(f.node); @@ -1824,7 +1823,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListfirstSourceLocation(); diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 89f602b409..92df98f201 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -73,13 +73,20 @@ void ScanFunctions::operator()(Node *node) calcEscapingVariables(); } +void ScanFunctions::enterGlobalEnvironment(CompilationMode compilationMode) +{ + enterEnvironment(astNodeForGlobalEnvironment, compilationMode); +} + void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { - Context *e = _cg->_module->newContext(node, _context, compilationMode); - if (!e->isStrict) - e->isStrict = _cg->_strictMode; - _contextStack.append(e); - _context = e; + Context *c = _cg->_module->contextMap.value(node); + if (!c) + c = _cg->_module->newContext(node, _context, compilationMode); + if (!c->isStrict) + c->isStrict = _cg->_strictMode; + _contextStack.append(c); + _context = c; } void ScanFunctions::leaveEnvironment() @@ -440,7 +447,7 @@ void ScanFunctions::calcEscapingVariables() { Module *m = _cg->_module; - for (Context *inner : m->contextMap) { + for (Context *inner : qAsConst(m->contextMap)) { for (const QString &var : qAsConst(inner->usedVariables)) { Context *c = inner; while (c) { @@ -468,7 +475,7 @@ void ScanFunctions::calcEscapingVariables() static const bool showEscapingVars = qEnvironmentVariableIsSet("QV4_SHOW_ESCAPING_VARS"); if (showEscapingVars) { qDebug() << "==== escaping variables ===="; - for (Context *c : m->contextMap) { + for (Context *c : qAsConst(m->contextMap)) { qDebug() << "Context" << c->name << ":"; qDebug() << " Arguments escape" << c->argumentsCanEscape; for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) { diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index 745e9f8a73..87b7210879 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -84,12 +84,10 @@ public: ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode); void operator()(AST::Node *node); + void enterGlobalEnvironment(CompilationMode compilationMode); void enterEnvironment(AST::Node *node, CompilationMode compilationMode); void leaveEnvironment(); - void enterQmlScope(AST::Node *ast, const QString &name) - { enterFunction(ast, name, /*formals*/nullptr, /*body*/nullptr, /*expr*/nullptr); } - void enterQmlFunction(AST::FunctionDeclaration *ast) { enterFunction(ast, false); } @@ -149,6 +147,9 @@ protected: bool _allowFuncDecls; CompilationMode defaultProgramMode; + +private: + static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; }; } -- cgit v1.2.3 From 73a34bf8319178f2761811240e7c885849f272e9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 10 Apr 2018 13:33:11 +0200 Subject: Fix plugins.qmltypes for QtQml installation in static builds Mention plugins.qmltypes in AUX_QML_FILES to ensure that it is always copied to the build directory. qml_module.prf usually takes care of this by having QML_FILES in qmldir.files with INSTALLS += qmldir, but with CONFIG += builtin_resources (set for static) that's not the case. AUX_QML_FILES is the correct variable though. Task-number: QTBUG-67600 Change-Id: I4291b942e14ca26749758c3511240f824288f07e Reviewed-by: Oswald Buddenhagen --- src/imports/qtqml/qtqml.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/qtqml/qtqml.pro b/src/imports/qtqml/qtqml.pro index 05ef15a542..8804c944e7 100644 --- a/src/imports/qtqml/qtqml.pro +++ b/src/imports/qtqml/qtqml.pro @@ -1,5 +1,5 @@ TARGETPATH = QtQml -QML_FILES += plugins.qmltypes +AUX_QML_FILES += plugins.qmltypes load(qml_module) -- cgit v1.2.3 From e185d303839f2a8bb0e5769ba465b971ae354bd5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 10 Apr 2018 16:53:46 +0200 Subject: Fix crash with JIT on x86 The stack must be 16-byte aligned in order for compiler generated alignment requiring SSE instructions to work on the stack. For x86 the stack upon entry is 4 bytes off due to the saved eip. Then another 20 bytes for the saved registers (ebp, eax, ebx, etc.). That means we have to add another 8 bytes to reach the next 16-byte alignment. Change-Id: Ifde49a89224a129f8307fff3713563b80772cff1 Task-number: QTBUG-66773 Reviewed-by: Lars Knoll --- src/qml/jit/qv4assembler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 72b057b2bc..c3e16c4093 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -276,12 +276,16 @@ struct PlatformAssembler_X86_All : JSC::MacroAssembler push(JSStackFrameRegister); push(CppStackFrameRegister); push(EngineRegister); + // Ensure the stack is 16-byte aligned in order for compiler generated aligned SSE2 + // instructions to be able to target the stack. + subPtr(TrustedImm32(8), StackPointerRegister); loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister); loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister); } void generatePlatformFunctionExit() { + addPtr(TrustedImm32(8), StackPointerRegister); pop(EngineRegister); pop(CppStackFrameRegister); pop(JSStackFrameRegister); -- cgit v1.2.3 From 8780764b274217b256aadd00114a76bdffbdb1ef Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 10 Apr 2018 11:07:24 +0200 Subject: Warn about non spec compliant extension being used eval("function(){}") would return a function object in our engine. This is not compliant with the ES spec, so warn about it, as it'll start throwing a syntax error in 5.12. Also fix the two places where we were using that syntax in our auto tests. Change-Id: I573c2ad0ec4955570b857c69edef2f75998d55a9 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4script.cpp | 6 ++++++ tests/auto/qml/qjsengine/tst_qjsengine.cpp | 4 ++-- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index b4d9e11716..afa7d2ed52 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -90,6 +90,12 @@ void Script::parse() Module module(v4->debugger() != nullptr); + if (sourceCode.startsWith(QLatin1String("function("))) { + qWarning() << "Warning: Using function expressions as statements in scripts in not compliant with the ECMAScript specification at\n" + << (sourceCode.leftRef(70) + QLatin1String("...")) + << "\nThis will throw a syntax error in Qt 5.12. If you want a function expression, surround it by parentheses."; + } + Engine ee, *engine = ⅇ Lexer lexer(engine); lexer.setCode(sourceCode, line, parseAsBinding); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 0455895c14..e62e4a0980 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -2996,7 +2996,7 @@ void tst_QJSEngine::arraySort() void tst_QJSEngine::lookupOnDisappearingProperty() { QJSEngine eng; - QJSValue func = eng.evaluate("(function(){\"use strict\"; return eval(\"function(obj) { return obj.someProperty; }\")})()"); + QJSValue func = eng.evaluate("(function(){\"use strict\"; return eval(\"(function(obj) { return obj.someProperty; })\")})()"); QVERIFY(func.isCallable()); QJSValue o = eng.newObject(); @@ -3341,7 +3341,7 @@ void tst_QJSEngine::prototypeChainGc() QJSValue getProto = engine.evaluate("Object.getPrototypeOf"); - QJSValue factory = engine.evaluate("function() { return Object.create(Object.create({})); }"); + QJSValue factory = engine.evaluate("(function() { return Object.create(Object.create({})); })"); QVERIFY(factory.isCallable()); QJSValue obj = factory.call(); engine.collectGarbage(); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 18b1718ddf..8913528d79 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2131,7 +2131,7 @@ void tst_qqmllanguage::scriptStringJs() QVERIFY(!object->scriptProperty().booleanLiteral(&ok) && !ok); QJSValue inst = engine.newQObject(object.data()); - QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); + QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); func.callWithInstance(inst, QJSValueList() << "test a \"string "); QCOMPARE(QQmlScriptStringPrivate::get(object->scriptProperty())->script, QString("\"test a \\\"string \"")); @@ -2263,7 +2263,7 @@ void tst_qqmllanguage::scriptStringComparison() //QJSValue inst1 = engine.newQObject(object1); QJSValue inst2 = engine.newQObject(object2.data()); QJSValue inst3 = engine.newQObject(object3.data()); - QJSValue func = engine.evaluate("function(value) { this.scriptProperty = value }"); + QJSValue func = engine.evaluate("(function(value) { this.scriptProperty = value })"); const QString s = "hello\\n\\\"world\\\""; const qreal n = 12.345; -- cgit v1.2.3 From 996c6d452702162e909285b5be9a831290cff1c9 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Wed, 1 Nov 2017 15:40:14 +0100 Subject: Pass on tab presses to the offscreen window to handle first When pressing tab/backtab then the offscreen window needs to pass it on to the item in case it will handle this for changing focus. If it does not handle the event, it will pass it back for QWidget handling. [ChangeLog][QQuickWidget] Tab presses are now passed on to the root item to be handled first. When not handled by the root item, it will be handled like a standard QWidget. Task-number: QTBUG-45641 Change-Id: Ief0552ba496c87ab0b6e12aa8e67ef44b5a20ae2 Reviewed-by: Liang Qi --- src/quickwidgets/qquickwidget.cpp | 22 ++++++++ src/quickwidgets/qquickwidget.h | 1 + .../qquickwidget/data/activeFocusOnTab.qml | 60 ++++++++++++++++++++++ .../qquickwidget/data/noActiveFocusOnTab.qml | 57 ++++++++++++++++++++ .../quickwidgets/qquickwidget/tst_qquickwidget.cpp | 40 ++++++++++++++- 5 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml create mode 100644 tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 6f3b685974..920b400eac 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -485,6 +485,12 @@ QImage QQuickWidgetPrivate::grabFramebuffer() compatible however and attempting to construct a QQuickWidget will lead to problems. + \section1 Tab Key Handling + + On press of the \c[TAB] key, the item inside the QQuickWidget gets focus. If + this item can handle \c[TAB] key press, focus will change accordingly within + the item, otherwise the next widget in the focus chain gets focus. + \sa {Exposing Attributes of C++ Types to QML}, {Qt Quick Widgets Example}, QQuickView */ @@ -1221,6 +1227,22 @@ void QQuickWidget::resizeEvent(QResizeEvent *e) d->render(needsSync); } +/*! \reimp */ +bool QQuickWidget::focusNextPrevChild(bool next) +{ + Q_D(QQuickWidget); + QKeyEvent event(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier); + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, event.key(), + Qt::NoModifier); + QCoreApplication::sendEvent(d->offscreenWindow, &event); + + QKeyEvent releaseEvent(QEvent::KeyRelease, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier); + Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, releaseEvent.key(), + Qt::NoModifier); + QCoreApplication::sendEvent(d->offscreenWindow, &releaseEvent); + return event.isAccepted(); +} + /*! \reimp */ void QQuickWidget::keyPressEvent(QKeyEvent *e) { diff --git a/src/quickwidgets/qquickwidget.h b/src/quickwidgets/qquickwidget.h index 3ddb0613ad..5543705f13 100644 --- a/src/quickwidgets/qquickwidget.h +++ b/src/quickwidgets/qquickwidget.h @@ -143,6 +143,7 @@ protected: bool event(QEvent *) override; void paintEvent(QPaintEvent *event) override; + bool focusNextPrevChild(bool next) override; private: Q_DISABLE_COPY(QQuickWidget) diff --git a/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml b/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml new file mode 100644 index 0000000000..9ad1cafed9 --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/activeFocusOnTab.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + Rectangle { + objectName: "topRect" + x: 0 + width: 50 + height: 50 + activeFocusOnTab: true + focus: true + color: activeFocus ? "green" : "red" + } + Rectangle { + objectName: "middleRect" + x: 50 + width: 50 + height: 50 + focus: true + activeFocusOnTab: true + color: activeFocus ? "green" : "red" + } + Rectangle { + objectName: "bottomRect" + x: 100 + width: 50 + height: 50 + focus: true + activeFocusOnTab: true + color: activeFocus ? "green" : "red" + } +} + diff --git a/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml b/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml new file mode 100644 index 0000000000..6d86ce45bb --- /dev/null +++ b/tests/auto/quickwidgets/qquickwidget/data/noActiveFocusOnTab.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + Rectangle { + objectName: "topRect2" + x: 0 + width: 50 + height: 50 + focus: true + color: activeFocus ? "green" : "red" + } + Rectangle { + objectName: "middleRect2" + x: 50 + width: 50 + height: 50 + focus: true + color: activeFocus ? "green" : "red" + } + Rectangle { + objectName: "bottomRect3" + x: 100 + width: 50 + height: 50 + focus: true + color: activeFocus ? "green" : "red" + } +} + diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index a97e3c0538..af358925fe 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -40,7 +40,7 @@ #include #include - +#include #include #include @@ -140,6 +140,7 @@ private slots: void mouseEventWindowPos(); void synthMouseFromTouch_data(); void synthMouseFromTouch(); + void tabKey(); private: QTouchDevice *device = QTest::createTouchDevice(); @@ -620,6 +621,43 @@ void tst_qquickwidget::synthMouseFromTouch() QCOMPARE(ev.source(), Qt::MouseEventSynthesizedByQt); } +void tst_qquickwidget::tabKey() +{ + if (QGuiApplication::styleHints()->tabFocusBehavior() != Qt::TabFocusAllControls) + QSKIP("This function doesn't support NOT iterating all."); + QWidget window1; + QQuickWidget *qqw = new QQuickWidget(&window1); + qqw->setSource(testFileUrl("activeFocusOnTab.qml")); + QQuickWidget *qqw2 = new QQuickWidget(&window1); + qqw2->setSource(testFileUrl("noActiveFocusOnTab.qml")); + qqw2->move(100, 0); + window1.show(); + qqw->setFocus(); + QVERIFY(QTest::qWaitForWindowExposed(&window1, 5000)); + QVERIFY(qqw->hasFocus()); + QQuickItem *item = qobject_cast(qqw->rootObject()); + QQuickItem *topItem = item->findChild("topRect"); + QQuickItem *middleItem = item->findChild("middleRect"); + QQuickItem *bottomItem = item->findChild("bottomRect"); + topItem->forceActiveFocus(); + QVERIFY(topItem->property("activeFocus").toBool()); + QTest::keyClick(qqw, Qt::Key_Tab); + QTRY_VERIFY(middleItem->property("activeFocus").toBool()); + QTest::keyClick(qqw, Qt::Key_Tab); + QTRY_VERIFY(bottomItem->property("activeFocus").toBool()); + QTest::keyClick(qqw, Qt::Key_Backtab); + QTRY_VERIFY(middleItem->property("activeFocus").toBool()); + + qqw2->setFocus(); + QQuickItem *item2 = qobject_cast(qqw2->rootObject()); + QQuickItem *topItem2 = item2->findChild("topRect2"); + QTRY_VERIFY(qqw2->hasFocus()); + QVERIFY(topItem2->property("activeFocus").toBool()); + QTest::keyClick(qqw2, Qt::Key_Tab); + QTRY_VERIFY(qqw->hasFocus()); + QVERIFY(middleItem->property("activeFocus").toBool()); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" -- cgit v1.2.3 From 10c56615d1418c155648e2a150b45b6cb768b33e Mon Sep 17 00:00:00 2001 From: Jan Marker Date: Sun, 8 Apr 2018 17:40:36 +0200 Subject: Make QSGLayer::grab work correctly in software renderer Fix three separate issues: 1. It was possible that the QSGSoftwarePixmapRenderer's background image's rectangle was set to a non-normalized rectangle. That would have led to the damage area detection creating an empty QRegion for the damage area and QQuickItem::grabToImage would grab an empty image. 2. The QSGSoftwarePixmapRenderer was rendering the image vertically mirrored compared to what its equivalent in the OpenGL backend was doing. Therefore QSGLayer::grab was vertically mirrored, too, so QQuickItem::grabToImage would grab a mirrored image, too. Additionally QSGSoftwareInternalImageNode (used by QQuickShaderEffectSource) now has to mirror its internal texture if that one is a QSGSoftwareLayer. 3. QSGSoftwareInternalImageNode (used by QQuickShaderEffectSource) was not updating correctly when mirroring (with the fix for 2 also in case of a QSGSoftwareLayer as texture). Related to QTBUG-63185 and QTBUG-65975. Change-Id: I0d0ead7fb1c839a8ff427ff7881d8a881e538409 Reviewed-by: Eirik Aavitsland Reviewed-by: Andy Nichols --- .../adaptations/software/qsgsoftwareinternalimagenode.cpp | 14 +++++++++++--- .../adaptations/software/qsgsoftwareinternalimagenode_p.h | 1 + .../scenegraph/adaptations/software/qsgsoftwarelayer.cpp | 4 ++-- .../adaptations/software/qsgsoftwarepixmaprenderer.cpp | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp index 3b0f3c48ff..aa83709b72 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp @@ -320,6 +320,7 @@ QSGSoftwareInternalImageNode::QSGSoftwareInternalImageNode() , m_subSourceRect(0, 0, 1, 1) , m_texture(nullptr) , m_mirror(false) + , m_textureIsLayer(false) , m_smooth(true) , m_tileHorizontal(false) , m_tileVertical(false) @@ -366,6 +367,7 @@ void QSGSoftwareInternalImageNode::setTexture(QSGTexture *texture) { m_texture = texture; m_cachedMirroredPixmapIsDirty = true; + m_textureIsLayer = static_cast(qobject_cast(texture)); markDirty(DirtyMaterial); } @@ -415,8 +417,13 @@ void QSGSoftwareInternalImageNode::setVerticalWrapMode(QSGTexture::WrapMode wrap void QSGSoftwareInternalImageNode::update() { if (m_cachedMirroredPixmapIsDirty) { - if (m_mirror) { - m_cachedMirroredPixmap = pixmap().transformed(QTransform(-1, 0, 0, 1, 0, 0)); + if (m_mirror || m_textureIsLayer) { + QTransform transform( + (m_mirror ? -1 : 1), 0, + 0 , (m_textureIsLayer ? -1 :1), + 0 , 0 + ); + m_cachedMirroredPixmap = pixmap().transformed(transform); } else { //Cleanup cached pixmap if necessary if (!m_cachedMirroredPixmap.isNull()) @@ -436,6 +443,7 @@ void QSGSoftwareInternalImageNode::preprocess() } if (doDirty) markDirty(DirtyMaterial); + m_cachedMirroredPixmapIsDirty = doDirty; } static Qt::TileRule getTileRule(qreal factor) @@ -454,7 +462,7 @@ void QSGSoftwareInternalImageNode::paint(QPainter *painter) { painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); - const QPixmap &pm = m_mirror ? m_cachedMirroredPixmap : pixmap(); + const QPixmap &pm = m_mirror || m_textureIsLayer ? m_cachedMirroredPixmap : pixmap(); if (m_innerTargetRect != m_targetRect) { // border image diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h index 5c95eb064a..b80bacbaa0 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode_p.h @@ -136,6 +136,7 @@ private: QPixmap m_cachedMirroredPixmap; bool m_mirror; + bool m_textureIsLayer; bool m_smooth; bool m_tileHorizontal; bool m_tileVertical; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp index b4301451d8..70378d2950 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp @@ -240,9 +240,9 @@ void QSGSoftwareLayer::grab() m_renderer->setDeviceRect(m_size); m_renderer->setViewportRect(m_size); QRect mirrored(m_mirrorHorizontal ? m_rect.right() * m_device_pixel_ratio : m_rect.left() * m_device_pixel_ratio, - m_mirrorVertical ? m_rect.top() * m_device_pixel_ratio : m_rect.bottom() * m_device_pixel_ratio, + m_mirrorVertical ? m_rect.bottom() * m_device_pixel_ratio : m_rect.top() * m_device_pixel_ratio, m_mirrorHorizontal ? -m_rect.width() * m_device_pixel_ratio : m_rect.width() * m_device_pixel_ratio, - m_mirrorVertical ? m_rect.height() * m_device_pixel_ratio : -m_rect.height() * m_device_pixel_ratio); + m_mirrorVertical ? -m_rect.height() * m_device_pixel_ratio : m_rect.height() * m_device_pixel_ratio); m_renderer->setProjectionRect(mirrored); m_renderer->setClearColor(Qt::transparent); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp index 303f98c801..bb4afc8301 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -79,7 +79,7 @@ void QSGSoftwarePixmapRenderer::render(QPaintDevice *target) QElapsedTimer renderTimer; // Setup background item - setBackgroundRect(m_projectionRect); + setBackgroundRect(m_projectionRect.normalized()); setBackgroundColor(clearColor()); renderTimer.start(); -- cgit v1.2.3 From 24b0ff31c06097bdbbfc90b235c75fb93ec2d465 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 12 Apr 2018 09:50:49 +0200 Subject: Qml/ArrayElementLessThan: Remove unused member variable thisObject Fix a warning by clang-cl. Change-Id: Ie9285a8937cdfa2640403b07b27ae938c5f61743 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraydata.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index b9c0e12305..b33b34ee08 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -662,14 +662,13 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) class ArrayElementLessThan { public: - inline ArrayElementLessThan(ExecutionEngine *engine, Object *thisObject, const Value &comparefn) - : m_engine(engine), thisObject(thisObject), m_comparefn(comparefn) {} + inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn) + : m_engine(engine), m_comparefn(comparefn) {} bool operator()(Value v1, Value v2) const; private: ExecutionEngine *m_engine; - Object *thisObject; const Value &m_comparefn; }; @@ -842,7 +841,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } - ArrayElementLessThan lessThan(engine, thisObject, static_cast(comparefn)); + ArrayElementLessThan lessThan(engine, static_cast(comparefn)); Value *begin = thisObject->arrayData()->values.values; sortHelper(begin, begin + len, *begin, lessThan); -- cgit v1.2.3 From 191413704be811c31d5f9d8b5916681ee6c54ddf Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 12 Apr 2018 09:55:12 +0200 Subject: QQuickShapeGenericRenderer: Fix clang-cl-warning about uninitialized variable Add a return to the default branch capturing NoGradient, fixing: warning: variable 'gradMat' is used uninitialized whenever switch default is taken Change-Id: Iad9d619b59677a18a0718d750516c9b2fe51569d Reviewed-by: Laszlo Agocs --- src/imports/shapes/qquickshapegenericrenderer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp index 411252a906..8a4785a83a 100644 --- a/src/imports/shapes/qquickshapegenericrenderer.cpp +++ b/src/imports/shapes/qquickshapegenericrenderer.cpp @@ -600,6 +600,7 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen break; default: Q_UNREACHABLE(); + return; } n->activateMaterial(m_item->window(), gradMat); if (d->effectiveDirty & DirtyFillGradient) { -- cgit v1.2.3 From 539d724890aec3f00eadb39aeae973670e664ec6 Mon Sep 17 00:00:00 2001 From: Eirik Aavitsland Date: Wed, 28 Feb 2018 15:35:36 +0100 Subject: Doc: Describe new 5.11 features in Image element Add user documentation of the compressed texture file support and the file extension auto detection. Change-Id: Icfae8574dd3acba30e8275ccd6ff3438fa037868 Reviewed-by: Mitch Curtis --- src/quick/doc/snippets/qml/image-ext.qml | 68 ++++++++++++++++++++++++++++++ src/quick/items/qquickimage.cpp | 46 +++++++++++++++++++- tests/auto/quick/examples/tst_examples.cpp | 1 + 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/quick/doc/snippets/qml/image-ext.qml diff --git a/src/quick/doc/snippets/qml/image-ext.qml b/src/quick/doc/snippets/qml/image-ext.qml new file mode 100644 index 0000000000..5e95f2b4cf --- /dev/null +++ b/src/quick/doc/snippets/qml/image-ext.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [ext] +// Assuming the "pics" directory contains the following files: +// dog.jpg +// cat.png +// cat.pkm + +Image { + source: "pics/cat.png" // loads cat.png +} + +Image { + source: "pics/dog" // loads dog.jpg +} + +Image { + source: "pics/cat" // normally loads cat.pkm, but if no OpenGL, loads cat.png instead. +} +//! [ext] diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index dc2cd17b4e..db5cfd2526 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -135,6 +135,48 @@ QQuickImagePrivate::QQuickImagePrivate() \clearfloat + \section1 OpenGL Texture Files + + When the default OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in + use, images can also be supplied in compressed texture files. The content + must be a simple RGB(A) format 2D texture. Supported compression schemes + are only limited by the underlying OpenGL driver and GPU. The following + container file formats are supported: + + \list + \li \c PKM (since Qt 5.10) + \li \c KTX (since Qt 5.11) + \endlist + + \note Semi-transparent original images require alpha pre-multiplication + prior to texture compression in order to be correctly displayed in Qt + Quick. This can be done with the following ImageMagick command + line: + \badcode + convert MYORIGIMAGE \( +clone -alpha Extract \) -channel RGB -compose Multiply -composite MYPMIMAGE + \endcode + + \section1 Automatic Detection of File Extension + + If the \l source URL indicates a non-existing local file or resource, the + Image element attempts to auto-detect the file extension. If an existing + file can be found by appending any of the supported image file extensions + to the \l source URL, then that file will be loaded. + + If the OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in use, the + file search the attempts the OpenGL texture file extensions first. If the + search is unsuccessful, it attempts to search with the file extensions for + the \l{QImageReader::supportedImageFormats()}{conventional image file + types}. For example: + + \snippet qml/image-ext.qml ext + + This functionality facilitates deploying different image asset file types + on different target platforms. This can be useful in order to tune + application performance and adapt to different graphics hardware. + + This functionality was introduced in Qt 5.11. + \section1 Performance By default, locally available images are loaded immediately, and the user interface @@ -154,7 +196,7 @@ QQuickImagePrivate::QQuickImagePrivate() size bounded via the \l sourceSize property. This is especially important for content that is loaded from external sources or provided by the user. - \sa {Qt Quick Examples - Image Elements}, QQuickImageProvider + \sa {Qt Quick Examples - Image Elements}, QQuickImageProvider, QImageReader::setAutoDetectImageFormat() */ QQuickImage::QQuickImage(QQuickItem *parent) @@ -461,7 +503,7 @@ qreal QQuickImage::paintedHeight() const The URL may be absolute, or relative to the URL of the component. - \sa QQuickImageProvider + \sa QQuickImageProvider {OpenGL Texture Files} {Automatic Detection of File Extension} */ /*! diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 716651ec0c..eee8dfcf26 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -86,6 +86,7 @@ tst_examples::tst_examples() excludedDirs << "snippets/qml/visualdatamodel_rootindex"; excludedDirs << "snippets/qml/qtbinding"; excludedDirs << "snippets/qml/imports"; + excludedFiles << "snippets/qml/image-ext.qml"; excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources -- cgit v1.2.3 From 8e2cfa1d77dd4568a126f5ed5736dfef844a28ef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 16 Apr 2018 13:53:03 +0200 Subject: Fix JSON.stringify with sequence types Stringify::JA takes an ArrayObject* but it merely gets the length property and does indexed get calls. Those work also on array-like objects such as our sequence wrappers. Task-number: QTBUG-45018 Change-Id: I4ec4f89a2e09c918fbc2ff1d48ae5915e67ce280 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4jsonobject.cpp | 8 ++++---- .../qml/qqmlecmascript/data/sequenceConversion.array.qml | 14 ++++++++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 13 +++++++------ 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 99666806be..c3569c29d2 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -637,7 +637,7 @@ struct Stringify Stringify(ExecutionEngine *e) : v4(e), replacerFunction(nullptr), propertyList(nullptr), propertyListSize(0) {} QString Str(const QString &key, const Value &v); - QString JA(ArrayObject *a); + QString JA(Object *a); QString JO(Object *o); QString makeMember(const QString &key, const Value &v); @@ -743,8 +743,8 @@ QString Stringify::Str(const QString &key, const Value &v) o = value->asReturnedValue(); if (o) { if (!o->as()) { - if (o->as()) { - return JA(static_cast(o.getPointer())); + if (o->as() || o->isListType()) { + return JA(o.getPointer()); } else { return JO(o); } @@ -827,7 +827,7 @@ QString Stringify::JO(Object *o) return result; } -QString Stringify::JA(ArrayObject *a) +QString Stringify::JA(Object *a) { if (stackContains(a)) { v4->throwTypeError(); diff --git a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml index 5103168fd3..99c49ebf62 100644 --- a/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml +++ b/tests/auto/qml/qqmlecmascript/data/sequenceConversion.array.qml @@ -10,6 +10,11 @@ Item { objectName: "msco" } + Component { + id: mscoComponent + MySequenceConversionObject { } + } + property bool success: false property variant intList @@ -252,4 +257,13 @@ Item { if (testSequence.valueOf() == prevValueOf) referenceDeletion = false; if (testSequence.length == prevLength) referenceDeletion = false; } + + function jsonConversion() { + success = true + var msco = mscoComponent.createObject() + if (JSON.stringify(msco.intListProperty) != "[1,2,3,4]") success = false; + if (JSON.stringify(msco.qrealListProperty) != "[1.1,2.2,3.3,4.4]") success = false; + if (JSON.stringify(msco.boolListProperty) != "[true,false,true,false]") success = false; + if (JSON.stringify(msco.stringListProperty) != "[\"first\",\"second\",\"third\",\"fourth\"]") success = false; + } } diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index e0b8127dfb..c0cf123243 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -5562,17 +5562,18 @@ void tst_qqmlecmascript::sequenceConversionArray() // ensure that in JS the returned sequences act just like normal JS Arrays. QUrl qmlFile = testFileUrl("sequenceConversion.array.qml"); QQmlComponent component(&engine, qmlFile); - QObject *object = component.create(); + QScopedPointer object(component.create()); QVERIFY(object != nullptr); - QMetaObject::invokeMethod(object, "indexedAccess"); + QMetaObject::invokeMethod(object.data(), "indexedAccess"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "arrayOperations"); + QMetaObject::invokeMethod(object.data(), "arrayOperations"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "testEqualitySemantics"); + QMetaObject::invokeMethod(object.data(), "testEqualitySemantics"); QVERIFY(object->property("success").toBool()); - QMetaObject::invokeMethod(object, "testReferenceDeletion"); + QMetaObject::invokeMethod(object.data(), "testReferenceDeletion"); QCOMPARE(object->property("referenceDeletion").toBool(), true); - delete object; + QMetaObject::invokeMethod(object.data(), "jsonConversion"); + QVERIFY(object->property("success").toBool()); } -- cgit v1.2.3 From 4909773f8162de49830d65e886747c11fff72934 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 20 Mar 2018 12:46:58 +0100 Subject: Fix calling Qt.binding() on bound functions Calling Qt.binding() on a bound function object is a valid use case and used to work until Qt 5.8. The problem was that we optimized the code in QQmlBinding and QQmlJavascriptExpression to directly work on a QV4::Function, so this wouldn't work anymore. To fix this make sure recursive calls to Function.bind() are unrolled (so that the BoundFunction's target is never a bound function itself), then add the bound function as an optional member to the QQmlBinding and use it's bound arguments if present. Task-number: QTBUG-61927 Change-Id: I472214ddd82fc2a1212efd9b769861fc43d2ddaf Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4functionobject.cpp | 47 ++++++++++++++------- src/qml/jsruntime/qv4functionobject_p.h | 2 + src/qml/jsruntime/qv4qobjectwrapper.cpp | 5 ++- src/qml/jsruntime/qv4runtime.cpp | 4 +- src/qml/qml/qqmlbinding.cpp | 48 +++++++++++++++++++++- src/qml/qml/qqmlbinding_p.h | 11 +++++ src/qml/qml/qqmljavascriptexpression.cpp | 14 +------ src/qml/qml/qqmljavascriptexpression_p.h | 4 +- src/qml/qml/qqmlvaluetypewrapper.cpp | 8 +++- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 9 ++-- src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 8 +++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 13 ++++++ .../qqmlecmascript/data/bindingBoundFunctions.qml | 34 +++++++++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 12 ++++++ 14 files changed, 176 insertions(+), 43 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index a8c1640767..dc8ee550d5 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -75,7 +75,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, jsConstruct = QV4::FunctionObject::callAsConstructor; Object::init(); - function = nullptr; this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); @@ -88,7 +87,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, jsConstruct = reinterpret_cast(vtable())->callAsConstructor; Object::init(); - function = nullptr; this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); @@ -101,8 +99,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function jsConstruct = reinterpret_cast(vtable())->callAsConstructor; Object::init(); - this->function = function; - function->compilationUnit->addref(); + setFunction(function); this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedString name(s, function->name()); @@ -123,13 +120,18 @@ void Heap::FunctionObject::init() jsConstruct = reinterpret_cast(vtable())->callAsConstructor; Object::init(); - function = nullptr; this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } - +void Heap::FunctionObject::setFunction(Function *f) +{ + if (f) { + function = f; + function->compilationUnit->addref(); + } +} void Heap::FunctionObject::destroy() { if (function) @@ -347,20 +349,36 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu { QV4::Scope scope(b); ScopedFunctionObject target(scope, thisObject); - if (!target) + if (!target || target->isBinding()) return scope.engine->throwTypeError(); ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue()); Scoped boundArgs(scope, (Heap::MemberData *)nullptr); - if (argc > 1) { - boundArgs = MemberData::allocate(scope.engine, argc - 1); - boundArgs->d()->values.size = argc - 1; - for (uint i = 0, ei = static_cast(argc - 1); i < ei; ++i) + + int nArgs = (argc - 1 >= 0) ? argc - 1 : 0; + if (target->isBoundFunction()) { + BoundFunction *bound = static_cast(target.getPointer()); + Scoped oldArgs(scope, bound->boundArgs()); + boundThis = bound->boundThis(); + int oldSize = oldArgs->size(); + boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs); + boundArgs->d()->values.size = oldSize + nArgs; + for (uint i = 0; i < static_cast(oldSize); ++i) + boundArgs->set(scope.engine, i, oldArgs->data()[i]); + for (uint i = 0; i < static_cast(nArgs); ++i) + boundArgs->set(scope.engine, oldSize + i, argv[i + 1]); + target = bound->target(); + } else if (nArgs) { + boundArgs = MemberData::allocate(scope.engine, nArgs); + boundArgs->d()->values.size = nArgs; + for (uint i = 0, ei = static_cast(nArgs); i < ei; ++i) boundArgs->set(scope.engine, i, argv[i + 1]); } - ExecutionContext *global = scope.engine->rootContext(); - return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue(); + ScopedContext ctx(scope, target->scope()); + Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs); + bound->setFunction(target->function()); + return bound->asReturnedValue(); } DEFINE_OBJECT_VTABLE(ScriptFunction); @@ -393,8 +411,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function FunctionObject::init(); this->scope.set(scope->engine(), scope->d()); - this->function = function; - function->compilationUnit->addref(); + setFunction(function); Q_ASSERT(function); Q_ASSERT(function->code); diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index d6066ec648..32e71a175b 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -90,6 +90,8 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { void init(); void destroy(); + void setFunction(Function *f); + unsigned int formalParameterCount() { return function ? function->nFormals : 0; } unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index c1bbe2a330..816c259b9b 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -460,9 +460,12 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QV4::Scoped bindingFunction(scope, (const Value &)f); + QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); QV4::ScopedContext ctx(scope, bindingFunction->scope()); - newBinding = QQmlBinding::create(property, bindingFunction->function(), object, callingQmlContext, ctx); + newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx); newBinding->setSourceLocation(bindingFunction->currentLocation()); + if (f->isBoundFunction()) + newBinding->setBoundFunction(static_cast(f.getPointer())); newBinding->setTarget(object, *property, nullptr); } } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 2506777e76..0211ad1011 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1271,7 +1271,9 @@ QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) { - return engine->qmlContext()->asReturnedValue(); + Heap::QmlContext *ctx = engine->qmlContext(); + Q_ASSERT(ctx); + return ctx->asReturnedValue(); } ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index c314833304..30a18440a8 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr return b; } +QQmlSourceLocation QQmlBinding::sourceLocation() const +{ + if (m_sourceLocation) + return *m_sourceLocation; + return QQmlJavaScriptExpression::sourceLocation(); +} + +void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location) +{ + if (m_sourceLocation) + delete m_sourceLocation; + m_sourceLocation = new QQmlSourceLocation(location); +} + + QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt, const QString &url, quint16 lineNumber) { @@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function QQmlBinding::~QQmlBinding() { + delete m_sourceLocation; } void QQmlBinding::setNotifyOnValueChanged(bool v) @@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) setUpdatingFlag(false); } +QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = context()->engine->handle(); + int argc = 0; + const QV4::Value *argv = nullptr; + const QV4::Value *thisObject = nullptr; + QV4::BoundFunction *b = nullptr; + if ((b = static_cast(m_boundFunction.valueRef()))) { + QV4::Heap::MemberData *args = b->boundArgs(); + if (args) { + argc = args->values.size; + argv = args->values.data(); + } + thisObject = &b->d()->boundThis; + } + QV4::Scope scope(v4); + QV4::JSCallData jsCall(scope, argc, argv, thisObject); + + return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined); +} + + // QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or // double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant // expression for the switch for the compiler to generate the optimal code, but @@ -203,7 +242,7 @@ protected: bool isUndefined = false; - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedValue result(scope, evaluate(&isUndefined)); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) @@ -302,9 +341,14 @@ public: { setCompilationUnit(compilationUnit); m_binding = binding; - setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column)); } + QQmlSourceLocation sourceLocation() const override final + { + return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column); + } + + void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final { diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index b67b02975f..a1295bd0ac 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -104,6 +104,12 @@ public: QString expressionIdentifier() const override; void expressionChanged() override; + QQmlSourceLocation sourceLocation() const override; + void setSourceLocation(const QQmlSourceLocation &location); + void setBoundFunction(QV4::BoundFunction *boundFunction) { + m_boundFunction.set(boundFunction->engine(), *boundFunction); + } + /** * This method returns a snapshot of the currently tracked dependencies of * this binding. The dependencies can change upon reevaluation. This method is @@ -123,6 +129,8 @@ protected: bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags); + QV4::ReturnedValue evaluate(bool *isUndefined); + private: inline bool updatingFlag() const; inline void setUpdatingFlag(bool); @@ -130,6 +138,9 @@ private: inline void setEnabledFlag(bool); static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property); + + QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions + QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 40cf1417d0..93ec9421ed 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression() m_context(nullptr), m_prevExpression(nullptr), m_nextExpression(nullptr), - m_v4Function(nullptr), - m_sourceLocation(nullptr) + m_v4Function(nullptr) { } @@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; - - delete m_sourceLocation; } void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) @@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged() QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const { - if (m_sourceLocation) - return *m_sourceLocation; if (m_v4Function) return m_v4Function->sourceLocation(); return QQmlSourceLocation(); } -void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location) -{ - if (m_sourceLocation) - delete m_sourceLocation; - m_sourceLocation = new QQmlSourceLocation(location); -} - void QQmlJavaScriptExpression::setContext(QQmlContextData *context) { if (m_prevExpression) { diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index bff8866011..01af3b89ca 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -116,8 +116,7 @@ public: inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); - QQmlSourceLocation sourceLocation() const; - void setSourceLocation(const QQmlSourceLocation &location); + virtual QQmlSourceLocation sourceLocation() const; bool isValid() const { return context() != nullptr; } @@ -188,7 +187,6 @@ private: QV4::PersistentValue m_qmlScope; QQmlRefPointer m_compilationUnit; QV4::Function *m_v4Function; - QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions }; class QQmlPropertyCapture diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a28115d192..6196a09d94 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scoped bindingFunction(scope, (const Value &)f); - QV4::ScopedContext ctx(scope, bindingFunction->scope()); - QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx); + QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); + QV4::ScopedContext ctx(scope, f->scope()); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx); + newBinding->setSourceLocation(bindingFunction->currentLocation()); + if (f->isBoundFunction()) + newBinding->setBoundFunction(static_cast(f.getPointer())); newBinding->setSourceLocation(bindingFunction->currentLocation()); newBinding->setTarget(referenceObject, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 1371f1f041..9e7c84011b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1344,11 +1344,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co return QQmlLocale::locale(scope.engine, code); } -void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) +void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction) { - Scope scope(originalFunction->engine()); - ScopedContext context(scope, originalFunction->scope()); - FunctionObject::init(context, originalFunction->function()); + Scope scope(bindingFunction->engine()); + ScopedContext context(scope, bindingFunction->scope()); + FunctionObject::init(context, bindingFunction->function()); + this->bindingFunction.set(internalClass->engine, bindingFunction->d()); } QQmlSourceLocation QQmlBindingFunction::currentLocation() const diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 104dae5d79..ee3b5f7d6e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -80,8 +80,11 @@ struct ConsoleObject : Object { void init(); }; -struct QQmlBindingFunction : FunctionObject { - void init(const QV4::FunctionObject *originalFunction); +#define QQmlBindingFunctionMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, bindingFunction) +DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) { + DECLARE_MARKOBJECTS(QQmlBindingFunction) + void init(const QV4::FunctionObject *bindingFunction); }; } @@ -179,6 +182,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject { V4_OBJECT2(QQmlBindingFunction, FunctionObject) + Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; } QQmlSourceLocation currentLocation() const; // from caller stack trace }; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index e62e4a0980..f862cdb048 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -129,6 +129,7 @@ private slots: void arraySort(); void lookupOnDisappearingProperty(); void arrayConcat(); + void recursiveBoundFunctions(); void qRegExpInport_data(); void qRegExpInport(); @@ -3022,6 +3023,18 @@ void tst_QJSEngine::arrayConcat() QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12")); } +void tst_QJSEngine::recursiveBoundFunctions() +{ + + QJSEngine eng; + QJSValue v = eng.evaluate("function foo(x, y, z)" + "{ return this + x + y + z; }" + "var bar = foo.bind(-1, 10);" + "var baz = bar.bind(-2, 20);" + "baz(30)"); + QCOMPARE(v.toInt(), 59); +} + static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } void tst_QJSEngine::qRegExpInport_data() diff --git a/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml new file mode 100644 index 0000000000..8dbd2fd3d9 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/bindingBoundFunctions.qml @@ -0,0 +1,34 @@ +import QtQuick 2.6 + +QtObject { + property bool success: false + property var num: 100 + property var simple: 0 + property var complex: 0 + + + Component.onCompleted: { + function s(x) { + return x + } + function c(x) { + return x + num + } + + var bound = s.bind(undefined, 100) + simple = Qt.binding(bound) + if (simple != 100) + return; + var bound = c.bind(undefined, 100) + complex = Qt.binding(bound); + + if (complex != 200) + return; + num = 0; + if (complex != 100) + return; + + print("success!!!"); + success = true; + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index c0cf123243..f40a9758f7 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -289,6 +289,7 @@ private slots: void withStatement(); void tryStatement(); void replaceBinding(); + void bindingBoundFunctions(); void deleteRootObjectInCreation(); void onDestruction(); void onDestructionViaGC(); @@ -7247,6 +7248,17 @@ void tst_qqmlecmascript::replaceBinding() delete obj; } +void tst_qqmlecmascript::bindingBoundFunctions() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("bindingBoundFunctions.qml")); + QObject *obj = c.create(); + QVERIFY(obj != nullptr); + + QVERIFY(obj->property("success").toBool()); + delete obj; +} + void tst_qqmlecmascript::deleteRootObjectInCreation() { { -- cgit v1.2.3 From 2e6196f727cbc5c23be8d264e160933b283cb459 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 16 Apr 2018 10:35:54 +0200 Subject: Add a null pointer check The data pointer in ArrayBuffer can be null, if the constructor tried to allocate an object with an invalid length; Change-Id: I4a37dfa2c749db02982c69ca065c2e7ce9902a93 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arraybuffer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index c4eddb6b2a..59a2b9d913 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -96,7 +96,6 @@ void Heap::ArrayBuffer::init(size_t length) Object::init(); data = QTypedArrayData::allocate(length + 1); if (!data) { - data = nullptr; internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory")); return; } @@ -113,7 +112,7 @@ void Heap::ArrayBuffer::init(const QByteArray& array) void Heap::ArrayBuffer::destroy() { - if (!data->ref.deref()) + if (data && !data->ref.deref()) QTypedArrayData::deallocate(data); Object::destroy(); } -- cgit v1.2.3 From 685a267d37b5a13ed357d6c03953096d6b88907b Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 16 Apr 2018 10:51:29 +0200 Subject: Fix crashes when parsing destructuring expressions x = [y] = z would crash because [y] is a literal and not a valid lvalue in ES5, something our parser didn't catch correctly. Change-Id: I0d7abd9b3f812f1de61c77dccfab5d1778dac793 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 3 ++- src/qml/compiler/qv4codegen_p.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index a262908960..8ada1d505e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -784,9 +784,10 @@ bool Codegen::visit(BinaryExpression *ast) left = left.asLValue(); if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation())) return false; - expression(ast->right).loadInAccumulator(); + Reference r = expression(ast->right); if (hasError) return false; + r.loadInAccumulator(); if (_expr.accept(nx)) _expr.setResult(left.storeConsumeAccumulator()); else diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index a46d47cb67..d51dc29517 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -188,7 +188,7 @@ public: Const } type = Invalid; - bool isLValue() const { return !isReadonly; } + bool isLValue() const { return !isReadonly && type > Accumulator; } Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {} Reference() {} -- cgit v1.2.3 From e8dec2a12b9bf3e1cad74c7387ad9a23c42e64d9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 16 Apr 2018 10:56:52 +0200 Subject: Remove a bogus assert It's actually possible to have oldLength != 0 and no arrayData in this code path if someone redefines the length property of the JS array. Change-Id: Ib699425b95fa1e1981483ccb2b2babd476b86f60 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4object.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 8e9bf794a9..bcbe475c2c 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -1091,9 +1091,7 @@ bool Object::setArrayLength(uint newLen) uint oldLen = getLength(); bool ok = true; if (newLen < oldLen) { - if (!arrayData()) { - Q_ASSERT(!newLen); - } else { + if (arrayData()) { uint l = arrayData()->vtable()->truncate(this, newLen); if (l != newLen) ok = false; -- cgit v1.2.3 From abe6c632161375df12a1ab73a9255486ba9436de Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 16 Apr 2018 11:41:31 +0200 Subject: Properly handle redeclarations of variables This is only allowed for var type variables. Also fixes an assertion we'd run into with code such as let x; var x; Change-Id: I2588cf37e0964c879c60b4fd292e7d7b5476e322 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compilercontext_p.h | 43 ++++++++++++++------------- src/qml/compiler/qv4compilerscanfunctions.cpp | 28 +++++++++-------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 8fabf41c40..455a76c729 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -257,29 +257,32 @@ struct Context { usedVariables.insert(name); } - void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr) + bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr) { - if (! name.isEmpty()) { - if (type != FunctionDefinition) { - for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next) - if (it->name == name) - return; - } - MemberMap::iterator it = members.find(name); - if (it == members.end()) { - Member m; - m.type = type; - m.function = function; - m.scope = scope; - members.insert(name, m); - } else { - Q_ASSERT(scope == (*it).scope); - if ((*it).type <= type) { - (*it).type = type; - (*it).function = function; - } + if (name.isEmpty()) + return true; + + if (type != FunctionDefinition) { + for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next) + if (it->name == name) + return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope); + } + MemberMap::iterator it = members.find(name); + if (it != members.end()) { + if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope) + return false; + if ((*it).type <= type) { + (*it).type = type; + (*it).function = function; } + return true; } + Member m; + m.type = type; + m.function = function; + m.scope = scope; + members.insert(name, m); + return true; } }; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 92df98f201..84ee452332 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -215,14 +215,10 @@ bool ScanFunctions::visit(VariableDeclaration *ast) return false; } QString name = ast->name.toString(); - const Context::Member *m = nullptr; - if (_context->memberInfo(name, &m)) { - if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { - _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); - return false; - } + if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; } - _context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope); return true; } @@ -397,16 +393,22 @@ bool ScanFunctions::visit(Block *ast) { void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr) { - if (_context) { - _context->hasNestedFunctions = true; + Context *outerContext = _context; + enterEnvironment(ast, FunctionCode); + + if (outerContext) { + outerContext->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. - if (expr) - _context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); + if (expr) { + if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) { + _cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name)); + return; + } + } if (name == QLatin1String("arguments")) - _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed; } - enterEnvironment(ast, FunctionCode); if (formalsContainName(formals, QStringLiteral("arguments"))) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; -- cgit v1.2.3 From befd1aa067b3908ec1ac3db1b7c307dfcb035552 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Wed, 4 Apr 2018 09:06:42 +0200 Subject: Document QObject::setProperty()'s behavior in relation to bindings Make it clear that setting a property via QObject::setProperty() will not break any binding already set on that property. Task-number: QTBUG-67451 Change-Id: Id032c2217a46133d1d6728598f79682dff459897 Reviewed-by: Simon Hausmann --- .../doc/src/cppintegration/interactqmlfromcpp.qdoc | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc index 7c2ff703c6..9c33979f40 100644 --- a/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc +++ b/src/qml/doc/src/cppintegration/interactqmlfromcpp.qdoc @@ -75,11 +75,26 @@ component, which is accessible via QQuickView::rootObject(): \endtable This \c object is the instance of the \c MyItem.qml component that has been -created. You can now modify the item's properties using QObject::setProperty() -or QQmlProperty: +created. You can now modify the item's properties using +\l QObject::setProperty() or \l QQmlProperty::write(): \snippet qml/qtbinding/loading/main.cpp properties +The difference between \c QObject::setProperty() and \c QQmlProperty::write() +is that the latter will also remove the binding in addition to setting the +property value. For example, suppose the \c width assignment above had been a +binding to \c height: + +\code + width: height +\endcode + +If the \c height of the \c Item changed after the +\c {object->setProperty("width", 500)} call, the \c width would be updated +again, as the binding remains active. However, if the \c height changes after the +\c {QQmlProperty(object, "width").write(500)} call, the \c width will not be +changed, as the binding does not exist anymore. + Alternatively, you can cast the object to its actual type and call methods with compile-time safety. In this case the base object of \c MyItem.qml is an \l Item, which is defined by the QQuickItem class: -- cgit v1.2.3 From 8ea8683ce8d8aaaf9811ffc339d057c1a57ae5b5 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 5 Apr 2018 10:14:01 +0200 Subject: Add note about revisions for grouped and attached property objects If they're not supported, it should be documented. Change-Id: I9219db1c9d31666e005074a863b04438052b3f05 Reviewed-by: Simon Hausmann --- src/qml/doc/src/cppintegration/definetypes.qdoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 027e4b9923..7f3ff416a1 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -297,6 +297,8 @@ qmlRegisterRevision("MyTypes", 1, 1); This is useful when deriving from base classes provided by other authors, e.g. when extending classes from the Qt Quick module. +\note The QML engine does not support revisions for properties or signals of +grouped and attached property objects. \section2 Registering Extension Objects -- cgit v1.2.3 From 0b394e30bba4f6bb7e6f7dbe5585a2e15aa0f21d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 13 Apr 2018 15:43:40 +0200 Subject: Fix memory leak when calling instanceof on QML items The return value of getType() has the ref count increased and needs handling on the caller side. Change-Id: I05ffa4dae221f2502f87b76762164bba1389db32 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypewrapper.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 6dbf6ad8c1..ce35e966aa 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -388,6 +388,7 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); CompiledData::CompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + td->release(); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } -- cgit v1.2.3 From 56ade46b4234bb828b8e4f9a6bf83b5687bd122e Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Mon, 9 Apr 2018 14:05:53 +0200 Subject: Fix Text with ElideRight not being rendered when reparented Consider the following example: Item { width: 100 height: 30 Text { width: parent ? parent.width : 0 height: parent ? parent.height : 0 elide: Text.ElideRight text: "wot" } } When setting the Text item's parent to null, its explicit width and height are set to 0. When restoring its parent (the Item), its explicit width and height are set to 100 and 30 again, but the text itself is still not rendered. The cause can be seen here: if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed if (newGeometry.height() > oldGeometry.height()) { if (!d->heightExceeded) // Height is adequate and growing. goto geomChangeDone; heightExceeded was false, because 30 > 12 (or whatever the implicit height happened to be), so the text was not laid out again, even though it went from having an explicit height of 0 to an explicit height of 30. Fix the issue by only executing the goto if the old explicit height wasn't 0. Task-number: QTBUG-60328 Task-number: QTBUG-67145 Change-Id: I7f4d2f95bc95c850133ba91ac2d1a02c7ee159b6 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/quick/items/qquicktext.cpp | 4 ++- .../quick/qquicktext/data/elideParentChanged.qml | 13 +++++++ tests/auto/quick/qquicktext/tst_qquicktext.cpp | 41 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/auto/quick/qquicktext/data/elideParentChanged.qml diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 383aa2b821..9eaf9f8d6d 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2318,8 +2318,10 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed if (newGeometry.height() > oldGeometry.height()) { - if (!d->heightExceeded) // Height is adequate and growing. + if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { + // Height is adequate and growing, and it wasn't 0 previously. goto geomChangeDone; + } if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing. goto geomChangeDone; } else if (newGeometry.height() < oldGeometry.height()) { diff --git a/tests/auto/quick/qquicktext/data/elideParentChanged.qml b/tests/auto/quick/qquicktext/data/elideParentChanged.qml new file mode 100644 index 0000000000..f605cf58f1 --- /dev/null +++ b/tests/auto/quick/qquicktext/data/elideParentChanged.qml @@ -0,0 +1,13 @@ +import QtQuick 2.0 + +Item { + width: 100 + height: 30 + + Text { + width: parent ? parent.width : 0 + height: parent ? parent.height : 0 + elide: Text.ElideRight + text: "wot" + } +} diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index 609a84ce82..89d2590c03 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,7 @@ private slots: void width(); void wrap(); void elide(); + void elideParentChanged(); void multilineElide_data(); void multilineElide(); void implicitElide_data(); @@ -554,6 +556,45 @@ void tst_qquicktext::elide() } } +// QTBUG-60328 +// Tests that text with elide set is rendered after +// having its parent cleared and then set again. +void tst_qquicktext::elideParentChanged() +{ + QQuickView window; + window.setSource(testFileUrl("elideParentChanged.qml")); + QTRY_COMPARE(window.status(), QQuickView::Ready); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QQuickItem *root = window.rootObject(); + QVERIFY(root); + QCOMPARE(root->childItems().size(), 1); + + // Store a snapshot of the scene so that we can compare it later. + QSharedPointer grabResult = root->grabToImage(); + QTRY_VERIFY(!grabResult->image().isNull()); + const QImage expectedItemImageGrab(grabResult->image()); + + // Clear the text's parent. It shouldn't render anything. + QQuickItem *text = root->childItems().first(); + text->setParentItem(nullptr); + QCOMPARE(text->width(), 0.0); + QCOMPARE(text->height(), 0.0); + + // Set the parent back to what it was. The text should + // be rendered identically to how it was before. + text->setParentItem(root); + QCOMPARE(text->width(), 100.0); + QCOMPARE(text->height(), 30.0); + + grabResult = root->grabToImage(); + QTRY_VERIFY(!grabResult->image().isNull()); + const QImage actualItemImageGrab(grabResult->image()); + QCOMPARE(actualItemImageGrab, expectedItemImageGrab); +} + void tst_qquicktext::multilineElide_data() { QTest::addColumn("format"); -- cgit v1.2.3 From 6069cc1cd1a6309cdffeb8bdd9c4035f33742228 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 21 Feb 2018 17:31:55 +0100 Subject: Doc: Add Licensing section to "Qt Quick Test", "Qt Quick Layouts" Mention licenses in the remaining landing pages. Change-Id: I3754d959db9d9e4914e50d670f159eba1afa7596 Reviewed-by: Simon Hausmann --- src/qmltest/doc/src/qtquicktest-index.qdoc | 9 +++++++++ src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index 6b5f8fdf4c..1f84db405c 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -182,4 +182,13 @@ #include "tst_mytest.moc" \endcode + + \section1 Licenses + + Qt Quick Tests is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under free software licenses. Since Qt 5.4, + these free software licenses are + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. */ diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc index f65b1152e9..a45ed4b45c 100644 --- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-index.qdoc @@ -39,6 +39,15 @@ Visit the \l{Qt Quick Layouts Overview} page to get started. + \section1 Licenses + + Qt Quick Layouts is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under free software licenses. Since Qt 5.4, + these free software licenses are + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. + \section1 Layouts \annotatedlist layouts -- cgit v1.2.3 From c35dae0553ef181af263c0bf8f528619324d275e Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 15:42:48 +0200 Subject: Doc: Fix missing \endcode error This got introduced in commit 67c9f735ce. Change-Id: I61a9a3886125cebe64f9eaa5ae1c7b8d226b2e1c Reviewed-by: Mitch Curtis --- src/qml/doc/src/qmlfunctions.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 34dce8f4da..55ca040af6 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -345,7 +345,7 @@ \code qmlRegisterType("App", 1, 0, "Foo"); qmlRegisterType(); - \code + \endcode As the \c Foo type is instantiated in QML, it must be registered with the version of \l qmlRegisterType() that takes an import URI. -- cgit v1.2.3 From 9b0580f14ef0ca527efb5f397b1440ea79615b3b Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 09:25:43 +0200 Subject: Update plugins.qmltypes for QtQuick Change-Id: I5d2019c079a5e28b7fd8bfcb1ed63e5a5e3fe956 Reviewed-by: Simon Hausmann --- src/imports/qtquick2/plugins.qmltypes | 46 ++++++++++++++++++++++++++--------- src/imports/qtquick2/qtquick2.pro | 2 +- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index 0fce98a212..6f6f1de8c3 100644 --- a/src/imports/qtquick2/plugins.qmltypes +++ b/src/imports/qtquick2/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.10' +// 'qmlplugindump -nonrelocatable QtQuick 2.11' Module { dependencies: [] @@ -19,6 +19,15 @@ Module { "HorizontalSortHint": 2 } } + Enum { + name: "CheckIndexOption" + values: { + "NoOption": 0, + "IndexIsValid": 1, + "DoNotUseParent": 2, + "ParentIsInvalid": 4 + } + } Signal { name: "dataChanged" Parameter { name: "topLeft"; type: "QModelIndex" } @@ -936,14 +945,16 @@ Module { name: "QQuickAnimatedImage" defaultProperty: "data" prototype: "QQuickImage" - exports: ["QtQuick/AnimatedImage 2.0"] - exportMetaObjectRevisions: [0] + exports: ["QtQuick/AnimatedImage 2.0", "QtQuick/AnimatedImage 2.11"] + exportMetaObjectRevisions: [0, 11] Property { name: "playing"; type: "bool" } Property { name: "paused"; type: "bool" } Property { name: "currentFrame"; type: "int" } Property { name: "frameCount"; type: "int"; isReadonly: true } + Property { name: "speed"; revision: 11; type: "double" } Property { name: "sourceSize"; type: "QSize"; isReadonly: true } Signal { name: "frameChanged" } + Signal { name: "speedChanged"; revision: 11 } } Component { name: "QQuickAnimatedSprite" @@ -1814,6 +1825,9 @@ Module { Component { name: "QQuickGrabGestureEvent" prototype: "QObject" + exports: ["QtQuick/GestureEvent 2.0"] + isCreatable: false + exportMetaObjectRevisions: [0] Property { name: "touchPoints"; type: "QObject"; isList: true; isReadonly: true } Property { name: "dragThreshold"; type: "double"; isReadonly: true } Method { name: "grab" } @@ -2118,10 +2132,11 @@ Module { exports: [ "QtQuick/Item 2.0", "QtQuick/Item 2.1", + "QtQuick/Item 2.11", "QtQuick/Item 2.4", "QtQuick/Item 2.7" ] - exportMetaObjectRevisions: [0, 1, 2, 7] + exportMetaObjectRevisions: [0, 1, 11, 2, 7] Enum { name: "TransformOrigin" values: { @@ -2175,6 +2190,7 @@ Module { Property { name: "antialiasing"; type: "bool" } Property { name: "implicitWidth"; type: "double" } Property { name: "implicitHeight"; type: "double" } + Property { name: "containmentMask"; revision: 11; type: "QObject"; isPointer: true } Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true } Signal { name: "childrenRectChanged" @@ -2226,6 +2242,7 @@ Module { revision: 1 Parameter { name: "window"; type: "QQuickWindow"; isPointer: true } } + Signal { name: "containmentMaskChanged"; revision: 11 } Method { name: "update" } Method { name: "grabToImage" @@ -3026,6 +3043,19 @@ Module { Property { name: "closed"; type: "bool"; isReadonly: true } Signal { name: "changed" } } + Component { + name: "QQuickPathAngleArc" + prototype: "QQuickCurve" + exports: ["QtQuick/PathAngleArc 2.11"] + exportMetaObjectRevisions: [0] + Property { name: "centerX"; type: "double" } + Property { name: "centerY"; type: "double" } + Property { name: "radiusX"; type: "double" } + Property { name: "radiusY"; type: "double" } + Property { name: "startAngle"; type: "double" } + Property { name: "sweepAngle"; type: "double" } + Property { name: "moveToStart"; type: "bool" } + } Component { name: "QQuickPathAnimation" prototype: "QQuickAbstractAnimation" @@ -3415,14 +3445,6 @@ Module { name: "durationChanged" Parameter { type: "int" } } - Signal { - name: "fromChanged" - Parameter { type: "QVariant" } - } - Signal { - name: "toChanged" - Parameter { type: "QVariant" } - } Signal { name: "easingChanged" Parameter { type: "QEasingCurve" } diff --git a/src/imports/qtquick2/qtquick2.pro b/src/imports/qtquick2/qtquick2.pro index 118343588a..01ac034104 100644 --- a/src/imports/qtquick2/qtquick2.pro +++ b/src/imports/qtquick2/qtquick2.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qtquick2plugin TARGETPATH = QtQuick.2 -IMPORT_VERSION = 2.6 +IMPORT_VERSION = 2.11 SOURCES += \ plugin.cpp -- cgit v1.2.3 From bbda0e5a7111f226b6540cd4e6d99311bcd8e7ce Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 08:51:38 +0200 Subject: Doc: Bump QtQuick import to 2.11 Task-number: QTBUG-67786 Change-Id: Ie371d4c931ce31f728cdd960ad454e26c40cc4ed Reviewed-by: Simon Hausmann --- src/qml/doc/src/qmltypereference.qdoc | 2 +- src/quick/doc/src/qmltypereference.qdoc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index cfd4d55a24..f2e0ce60c0 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -55,7 +55,7 @@ are also provided by the \c QtQuick namespace which may be imported as follows: \qml -import QtQuick 2.7 +import QtQuick 2.11 \endqml See the \l{Qt Quick} module documentation for more information about the \c diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index f5b189c580..391b8b5f26 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! -\qmlmodule QtQuick 2.7 +\qmlmodule QtQuick 2.11 \title Qt Quick QML Types \ingroup qmlmodules \brief Provides graphical QML types. @@ -34,11 +34,11 @@ The \l{Qt Quick} module provides graphical primitive types. These types are only available in a QML document if that document imports the \c QtQuick namespace. -The current version of the \c QtQuick module is version 2.7, and thus it may be +The current version of the \c QtQuick module is version 2.11, and thus it may be imported via the following statement: \qml -import QtQuick 2.7 +import QtQuick 2.11 \endqml Visit the \l {Qt Quick} module documentation for more -- cgit v1.2.3 From 1bc0593547218cc59ed8c0e34c559df2a4d2a313 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 15:55:21 +0200 Subject: Document latest QtQuick.LocalStorage version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: If0b8a0daecc62d191a9c5efd450ec0f5214ee387 Reviewed-by: Simon Hausmann --- src/imports/localstorage/localstorage.pro | 2 +- src/imports/localstorage/plugin.cpp | 2 +- src/imports/localstorage/plugins.qmltypes | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imports/localstorage/localstorage.pro b/src/imports/localstorage/localstorage.pro index 15753263b8..2fc976d37d 100644 --- a/src/imports/localstorage/localstorage.pro +++ b/src/imports/localstorage/localstorage.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qmllocalstorageplugin TARGETPATH = QtQuick/LocalStorage -IMPORT_VERSION = 2.0 +IMPORT_VERSION = 2.$${QT_MINOR_VERSION} QT = sql qml-private core-private diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index fb71fcaff6..9cca11ac5d 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -523,7 +523,7 @@ through the data. /*! - \qmlmodule QtQuick.LocalStorage 2 + \qmlmodule QtQuick.LocalStorage 2.11 \title Qt Quick Local Storage QML Types \ingroup qmlmodules \brief Provides a JavaScript object singleton type for accessing a local diff --git a/src/imports/localstorage/plugins.qmltypes b/src/imports/localstorage/plugins.qmltypes index dee81a78d0..412989c001 100644 --- a/src/imports/localstorage/plugins.qmltypes +++ b/src/imports/localstorage/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.LocalStorage 2.0' +// 'qmlplugindump -nonrelocatable QtQuick.LocalStorage 2.11' Module { dependencies: [] -- cgit v1.2.3 From 017bd28f5f3854d0f4ad62a80630f08387bca9f6 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 15:58:09 +0200 Subject: Document latest QtQml.Models import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: Ife465afc101d400b47e9f98c58cac3894224da00 Reviewed-by: Simon Hausmann --- src/imports/models/models.pro | 2 +- src/imports/models/plugin.cpp | 4 ++-- src/imports/models/plugins.qmltypes | 11 ++++++++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/imports/models/models.pro b/src/imports/models/models.pro index c94ba833ad..fc87533cea 100644 --- a/src/imports/models/models.pro +++ b/src/imports/models/models.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = modelsplugin TARGETPATH = QtQml/Models.2 -IMPORT_VERSION = 2.3 +IMPORT_VERSION = 2.$$QT_MINOR_VERSION SOURCES += \ plugin.cpp diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp index fb1d42e85e..83f8597408 100644 --- a/src/imports/models/plugin.cpp +++ b/src/imports/models/plugin.cpp @@ -52,7 +52,7 @@ static void initResources() QT_BEGIN_NAMESPACE /*! - \qmlmodule QtQml.Models 2.2 + \qmlmodule QtQml.Models 2.11 \title Qt QML Models QML Types \ingroup qmlmodules \brief Provides QML types for data models @@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE To use the types in this module, import the module with the following line: \code - import QtQml.Models 2.2 + import QtQml.Models 2.11 \endcode Note that QtQml.Models module started at version 2.1 to match the version diff --git a/src/imports/models/plugins.qmltypes b/src/imports/models/plugins.qmltypes index e6d09b76d6..60146f51ba 100644 --- a/src/imports/models/plugins.qmltypes +++ b/src/imports/models/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml.Models 2.3' +// 'qmlplugindump -nonrelocatable QtQml.Models 2.11' Module { dependencies: [] @@ -19,6 +19,15 @@ Module { "HorizontalSortHint": 2 } } + Enum { + name: "CheckIndexOption" + values: { + "NoOption": 0, + "IndexIsValid": 1, + "DoNotUseParent": 2, + "ParentIsInvalid": 4 + } + } Signal { name: "dataChanged" Parameter { name: "topLeft"; type: "QModelIndex" } -- cgit v1.2.3 From e7e08161693e8841e72a64dba60890b2c7c0a2c5 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:02:25 +0200 Subject: Document latest QtQuick.Window version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I5ba3c5947262e7fab5df3bfcc05d2282234dafcc Reviewed-by: Simon Hausmann --- src/imports/window/plugin.cpp | 4 ++-- src/imports/window/window.pro | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp index d8d21ce27e..907eecb060 100644 --- a/src/imports/window/plugin.cpp +++ b/src/imports/window/plugin.cpp @@ -51,7 +51,7 @@ static void initResources() QT_BEGIN_NAMESPACE /*! - \qmlmodule QtQuick.Window 2.2 + \qmlmodule QtQuick.Window 2.11 \title Qt Quick Window QML Types \ingroup qmlmodules \brief Provides QML types for window management @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE To use the types in this module, import the module with the following line: \code - import QtQuick.Window 2.2 + import QtQuick.Window 2.11 \endcode */ diff --git a/src/imports/window/window.pro b/src/imports/window/window.pro index a938e0eeef..77bd9518e9 100644 --- a/src/imports/window/window.pro +++ b/src/imports/window/window.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = windowplugin TARGETPATH = QtQuick/Window.2 -IMPORT_VERSION = 2.2 +IMPORT_VERSION = 2.$$QT_MINOR_VERSION SOURCES += \ plugin.cpp -- cgit v1.2.3 From 8ec21eecb27441a67198629c01ad9d1cf87924cf Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:10:17 +0200 Subject: Document latest QtQuick.XmlListModule import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I4ed5d3e08d718ebb6cf15f97806c18b8aa514e1c Reviewed-by: Simon Hausmann --- src/imports/xmllistmodel/qqmlxmllistmodel.cpp | 4 ++-- src/imports/xmllistmodel/xmllistmodel.pro | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp index d14810a01b..a08b59a598 100644 --- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp +++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp @@ -72,7 +72,7 @@ typedef QPair QQuickXmlListRange; #define XMLLISTMODEL_CLEAR_ID 0 /*! - \qmlmodule QtQuick.XmlListModel 2 + \qmlmodule QtQuick.XmlListModel 2.11 \title Qt Quick XmlListModel QML Types \ingroup qmlmodules \brief Provides QML types for creating models from XML data @@ -82,7 +82,7 @@ typedef QPair QQuickXmlListRange; To use the types in this module, import the module with the following line: \code - import QtQuick.XmlListModel 2.0 + import QtQuick.XmlListModel 2.11 \endcode */ diff --git a/src/imports/xmllistmodel/xmllistmodel.pro b/src/imports/xmllistmodel/xmllistmodel.pro index 2308f26d1b..b5f559fc93 100644 --- a/src/imports/xmllistmodel/xmllistmodel.pro +++ b/src/imports/xmllistmodel/xmllistmodel.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qmlxmllistmodelplugin TARGETPATH = QtQuick/XmlListModel -IMPORT_VERSION = 2.0 +IMPORT_VERSION = 2.$$QT_MINOR_VERSION QT = network xmlpatterns qml-private core-private -- cgit v1.2.3 From 9d90644db63f88e041d29f5a566db71a8ac25098 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:14:11 +0200 Subject: Document latest QtQml import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I232dcef923f3dfa5a6a2377bc8297b07fbe01f37 Reviewed-by: Simon Hausmann --- src/qml/doc/src/qmltypereference.qdoc | 6 +++--- src/qml/doc/src/qtqml.qdoc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index f2e0ce60c0..960ea116c9 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! -\qmlmodule QtQml 2.2 +\qmlmodule QtQml 2.11 \title Qt QML QML Types \ingroup qmlmodules \brief List of QML types provided by the Qt QML module @@ -43,11 +43,11 @@ The types provided by the \c QtQml module are only available in a QML document if that document imports the \c QtQml namespace (or if the document imports the \c QtQuick namespace, as noted below). -The current version of the \c QtQml module is version 2.2, and thus it may be +The current version of the \c QtQml module is version 2.11, and thus it may be imported via the following statement: \qml -import QtQml 2.2 +import QtQml 2.11 \endqml Most clients will never need to use the \c QtQml import, as all of the types diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc index a9f8e2a960..b4bc9a0774 100644 --- a/src/qml/doc/src/qtqml.qdoc +++ b/src/qml/doc/src/qtqml.qdoc @@ -58,7 +58,7 @@ following directive: The QML types in Qt QML are available through the \c QtQML import. To use the types, add the following import statement to your .qml file: \code -import QtQml 2.0 +import QtQml 2.11 \endcode -- cgit v1.2.3 From 54f3b6fbbd95ba06b742ced902b4507177fcf654 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:18:21 +0200 Subject: Document latest QtQml.StateMachine import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I61a1015d74beaeac9d7a2aecd73e3f05c8a545c9 Reviewed-by: Simon Hausmann --- src/imports/statemachine/plugins.qmltypes | 2 +- src/imports/statemachine/statemachine.pro | 2 +- src/qml/doc/src/statemachine.qdoc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imports/statemachine/plugins.qmltypes b/src/imports/statemachine/plugins.qmltypes index 0fe9b63e03..e6ecaa75c8 100644 --- a/src/imports/statemachine/plugins.qmltypes +++ b/src/imports/statemachine/plugins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml.StateMachine 1.0' +// 'qmlplugindump -nonrelocatable QtQml.StateMachine 1.11' Module { dependencies: [] diff --git a/src/imports/statemachine/statemachine.pro b/src/imports/statemachine/statemachine.pro index 9bb88074e9..926a9d4a5e 100644 --- a/src/imports/statemachine/statemachine.pro +++ b/src/imports/statemachine/statemachine.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qtqmlstatemachine TARGETPATH = QtQml/StateMachine -IMPORT_VERSION = 1.0 +IMPORT_VERSION = 1.$$QT_MINOR_VERSION QT = core-private qml-private diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc index 0cd19d2a68..59170a223f 100644 --- a/src/qml/doc/src/statemachine.qdoc +++ b/src/qml/doc/src/statemachine.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \qmlmodule QtQml.StateMachine 1.0 + \qmlmodule QtQml.StateMachine 1.11 \title Declarative State Machine QML Types \brief Provides QML types to create and execute state graphs. -- cgit v1.2.3 From bff9a9a47ee501b6f8479d472ba7909766ee62e7 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:21:29 +0200 Subject: Document latest QtQuick.Particles import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I9443be62da5cc9fc281e167f38fa299c73115eda Reviewed-by: Simon Hausmann --- src/imports/particles/particles.pro | 2 +- src/quick/doc/src/concepts/effects/particles.qdoc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/imports/particles/particles.pro b/src/imports/particles/particles.pro index 4460d03a04..41146c75b0 100644 --- a/src/imports/particles/particles.pro +++ b/src/imports/particles/particles.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = particlesplugin TARGETPATH = QtQuick/Particles.2 -IMPORT_VERSION = 2.0 +IMPORT_VERSION = 2.$$QT_MINOR_VERSION SOURCES += \ plugin.cpp diff --git a/src/quick/doc/src/concepts/effects/particles.qdoc b/src/quick/doc/src/concepts/effects/particles.qdoc index 0de0aa93ad..59f9c0db83 100644 --- a/src/quick/doc/src/concepts/effects/particles.qdoc +++ b/src/quick/doc/src/concepts/effects/particles.qdoc @@ -26,14 +26,14 @@ ****************************************************************************/ /*! - \qmlmodule QtQuick.Particles 2 + \qmlmodule QtQuick.Particles 2.11 \title Qt Quick Particles QML Types \ingroup qmlmodules \brief Provides QML types for particle effects This QML module contains a particle system for Qt Quick. To use these types, import the module with the following line: \code - import QtQuick.Particles 2.0 + import QtQuick.Particles 2.11 \endcode For a simple overview of how the system can be used, see \l{Using the Qt Quick Particle System}. @@ -50,7 +50,7 @@ Note that to use types from the particles module, you will need to import the types with the following line: \code - import QtQuick.Particles 2.0 + import QtQuick.Particles 2.11 \endcode \section1 The ParticleSystem -- cgit v1.2.3 From 551cb80abf98db21a8fa1a3616f2b44d33a6288e Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:25:33 +0200 Subject: Document latest QtQuick.Layouts import to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: I4131a399cca7c66b0e9df549f14be43eeaa94af3 Reviewed-by: Simon Hausmann --- src/imports/layouts/layouts.pro | 2 +- src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc | 2 +- src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/imports/layouts/layouts.pro b/src/imports/layouts/layouts.pro index 26574150de..addf396746 100644 --- a/src/imports/layouts/layouts.pro +++ b/src/imports/layouts/layouts.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qquicklayoutsplugin TARGETPATH = QtQuick/Layouts -IMPORT_VERSION = 1.2 +IMPORT_VERSION = 1.$$QT_MINOR_VERSION QT *= qml-private quick-private gui-private core-private diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc index 06ebe2d3d1..a5f93b972a 100644 --- a/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts-overview.qdoc @@ -38,7 +38,7 @@ The QML types can be imported into your application using the following import statement in your \c {.qml} file. \code - import QtQuick.Layouts 1.2 + import QtQuick.Layouts 1.11 \endcode \section1 Key Features diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc index 8f390c83db..d82e35fb93 100644 --- a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc +++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc @@ -26,7 +26,7 @@ ****************************************************************************/ /*! - \qmlmodule QtQuick.Layouts 1.3 + \qmlmodule QtQuick.Layouts 1.11 \title Qt Quick Layouts QML Types \ingroup qmlmodules \brief Provides QML types for arranging QML items in a user interface. @@ -40,7 +40,7 @@ following import statement in your .qml file. \code - import QtQuick.Layouts 1.3 + import QtQuick.Layouts 1.11 \endcode */ -- cgit v1.2.3 From 61b6180c02202921245569c1a1278be6942d6ec6 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 16:32:58 +0200 Subject: Document latest QtQuick.Test import version to be 2.11 Since commit 214fbaa57b73296a0 the latest minor version of the import is automatically the Qt minor version, so let's also document this. Change-Id: Ie2fe1d172898bd0d51f0841c078abd1f8ab8d744 Reviewed-by: Simon Hausmann --- src/imports/testlib/testlib.pro | 2 +- src/quick/doc/src/qmltypereference.qdoc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/imports/testlib/testlib.pro b/src/imports/testlib/testlib.pro index 6e8a6aee72..acbe82e5e6 100644 --- a/src/imports/testlib/testlib.pro +++ b/src/imports/testlib/testlib.pro @@ -1,7 +1,7 @@ CXX_MODULE = qml TARGET = qmltestplugin TARGETPATH = QtTest -IMPORT_VERSION = 1.0 +IMPORT_VERSION = 1.$$QT_MINOR_VERSION QT += qml quick qmltest qmltest-private qml-private core-private testlib diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 391b8b5f26..80b9241f36 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -883,14 +883,14 @@ console.log(c + " " + d); // false true */ /*! -\qmlmodule QtTest 1.1 +\qmlmodule QtTest 1.11 \title Qt Quick Test QML Types \brief This module provides QML types to unit test your QML application \ingroup qmlmodules You can import this module using the following statement: \code -import QtTest 1.1 +import QtTest 1.11 \endcode For more information about how to use these types, see -- cgit v1.2.3 From 7bd77083032c6414f23b994617fb907be32f4d83 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Wed, 18 Apr 2018 17:02:45 +0200 Subject: Update builtins.qmltypes Change-Id: I2e08061ff7d084b08d1fb6d857dc6fb985c66a48 Reviewed-by: Simon Hausmann --- src/imports/builtins/builtins.qmltypes | 39 ++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes index 5fb68d15d9..53c5300685 100644 --- a/src/imports/builtins/builtins.qmltypes +++ b/src/imports/builtins/builtins.qmltypes @@ -4,7 +4,7 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -builtins' +// 'qmlplugindump -builtins' Module { dependencies: [] @@ -427,7 +427,8 @@ Module { "WA_MacNoShadow": 127, "WA_AlwaysStackOnTop": 128, "WA_TabletTracking": 129, - "WA_AttributeCount": 130 + "WA_ContentsMarginsRespectsSafeArea": 130, + "WA_AttributeCount": 131 } } Enum { @@ -760,6 +761,36 @@ Module { "Key_Dead_Belowdot": 16781920, "Key_Dead_Hook": 16781921, "Key_Dead_Horn": 16781922, + "Key_Dead_Stroke": 16781923, + "Key_Dead_Abovecomma": 16781924, + "Key_Dead_Abovereversedcomma": 16781925, + "Key_Dead_Doublegrave": 16781926, + "Key_Dead_Belowring": 16781927, + "Key_Dead_Belowmacron": 16781928, + "Key_Dead_Belowcircumflex": 16781929, + "Key_Dead_Belowtilde": 16781930, + "Key_Dead_Belowbreve": 16781931, + "Key_Dead_Belowdiaeresis": 16781932, + "Key_Dead_Invertedbreve": 16781933, + "Key_Dead_Belowcomma": 16781934, + "Key_Dead_Currency": 16781935, + "Key_Dead_a": 16781952, + "Key_Dead_A": 16781953, + "Key_Dead_e": 16781954, + "Key_Dead_E": 16781955, + "Key_Dead_i": 16781956, + "Key_Dead_I": 16781957, + "Key_Dead_o": 16781958, + "Key_Dead_O": 16781959, + "Key_Dead_u": 16781960, + "Key_Dead_U": 16781961, + "Key_Dead_Small_Schwa": 16781962, + "Key_Dead_Capital_Schwa": 16781963, + "Key_Dead_Greek": 16781964, + "Key_Dead_Lowline": 16781968, + "Key_Dead_Aboveverticalline": 16781969, + "Key_Dead_Belowverticalline": 16781970, + "Key_Dead_Longsolidusoverlay": 16781971, "Key_Back": 16777313, "Key_Forward": 16777314, "Key_Stop": 16777315, @@ -1348,6 +1379,8 @@ Module { "ImhTime": 256, "ImhPreferLatin": 512, "ImhMultiLine": 1024, + "ImhNoEditMenu": 2048, + "ImhNoTextHandles": 4096, "ImhDigitsOnly": 65536, "ImhFormattedNumbersOnly": 131072, "ImhUppercaseOnly": 262144, @@ -1374,6 +1407,8 @@ Module { "ImhTime": 256, "ImhPreferLatin": 512, "ImhMultiLine": 1024, + "ImhNoEditMenu": 2048, + "ImhNoTextHandles": 4096, "ImhDigitsOnly": 65536, "ImhFormattedNumbersOnly": 131072, "ImhUppercaseOnly": 262144, -- cgit v1.2.3 From 950de04322191c16c3066707889b17b0f5eb2ee6 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Apr 2018 09:09:19 +0200 Subject: Fix crash in Function.prototype.bind Allocating a 0 sized MemberData hits an assertion in debug builds. Change-Id: I0251b2b38f4b48c7ed35d22f88c0c5c4a98e6464 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4functionobject.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index dc8ee550d5..83608070ec 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -360,13 +360,15 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu BoundFunction *bound = static_cast(target.getPointer()); Scoped oldArgs(scope, bound->boundArgs()); boundThis = bound->boundThis(); - int oldSize = oldArgs->size(); - boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs); - boundArgs->d()->values.size = oldSize + nArgs; - for (uint i = 0; i < static_cast(oldSize); ++i) - boundArgs->set(scope.engine, i, oldArgs->data()[i]); - for (uint i = 0; i < static_cast(nArgs); ++i) - boundArgs->set(scope.engine, oldSize + i, argv[i + 1]); + int oldSize = !oldArgs ? 0 : oldArgs->size(); + if (oldSize + nArgs) { + boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs); + boundArgs->d()->values.size = oldSize + nArgs; + for (uint i = 0; i < static_cast(oldSize); ++i) + boundArgs->set(scope.engine, i, oldArgs->data()[i]); + for (uint i = 0; i < static_cast(nArgs); ++i) + boundArgs->set(scope.engine, oldSize + i, argv[i + 1]); + } target = bound->target(); } else if (nArgs) { boundArgs = MemberData::allocate(scope.engine, nArgs); -- cgit v1.2.3 From fb027435f597d55b9e72465036cb0daa2b272ec0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 25 Apr 2018 15:19:54 +0200 Subject: Only create CallContext objects for bindings that might be signal handlers Creating the callcontext for all bindings causes a 15% performance regression in both of the moving images benchmarks of qmlbench. But in most cases, we know that a binding can't be a signal handler, as those always have to start with 'on'. Take this into account and avoid creating the context for most binding expressions. Task-number: QTBUG-67782 Task-number: QTBUG-67783 Change-Id: I9a25cb916e374c7d03693e49646ca28853c6ba54 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4codegen.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 8ada1d505e..c281275da1 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2104,9 +2104,14 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope); bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval; - if (_context->compilationMode == QmlBinding // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not. - || (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && - (_context->compilationMode != EvalCode || _context->isStrict))) { + bool needsCallContext = false; + const QLatin1String exprForOn("expression for on"); + if (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && (_context->compilationMode != EvalCode || _context->isStrict)) + needsCallContext = true; + else if (_context->compilationMode == QmlBinding && name.length() > exprForOn.size() && name.startsWith(exprForOn) && name.at(exprForOn.size()).isUpper()) + // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not. + needsCallContext = true; + if (needsCallContext) { Instruction::CreateCallContext createContext; bytecodeGenerator->addInstruction(createContext); } -- cgit v1.2.3 From 1e82f11629e5572783e5bfc36f24ad10c235ca53 Mon Sep 17 00:00:00 2001 From: Antti Kokko Date: Fri, 20 Apr 2018 14:21:22 +0300 Subject: Add changes file for Qt 5.11.0 Change-Id: I6ab3877b8fff6966141abe1bb40609e5d8d842e3 Reviewed-by: Eirik Aavitsland Reviewed-by: Shawn Rutledge --- dist/changes-5.11.0 | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 dist/changes-5.11.0 diff --git a/dist/changes-5.11.0 b/dist/changes-5.11.0 new file mode 100644 index 0000000000..f7afe87eb7 --- /dev/null +++ b/dist/changes-5.11.0 @@ -0,0 +1,167 @@ +Qt 5.11 introduces many new features and improvements as well as bugfixes +over the 5.10.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.11 series is binary compatible with the 5.10.x series. +Applications compiled for 5.10 will continue to run with 5.11. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important Behavior Changes * +**************************************************************************** + +* QML module plugins used to be limited to type registrations in the + primary module namespace in the virtual registerTypes() function. + Module authors worked around this limitation by placing necessary + internal type registrations into initializeEngine() that may cause + memory leaks. Therefore this restriction has been moved and types in + any (non-protected) namespaces can be registered in the + registerTypes() function. + +* In Qt 5.11 and newer versions, QML plugin modules are available with + the same minor version as the Qt release minor version number. For + example it's possible to import QtQuick.Window 2.11 or import + QtQuick.Layouts 1.11 even though there haven't been any API changes in + these modules for Qt 5.11, and the maximum possible import version + will automatically increment in future Qt versions. This is intended + to reduce confusion. + +**************************************************************************** +* Library * +**************************************************************************** + +QtQml +----- + + - Qt Qml uses a completely new compiler pipeline to compile Javascript (and QML) first + into bytecode and then JIT heavily used functions into assembly. + * Lots of cleanups and performance improvement to the way function calls and Javascript + scopes are being handled. + * Improved performance of JS property lookups. + * A new bytecode format that is very compact, saving memory in many cases. + * significantly faster bytecode interpreter than in earlier versions of Qt, in many cases + reaching almost the performance of the old JIT. + * A new JIT that works on top of the bytecode interpreter and only compiles hot functions + into assembly. + * Overall test results show almost a doubling of the JS performance on platforms where we + can't use a JIT (iOS and WinRT) compared to 5.10. + * With the new JIT, JS performance is usually around 10-40% faster than in older Qt versions + (depending on the use case). + + - The commercial only Qt Quick Compiler has been removed and replaced with a common solution + that works in both the open source and commercial version of Qt. No code changes are needed + for users of the old compiler. + + - Fix qmlplugindump to work correctly with shadow builds. + + - Fixed creation of QWidgets in QML. + + - Various fixes to the debugging bridge. + + - ListModel + * Support assignment of function declarations in ListElement, to allow for + models with actions. + +QtQuick +------- + + - QQuickWindow: + * [QTBUG-66329] We no longer synthesize redundant mouse events based on + touch events that come from a trackpad which can generate mouse events. + + - Item: + * [QTBUG-20524] Added a containmentMask property. This allows any + QObject which defines Q_INVOKABLE bool contains(const QPointF &point) + (including a Shape) to mask the area of an Item that will be + sensitive to mouse and touch events. + + - AnimatedSprite: + * [QTBUG-36341] AnimatedSprite's implicitWidth and implicitHeight are + now based on frameWidth and frameHeight, respectively. This means it + is no longer necessary to explicitly size AnimatedSprite. + + - Image: + * [QTBUG-66127] Support detection of suitable file extension. + * Add support for ktx files containing compressed textures. + * Add experimental automatic atlasing of ETC-compressed + textures (can be enabled with QSG_ENABLE_COMPRESSED_ATLAS=1). + * [QTBUG-67019] SVG images are now scaled up if the source size is larger + than the original size. + * [QTBUG-65789] SVG images are rendered with the correct aspect ratio + when width or height is set to zero. + + - AnimatedImage: + * Added a speed property. It's also OK to set it to 0 to pause + the animation. + * There is now an example to demonstrate usage of AnimatedImage. + * [QTBUG-62913] frameCount now has a NOTIFY signal to avoid binding warnings. + + - Path: + * [QTBUG-62684] Add new PathAngleArc type. + + - Shape: + * A containsMode property is added. If it is set to FillContains, then + Shape.contains() returns true only within the visible bounds, so its + Pointer Handlers also respond only within those bounds. + + - ShapePath: + * Improved performance for complex paths. + + - Text: + * [QTBUG-60328][QTBUG-67145] Fixed Text with ElideRight not being rendered + when reparented. + * [QTBUG-67007] Fixed non-integer scale factors with Text native rendering. + + - Pointer Handlers: + * Added singleTapped and doubleTapped convenience signals to TapHandler. + * PointerHandlers implement QQmlParserStatus, so that subclasses can + have custom initialization in componentComplete() (just as Items can). + * [QTBUG-65651] Restore 5.9 behavior: if an Item has no PointerHandlers, + childMouseEventFilter() will not be called during post-delivery of + an unhandled event. + * TapHandler.gesturePolicy is now DragThreshold by default. + * DragHandler with target: null no longer crashes. + * [QTBUG-64852] DragHandler enforces its axis constraints even when + the parent coordinate system changes during the drag. For example + the knob of a DragHandler-based slider inside a Flickable doesn't + come out of its "groove" if you manage to drag the slider and the + Flickable at the same time. + + - Loader: + * [QTBUG-63729] Avoid evaluating bindings during destruction, to + avoid some spurious errors. + * [QTBUG-66822] When deactivating a loader, do not immediately clear + its context. + + - Software renderer + * [QTBUG-63185][QTBUG-65975] QSGLayer::grab now works correctly. + * [QTBUG-66381] Fixed a crash when a Window has a QObject parent + (such as Loader). + * [QTBUG-65934] Fixed renering of rounded rectangles with floating point + pixel device ratios. + + - Platform Specific Changes: + * [QTBUG-67460] Fixed a problem with jumping back to the highlight + while slowly scrolling a ListView on macOS. + +QtQuickTest +----------- + + - [QTBUG-50064] Added QUICK_TEST_MAIN_WITH_SETUP macro to allow executing + C++ before a QML test (such as registering context properties). + +QQuickWidget +------------ + + - [QTBUG-45641] Tab presses are now passed on to the root item to be + handled first. When not handled by the root item, it will be handled + like a standard QWidget. -- cgit v1.2.3