aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2018-06-29 01:02:07 +0200
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2018-06-29 01:02:07 +0200
commitdd333172e2ade1db6d0af72b4ed44262c2b4d8c7 (patch)
tree16301a9b938826cf4b78b751907378d96423c5d0
parent3d266d90cb920375bb856844baea290e52355aad (diff)
parentffeaac704efc9eb85464d0a401d98e28991ec4d3 (diff)
Merge remote-tracking branch 'origin/5.11' into dev
-rw-r--r--src/qml/doc/src/javascript/resources.qdoc27
-rw-r--r--src/qml/qml/qqmlimport.cpp14
-rw-r--r--src/qml/qml/qqmlmetatype.cpp42
-rw-r--r--src/qml/qml/qqmlmetatype_p.h13
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc2
-rw-r--r--src/quick/doc/src/qtquick.qdoc2
-rw-r--r--src/quick/items/qquickdrag.cpp4
-rw-r--r--src/quick/items/qquickpathview.cpp5
-rw-r--r--src/quick/items/qquicktextcontrol.cpp1
-rw-r--r--tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp2
-rw-r--r--tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp14
-rw-r--r--tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp30
-rw-r--r--tests/auto/quick/qquickpathview/data/objectModelMove.qml123
-rw-r--r--tests/auto/quick/qquickpathview/tst_qquickpathview.cpp48
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp12
15 files changed, 278 insertions, 61 deletions
diff --git a/src/qml/doc/src/javascript/resources.qdoc b/src/qml/doc/src/javascript/resources.qdoc
index 34b0610f74..60f97c2007 100644
--- a/src/qml/doc/src/javascript/resources.qdoc
+++ b/src/qml/doc/src/javascript/resources.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
\page qtqml-javascript-resources.html
-\title Defining JavaScript Resources In QML
+\title Defining JavaScript Resources in QML
\brief Description of how JavaScript files may be defined for use in QML
The program logic for a QML application may be defined in JavaScript. The
@@ -60,7 +60,8 @@ An example of a code-behind implementation resource follows:
\code
// MyButton.qml
import QtQuick 2.0
-import "my_button_impl.js" as Logic // a new instance of this JavaScript resource is loaded for each instance of Button.qml
+import "my_button_impl.js" as Logic // A new instance of this JavaScript resource
+ // is loaded for each instance of Button.qml.
Rectangle {
id: rect
@@ -95,13 +96,18 @@ for maintainability and readability.
\section1 Shared JavaScript Resources (Libraries)
-Some JavaScript files act more like libraries - they provide a set of helper
-functions that take input and compute output, but never manipulate QML
-component instances directly.
+By default, JavaScript files imported from QML share their context with the QML
+component. That means the JavaScript files have access to the same QML objects
+and can modify them. As a consequence, each import must have a unique copy of
+these files.
-As it would be wasteful for each QML component instance to have a unique copy of
-these libraries, the JavaScript programmer can indicate a particular file is a
-shared library through the use of a pragma, as shown in the following example.
+\l {Defining JavaScript Resources in QML#Code-Behind Implementation Resource}
+{The previous section} covers stateful imports of JavaScript files. However,
+some JavaScript files are stateless and act more like reusable libraries, in
+the sense that they provide a set of helper functions that do not require
+anything from where they were imported from. You can save significant amounts
+of memory and speed up the instantiation of QML components if you mark such
+libraries with a special pragma, as shown in the following example.
\code
// factorial.js
@@ -141,7 +147,10 @@ For example:
\code
// Calculator.qml
import QtQuick 2.0
-import "factorial.js" as FactorialCalculator // this JavaScript resource is only ever loaded once by the engine, even if multiple instances of Calculator.qml are created
+import "factorial.js" as FactorialCalculator // This JavaScript resource is only
+ // ever loaded once by the engine,
+ // even if multiple instances of
+ // Calculator.qml are created.
Text {
width: 500
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 962c92fdb0..1c37894751 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -158,6 +158,8 @@ QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRe
// can guarentee it will live long enough to reach qmlregister.
QByteArray buf(unqualifiedtype.toString().toUtf8());
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder;
+
// Register the type. Note that the URI parameters here are empty; for
// file type imports, we do not place them in a URI as we don't
// necessarily have a good and unique one (picture a library import,
@@ -200,9 +202,9 @@ QQmlType fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRe
// data.
if (!ret.isValid()) {
if (!errors) // Cannot list errors properly, just quit
- qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData());
+ qFatal("%s", failureRecorder.failures().join('\n').toLatin1().constData());
QQmlError error;
- error.setDescription(QQmlMetaType::typeRegistrationFailures().join('\n'));
+ error.setDescription(failureRecorder.failures().join('\n'));
errors->prepend(error);
}
return ret;
@@ -2022,7 +2024,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
const QByteArray bytes = uri.toUtf8();
const char *moduleId = bytes.constData();
- QStringList registrationFailures;
+ QQmlMetaTypeRegistrationFailureRecorder failureRecorder;
{
// Create a scope for QWriteLocker to keep it as narrow as possible, and
// to ensure that we release it before the call to initalizeEngine below
@@ -2064,14 +2066,12 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
}
iface->registerTypes(moduleId);
-
- registrationFailures = QQmlMetaType::typeRegistrationFailures();
QQmlMetaType::setTypeRegistrationNamespace(QString());
} // QWriteLocker lock(QQmlMetaType::typeRegistrationLock())
- if (!registrationFailures.isEmpty()) {
+ if (!failureRecorder.failures().isEmpty()) {
if (errors) {
- for (const QString &failure : qAsConst(registrationFailures)) {
+ for (const QString &failure : failureRecorder.failures()) {
QQmlError error;
error.setDescription(failure);
errors->prepend(error);
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 69a8ff034a..b31058ece5 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -114,13 +114,27 @@ struct QQmlMetaTypeData
QSet<QString> protectedNamespaces;
QString typeRegistrationNamespace;
- QStringList typeRegistrationFailures;
QHash<int, int> qmlLists;
QHash<const QMetaObject *, QQmlPropertyCache *> propertyCaches;
QQmlPropertyCache *propertyCache(const QMetaObject *metaObject);
QQmlPropertyCache *propertyCache(const QQmlType &type, int minorVersion);
+
+ void startRecordingTypeRegFailures(QStringList *storage)
+ { typeRegistrationFailures = storage; }
+ void stopRecordingTypeRegFailures()
+ { startRecordingTypeRegFailures(nullptr); }
+ void recordTypeRegFailure(const QString &message)
+ {
+ if (typeRegistrationFailures)
+ typeRegistrationFailures->append(message);
+ else
+ qWarning("%s", message.toUtf8().constData());
+ }
+
+private:
+ QStringList *typeRegistrationFailures = nullptr;
};
class QQmlTypeModulePrivate
@@ -152,6 +166,16 @@ static uint qHash(const QQmlMetaTypeData::VersionedUri &v)
return v.uri.hash() ^ qHash(v.majorVersion);
}
+QQmlMetaTypeRegistrationFailureRecorder::QQmlMetaTypeRegistrationFailureRecorder()
+{
+ metaTypeData()->startRecordingTypeRegFailures(&_failures);
+}
+
+QQmlMetaTypeRegistrationFailureRecorder::~QQmlMetaTypeRegistrationFailureRecorder()
+{
+ metaTypeData()->stopRecordingTypeRegFailures();
+}
+
QQmlMetaTypeData::QQmlMetaTypeData()
{
}
@@ -1581,7 +1605,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (!typeName.isEmpty()) {
if (typeName.at(0).isLower()) {
QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter"));
- data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName));
+ data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName));
return false;
}
@@ -1589,7 +1613,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
for (int ii = 0; ii < typeNameLen; ++ii) {
if (!(typeName.at(ii).isLetterOrNumber() || typeName.at(ii) == '_')) {
QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\""));
- data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName));
+ data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName));
return false;
}
}
@@ -1603,7 +1627,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (data->protectedNamespaces.contains(nameSpace)) {
QString failure(QCoreApplication::translate("qmlRegisterType",
"Cannot install %1 '%2' into protected namespace '%3'"));
- data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace));
+ data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace));
return false;
}
} else if (majorVersion >= 0) {
@@ -1614,7 +1638,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da
if (QQmlTypeModulePrivate::get(qqtm)->locked){
QString failure(QCoreApplication::translate("qmlRegisterType",
"Cannot install %1 '%2' into protected module '%3' version '%4'"));
- data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion));
+ data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion));
return false;
}
}
@@ -1905,14 +1929,6 @@ void QQmlMetaType::setTypeRegistrationNamespace(const QString &uri)
QQmlMetaTypeData *data = metaTypeData();
data->typeRegistrationNamespace = uri;
- data->typeRegistrationFailures.clear();
-}
-
-QStringList QQmlMetaType::typeRegistrationFailures()
-{
- QQmlMetaTypeData *data = metaTypeData();
-
- return data->typeRegistrationFailures;
}
QMutex *QQmlMetaType::typeRegistrationLock()
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index 6df439cd7a..4a5e4ba266 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -155,7 +155,6 @@ public:
static void protectNamespace(const QString &);
static void setTypeRegistrationNamespace(const QString &);
- static QStringList typeRegistrationFailures();
static QMutex *typeRegistrationLock();
@@ -361,6 +360,18 @@ private:
int m_minor;
};
+class Q_AUTOTEST_EXPORT QQmlMetaTypeRegistrationFailureRecorder
+{
+ QStringList _failures;
+
+public:
+ QQmlMetaTypeRegistrationFailureRecorder();
+ ~QQmlMetaTypeRegistrationFailureRecorder();
+
+ QStringList failures() const
+ { return _failures; }
+};
+
QT_END_NAMESPACE
#endif // QQMLMETATYPE_P_H
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index 30aca38678..f27f87e743 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -420,7 +420,7 @@ on the display resolution on offer.
\section2 Related Information
\list
- \li \l{Qt Quick Controls2 - Gallery Example}{Gallery example}
+ \li \l{Qt Quick Controls 2 - Gallery Example}{Gallery example}
\li \l{Qt Quick Controls 2 - Text Editor}{Text Editor example}
\li \l{Font Awesome}
\li \l{Scalability}
diff --git a/src/quick/doc/src/qtquick.qdoc b/src/quick/doc/src/qtquick.qdoc
index 4d8da5ed5a..4b843f366b 100644
--- a/src/quick/doc/src/qtquick.qdoc
+++ b/src/quick/doc/src/qtquick.qdoc
@@ -118,7 +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}
+\li \l{Best Practices for QML and Qt Quick}{Qt Quick Guidelines}
\endlist
Further information for writing QML applications:
diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp
index 91f45894a0..f60f3c1ccf 100644
--- a/src/quick/items/qquickdrag.cpp
+++ b/src/quick/items/qquickdrag.cpp
@@ -41,7 +41,6 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
-#include <qpa/qplatformdrag.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <private/qquickitemchangelistener_p.h>
@@ -50,12 +49,13 @@
#include <private/qv4scopedvalue_p.h>
#include <QtCore/qmimedata.h>
#include <QtQml/qqmlinfo.h>
-#include <QtGui/qdrag.h>
#include <QtGui/qevent.h>
#include <QtGui/qstylehints.h>
#include <QtGui/qguiapplication.h>
#if QT_CONFIG(draganddrop)
+#include <qpa/qplatformdrag.h>
+#include <QtGui/qdrag.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp
index 74c8eaa169..879db6284e 100644
--- a/src/quick/items/qquickpathview.cpp
+++ b/src/quick/items/qquickpathview.cpp
@@ -240,9 +240,13 @@ void QQuickPathViewPrivate::clear()
releaseItem(currentItem);
currentItem = nullptr;
}
+
for (QQuickItem *p : qAsConst(items))
releaseItem(p);
+ for (QQuickItem *p : qAsConst(itemCache))
+ releaseItem(p);
+
if (requestedIndex >= 0) {
if (model)
model->cancel(requestedIndex);
@@ -250,6 +254,7 @@ void QQuickPathViewPrivate::clear()
}
items.clear();
+ itemCache.clear();
tl.clear();
}
diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp
index 1cb12235c1..38ca7283b4 100644
--- a/src/quick/items/qquicktextcontrol.cpp
+++ b/src/quick/items/qquicktextcontrol.cpp
@@ -47,7 +47,6 @@
#include <qfontmetrics.h>
#include <qevent.h>
#include <qdebug.h>
-#include <qdrag.h>
#include <qclipboard.h>
#include <qtimer.h>
#include <qinputmethod.h>
diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
index 6dc54a323e..48eb694167 100644
--- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
+++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp
@@ -253,7 +253,7 @@ void tst_qmlcachegen::errorOnArgumentsInSignalHandler()
QByteArray errorOutput;
QVERIFY(!generateCache(testFilePath, &errorOutput));
- QVERIFY2(errorOutput.contains("error: The use of the arguments object in signal handlers is"), errorOutput);
+ QVERIFY2(errorOutput.contains("error: The use of eval() or the use of the arguments object in signal handlers is"), errorOutput);
}
void tst_qmlcachegen::aheadOfTimeCompilation()
diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
index 1d0d353668..e83dac48fb 100644
--- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
+++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp
@@ -227,17 +227,13 @@ void tst_qqmlmetatype::qmlType()
void tst_qqmlmetatype::invalidQmlTypeName()
{
- QStringList currFailures = QQmlMetaType::typeRegistrationFailures();
+ QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"testtype\"; type names must begin with an uppercase letter");
+ QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"Test$Type\"");
+ QTest::ignoreMessage(QtWarningMsg, "Invalid QML element name \"EndingInSlash/\"");
+
QCOMPARE(qmlRegisterType<TestType>("TestNamespace", 1, 0, "Test$Type"), -1); // should fail due to invalid QML type name.
QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "EndingInSlash/"), -1);
- QStringList nowFailures = QQmlMetaType::typeRegistrationFailures();
-
- foreach (const QString &f, currFailures)
- nowFailures.removeOne(f);
-
- QCOMPARE(nowFailures.size(), 2);
- QCOMPARE(nowFailures.at(0), QStringLiteral("Invalid QML element name \"Test$Type\""));
- QCOMPARE(nowFailures.at(1), QStringLiteral("Invalid QML element name \"EndingInSlash/\""));
+ QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "testtype"), -1);
}
void tst_qqmlmetatype::prettyTypeName()
diff --git a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
index b8d5a4d3a3..ba7c85df15 100644
--- a/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
+++ b/tests/auto/qml/qqmlproperty/tst_qqmlproperty.cpp
@@ -2064,40 +2064,48 @@ void tst_qqmlproperty::floatToStringPrecision_data()
QTest::addColumn<QString>("propertyName");
QTest::addColumn<double>("number");
QTest::addColumn<QString>("qtString");
+ QTest::addColumn<QString>("alternateQtString");
QTest::addColumn<QString>("jsString");
- QTest::newRow("3.4") << "a" << 3.4 << "3.4" << "3.4";
- QTest::newRow("0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.035003945";
- QTest::newRow("0.0000012345") << "c" << 0.0000012345 << "1.2345e-6" << "0.0000012345";
- QTest::newRow("0.00000012345") << "d" << 0.00000012345 << "1.2345e-7" << "1.2345e-7";
- QTest::newRow("1e20") << "e" << 1e20 << "1e+20" << "100000000000000000000";
- QTest::newRow("1e21") << "f" << 1e21 << "1e+21" << "1e+21";
+ QTest::newRow("3.4") << "a" << 3.4 << "3.4" << "3.4" << "3.4";
+ QTest::newRow("0.035003945") << "b" << 0.035003945 << "0.035003945" << "0.0035003945" << "0.035003945";
+ QTest::newRow("0.0000012345") << "c" << 0.0000012345 << "1.2345e-6" << "1.2345e-06" << "0.0000012345";
+ QTest::newRow("0.00000012345") << "d" << 0.00000012345 << "1.2345e-7" << "1.2345e-07" << "1.2345e-7";
+ QTest::newRow("1e20") << "e" << 1e20 << "1e+20" << "1e+20" << "100000000000000000000";
+ QTest::newRow("1e21") << "f" << 1e21 << "1e+21" << "1e+21" << "1e+21";
}
void tst_qqmlproperty::floatToStringPrecision()
{
QQmlComponent comp(&engine, testFileUrl("floatToStringPrecision.qml"));
- QObject *obj = comp.create();
+ QScopedPointer<QObject> obj(comp.create());
QVERIFY(obj != nullptr);
QFETCH(QString, propertyName);
QFETCH(double, number);
QFETCH(QString, qtString);
+ QFETCH(QString, alternateQtString);
QFETCH(QString, jsString);
QByteArray name = propertyName.toLatin1();
QCOMPARE(obj->property(name).toDouble(), number);
- QCOMPARE(obj->property(name).toString(), qtString);
+ if (obj->property(name).toString() != qtString) {
+ QCOMPARE(obj->property(name).toString(), alternateQtString);
+ } else {
+ QCOMPARE(obj->property(name).toString(), qtString);
+ }
QByteArray name1 = (propertyName + QLatin1Char('1')).toLatin1();
QCOMPARE(obj->property(name1).toDouble(), number);
- QCOMPARE(obj->property(name1).toString(), qtString);
+ if (obj->property(name1).toString() != qtString) {
+ QCOMPARE(obj->property(name1).toString(), alternateQtString);
+ } else {
+ QCOMPARE(obj->property(name1).toString(), qtString);
+ }
QByteArray name2 = (propertyName + QLatin1Char('2')).toLatin1();
QCOMPARE(obj->property(name2).toDouble(), number);
QCOMPARE(obj->property(name2).toString(), jsString);
-
- delete obj;
}
void tst_qqmlproperty::initTestCase()
diff --git a/tests/auto/quick/qquickpathview/data/objectModelMove.qml b/tests/auto/quick/qquickpathview/data/objectModelMove.qml
new file mode 100644
index 0000000000..d5fa510d69
--- /dev/null
+++ b/tests/auto/quick/qquickpathview/data/objectModelMove.qml
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** 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: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$
+**
+****************************************************************************/
+
+import QtQml.Models 2.11
+import QtQuick 2.11
+
+Item {
+ id: root
+ width: 400
+ height: 400
+ visible: true
+
+ property Item pathViewItem
+
+ function destroyView() {
+ if (pathViewItem)
+ pathViewItem.destroy()
+ }
+
+ function newView() {
+ pathViewItem = pathViewComponent.createObject(root)
+ }
+
+ function move() {
+ objectModel.move(0, 1)
+ }
+
+ Component {
+ id: pathViewComponent
+
+ PathView {
+ id: pathView
+ objectName: "PathView"
+ width: 32 * 3
+ height: 32
+ model: objectModel
+
+ interactive: false
+ snapMode: PathView.SnapToItem
+ movementDirection: PathView.Positive
+ highlightMoveDuration: 100
+
+ path: Path {
+ startX: 16
+ startY: 16
+ PathLine {
+ x: 16 + (32 * 3)
+ y: 16
+ }
+ }
+ }
+ }
+
+ ObjectModel {
+ id: objectModel
+
+ Rectangle {
+ objectName: "red"
+ width: 32
+ height: 32
+ color: "red"
+ }
+ Rectangle {
+ objectName: "green"
+ width: 32
+ height: 32
+ color: "green"
+ }
+ Rectangle {
+ objectName: "blue"
+ width: 32
+ height: 32
+ color: "blue"
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
index 7a6f302403..7d297d3fa2 100644
--- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
+++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp
@@ -144,6 +144,7 @@ private slots:
void movementDirection_data();
void movementDirection();
void removePath();
+ void objectModelMove();
};
class TestObject : public QObject
@@ -2530,6 +2531,53 @@ void tst_QQuickPathView::removePath()
QVERIFY(QMetaObject::invokeMethod(pathview, "setPath"));
}
+/*
+ Tests that moving items in an ObjectModel and then deleting the view
+ doesn't cause heap-use-after-free when run through ASAN.
+
+ The test case is based on a Qt Quick Controls 2 test where the issue was
+ discovered.
+*/
+void tst_QQuickPathView::objectModelMove()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("objectModelMove.qml"));
+ window->show();
+
+ // Create the view.
+ QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "newView"));
+ QPointer<QQuickPathView> pathView = window->rootObject()->property("pathViewItem").value<QQuickPathView*>();
+ QVERIFY(pathView);
+ QCOMPARE(pathView->count(), 3);
+ pathView->highlightItem()->setObjectName("highlight");
+
+ // Move an item from index 0 to 1.
+ QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "move"));
+ QCOMPARE(pathView->count(), 3);
+
+ // Keep track of the amount of listeners
+ QVector<QString> itemObjectNames;
+ itemObjectNames << QLatin1String("red") << QLatin1String("green") << QLatin1String("blue");
+ QVector<QQuickItem*> childItems;
+ for (const QString itemObjectName : qAsConst(itemObjectNames)) {
+ QQuickItem *childItem = findItem<QQuickItem>(pathView, itemObjectName);
+ QVERIFY(childItem);
+ childItems.append(childItem);
+ }
+
+ // Destroy the view (via destroy()).
+ QVERIFY(QMetaObject::invokeMethod(window->rootObject(), "destroyView"));
+ // Ensure that the view has been destroyed. This check is also necessary in order for
+ // ASAN to complain (it will complain after the test function has finished).
+ QTRY_VERIFY(pathView.isNull());
+ // By this point, all of its cached items should have been released,
+ // which means none of the items should have any listeners.
+ for (const auto childItem : qAsConst(childItems)) {
+ const QQuickItemPrivate *childItemPrivate = QQuickItemPrivate::get(childItem);
+ QCOMPARE(childItemPrivate->changeListeners.size(), 0);
+ }
+}
+
QTEST_MAIN(tst_QQuickPathView)
#include "tst_qquickpathview.moc"
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index ef14819d28..b9a8763c97 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -148,11 +148,13 @@ static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc,
if (compiledFunction->column > 0)
error->message += QString::number(compiledFunction->column) + QLatin1Char(':');
- error->message += QLatin1String(" error: The use of the arguments object in signal handlers is\n"
- "not supported when compiling qml files ahead of time, because it may be ambiguous if\n"
- "any signal parameter is called \"arguments\". Unfortunately we cannot distinguish\n"
- "between it being a parameter or the JavaScript arguments object at this point.\n"
- "Consider renaming the parameter of the signal if applicable.");
+ error->message += QLatin1String(" error: The use of eval() or the use of the arguments object in signal handlers is\n"
+ "not supported when compiling qml files ahead of time. That is because it's ambiguous if \n"
+ "any signal parameter is called \"arguments\". Similarly the string passed to eval might use\n"
+ "\"arguments\". Unfortunately we cannot distinguish between it being a parameter or the\n"
+ "JavaScript arguments object at this point.\n"
+ "Consider renaming the parameter of the signal if applicable or moving the code into a\n"
+ "helper function.");
return false;
}
}