From 560557c5d7d6b48dada2c9966577c718eab7d19d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Sep 2017 09:41:16 +0200 Subject: tst_qquickmultipointtoucharea nonOverlapping: add visual feedback It's difficult to troubleshoot autotests like this without being able to either see what's happening while it runs or test it manually. Task-number: QTBUG-59960 Change-Id: Iba7b03036f2f631c9b6d34d563ebae2de77acf1f Reviewed-by: Shawn Rutledge --- .../data/nonOverlapping.qml | 40 +++++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml b/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml index 039607e26c..027f90c7f4 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml +++ b/tests/auto/quick/qquickmultipointtoucharea/data/nonOverlapping.qml @@ -11,9 +11,21 @@ Rectangle { maximumTouchPoints: 2 onGestureStarted: gesture.grab() touchPoints: [ - TouchPoint { objectName: "point11" }, - TouchPoint { objectName: "point12" } + TouchPoint { id: point11; objectName: "point11" }, + TouchPoint { id: point12; objectName: "point12" } ] + Rectangle { + color: "red" + width: 10; height: 10; radius: 5 + x: point11.x - radius; y: point11.y - radius + visible: point11.pressed + } + Rectangle { + color: "tomato" + width: 10; height: 10; radius: 5 + x: point12.x - radius; y: point12.y - radius + visible: point12.pressed + } } MultiPointTouchArea { @@ -24,9 +36,27 @@ Rectangle { maximumTouchPoints: 3 onGestureStarted: gesture.grab() touchPoints: [ - TouchPoint { objectName: "point21" }, - TouchPoint { objectName: "point22" }, - TouchPoint { objectName: "point23" } + TouchPoint { id: point21; objectName: "point21" }, + TouchPoint { id: point22; objectName: "point22" }, + TouchPoint { id: point23; objectName: "point23" } ] + Rectangle { + color: "lightgreen" + width: 10; height: 10; radius: 5 + x: point21.x - radius; y: point21.y - radius + visible: point21.pressed + } + Rectangle { + color: "green" + width: 10; height: 10; radius: 5 + x: point22.x - radius; y: point22.y - radius + visible: point22.pressed + } + Rectangle { + color: "darkgreen" + width: 10; height: 10; radius: 5 + x: point23.x - radius; y: point23.y - radius + visible: point23.pressed + } } } -- cgit v1.2.3 From 4578a92744d447222f5e22851433d5dbecc51855 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 4 Sep 2017 18:21:41 +0200 Subject: QmlProfiler: Avoid race conditions in QQmlTypeLoader We have to make sure all profiler calls to one adapter are done from the same thread. It turns out that all the calls from QQmlTypeLoader are done from the type loader thread. By using a separate adapter for that, we avoid any extra locking. Task-number: QTBUG-62987 Change-Id: I5cffcc30b84dd4154daa0c56312a1141ce2033e1 Reviewed-by: Simon Hausmann --- .../qmldbg_profiler/qqmlprofileradapter.cpp | 32 +++++++++++++++------- .../qmldbg_profiler/qqmlprofileradapter.h | 2 ++ .../qmldbg_profiler/qqmlprofilerservice.cpp | 7 +++-- src/qml/qml/qqmltypeloader.cpp | 14 +++++++--- src/qml/qml/qqmltypeloader_p.h | 15 ++++++++++ 5 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp index 510c745d4e..d676731ba7 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp @@ -44,24 +44,36 @@ QT_BEGIN_NAMESPACE -QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) : - next(0) +QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine) { - setService(service); engine->profiler = new QQmlProfiler; + init(service, engine->profiler); +} + +QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlTypeLoader *loader) +{ + QQmlProfiler *profiler = new QQmlProfiler; + loader->setProfiler(profiler); + init(service, profiler); +} + +void QQmlProfilerAdapter::init(QQmlProfilerService *service, QQmlProfiler *profiler) +{ + next = 0; + setService(service); connect(this, &QQmlProfilerAdapter::profilingEnabled, - engine->profiler, &QQmlProfiler::startProfiling); + profiler, &QQmlProfiler::startProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting, - engine->profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); + profiler, &QQmlProfiler::startProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled, - engine->profiler, &QQmlProfiler::stopProfiling); + profiler, &QQmlProfiler::stopProfiling); connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting, - engine->profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); + profiler, &QQmlProfiler::stopProfiling, Qt::DirectConnection); connect(this, &QQmlAbstractProfilerAdapter::dataRequested, - engine->profiler, &QQmlProfiler::reportData); + profiler, &QQmlProfiler::reportData); connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown, - engine->profiler, &QQmlProfiler::setTimer); - connect(engine->profiler, &QQmlProfiler::dataReady, + profiler, &QQmlProfiler::setTimer); + connect(profiler, &QQmlProfiler::dataReady, this, &QQmlProfilerAdapter::receiveData); } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h index 1fee5c389f..7531cd1dff 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h @@ -60,6 +60,7 @@ class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter { Q_OBJECT public: QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine); + QQmlProfilerAdapter(QQmlProfilerService *service, QQmlTypeLoader *loader); qint64 sendMessages(qint64 until, QList &messages, bool trackLocations) Q_DECL_OVERRIDE; @@ -67,6 +68,7 @@ public: const QQmlProfiler::LocationHash &locations); private: + void init(QQmlProfilerService *service, QQmlProfiler *profiler); QVector data; QQmlProfiler::LocationHash locations; int next; diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index edeb364f60..4176ede40e 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -119,9 +119,12 @@ void QQmlProfilerServiceImpl::engineAboutToBeAdded(QJSEngine *engine) QMutexLocker lock(&m_configMutex); if (QQmlEngine *qmlEngine = qobject_cast(engine)) { - QQmlProfilerAdapter *qmlAdapter = - new QQmlProfilerAdapter(this, QQmlEnginePrivate::get(qmlEngine)); + QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(qmlEngine); + QQmlProfilerAdapter *qmlAdapter = new QQmlProfilerAdapter(this, enginePrivate); addEngineProfiler(qmlAdapter, engine); + QQmlProfilerAdapter *compileAdapter + = new QQmlProfilerAdapter(this, &(enginePrivate->typeLoader)); + addEngineProfiler(compileAdapter, engine); } QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle())); addEngineProfiler(v4Adapter, engine); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 84410875ab..9b7b970e56 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -700,8 +700,7 @@ void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) { Q_ASSERT(m_waitingFor.contains(blob)); Q_ASSERT(blob->status() == Error || blob->status() == Complete); - QQmlCompilingProfiler prof(QQmlEnginePrivate::get(typeLoader()->engine())->profiler, - blob); + QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); m_inCallback = true; @@ -1262,7 +1261,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) { QML_MEMORY_SCOPE_URL(blob->url()); - QQmlCompilingProfiler prof(QQmlEnginePrivate::get(engine())->profiler, blob); + QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1282,7 +1281,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit) { QML_MEMORY_SCOPE_URL(blob->url()); - QQmlCompilingProfiler prof(QQmlEnginePrivate::get(engine())->profiler, blob); + QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -1597,6 +1596,9 @@ Constructs a new type loader that uses the given \a engine. */ QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) : m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)), +#ifndef QT_NO_QML_DEBUGGER + m_profiler(nullptr), +#endif m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) { } @@ -1613,6 +1615,10 @@ QQmlTypeLoader::~QQmlTypeLoader() clearCache(); invalidate(); + +#ifndef QT_NO_QML_DEBUGGER + delete m_profiler; +#endif } QQmlImportDatabase *QQmlTypeLoader::importDatabase() const diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 05923f77e8..722791ea5a 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -84,6 +84,7 @@ class QQmlComponentPrivate; class QQmlTypeData; class QQmlTypeLoader; class QQmlExtensionInterface; +class QQmlProfiler; struct QQmlCompileError; namespace QmlIR { @@ -320,6 +321,15 @@ public: void initializeEngine(QQmlExtensionInterface *, const char *); void invalidate(); +#ifdef QT_NO_QML_DEBUGGER + QQmlProfiler *profiler() const { return nullptr; } + void setProfiler(QQmlProfiler *) {} +#else + QQmlProfiler *profiler() const { return m_profiler; } + void setProfiler(QQmlProfiler *profiler) { Q_ASSERT(!m_profiler); m_profiler = profiler; } +#endif // QT_NO_QML_DEBUGGER + + private: friend class QQmlDataBlob; friend class QQmlTypeLoaderThread; @@ -368,6 +378,11 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; + +#ifndef QT_NO_QML_DEBUGGER + QQmlProfiler *m_profiler; +#endif + #if QT_CONFIG(qml_network) NetworkReplies m_networkReplies; #endif -- cgit v1.2.3 From 2376f45f46e42cb78b46fb77b57e76ed55c734f9 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 4 Sep 2017 12:27:08 +0200 Subject: Change mirror vertical for software QSGLayers to true This is the default for OpenGL and D3D12 QSGLayer implementations, and means that code using QSGLayer can end up rendering upside down if switched the software backend. Task-number: QTBUG-62929 Change-Id: Ib34b10c4eb834d2ef52e9005866242886b3addeb Reviewed-by: Simon Hausmann --- src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp index 2954f591ad..bd5d8f72c0 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer.cpp @@ -50,7 +50,7 @@ QSGSoftwareLayer::QSGSoftwareLayer(QSGRenderContext *renderContext) , m_renderer(0) , m_device_pixel_ratio(1) , m_mirrorHorizontal(false) - , m_mirrorVertical(false) + , m_mirrorVertical(true) , m_live(true) , m_grab(true) , m_recursive(false) -- cgit v1.2.3 From 4dbb3c7bc5829bcf699715157d3a82fb86f4ea5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 5 Sep 2017 13:24:51 +0200 Subject: Fix QtQml crashing with GHS compiler when using AOT In order to support compilers that do not support the packing attribute, replace the use of the pack macros with static assertions about the structure size, to ensure that what is generated by host tools (qmlcachegen, qqc) is compatible with what's loaded by the target at run-time. This requires padding and re-ordering some structures. Task-number: QTBUG-61468 Change-Id: I3d82457f086a9b066d1c6df4c46d9f154dd5f208 Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 43 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 5d71bc4ac6..815c1b92de 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x11 +#define QV4_DATA_STRUCTURE_VERSION 0x12 class QIODevice; class QQmlPropertyCache; @@ -122,10 +122,6 @@ struct TableIterator bool operator!=(const TableIterator &rhs) const { return index != rhs.index; } }; -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(push, 1) -#endif - struct Location { union { @@ -140,6 +136,7 @@ struct Location (line == other.line && column < other.column); } }; +static_assert(sizeof(Location) == 4, "Location structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct RegExp { @@ -155,6 +152,7 @@ struct RegExp RegExp() { flags.val = 0; stringIndex.val = 0; } }; +static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Lookup { @@ -173,6 +171,7 @@ struct Lookup Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } }; +static_assert(sizeof(Lookup) == 4, "Lookup structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct JSClassMember { @@ -183,6 +182,7 @@ struct JSClassMember JSClassMember() { nameOffset = 0; isAccessor = 0; } }; +static_assert(sizeof(JSClassMember) == 4, "JSClassMember structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct JSClass { @@ -191,6 +191,7 @@ struct JSClass static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; } }; +static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct String { @@ -201,6 +202,7 @@ struct String return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7; } }; +static_assert(sizeof(String) == 4, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties // for unaligned access. The ordering of the fields is also from largest to smallest. @@ -243,6 +245,8 @@ struct Function // Keep all unaligned data at the end quint8 flags; + quint8 padding1; + LEUInt16 padding2; const LEUInt32 *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } const LEUInt32 *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } @@ -261,6 +265,7 @@ struct Function return (sizeof(Function) + (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + 2 * nPropertyDependencies) * sizeof(quint32) + 7) & ~0x7; } }; +static_assert(sizeof(Function) == 72, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); // Qml data structures @@ -268,6 +273,7 @@ struct Q_QML_EXPORT TranslationData { LEUInt32 commentIndex; LEInt32 number; }; +static_assert(sizeof(TranslationData) == 8, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Q_QML_PRIVATE_EXPORT Binding { @@ -298,10 +304,7 @@ struct Q_QML_PRIVATE_EXPORT Binding IsCustomParserBinding = 0x100, }; - union { - QJsonPrivate::qle_bitfield<0, 16> flags; - QJsonPrivate::qle_bitfield<16, 16> type; - }; + LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) union { bool b; quint64 doubleValue; // do not access directly, needs endian protected access @@ -309,11 +312,17 @@ struct Q_QML_PRIVATE_EXPORT Binding LEUInt32 objectIndex; TranslationData translationData; // used when Type_Translation } value; - LEUInt32 stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings) + + union { + QJsonPrivate::qle_bitfield<0, 16> flags; + QJsonPrivate::qle_bitfield<16, 16> type; + }; Location location; Location valueLocation; + LEUInt32 padding; + bool isValueBinding() const { if (type == Type_AttachedProperty @@ -392,6 +401,8 @@ struct Q_QML_PRIVATE_EXPORT Binding }; +static_assert(sizeof(Binding) == 32, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + struct Parameter { LEUInt32 nameIndex; @@ -399,6 +410,7 @@ struct Parameter LEUInt32 customTypeNameIndex; Location location; }; +static_assert(sizeof(Parameter) == 16, "Parameter structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Signal { @@ -423,6 +435,7 @@ struct Signal int parameterCount() const { return nParameters; } // --- }; +static_assert(sizeof(Signal) == 12, "Signal structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Property { @@ -443,6 +456,7 @@ struct Property LEUInt32 customTypeNameIndex; // If type >= Custom Location location; }; +static_assert(sizeof(Property) == 16, "Property structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Alias { enum Flags : unsigned int { @@ -472,6 +486,7 @@ struct Alias { return encodedMetaPropertyIndex == -1; } }; +static_assert(sizeof(Alias) == 20, "Alias structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Object { @@ -579,6 +594,7 @@ struct Object int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; } // --- }; +static_assert(sizeof(Object) == 72, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Import { @@ -599,6 +615,7 @@ struct Import Import() { type = 0; uriIndex = 0; qualifierIndex = 0; majorVersion = 0; minorVersion = 0; } }; +static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); static const char magic_str[] = "qv4cdata"; @@ -651,6 +668,8 @@ struct Unit LEUInt32 offsetToObjects; LEUInt32 indexOfRootObject; + LEUInt32 padding; + const Import *importAt(int idx) const { return reinterpret_cast((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } @@ -715,9 +734,7 @@ struct Unit } }; -#if defined(Q_CC_MSVC) || defined(Q_CC_GNU) -#pragma pack(pop) -#endif +static_assert(sizeof(Unit) == 152, "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 { -- cgit v1.2.3 From fb52a593893f52468c01c2c6c8eea0e5ca0cab82 Mon Sep 17 00:00:00 2001 From: Jani Heikkinen Date: Thu, 24 Aug 2017 13:26:42 +0300 Subject: Add change file for Qt 5.6.3 Task-number: QTBUG-62707 Change-Id: I5ff6ddc2dfb1d495f20e257cbd014232cac335b7 Reviewed-by: Shawn Rutledge --- dist/changes-5.6.3 | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 dist/changes-5.6.3 diff --git a/dist/changes-5.6.3 b/dist/changes-5.6.3 new file mode 100644 index 0000000000..3dfc21eb39 --- /dev/null +++ b/dist/changes-5.6.3 @@ -0,0 +1,78 @@ +Qt 5.6.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.6.0. + +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.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +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. + +QQuickWindow +------------ + + - The relevant child item is now sent a hover event when the window + receives a QEnterEvent, making sure hovering is recognized without + waiting for mouse movement. + - [QTBUG-56075] Wheel events retain their timestamps during delivery. + +QtQml +----- + + - Fixed reading of enum properties from gadgets / value types when the + enum was registered with qRegisterMetaType(). + - [QTBUG-56271] Fixed a crash on big-endian architecture. + - [QTBUG-50592] JSON.stringify() uses QVariant::toString() so that URLs + and other types which are stored as variants will not be omitted. + - [QTBUG-56658] Fixed a crash in the Qt Quick compiler. + - [QTBUG-56830] Fixed conversion of long numeric strings to integers. + - [QTBUG-52356] Fixed binding re-evaluation when list model properties change. + - [QTBUG-58133] Fixed a crash when emitting a signal with an undefined QJSValue. + - [QTBUG-57633][QTBUG-46263] Fix crash with QObjects exposed to multiple QML engines. + - [QTBUG-58271] Fix PropertyChanges element to restore bindings on aliases. + - [QTBUG-56499] Fix Connection element not disconnecting when target became null. + - Work around crashes with gcc 5/6 caused by dead-store eliminations. + - Fix memory leak with QQmlExpression. + - [QTBUG-53261] Fix memory corruption when changing JS object properties in + certain ways. + - [QTBUG-39888] Fix crash with QQuickItem objects that do not have a parent but + should be kept alive by the JS garbage collector. + - [QTBUG-54822] Fix support for address space configurations where the kernel uses + more bits in pointer addressing + - [QTBUG-56551] Fix crash with Connection element and non-existent objects + +QtQuick +------- + + - [QTBUG-39888] Fixed crash with QQuickItems created via JavaScript being + garbage collected sometimes when they're not assigned to a window. + - [QTBUG-44038] TextInput no longer emits editingFinished twice on iOS. + - [QTBUG-55871][QTBUG-55886] Flickable::movementEnding and movementEnded + are emitted reliably at the right times on macOS. + - [QTBUG-55779] Text padding now has the proper effect on height when + there is more than one line of text. + - [QTBUG-37095] Canvas is rendered at high resolution if the scene is + initially rendered on a high-DPI screen. + - [QTBUG-56657] Fixed a bug resulting in invisible images in some cases. + - [QTBUG-58852] Large amounts of text are now rendered properly with + Text.NativeRendering. + +QuickTest +--------- + - [QTBUG-56223] Mouse events now include timestamps. + +QtWidgets +--------- + + - [QTBUG-42074][QTBUG-57003] Support characters in Private Use Area, as + well as zero-width joiners and zero-width non-joiners in input in + TextInput and TextEdit. -- cgit v1.2.3 From 131243b3c8d71febe5294f0035844cea8a9e3049 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 6 Sep 2017 10:30:03 +0200 Subject: Stabilize the threadSignal() test We've seen the case in the CI where we delete the worker thread object before it has had a chance (resulting in a crash). This patch attempts to stabilize this by waiting for the thread to terminate properly. In addition QSignalSpy's connection to the done(QString) signal is forced to be direct, which means the spy's internal list is accessed from the gui thread (via QCOMPARE) at the same time as the thread may be emitting the signal and calling the signalspy's slot (metacall), which helgrind complains about (rightly so). I don't see any purpose in connecting to the signal, so let's remove that code. The test continues to cover the threading code in QQmlData::signalEmitted, once as the thread is triggered via C++ and once via QML (doIt invocation). Change-Id: I5e8a4ae65e2d0890a26491d25c73de1ba33a6668 Reviewed-by: Ulf Hermann --- tests/auto/qml/qqmlecmascript/testtypes.cpp | 9 ++++++++- tests/auto/qml/qqmlecmascript/testtypes.h | 5 +++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 22 +++++++++------------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/auto/qml/qqmlecmascript/testtypes.cpp b/tests/auto/qml/qqmlecmascript/testtypes.cpp index 63c2918325..13bb0435cd 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.cpp +++ b/tests/auto/qml/qqmlecmascript/testtypes.cpp @@ -245,9 +245,16 @@ public: MyWorkerObject *o; }; +MyWorkerObject::~MyWorkerObject() +{ + if (m_thread) + m_thread->wait(); +} + void MyWorkerObject::doIt() { - new MyWorkerObjectThread(this); + Q_ASSERT(!m_thread); + m_thread = new MyWorkerObjectThread(this); } class MyDateClass : public QObject diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index eedeb66647..e15a05a00c 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1532,12 +1532,17 @@ private: class MyWorkerObject : public QObject { Q_OBJECT +public: + ~MyWorkerObject(); public Q_SLOTS: void doIt(); Q_SIGNALS: void done(const QString &result); + +private: + QThread *m_thread = 0; }; class MyUnregisteredEnumTypeObject : public QObject diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 7b9a43dc38..14c2aa18bf 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7440,21 +7440,17 @@ void tst_qqmlecmascript::signalEmitted() void tst_qqmlecmascript::threadSignal() { { - QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QTRY_VERIFY(object->property("passed").toBool()); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.qml")); + QScopedPointer object(c.create()); + QVERIFY(!object.isNull()); + QTRY_VERIFY(object->property("passed").toBool()); } { - QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); - QObject *object = c.create(); - QVERIFY(object != 0); - QSignalSpy doneSpy(object, SIGNAL(done(QString))); - QMetaObject::invokeMethod(object, "doIt"); - QTRY_VERIFY(object->property("passed").toBool()); - QCOMPARE(doneSpy.count(), 1); - delete object; + QQmlComponent c(&engine, testFileUrl("threadSignal.2.qml")); + QScopedPointer object(c.create()); + QVERIFY(!object.isNull()); + QMetaObject::invokeMethod(object.data(), "doIt"); + QTRY_VERIFY(object->property("passed").toBool()); } } -- cgit v1.2.3 From a88ca874970c57db275981a1a47a0bebb6b749a0 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 5 Sep 2017 15:23:05 +0200 Subject: Qml Tooling: Replace bare pointers with QScopedPointer This way we don't have to manually initialize and delete them. Change-Id: I0104c744dba380e957271d0924498e3643856e9e Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 14 ++------------ src/qml/jsruntime/qv4engine_p.h | 8 ++++---- src/qml/qml/qqmltypeloader.cpp | 15 ++++++++------- src/qml/qml/qqmltypeloader_p.h | 8 ++++---- 4 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 0c4facda4d..5e776ea09b 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -141,10 +141,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(0) , m_multiplyWrappedQObjects(0) -#ifndef QT_NO_QML_DEBUGGER - , m_debugger(0) - , m_profiler(0) -#endif { memoryManager = new QV4::MemoryManager(this); @@ -477,12 +473,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { -#ifndef QT_NO_QML_DEBUGGER - delete m_debugger; - m_debugger = 0; - delete m_profiler; - m_profiler = 0; -#endif delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = 0; delete identifierTable; @@ -508,13 +498,13 @@ ExecutionEngine::~ExecutionEngine() void ExecutionEngine::setDebugger(Debugging::Debugger *debugger) { Q_ASSERT(!m_debugger); - m_debugger = debugger; + m_debugger.reset(debugger); } void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) { Q_ASSERT(!m_profiler); - m_profiler = profiler; + m_profiler.reset(profiler); } #endif // QT_NO_QML_DEBUGGER diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index a569b8ddf9..d331fd7bff 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -353,8 +353,8 @@ public: void setDebugger(Debugging::Debugger *) {} void setProfiler(Profiling::Profiler *) {} #else - QV4::Debugging::Debugger *debugger() const { return m_debugger; } - QV4::Profiling::Profiler *profiler() const { return m_profiler; } + QV4::Debugging::Debugger *debugger() const { return m_debugger.data(); } + QV4::Profiling::Profiler *profiler() const { return m_profiler.data(); } void setDebugger(Debugging::Debugger *debugger); void setProfiler(Profiling::Profiler *profiler); @@ -464,8 +464,8 @@ private: void failStackLimitCheck(Scope &scope); #ifndef QT_NO_QML_DEBUGGER - QV4::Debugging::Debugger *m_debugger; - QV4::Profiling::Profiler *m_profiler; + QScopedPointer m_debugger; + QScopedPointer m_profiler; #endif }; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 9b7b970e56..ce795be757 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -962,6 +962,14 @@ void QQmlTypeLoader::invalidate() #endif // qml_network } +#ifndef QT_NO_QML_DEBUGGER +void QQmlTypeLoader::setProfiler(QQmlProfiler *profiler) +{ + Q_ASSERT(!m_profiler); + m_profiler.reset(profiler); +} +#endif + void QQmlTypeLoader::lock() { m_thread->lock(); @@ -1596,9 +1604,6 @@ Constructs a new type loader that uses the given \a engine. */ QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) : m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)), -#ifndef QT_NO_QML_DEBUGGER - m_profiler(nullptr), -#endif m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) { } @@ -1615,10 +1620,6 @@ QQmlTypeLoader::~QQmlTypeLoader() clearCache(); invalidate(); - -#ifndef QT_NO_QML_DEBUGGER - delete m_profiler; -#endif } QQmlImportDatabase *QQmlTypeLoader::importDatabase() const diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 722791ea5a..c214f0cd43 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -249,7 +249,7 @@ private: QString m_location; }; -class Q_AUTOTEST_EXPORT QQmlTypeLoader +class Q_QML_PRIVATE_EXPORT QQmlTypeLoader { Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) public: @@ -325,8 +325,8 @@ public: QQmlProfiler *profiler() const { return nullptr; } void setProfiler(QQmlProfiler *) {} #else - QQmlProfiler *profiler() const { return m_profiler; } - void setProfiler(QQmlProfiler *profiler) { Q_ASSERT(!m_profiler); m_profiler = profiler; } + QQmlProfiler *profiler() const { return m_profiler.data(); } + void setProfiler(QQmlProfiler *profiler); #endif // QT_NO_QML_DEBUGGER @@ -380,7 +380,7 @@ private: QQmlTypeLoaderThread *m_thread; #ifndef QT_NO_QML_DEBUGGER - QQmlProfiler *m_profiler; + QScopedPointer m_profiler; #endif #if QT_CONFIG(qml_network) -- cgit v1.2.3 From e22b624d9ab1f36021adb9cdbfa9b37054282bb8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 14 Aug 2017 16:29:46 +0200 Subject: Fix crashes with closures created in QML components When closures created inside QML components are called after the surrounding component (and consequently QML context) has been destroyed, we are in a somewhat limited environment. Initially we would just crash as the calling QML context is not valid anymore. We can alleviate that by introducing reference counting on the context and letting the QML context wrapper keep a strong reference. This avoids the crashes and also ensures that at least imports continue to be accessible within these contexts (as the singleton test case demonstrates). Task-number: QTBUG-61781 Change-Id: I893f171842d01b0863d95a02ea738adc2620e236 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4qmlcontext.cpp | 9 +- src/qml/jsruntime/qv4qmlcontext_p.h | 13 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 8 +- src/qml/qml/qqmlcontext.cpp | 59 ++++++--- src/qml/qml/qqmlcontext_p.h | 132 ++++++++++++++------- src/qml/qml/qqmldata_p.h | 36 +++++- src/qml/qml/qqmlengine.cpp | 20 ++-- src/qml/qml/qqmlobjectcreator.cpp | 2 +- src/qml/qml/qqmltypeloader.cpp | 6 +- src/qml/qml/qqmlxmlhttprequest.cpp | 2 +- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 4 + src/qml/types/qqmldelegatemodel.cpp | 23 ++-- src/qml/types/qqmldelegatemodel_p_p.h | 2 +- tests/auto/qml/qqmlcontext/data/Singleton.qml | 5 + .../data/contextViaClosureAfterDestruction.qml | 14 +++ tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp | 28 +++++ 16 files changed, 257 insertions(+), 106 deletions(-) create mode 100644 tests/auto/qml/qqmlcontext/data/Singleton.qml create mode 100644 tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 144ab1b1a5..d97d44379d 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -62,20 +62,17 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(QmlContextWrapper); DEFINE_MANAGED_VTABLE(QmlContext); -void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject, bool ownsContext) +void Heap::QmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject) { Object::init(); readOnly = true; - this->ownsContext = ownsContext; isNullWrapper = false; - this->context = new QQmlGuardedContextData(context); + this->context = new QQmlContextDataRef(context); this->scopeObject.init(scopeObject); } void Heap::QmlContextWrapper::destroy() { - if (*context && ownsContext) - (*context)->destroy(); delete context; scopeObject.destroy(); Object::destroy(); @@ -321,7 +318,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons context->isInternal = true; context->isJSContext = true; - Scoped qml(scope, scope.engine->memoryManager->allocObject(context, (QObject*)0, true)); + Scoped qml(scope, scope.engine->memoryManager->allocObject(context, (QObject*)0)); qml->d()->isNullWrapper = true; qml->setReadOnly(false); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 9aec7467da..9caeb75b8c 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -67,13 +67,12 @@ struct QmlContextWrapper; namespace Heap { struct QmlContextWrapper : Object { - void init(QQmlContextData *context, QObject *scopeObject, bool ownsContext = false); + void init(QQmlContextData *context, QObject *scopeObject); void destroy(); bool readOnly; - bool ownsContext; bool isNullWrapper; - QQmlGuardedContextData *context; + QQmlContextDataRef *context; QQmlQPointer scopeObject; }; @@ -90,10 +89,6 @@ struct Q_QML_EXPORT QmlContextWrapper : Object V4_OBJECT2(QmlContextWrapper, Object) V4_NEEDS_DESTROY - void takeContextOwnership() { - d()->ownsContext = true; - } - inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return *d()->context; } @@ -116,10 +111,6 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext QQmlContextData *qmlContext() const { return *d()->qml->context; } - - void takeContextOwnership() { - d()->qml->ownsContext = true; - } }; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 5dc5f5d568..8d96f5b480 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -977,8 +977,12 @@ void QObjectWrapper::destroyObject(bool lastCall) QQmlData *ddata = QQmlData::get(h->object(), false); if (ddata) { if (!h->object()->parent() && !ddata->indestructible) { - if (ddata && ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); + if (ddata && ddata->ownContext) { + Q_ASSERT(ddata->ownContext == ddata->context); + ddata->ownContext->emitDestruction(); + ddata->ownContext = 0; + ddata->context = 0; + } // This object is notionally destroyed now ddata->isQueuedForDeletion = true; if (lastCall) diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 531d9ae457..e06491a814 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -161,6 +161,7 @@ QQmlContext::QQmlContext(QQmlEngine *e, bool) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->engine = e; } @@ -174,6 +175,7 @@ QQmlContext::QQmlContext(QQmlEngine *engine, QObject *parent) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->setParent(engine?QQmlContextData::get(engine->rootContext()):0); } @@ -187,6 +189,7 @@ QQmlContext::QQmlContext(QQmlContext *parentContext, QObject *parent) { Q_D(QQmlContext); d->data = new QQmlContextData(this); + ++d->data->refCount; d->data->setParent(parentContext?QQmlContextData::get(parentContext):0); } @@ -199,6 +202,7 @@ QQmlContext::QQmlContext(QQmlContextData *data) { Q_D(QQmlContext); d->data = data; + // don't add a refcount here, as the data owns this context } /*! @@ -212,7 +216,8 @@ QQmlContext::~QQmlContext() { Q_D(QQmlContext); - if (!d->data->isInternal) + d->data->publicContext = 0; + if (!--d->data->refCount) d->data->destroy(); } @@ -521,12 +526,12 @@ QQmlContextData::QQmlContextData() } QQmlContextData::QQmlContextData(QQmlContext *ctxt) -: parent(0), engine(0), isInternal(false), ownedByParent(false), isJSContext(false), - isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), - publicContext(ctxt), incubator(0), componentObjectIndex(-1), - contextObject(0), childContexts(0), nextChild(0), prevChild(0), - expressions(0), contextObjects(0), contextGuards(0), idValues(0), idValueCount(0), linkedContext(0), - componentAttached(0) + : engine(0), isInternal(false), isJSContext(false), + isPragmaLibraryContext(false), unresolvedNames(false), hasEmittedDestruction(false), isRootObjectInCreation(false), + publicContext(ctxt), incubator(0), componentObjectIndex(-1), + contextObject(0), nextChild(0), prevChild(0), + expressions(0), contextObjects(0), idValues(0), idValueCount(0), + componentAttached(0) { } @@ -563,11 +568,8 @@ void QQmlContextData::invalidate() emitDestruction(); while (childContexts) { - if (childContexts->ownedByParent) { - childContexts->destroy(); - } else { - childContexts->invalidate(); - } + Q_ASSERT(childContexts != this); + childContexts->invalidate(); } if (prevChild) { @@ -601,12 +603,17 @@ void QQmlContextData::clearContext() void QQmlContextData::destroy() { - if (linkedContext) - linkedContext->destroy(); + Q_ASSERT(refCount == 0); + linkedContext = 0; - if (engine) invalidate(); + // avoid recursion + ++refCount; + if (engine) + invalidate(); + Q_ASSERT(refCount == 1); clearContext(); + Q_ASSERT(refCount == 1); while (contextObjects) { QQmlData *co = contextObjects; @@ -617,6 +624,7 @@ void QQmlContextData::destroy() co->nextContextObject = 0; co->prevContextObject = 0; } + Q_ASSERT(refCount == 1); QQmlGuardedContextData *contextGuard = contextGuards; while (contextGuard) { @@ -627,17 +635,29 @@ void QQmlContextData::destroy() contextGuard = next; } contextGuards = 0; + Q_ASSERT(refCount == 1); delete [] idValues; + idValues = 0; - if (isInternal) + Q_ASSERT(refCount == 1); + if (publicContext) { + // the QQmlContext destructor will remove one ref again + ++refCount; delete publicContext; + } + + Q_ASSERT(refCount == 1); + --refCount; + Q_ASSERT(refCount == 0); delete this; } -void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) +void QQmlContextData::setParent(QQmlContextData *p) { + if (p == parent) + return; if (p) { parent = p; engine = p->engine; @@ -645,7 +665,6 @@ void QQmlContextData::setParent(QQmlContextData *p, bool parentTakesOwnership) if (nextChild) nextChild->prevChild = &nextChild; prevChild = &p->childContexts; p->childContexts = this; - ownedByParent = parentTakesOwnership; } } @@ -660,6 +679,10 @@ void QQmlContextData::refreshExpressionsRecursive(QQmlJavaScriptExpression *expr expression->refresh(); } +QQmlContextData::~QQmlContextData() +{ +} + static inline bool expressions_to_run(QQmlContextData *ctxt, bool isGlobalRefresh) { return ctxt->expressions && (!isGlobalRefresh || ctxt->unresolvedNames); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index a259fd62d8..d01820a430 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -78,6 +78,7 @@ class QQmlExpression; class QQmlExpressionPrivate; class QQmlJavaScriptExpression; class QQmlContextData; +class QQmlGuardedContextData; class QQmlIncubatorPrivate; class QQmlContextPrivate : public QObjectPrivate @@ -106,7 +107,7 @@ public: }; class QQmlComponentAttached; -class QQmlGuardedContextData; + class Q_QML_PRIVATE_EXPORT QQmlContextData { public: @@ -114,7 +115,6 @@ public: QQmlContextData(QQmlContext *); void emitDestruction(); void clearContext(); - void destroy(); void invalidate(); inline bool isValid() const { @@ -122,10 +122,10 @@ public: } // My parent context and engine - QQmlContextData *parent; + QQmlContextData *parent = nullptr; QQmlEngine *engine; - void setParent(QQmlContextData *, bool parentTakesOwnership = false); + void setParent(QQmlContextData *); void refreshExpressions(); void addObject(QObject *); @@ -136,14 +136,14 @@ public: // If internal is false publicContext owns this. QQmlContext *asQQmlContext(); QQmlContextPrivate *asQQmlContextPrivate(); + quint32 refCount = 0; quint32 isInternal:1; - quint32 ownedByParent:1; // unrelated to isInternal; parent context deletes children if true. quint32 isJSContext:1; quint32 isPragmaLibraryContext:1; quint32 unresolvedNames:1; // True if expressions in this context failed to resolve a toplevel name quint32 hasEmittedDestruction:1; quint32 isRootObjectInCreation:1; - quint32 dummy:25; + quint32 dummy:26; QQmlContext *publicContext; // The incubator that is constructing this context if any @@ -178,7 +178,7 @@ public: QQmlRefPointer imports; // My children - QQmlContextData *childContexts; + QQmlContextData *childContexts = 0; // My peers in parent's childContexts list QQmlContextData *nextChild; @@ -191,7 +191,7 @@ public: QQmlData *contextObjects; // Doubly-linked list of context guards (XXX merge with contextObjects) - QQmlGuardedContextData *contextGuards; + QQmlGuardedContextData *contextGuards = 0; // id guards struct ContextGuard : public QQmlGuard @@ -210,7 +210,7 @@ public: void setIdProperty(int, QObject *); // Linked contexts. this owns linkedContext. - QQmlContextData *linkedContext; + QQmlContextDataRef linkedContext; // Linked list of uses of the Component attached property in this // context @@ -224,91 +224,137 @@ public: } private: + friend class QQmlContextDataRef; + friend class QQmlContext; // needs to do manual refcounting :/ void refreshExpressionsRecursive(bool isGlobal); void refreshExpressionsRecursive(QQmlJavaScriptExpression *); - ~QQmlContextData() {} + ~QQmlContextData(); + void destroy(); }; + class QQmlGuardedContextData { public: - inline QQmlGuardedContextData(); - inline QQmlGuardedContextData(QQmlContextData *); - inline ~QQmlGuardedContextData(); - - inline QQmlContextData *contextData() const; + inline QQmlGuardedContextData() = default; + inline QQmlGuardedContextData(QQmlContextData *data) + { setContextData(data); } + inline ~QQmlGuardedContextData() + { clear(); } + + inline QQmlContextData *contextData() const + { return m_contextData; } inline void setContextData(QQmlContextData *); inline bool isNull() const { return !m_contextData; } inline operator QQmlContextData*() const { return m_contextData; } inline QQmlContextData* operator->() const { return m_contextData; } - inline QQmlGuardedContextData &operator=(QQmlContextData *d); + inline QQmlGuardedContextData &operator=(QQmlContextData *d) { + setContextData(d); return *this; + } private: - QQmlGuardedContextData &operator=(const QQmlGuardedContextData &); - QQmlGuardedContextData(const QQmlGuardedContextData &); + QQmlGuardedContextData &operator=(const QQmlGuardedContextData &) = delete; + QQmlGuardedContextData(const QQmlGuardedContextData &) = delete; friend class QQmlContextData; inline void clear(); - QQmlContextData *m_contextData; - QQmlGuardedContextData *m_next; - QQmlGuardedContextData **m_prev; + QQmlContextData *m_contextData = 0; + QQmlGuardedContextData *m_next = 0; + QQmlGuardedContextData **m_prev = 0; }; -QQmlGuardedContextData::QQmlGuardedContextData() -: m_contextData(0), m_next(0), m_prev(0) + +void QQmlGuardedContextData::setContextData(QQmlContextData *contextData) + { + if (m_contextData == contextData) + return; + clear(); + + if (contextData) { + m_contextData = contextData; + m_next = contextData->contextGuards; + if (m_next) m_next->m_prev = &m_next; + m_prev = &contextData->contextGuards; + contextData->contextGuards = this; + } +} + +void QQmlGuardedContextData::clear() +{ + if (m_prev) { + *m_prev = m_next; + if (m_next) m_next->m_prev = m_prev; + m_contextData = 0; + m_next = 0; + m_prev = 0; + } +} + +QQmlContextDataRef::QQmlContextDataRef() + : m_contextData(0) { } -QQmlGuardedContextData::QQmlGuardedContextData(QQmlContextData *data) -: m_contextData(0), m_next(0), m_prev(0) +QQmlContextDataRef::QQmlContextDataRef(const QQmlContextDataRef &other) + : m_contextData(other.m_contextData) { - setContextData(data); + if (m_contextData) + ++m_contextData->refCount; } -QQmlGuardedContextData::~QQmlGuardedContextData() +QQmlContextDataRef::QQmlContextDataRef(QQmlContextData *data) + : m_contextData(data) +{ + if (m_contextData) + ++m_contextData->refCount; +} + +QQmlContextDataRef::~QQmlContextDataRef() { clear(); } -void QQmlGuardedContextData::setContextData(QQmlContextData *contextData) +void QQmlContextDataRef::setContextData(QQmlContextData *contextData) { + if (m_contextData == contextData) + return; clear(); if (contextData) { m_contextData = contextData; - m_next = contextData->contextGuards; - if (m_next) m_next->m_prev = &m_next; - m_prev = &contextData->contextGuards; - contextData->contextGuards = this; + ++m_contextData->refCount; } } -QQmlContextData *QQmlGuardedContextData::contextData() const +QQmlContextData *QQmlContextDataRef::contextData() const { return m_contextData; } -void QQmlGuardedContextData::clear() +void QQmlContextDataRef::clear() { - if (m_prev) { - *m_prev = m_next; - if (m_next) m_next->m_prev = m_prev; - m_contextData = 0; - m_next = 0; - m_prev = 0; - } + if (m_contextData && !--m_contextData->refCount) + m_contextData->destroy(); + m_contextData = 0; } -QQmlGuardedContextData & -QQmlGuardedContextData::operator=(QQmlContextData *d) +QQmlContextDataRef & +QQmlContextDataRef::operator=(QQmlContextData *d) { setContextData(d); return *this; } +QQmlContextDataRef & +QQmlContextDataRef::operator=(const QQmlContextDataRef &other) +{ + setContextData(other.m_contextData); + return *this; +} + QQmlContextData::ContextGuard::ContextGuard() : context(0) { diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 2083326cd5..75ea720358 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -78,6 +78,34 @@ struct CompilationUnit; } } +// This is declared here because QQmlData below needs it and this file +// in turn is included from qqmlcontext_p.h. +class QQmlContextData; +class Q_QML_PRIVATE_EXPORT QQmlContextDataRef +{ +public: + inline QQmlContextDataRef(); + inline QQmlContextDataRef(QQmlContextData *); + inline QQmlContextDataRef(const QQmlContextDataRef &); + inline ~QQmlContextDataRef(); + + inline QQmlContextData *contextData() const; + inline void setContextData(QQmlContextData *); + + inline bool isNull() const { return !m_contextData; } + + inline operator QQmlContextData*() const { return m_contextData; } + inline QQmlContextData* operator->() const { return m_contextData; } + inline QQmlContextDataRef &operator=(QQmlContextData *d); + inline QQmlContextDataRef &operator=(const QQmlContextDataRef &other); + +private: + + inline void clear(); + + QQmlContextData *m_contextData; +}; + // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QQmlInstruction::CreateSimpleObject instruction. @@ -114,7 +142,6 @@ public: quint32 ownedByQml1:1; // This bit is shared with QML1's QDeclarativeData. quint32 ownMemory:1; - quint32 ownContext:1; quint32 indestructible:1; quint32 explicitIndestructibleSet:1; quint32 hasTaintedV4Object:1; @@ -127,7 +154,7 @@ public: quint32 hasInterceptorMetaObject:1; quint32 hasVMEMetaObject:1; quint32 parentFrozen:1; - quint32 dummy:21; + quint32 dummy:22; // When bindingBitsSize < sizeof(ptr), we store the binding bit flags inside // bindingBitsValue. When we need more than sizeof(ptr) bits, we allocated @@ -161,9 +188,10 @@ public: void disconnectNotifiers(); // The context that created the C++ object - QQmlContextData *context; + QQmlContextData *context = 0; // The outermost context in which this object lives - QQmlContextData *outerContext; + QQmlContextData *outerContext = 0; + QQmlContextDataRef ownContext; QQmlAbstractBinding *bindings; QQmlBoundSignal *signalHandlers; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 9676feb279..f31db457d3 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -712,8 +712,11 @@ QQmlEnginePrivate::~QQmlEnginePrivate() void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { if (QQmlData *d = QQmlData::get(o)) { - if (d->ownContext && d->context) { - d->context->destroy(); + if (d->ownContext) { + for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) + lc->invalidate(); + d->ownContext->invalidate(); + d->ownContext = 0; d->context = 0; } @@ -729,10 +732,10 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) } QQmlData::QQmlData() - : ownedByQml1(false), ownMemory(true), ownContext(false), indestructible(true), explicitIndestructibleSet(false), + : ownedByQml1(false), ownMemory(true), indestructible(true), explicitIndestructibleSet(false), hasTaintedV4Object(false), isQueuedForDeletion(false), rootObjectInCreation(false), hasInterceptorMetaObject(false), hasVMEMetaObject(false), parentFrozen(false), - bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), context(0), outerContext(0), + bindingBitsSize(MaxInlineBits), bindingBitsValue(0), notifyList(0), bindings(0), signalHandlers(0), nextContextObject(0), prevContextObject(0), lineNumber(0), columnNumber(0), jsEngineId(0), compilationUnit(0), deferredData(0), propertyCache(0), guards(0), extendedData(0) @@ -882,8 +885,12 @@ void QQmlData::setQueuedForDeletion(QObject *object) { if (object) { if (QQmlData *ddata = QQmlData::get(object)) { - if (ddata->ownContext && ddata->context) + if (ddata->ownContext) { + Q_ASSERT(ddata->ownContext == ddata->context); ddata->context->emitDestruction(); + ddata->ownContext = 0; + ddata->context = 0; + } ddata->isQueuedForDeletion = true; } } @@ -1753,8 +1760,7 @@ void QQmlData::destroyed(QObject *object) if (propertyCache) propertyCache->release(); - if (ownContext && context) - context->destroy(); + ownContext = 0; while (guards) { QQmlGuard *guard = static_cast *>(guards); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b34c2ea5e6..890d1ca7fe 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1131,7 +1131,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo c->linkedContext = context; } else context->addObject(instance); - ddata->ownContext = true; + ddata->ownContext = ddata->context; } else if (!ddata->context) context->addObject(instance); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index ce795be757..342e6c0725 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2817,7 +2817,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent effectiveCtxt = 0; // Create the script context if required - QQmlContextData *ctxt = new QQmlContextData; + QQmlContextDataRef ctxt(new QQmlContextData); ctxt->isInternal = true; ctxt->isJSContext = true; if (shared) @@ -2837,7 +2837,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent } if (effectiveCtxt) { - ctxt->setParent(effectiveCtxt, true); + ctxt->setParent(effectiveCtxt); } else { ctxt->engine = parentCtxt->engine; // Fix for QTBUG-21620 } @@ -2859,12 +2859,10 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent if (!m_program) { if (shared) m_loaded = true; - ctxt->destroy(); return QV4::Encode::undefined(); } QV4::Scoped qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, 0)); - qmlContext->takeContextOwnership(); m_program->qmlContext.set(scope.engine, qmlContext); m_program->run(); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index f3a39313c1..58d8322675 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1058,7 +1058,7 @@ private: void readEncoding(); PersistentValue m_thisObject; - QQmlGuardedContextData m_qmlContext; + QQmlContextDataRef m_qmlContext; static void dispatchCallback(Object *thisObj, QQmlContextData *context); void dispatchCallback(); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 8cc0b32168..96d83b9870 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1150,6 +1150,10 @@ void QtObject::method_createQmlObject(const BuiltinFunction *, Scope &scope, Cal if (!component.isReady()) THROW_GENERIC_ERROR("Qt.createQmlObject(): Component is not ready"); + if (!effectiveContext->isValid()) { + THROW_GENERIC_ERROR("Qt.createQmlObject(): Cannot create a component in an invalid context"); + } + QObject *obj = component.beginCreate(effectiveContext); if (obj) { QQmlData::get(obj, true)->explicitIndestructibleSet = false; diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 6584ecfb84..9b1417eccd 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -269,7 +269,8 @@ QQmlDelegateModel::~QQmlDelegateModel() delete cacheItem->object; cacheItem->object = 0; - cacheItem->contextData->destroy(); + cacheItem->contextData->invalidate(); + Q_ASSERT(cacheItem->contextData->refCount == 1); cacheItem->contextData = 0; cacheItem->scriptRef -= 1; } @@ -840,7 +841,8 @@ void QQDMIncubationTask::statusChanged(Status status) delete incubating->object; incubating->object = 0; if (incubating->contextData) { - incubating->contextData->destroy(); + incubating->contextData->invalidate(); + Q_ASSERT(incubating->contextData->refCount == 1); incubating->contextData = 0; } incubating->scriptRef = 0; @@ -900,8 +902,10 @@ void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incuba delete cacheItem->object; cacheItem->object = 0; cacheItem->scriptRef -= 1; - if (cacheItem->contextData) - cacheItem->contextData->destroy(); + if (cacheItem->contextData) { + cacheItem->contextData->invalidate(); + Q_ASSERT(cacheItem->contextData->refCount == 1); + } cacheItem->contextData = 0; if (!cacheItem->isReferenced()) { @@ -983,7 +987,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo if (QQmlAdaptorModelProxyInterface *proxy = qobject_cast(cacheItem)) { ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, true); + ctxt->setParent(cacheItem->contextData); ctxt->contextObject = proxy->proxiedObject(); } } @@ -1966,8 +1970,11 @@ void QQmlDelegateModelItem::destroyObject() QQmlData *data = QQmlData::get(object); Q_ASSERT(data); - if (data->ownContext && data->context) - data->context->clearContext(); + if (data->ownContext) { + data->ownContext->clearContext(); + data->ownContext = 0; + data->context = 0; + } object->deleteLater(); if (attached) { @@ -1975,7 +1982,7 @@ void QQmlDelegateModelItem::destroyObject() attached = 0; } - contextData->destroy(); + contextData->invalidate(); contextData = 0; object = 0; } diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index cb4a1f79ba..0f8438870a 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -140,7 +140,7 @@ public: QV4::ExecutionEngine *v4; QQmlDelegateModelItemMetaType * const metaType; - QQmlContextData *contextData; + QQmlContextDataRef contextData; QPointer object; QPointer attached; QQDMIncubationTask *incubationTask; diff --git a/tests/auto/qml/qqmlcontext/data/Singleton.qml b/tests/auto/qml/qqmlcontext/data/Singleton.qml new file mode 100644 index 0000000000..68ef5850e3 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/Singleton.qml @@ -0,0 +1,5 @@ +pragma Singleton +import QtQml 2.0 +QtObject { + readonly property string song: "Highway to Hell" +} diff --git a/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml new file mode 100644 index 0000000000..2e0e6f20e2 --- /dev/null +++ b/tests/auto/qml/qqmlcontext/data/contextViaClosureAfterDestruction.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +import constants 1.0 + +QtObject { + function createClosure() { + return function() { return Sing.song; } + } + function createComponentFactory() { + return function(parentObj) { + return Qt.createQmlObject('import QtQml 2.0; QtObject { property string test: "ok"; }', parentObj); + } + } +} diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index f49fd391ac..5f57b9ebb0 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -62,6 +62,7 @@ private slots: void evalAfterInvalidate(); void qobjectDerived(); void qtbug_49232(); + void contextViaClosureAfterDestruction(); private: QQmlEngine engine; @@ -723,6 +724,33 @@ void tst_qqmlcontext::qtbug_49232() QCOMPARE(obj->property("valueTwo"), QVariant(97)); } +void tst_qqmlcontext::contextViaClosureAfterDestruction() +{ + qmlRegisterSingletonType(testFileUrl("Singleton.qml"), "constants", 1, 0, "Sing"); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("contextViaClosureAfterDestruction.qml")); + QJSValue valueClosure; + QJSValue componentFactoryClosure; + { + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + // meta-calls don't support QJSValue return types, so do the call "by hand" + valueClosure = engine.newQObject(obj.data()).property(QStringLiteral("createClosure")).call(); + QVERIFY(valueClosure.isCallable()); + componentFactoryClosure = engine.newQObject(obj.data()).property(QStringLiteral("createComponentFactory")).call(); + QVERIFY(componentFactoryClosure.isCallable()); + } + QCOMPARE(valueClosure.call().toString(), QLatin1String("Highway to Hell")); + + QScopedPointer parent(new QObject); + QJSValue parentWrapper = engine.newQObject(parent.data()); + QQmlEngine::setObjectOwnership(parent.data(), QQmlEngine::CppOwnership); + + QJSValue subObject = componentFactoryClosure.callWithInstance(componentFactoryClosure, QJSValueList() << parentWrapper); + QVERIFY(subObject.isError()); + QCOMPARE(subObject.toString(), QLatin1String("Error: Qt.createQmlObject(): Cannot create a component in an invalid context")); +} + QTEST_MAIN(tst_qqmlcontext) #include "tst_qqmlcontext.moc" -- cgit v1.2.3 From a32cf1a22d096b33340cddbe91328c6c088e221d Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 6 Sep 2017 16:26:27 +0200 Subject: Disable deferring when referenced as a grouped property This allows us to fix QTBUG-50992 - the issue with most votes in QQC2. Task-number: QTBUG-63036 Change-Id: I996cd1128582b80e0c8480ae143d682c1e8eb8fe Reviewed-by: Simon Hausmann Reviewed-by: Qt CI Bot --- src/qml/compiler/qqmltypecompiler.cpp | 2 +- .../qqmllanguage/data/MyLazyDeferredSubObject.qml | 6 ++++++ .../qml/qqmllanguage/data/lazyDeferredSubObject.qml | 5 +++++ tests/auto/qml/qqmllanguage/testtypes.cpp | 2 ++ tests/auto/qml/qqmllanguage/testtypes.h | 20 ++++++++++++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 16 ++++++++++++++++ 6 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml create mode 100644 tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index e17ab354cf..cd7d8ca049 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -1251,7 +1251,7 @@ bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) _seenObjectWithId |= seenSubObjectWithId; } - if (!seenSubObjectWithId + if (!seenSubObjectWithId && binding->type != QV4::CompiledData::Binding::Type_GroupProperty && !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) { binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding; diff --git a/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml new file mode 100644 index 0000000000..f311f6b602 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/MyLazyDeferredSubObject.qml @@ -0,0 +1,6 @@ +import QtQml 2.0 +import Test 1.0 +LazyDeferredSubObject { + subObject: QtObject { objectName: 'default' } + objectName: subObject.objectName +} diff --git a/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml new file mode 100644 index 0000000000..2465a18320 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/lazyDeferredSubObject.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 +import Test 1.0 +MyLazyDeferredSubObject { + subObject.objectName: 'custom' +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index bdcdaa8137..72e06d26aa 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -103,6 +103,8 @@ void registerTypes() qmlRegisterSingletonType("Test", 1, 0, "MyTypeObjectSingleton", myTypeObjectSingleton); qmlRegisterType("Test", 1, 0, "MyArrayBufferTestClass"); + + qmlRegisterType("Test", 1, 0, "LazyDeferredSubObject"); } QVariant myCustomVariantTypeConverter(const QString &data) diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index 7d7a8ac6d3..b0e677feb8 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1331,6 +1331,26 @@ private: QObject *obj; }; +class LazyDeferredSubObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QObject *subObject READ subObject WRITE setSubObject NOTIFY subObjectChanged FINAL) + Q_CLASSINFO("DeferredPropertyNames", "subObject"); +public: + LazyDeferredSubObject() + : obj(0) + {} + + QObject *subObject() const { if (!obj) qmlExecuteDeferred(const_cast(this)); return obj; } + void setSubObject(QObject *o) { if (obj == o) return; obj = o; emit subObjectChanged(); } + +signals: + void subObjectChanged(); + +private: + QObject *obj; +}; + void registerTypes(); #endif // TESTTYPES_H diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 39f5082c70..8b84ee878f 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -247,6 +247,7 @@ private slots: void propertyCacheInSync(); void rootObjectInCreationNotForSubObjects(); + void lazyDeferredSubObject(); void noChildEvents(); @@ -4242,6 +4243,21 @@ void tst_qqmllanguage::rootObjectInCreationNotForSubObjects() QVERIFY(!ddata->rootObjectInCreation); } +// QTBUG-63036 +void tst_qqmllanguage::lazyDeferredSubObject() +{ + QQmlComponent component(&engine, testFile("lazyDeferredSubObject.qml")); + VERIFY_ERRORS(0); + QScopedPointer object(component.create()); + QVERIFY(!object.isNull()); + + QObject *subObject = qvariant_cast(object->property("subObject")); + QVERIFY(subObject); + + QCOMPARE(object->objectName(), QStringLiteral("custom")); + QCOMPARE(subObject->objectName(), QStringLiteral("custom")); +} + void tst_qqmllanguage::noChildEvents() { QQmlComponent component(&engine); -- cgit v1.2.3 From c103ab2c2257ae99c541d4be15532edeba144762 Mon Sep 17 00:00:00 2001 From: Oleg Yadrov Date: Wed, 6 Sep 2017 15:54:50 -0400 Subject: QQuickDragAttached: make hotSpot work with imageSource This amends patch 4b982c744f538a24e21a2af146c45f93d27dd1cb. Previously, setting hotSpot had no effect on the image position because QDrag object used for the drag is created in startDrag(), and the hotspot was never updated before drag->exec(...). Task-number: QTBUG-61980 Change-Id: I9c11c456d3b32b5986cf287b2610437e3825d9d9 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickdrag.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp index 41847e5f01..7c936ff21c 100644 --- a/src/quick/items/qquickdrag.cpp +++ b/src/quick/items/qquickdrag.cpp @@ -772,6 +772,7 @@ Qt::DropAction QQuickDragAttachedPrivate::startDrag(Qt::DropActions supportedAct drag->setPixmap(QPixmap::fromImage(pixmapLoader.image())); } + drag->setHotSpot(hotSpot.toPoint()); emit q->dragStarted(); Qt::DropAction dropAction = drag->exec(supportedActions); -- cgit v1.2.3 From 62271c22e5472b9a6c4688fa906bf5d7cfda1953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Wed, 5 Oct 2016 14:27:27 +0200 Subject: Canvas: Repaint on devicePixelRatio Change Handle moving between high- and normal-DPI displays. The texture gets a setCanvasWindow() call on screen change. Read window->effectiveDevicePixelRatio() here and set m_canvasWindowChanged on change to trigger a repaint if there was a change. Task-number: QTBUG-37095 Change-Id: I96ff07bd7334269cad219eb0a9056c62e850aac7 Reviewed-by: Shawn Rutledge --- .../items/context2d/qquickcontext2dtexture.cpp | 25 ++++++++++++---------- .../items/context2d/qquickcontext2dtexture_p.h | 1 + 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index df6dd9dba2..3e52cb167c 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -96,6 +96,7 @@ QQuickContext2DTexture::QQuickContext2DTexture() #endif , m_surface(0) , m_item(0) + , m_canvasDevicePixelRatio(1) , m_canvasWindowChanged(false) , m_dirtyTexture(false) , m_smooth(true) @@ -161,12 +162,19 @@ void QQuickContext2DTexture::setItem(QQuickCanvasItem* item) bool QQuickContext2DTexture::setCanvasWindow(const QRect& r) { + qreal canvasDevicePixelRatio = (m_item && m_item->window()) ? + m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio(); + if (!qFuzzyCompare(m_canvasDevicePixelRatio, canvasDevicePixelRatio)) { + m_canvasDevicePixelRatio = canvasDevicePixelRatio; + m_canvasWindowChanged = true; + } + if (m_canvasWindow != r) { m_canvasWindow = r; m_canvasWindowChanged = true; - return true; } - return false; + + return m_canvasWindowChanged; } bool QQuickContext2DTexture::setDirtyRect(const QRect &r) @@ -549,9 +557,6 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() { QQuickContext2DTexture::beginPainting(); - const qreal devicePixelRatio = (m_item && m_item->window()) ? - m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio(); - if (m_canvasWindow.size().isEmpty()) { delete m_fbo; delete m_multisampledFbo; @@ -566,7 +571,7 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() delete m_paint_device; m_paint_device = 0; - m_fboSize = npotAdjustedSize(m_canvasWindow.size() * devicePixelRatio); + m_fboSize = npotAdjustedSize(m_canvasWindow.size() * m_canvasDevicePixelRatio); m_canvasWindowChanged = false; if (doMultisampling()) { @@ -604,7 +609,7 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size()); gl_device->setPaintFlipped(true); gl_device->setSize(m_fbo->size()); - gl_device->setDevicePixelRatio(devicePixelRatio); + gl_device->setDevicePixelRatio(m_canvasDevicePixelRatio); m_paint_device = gl_device; } @@ -710,12 +715,10 @@ QPaintDevice* QQuickContext2DImageTexture::beginPainting() if (m_canvasWindow.size().isEmpty()) return 0; - const qreal devicePixelRatio = (m_item && m_item->window()) ? - m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio(); if (m_canvasWindowChanged) { - m_image = QImage(m_canvasWindow.size() * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); - m_image.setDevicePixelRatio(devicePixelRatio); + m_image = QImage(m_canvasWindow.size() * m_canvasDevicePixelRatio, QImage::Format_ARGB32_Premultiplied); + m_image.setDevicePixelRatio(m_canvasDevicePixelRatio); m_image.fill(0x00000000); m_canvasWindowChanged = false; } diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 97135816a2..81896dcdc1 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -168,6 +168,7 @@ protected: QSize m_canvasSize; QSize m_tileSize; QRect m_canvasWindow; + qreal m_canvasDevicePixelRatio; QMutex m_mutex; QWaitCondition m_condition; -- cgit v1.2.3 From fdf9990c71e7bfdf1af7f433b1f9429fe30777fc Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 7 Sep 2017 10:53:18 +0200 Subject: add qt.quick.canvas logging category, show size change due to DPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly as a way of verifying the fix for QTBUG-37095, so far. But of course other log messages can be added to this category later. Task-number: QTBUG-37095 Change-Id: I57930e9376529b6eacca1b554d31382d41582fda Reviewed-by: Morten Johan Sørvig --- src/quick/items/context2d/qquickcontext2dtexture.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 3e52cb167c..6136feb4ea 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -57,6 +57,8 @@ QT_BEGIN_NAMESPACE #if QT_CONFIG(opengl) #define QT_MINIMUM_FBO_SIZE 64 +Q_LOGGING_CATEGORY(lcCanvas, "qt.quick.canvas") + static inline int qt_next_power_of_two(int v) { v--; @@ -165,6 +167,9 @@ bool QQuickContext2DTexture::setCanvasWindow(const QRect& r) qreal canvasDevicePixelRatio = (m_item && m_item->window()) ? m_item->window()->effectiveDevicePixelRatio() : qApp->devicePixelRatio(); if (!qFuzzyCompare(m_canvasDevicePixelRatio, canvasDevicePixelRatio)) { + qCDebug(lcCanvas, "%s device pixel ratio %.1lf -> %.1lf", + (m_item->objectName().isEmpty() ? "Canvas" : qPrintable(m_item->objectName())), + m_canvasDevicePixelRatio, canvasDevicePixelRatio); m_canvasDevicePixelRatio = canvasDevicePixelRatio; m_canvasWindowChanged = true; } @@ -610,6 +615,9 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() gl_device->setPaintFlipped(true); gl_device->setSize(m_fbo->size()); gl_device->setDevicePixelRatio(m_canvasDevicePixelRatio); + qCDebug(lcCanvas, "%s size %.1lf x %.1lf painting with size %d x %d DPR %.1lf", + (m_item->objectName().isEmpty() ? "Canvas" : qPrintable(m_item->objectName())), + m_item->width(), m_item->height(), m_fbo->size().width(), m_fbo->size().height(), m_canvasDevicePixelRatio); m_paint_device = gl_device; } @@ -721,6 +729,9 @@ QPaintDevice* QQuickContext2DImageTexture::beginPainting() m_image.setDevicePixelRatio(m_canvasDevicePixelRatio); m_image.fill(0x00000000); m_canvasWindowChanged = false; + qCDebug(lcCanvas, "%s size %.1lf x %.1lf painting with size %d x %d DPR %.1lf", + (m_item->objectName().isEmpty() ? "Canvas" : qPrintable(m_item->objectName())), + m_item->width(), m_item->height(), m_image.size().width(), m_image.size().height(), m_canvasDevicePixelRatio); } return &m_image; -- cgit v1.2.3 From 161159bfec511112daa7ad0fe1d2375edbf41401 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 7 Sep 2017 10:15:49 +0200 Subject: QQmlPropertyValidator::validateObject(): remove unused code This code was moved to QQmlDeferredBindingScanner in f27d058. Change-Id: I8cf261c497ec433a14e00e17b67a284b45cf8d75 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertyvalidator.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 4ac7aad553..6929318c59 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -94,16 +94,6 @@ QVector QQmlPropertyValidator::validateObject(int objectIndex, if (!propertyCache) return QVector(); - QStringList deferredPropertyNames; - { - const QMetaObject *mo = propertyCache->firstCppMetaObject(); - const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames"); - if (namesIndex != -1) { - QMetaClassInfo classInfo = mo->classInfo(namesIndex); - deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(',')); - } - } - QQmlCustomParser *customParser = 0; if (auto typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) { if (typeRef->type.isValid()) -- cgit v1.2.3 From aff417321d0aad5da462a63b103b82bc19809841 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Sep 2017 13:02:57 +0200 Subject: Fix crashes in QML apps when upgrading Qt snapshots When upgrading from one Qt snapshot to another, we may not end up bumping the Qt version. However we do need to re-generate QML cache files. Therefore let's encode the commit hash of declarative in the checksums. Task-number: QTBUG-62302 Change-Id: Ia597fcbe05ea2d32664da2572a1b35c624490095 Reviewed-by: Joerg Bornemann --- src/qml/compiler/qv4compileddata.cpp | 16 ++++++---------- src/qml/qml.pro | 13 +++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 32e8ee4da2..6702966d11 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -731,8 +731,6 @@ void ResolvedTypeReference::doDynamicTypeCheck() isFullyDynamicType = qtTypeInherits(mo); } -#if defined(QT_BUILD_INTERNAL) - static QByteArray ownLibraryChecksum() { static QByteArray libraryChecksum; @@ -740,7 +738,10 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen) +#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)); @@ -750,14 +751,14 @@ static QByteArray ownLibraryChecksum() libraryChecksum = hash.result(); } } +#elif defined(QML_COMPILE_HASH) + libraryChecksum = QByteArray(QT_STRINGIFY(QML_COMPILE_HASH)); #else // Not implemented. #endif return libraryChecksum; } -#endif - bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *engine) const { for (auto it = constBegin(), end = constEnd(); it != end; ++it) { @@ -765,12 +766,7 @@ bool ResolvedTypeReferenceMap::addToHash(QCryptographicHash *hash, QQmlEngine *e return false; } - // 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. -#if defined(QT_BUILD_INTERNAL) hash->addData(ownLibraryChecksum()); -#endif return true; } diff --git a/src/qml/qml.pro b/src/qml/qml.pro index be3956bb61..e9d7dcbd2d 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -16,6 +16,19 @@ gcc:isEqual(QT_ARCH, "mips"): QMAKE_CXXFLAGS += -fno-reorder-blocks DEFINES += QT_NO_FOREACH +tagFile=$$PWD/../../.tag +tag= +exists($$tagFile) { + tag=$$cat($$tagFile, singleline) + QMAKE_INTERNAL_INCLUDED_FILES += $$tagFile +} +!equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") { + DEFINES += QML_COMPILE_HASH="$$tag" +} else:exists($$PWD/../../.git) { + commit=$$system(git describe --tags --always --long --dirty) + DEFINES += QML_COMPILE_HASH="$$commit" +} + exists("qqml_enable_gcov") { QMAKE_CXXFLAGS = -fprofile-arcs -ftest-coverage -fno-elide-constructors LIBS_PRIVATE += -lgcov -- cgit v1.2.3 From 1532a15002082486757a475d0c01b7779da3e276 Mon Sep 17 00:00:00 2001 From: Mitch Curtis Date: Thu, 7 Sep 2017 11:49:16 +0200 Subject: Settings: document how to have several categories Change-Id: I0f27a162936015e2aeb9d52079a65b218498b50a Reviewed-by: J-P Nurmi --- src/imports/settings/qqmlsettings.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/imports/settings/qqmlsettings.cpp b/src/imports/settings/qqmlsettings.cpp index df67c04654..ae12d54f5a 100644 --- a/src/imports/settings/qqmlsettings.cpp +++ b/src/imports/settings/qqmlsettings.cpp @@ -188,6 +188,9 @@ QT_BEGIN_NAMESPACE only provides a cleaner settings structure, but also prevents possible conflicts between setting keys. + If several categories are required, use several Settings objects, each with + their own category: + \qml Item { id: panel @@ -199,6 +202,12 @@ QT_BEGIN_NAMESPACE property alias visible: panel.visible // ... } + + Settings { + category: "General" + property alias fontSize: fontSizeSpinBox.value + // ... + } } \endqml -- cgit v1.2.3 From 94641380148e65e126c8533a278f261aaeb87113 Mon Sep 17 00:00:00 2001 From: Marco Benelli Date: Thu, 7 Sep 2017 15:14:43 +0200 Subject: qmlplugindump: skip imports containing "private" Skipping paths that ends with "private" is not enough. Task-number: QTBUG-47027 Change-Id: I25af518b76f594c268db6b77dbafd343f2f57ee8 Reviewed-by: Teemu Holappa Reviewed-by: Thomas Hartmann --- tools/qmlplugindump/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index a71899941e..42da681e82 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -788,7 +788,7 @@ static bool readDependenciesData(QString dependenciesFile, const QByteArray &fil QString version = obj.value(QStringLiteral("version")).toString(); if (name.isEmpty() || urisToSkip.contains(name) || version.isEmpty()) continue; - if (name.endsWith(QLatin1String("Private"), Qt::CaseInsensitive)) { + if (name.contains(QLatin1String("Private"), Qt::CaseInsensitive)) { if (verbose) std::cerr << "skipping private dependecy " << qPrintable( name ) << " " << qPrintable(version) << std::endl; -- cgit v1.2.3 From 286d72e458c10bae3ce11c58a16352a53ba06aef Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Sep 2017 12:15:31 +0200 Subject: Fix crash with dangling context object pointers This is a regression introduced by commit e22b624d9ab1f36021adb9cdbfa9b37054282bb8, where the object that owns the QML context would destroy the context upon struction. Now the context may live longer and thus the context->contextObject pointer would become a dangling pointer. Task-number: QTBUG-63078 Change-Id: I8fdab4086b0472d5d4930cf57aa76922b7ed9e2e Reviewed-by: Lars Knoll --- src/qml/qml/qqmlengine.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f31db457d3..13b17e6946 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -716,6 +716,8 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) for (QQmlContextData *lc = d->ownContext->linkedContext; lc; lc = lc->linkedContext) lc->invalidate(); d->ownContext->invalidate(); + if (d->ownContext->contextObject == o) + d->ownContext->contextObject = nullptr; d->ownContext = 0; d->context = 0; } -- cgit v1.2.3 From 874539649508cc1a9cd0ec7ab45b374d18c2c4e5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Sep 2017 09:43:21 +0200 Subject: Fix crash with loading cache files originating from top-level components If a .qml file starts with Component {} and its item(s) define their own properties, alias, etc. then loading this file initially would work, but loading it from a cache file would crash with dangling pointers in the property cache. This was due to us registering aliases, properties, etc. twice in the property cache, exceeding the reservation in the property cache vectors. The minimal fix is to skip the root object in the property cache creating loop as we do handle it separately afterwards. It needs to be separate because the first object index within the component does not stem from a binding. However as the root object index is always zero, I'll make a follow-up patch to get rid of of the variable. Task-number: QTBUG-62263 Change-Id: I86b76d38cb490750a561eac2b0ad6fff6ef2e20a Reviewed-by: Friedemann Kleint Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlpropertycachecreator_p.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 8fc8943366..2dd2e82651 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -562,6 +562,8 @@ template inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesToMetaObjects() { for (int i = 0; i < objectContainer->objectCount(); ++i) { + if (i == objectContainer->rootObjectIndex()) + continue; const CompiledObject &component = *objectContainer->objectAt(i); if (!(component.flags & QV4::CompiledData::Object::IsComponent)) continue; -- cgit v1.2.3 From ebda8170a6b00b830fba59a230506ab3ca52e6c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Sep 2017 10:03:48 +0200 Subject: Get rid of the root object index variable This is a follow-up to the parent commit to remove the variable that is really a constant (zero). Change-Id: I8fc20027c5c7b871269b814cb8b93636e94be267 Reviewed-by: Lars Knoll --- src/qml/compiler/qqmlirbuilder.cpp | 9 ++++---- src/qml/compiler/qqmlirbuilder_p.h | 1 - src/qml/compiler/qqmlpropertycachecreator_p.h | 14 ++++++------ src/qml/compiler/qqmlpropertyvalidator.cpp | 2 +- src/qml/compiler/qqmltypecompiler.cpp | 26 +++++++++------------- src/qml/compiler/qqmltypecompiler_p.h | 2 -- src/qml/compiler/qv4compileddata.cpp | 6 ++--- src/qml/compiler/qv4compileddata_p.h | 6 ++--- src/qml/compiler/qv4compiler.cpp | 1 - src/qml/qml/qqmlcontext.cpp | 2 +- src/qml/qml/qqmlobjectcreator.cpp | 4 ++-- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 2 +- .../qml/qqmltranslation/tst_qqmltranslation.cpp | 4 ++-- 13 files changed, 33 insertions(+), 46 deletions(-) diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 8db054c91f..092c0020fa 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -300,7 +300,6 @@ void Document::removeScriptPragmas(QString &script) Document::Document(bool debugMode) : jsModule(debugMode) , program(0) - , indexOfRootObject(0) , jsGenerator(&jsModule) { } @@ -404,7 +403,10 @@ bool IRBuilder::generateFromQml(const QString &code, const QString &url, Documen QQmlJS::AST::UiObjectDefinition *rootObject = QQmlJS::AST::cast(program->members->member); Q_ASSERT(rootObject); - defineQMLObject(&output->indexOfRootObject, rootObject); + int rootObjectIndex = -1; + if (defineQMLObject(&rootObjectIndex, rootObject)) { + Q_ASSERT(rootObjectIndex == 0); + } qSwap(_imports, output->imports); qSwap(_pragmas, output->pragmas); @@ -1399,7 +1401,6 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4: qmlUnit->nImports = output.imports.count(); qmlUnit->offsetToObjects = unitSize + importSize; qmlUnit->nObjects = output.objects.count(); - qmlUnit->indexOfRootObject = output.indexOfRootObject; qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData(); qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount(); @@ -2085,8 +2086,6 @@ void IRLoader::load() output->pragmas << p; } - output->indexOfRootObject = unit->indexOfRootObject; - for (uint i = 0; i < unit->nObjects; ++i) { const QV4::CompiledData::Object *serializedObject = unit->objectAt(i); QmlIR::Object *object = loadObject(serializedObject); diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 64bf111d9a..a6ff18927d 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -432,7 +432,6 @@ struct Q_QML_PRIVATE_EXPORT Document QList imports; QList pragmas; QQmlJS::AST::UiProgram *program; - int indexOfRootObject; QVector objects; QV4::Compiler::JSUnitGenerator jsGenerator; diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 2dd2e82651..22e83de9ae 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -108,7 +108,7 @@ template inline QQmlCompileError QQmlPropertyCacheCreator::buildMetaObjects() { QQmlBindingInstantiationContext context; - return buildMetaObjectRecursively(objectContainer->rootObjectIndex(), context); + return buildMetaObjectRecursively(/*root object*/0, context); } template @@ -278,7 +278,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj QByteArray newClassName; - if (objectIndex == objectContainer->rootObjectIndex()) { + if (objectIndex == /*root object*/0) { const QString path = objectContainer->url().path(); int lastSlash = path.lastIndexOf(QLatin1Char('/')); if (lastSlash > -1) { @@ -561,9 +561,9 @@ inline QQmlPropertyCacheAliasCreator::QQmlPropertyCacheAliasCre template inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesToMetaObjects() { - for (int i = 0; i < objectContainer->objectCount(); ++i) { - if (i == objectContainer->rootObjectIndex()) - continue; + // skip the root object (index 0) as that one does not have a first object index originating + // from a binding. + for (int i = 1; i < objectContainer->objectCount(); ++i) { const CompiledObject &component = *objectContainer->objectAt(i); if (!(component.flags & QV4::CompiledData::Object::IsComponent)) continue; @@ -572,7 +572,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); } - const int rootObjectIndex = objectContainer->rootObjectIndex(); + const int rootObjectIndex = 0; appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); } @@ -635,7 +635,7 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl objectsWithAliases->append(objectIndex); // Stop at Component boundary - if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != objectContainer->rootObjectIndex()) + if (object.flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) return; auto binding = object.bindingsBegin(); diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 6929318c59..7ea89b378d 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -58,7 +58,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, c QVector QQmlPropertyValidator::validate() { - return validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0); + return validateObject(/*root object*/0, /*instantiatingBinding*/0); } typedef QVarLengthArray GroupPropertyVector; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index cd7d8ca049..eeb5595a9a 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -228,15 +228,10 @@ QVector *QQmlTypeCompiler::qmlObjects() const return &document->objects; } -int QQmlTypeCompiler::rootObjectIndex() const -{ - return document->indexOfRootObject; -} - void QQmlTypeCompiler::setPropertyCaches(QQmlPropertyCacheVector &&caches) { m_propertyCaches = std::move(caches); - Q_ASSERT(m_propertyCaches.count() >= document->indexOfRootObject); + Q_ASSERT(m_propertyCaches.count() > 0); } const QQmlPropertyCacheVector *QQmlTypeCompiler::propertyCaches() const @@ -680,7 +675,7 @@ QQmlCustomParserScriptIndexer::QQmlCustomParserScriptIndexer(QQmlTypeCompiler *t void QQmlCustomParserScriptIndexer::annotateBindingsWithScriptStrings() { - scanObjectRecursively(compiler->rootObjectIndex()); + scanObjectRecursively(/*root object*/0); } void QQmlCustomParserScriptIndexer::scanObjectRecursively(int objectIndex, bool annotateScriptBindings) @@ -775,7 +770,6 @@ QQmlComponentAndAliasResolver::QQmlComponentAndAliasResolver(QQmlTypeCompiler *t , enginePrivate(typeCompiler->enginePrivate()) , pool(typeCompiler->memoryPool()) , qmlObjects(typeCompiler->qmlObjects()) - , indexOfRootObject(typeCompiler->rootObjectIndex()) , resolvedTypes(&typeCompiler->resolvedTypes) , propertyCaches(std::move(typeCompiler->takePropertyCaches())) { @@ -913,9 +907,9 @@ bool QQmlComponentAndAliasResolver::resolve() if (rootBinding->next || rootBinding->type != QV4::CompiledData::Binding::Type_Object) COMPILE_EXCEPTION(obj, tr("Invalid component body specification")); - // We are going to collect ids/aliases and resolve them for the root object as a separate + // For the root object, we are going to collect ids/aliases and resolve them for as a separate // last pass. - if (i != indexOfRootObject) + if (i != 0) componentRoots.append(i); } @@ -941,12 +935,12 @@ bool QQmlComponentAndAliasResolver::resolve() _idToObjectIndex.clear(); _objectsWithAliases.clear(); - collectIdsAndAliases(indexOfRootObject); + collectIdsAndAliases(/*root object*/0); - QmlIR::Object *rootComponent = qmlObjects->at(indexOfRootObject); + QmlIR::Object *rootComponent = qmlObjects->at(/*root object*/0); rootComponent->namedObjectsInComponent.allocate(pool, _idToObjectIndex); - if (!resolveAliases(indexOfRootObject)) + if (!resolveAliases(/*root object*/0)) return false; // Implicit component insertion may have added objects and thus we also need @@ -974,7 +968,7 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex) _objectsWithAliases.append(objectIndex); // Stop at Component boundary - if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != compiler->rootObjectIndex()) + if (obj->flags & QV4::CompiledData::Object::IsComponent && objectIndex != /*root object*/0) return true; for (const QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { @@ -1165,7 +1159,7 @@ QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingSca bool QQmlDeferredAndCustomParserBindingScanner::scanObject() { - return scanObject(compiler->rootObjectIndex()); + return scanObject(/*root object*/0); } bool QQmlDeferredAndCustomParserBindingScanner::scanObject(int objectIndex) @@ -1291,7 +1285,7 @@ bool QQmlJSCodeGenerator::generateCodeForComponents() return false; } - return compileComponent(compiler->rootObjectIndex()); + return compileComponent(/*root object*/0); } bool QQmlJSCodeGenerator::compileComponent(int contextObject) diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 79fc073d8b..0e953ec01e 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -119,7 +119,6 @@ public: QQmlEnginePrivate *enginePrivate() const { return engine; } const QQmlImports *imports() const; QVector *qmlObjects() const; - int rootObjectIndex() const; void setPropertyCaches(QQmlPropertyCacheVector &&caches); const QQmlPropertyCacheVector *propertyCaches() const; QQmlPropertyCacheVector &&takePropertyCaches(); @@ -282,7 +281,6 @@ protected: QQmlJS::MemoryPool *pool; QVector *qmlObjects; - const int indexOfRootObject; // indices of the objects that are actually Component {} QVector componentRoots; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6702966d11..9e9a419be7 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -212,7 +212,7 @@ void CompilationUnit::unlink() engine->compilationUnits.erase(engine->compilationUnits.find(this)); if (isRegisteredWithEngine) { - Q_ASSERT(data && quint32(propertyCaches.count()) > data->indexOfRootObject && propertyCaches.at(data->indexOfRootObject)); + Q_ASSERT(data && propertyCaches.count() > 0 && propertyCaches.at(/*root object*/0)); if (qmlEngine) qmlEngine->unregisterInternalCompositeType(this); QQmlMetaType::unregisterInternalCompositeType(this); @@ -288,11 +288,11 @@ void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine) this->qmlEngine = qmlEngine; // Add to type registry of composites - if (propertyCaches.needsVMEMetaObject(data->indexOfRootObject)) { + if (propertyCaches.needsVMEMetaObject(/*root object*/0)) { QQmlMetaType::registerInternalCompositeType(this); qmlEngine->registerInternalCompositeType(this); } else { - const QV4::CompiledData::Object *obj = objectAt(data->indexOfRootObject); + const QV4::CompiledData::Object *obj = objectAt(/*root object*/0); auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex); Q_ASSERT(typeRef); if (typeRef->compilationUnit) { diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 815c1b92de..6e6d641d94 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x12 +#define QV4_DATA_STRUCTURE_VERSION 0x13 class QIODevice; class QQmlPropertyCache; @@ -666,7 +666,6 @@ struct Unit LEUInt32 offsetToImports; LEUInt32 nObjects; LEUInt32 offsetToObjects; - LEUInt32 indexOfRootObject; LEUInt32 padding; @@ -854,7 +853,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public // QML specific fields QQmlPropertyCacheVector propertyCaches; - QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } + QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(/*root object*/0); } QQmlRefPointer typeNameCache; @@ -891,7 +890,6 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public // --- interface for QQmlPropertyCacheCreator typedef Object CompiledObject; int objectCount() const { return data->nObjects; } - int rootObjectIndex() const { return data->indexOfRootObject; } const Object *objectAt(int index) const { return data->objectAt(index); } QString stringAt(int index) const { return data->stringAt(index); } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index ede432a1e0..3abdd0370f 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -432,7 +432,6 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToImports = 0; unit.nObjects = 0; unit.offsetToObjects = 0; - unit.indexOfRootObject = 0; unit.unitSize = nextOffset; diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index e06491a814..37cb328b36 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -818,7 +818,7 @@ QQmlContextPrivate *QQmlContextData::asQQmlContextPrivate() void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer &unit, int subComponentIndex) { typeCompilationUnit = unit; - componentObjectIndex = subComponentIndex == -1 ? typeCompilationUnit->data->indexOfRootObject : subComponentIndex; + componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex; Q_ASSERT(!idValues); idValueCount = typeCompilationUnit->data->objectAt(componentObjectIndex)->nNamedObjectsInComponent; idValues = new ContextGuard[idValueCount]; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 890d1ca7fe..07a90d4d63 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -163,7 +163,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI int objectToCreate; if (subComponentIndex == -1) { - objectToCreate = qmlUnit->indexOfRootObject; + objectToCreate = /*root object*/0; } else { const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex); objectToCreate = compObj->bindingTable()->value.objectIndex; @@ -1121,7 +1121,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo } ddata->setImplicitDestructible(); - if (static_cast(index) == qmlUnit->indexOfRootObject || ddata->rootObjectInCreation) { + if (static_cast(index) == /*root object*/0 || ddata->rootObjectInCreation) { if (ddata->context) { Q_ASSERT(ddata->context != context); Q_ASSERT(ddata->outerContext); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 8b84ee878f..30c34426af 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2136,7 +2136,7 @@ void tst_qqmllanguage::scriptStringWithoutSourceCode() qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData; td->compilationUnit()->data = qmlUnit; - const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(qmlUnit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = qmlUnit->objectAt(/*root object*/0); QCOMPARE(qmlUnit->stringAt(rootObject->inheritedTypeNameIndex), QString("MyTypeObject")); quint32 i; for (i = 0; i < rootObject->nBindings; ++i) { diff --git a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp index 1fc803a395..dd4edeb97d 100644 --- a/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp +++ b/tests/auto/qml/qqmltranslation/tst_qqmltranslation.cpp @@ -85,7 +85,7 @@ void tst_qqmltranslation::translation() << QStringLiteral("singular") << QStringLiteral("plural"); const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = unit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = unit->stringAt(binding->propertyNameIndex); @@ -140,7 +140,7 @@ void tst_qqmltranslation::idTranslation() QVERIFY(compilationUnit); const QV4::CompiledData::Unit *unit = compilationUnit->data; - const QV4::CompiledData::Object *rootObject = unit->objectAt(unit->indexOfRootObject); + const QV4::CompiledData::Object *rootObject = unit->objectAt(/*root object*/0); const QV4::CompiledData::Binding *binding = rootObject->bindingTable(); for (quint32 i = 0; i < rootObject->nBindings; ++i, ++binding) { const QString propertyName = unit->stringAt(binding->propertyNameIndex); -- cgit v1.2.3 From 5f17840a14e22da0cfa973b36b9f6ce3bc8d4067 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Sep 2017 10:34:45 +0200 Subject: Remove temporary allocations during type cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a follow-up commit to 48c09a85, avoid the use of key() to create a temporary string when cleaning the QML type registry. There is strictly speaking no need to perform another hash lookup anyway. Change-Id: Ibd5f0210d5584d1f847d8ec61f25cb0972076365 Reviewed-by: Pasi Petäjäjärvi Reviewed-by: Lars Knoll --- src/qml/qml/qqmlmetatype.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ca0522aa99..5bbc250d5e 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1215,7 +1215,7 @@ void QQmlTypeModulePrivate::add(QQmlTypePrivate *type) void QQmlTypeModulePrivate::remove(const QQmlTypePrivate *type) { for (TypeHash::ConstIterator elementIt = typeHash.begin(); elementIt != typeHash.end();) { - QList &list = typeHash[elementIt.key()]; + QList &list = const_cast &>(elementIt.value()); removeQQmlTypePrivate(list, type); -- cgit v1.2.3 From c1fed764a2495373a9e4563bc3ac0d578b2f9409 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 8 Aug 2017 12:39:54 +0200 Subject: Fix reuse of regexp objects by regexp literals Accoding to the standard the regexp objects created by literals should be separate objects as if calling new. We were violating that by caching the same object for every instance of a literal. This also fixes a problem with leaking values of lastIndex between separate instances of the same global regexp literal. Task-number: QTBUG-62175 Change-Id: Ib22e9ee68de1d1209fbd4212e72f576bc059d245 Reviewed-by: Simon Hausmann --- src/qml/compiler/qv4compileddata.cpp | 13 ++++++++----- src/qml/jsruntime/qv4engine.cpp | 16 ++++++---------- src/qml/jsruntime/qv4engine_p.h | 2 +- src/qml/jsruntime/qv4regexp.cpp | 9 +++++---- src/qml/jsruntime/qv4regexp_p.h | 13 +++++++++---- src/qml/jsruntime/qv4regexpobject.cpp | 14 +++++--------- src/qml/jsruntime/qv4regexpobject_p.h | 6 +++--- src/qml/jsruntime/qv4runtime.cpp | 7 ++++++- src/qml/jsruntime/qv4stringobject.cpp | 2 +- src/qml/jsruntime/qv4vme_moth.cpp | 7 ++++++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 23 +++++++++++++++++++++++ 11 files changed, 73 insertions(+), 39 deletions(-) diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 9e9a419be7..34491a3a84 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -136,14 +137,16 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value)); for (uint i = 0; i < data->regexpTableSize; ++i) { const CompiledData::RegExp *re = data->regexpAt(i); - int flags = 0; + bool global = false; + bool multiline = false; + bool ignoreCase = false; if (re->flags & CompiledData::RegExp::RegExp_Global) - flags |= IR::RegExp::RegExp_Global; + global = true; if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase) - flags |= IR::RegExp::RegExp_IgnoreCase; + ignoreCase = true; if (re->flags & CompiledData::RegExp::RegExp_Multiline) - flags |= IR::RegExp::RegExp_Multiline; - runtimeRegularExpressions[i] = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); + multiline = true; + runtimeRegularExpressions[i] = QV4::RegExp::create(engine, data->stringAt(re->stringIndex), ignoreCase, multiline, global); } if (data->lookupTableSize) { diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5e776ea09b..a090ac7c47 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -660,21 +660,17 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { bool global = (flags & IR::RegExp::RegExp_Global); - bool ignoreCase = false; - bool multiline = false; - if (flags & IR::RegExp::RegExp_IgnoreCase) - ignoreCase = true; - if (flags & IR::RegExp::RegExp_Multiline) - multiline = true; + bool ignoreCase = (flags & IR::RegExp::RegExp_IgnoreCase); + bool multiline = (flags & IR::RegExp::RegExp_Multiline); Scope scope(this); - Scoped re(scope, RegExp::create(this, pattern, ignoreCase, multiline)); - return newRegExpObject(re, global); + Scoped re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global)); + return newRegExpObject(re); } -Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re, bool global) +Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re) { - return memoryManager->allocObject(re, global); + return memoryManager->allocObject(re); } Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re) diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index d331fd7bff..688a58e345 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -391,7 +391,7 @@ public: Heap::DateObject *newDateObjectFromTime(const QTime &t); Heap::RegExpObject *newRegExpObject(const QString &pattern, int flags); - Heap::RegExpObject *newRegExpObject(RegExp *re, bool global); + Heap::RegExpObject *newRegExpObject(RegExp *re); Heap::RegExpObject *newRegExpObject(const QRegExp &re); Heap::Object *newErrorObject(const Value &value); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 9e94c58432..8cb15b9d7e 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -69,9 +69,9 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets); } -Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline) +Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global) { - RegExpCacheKey key(pattern, ignoreCase, multiline); + RegExpCacheKey key(pattern, ignoreCase, multiline, global); RegExpCache *cache = engine->regExpCache; if (!cache) @@ -82,7 +82,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); Scope scope(engine); - Scoped result(scope, engine->memoryManager->alloc(engine, pattern, ignoreCase, multiline)); + Scoped result(scope, engine->memoryManager->alloc(engine, pattern, ignoreCase, multiline, global)); result->d()->cache = cache; cachedValue.set(engine, result); @@ -90,12 +90,13 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); } -void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline) +void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline, bool global) { Base::init(); this->pattern = new QString(pattern); this->ignoreCase = ignoreCase; this->multiLine = multiline; + this->global = global; const char* error = 0; JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 04cdb468bf..d9b536406c 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -76,7 +76,7 @@ struct RegExpCacheKey; namespace Heap { struct RegExp : Base { - void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline); + void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global); void destroy(); QString *pattern; @@ -88,6 +88,7 @@ struct RegExp : Base { int subPatternCount; bool ignoreCase; bool multiLine; + bool global; int captureCount() const { return subPatternCount + 1; } }; @@ -111,8 +112,9 @@ struct RegExp : public Managed int subPatternCount() const { return d()->subPatternCount; } bool ignoreCase() const { return d()->ignoreCase; } bool multiLine() const { return d()->multiLine; } + bool global() const { return d()->global; } - static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false); + static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false); bool isValid() const { return d()->byteCode; } @@ -127,27 +129,30 @@ struct RegExp : public Managed struct RegExpCacheKey { - RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine) + RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine, bool global) : pattern(pattern) , ignoreCase(ignoreCase) , multiLine(multiLine) + , global(global) { } explicit inline RegExpCacheKey(const RegExp::Data *re); bool operator==(const RegExpCacheKey &other) const - { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine; } + { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine && global == other.global; } bool operator!=(const RegExpCacheKey &other) const { return !operator==(other); } QString pattern; uint ignoreCase : 1; uint multiLine : 1; + uint global : 1; }; inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re) : pattern(*re->pattern) , ignoreCase(re->ignoreCase) , multiLine(re->multiLine) + , global(re->global) {} inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 1f758e36f6..82b0f67075 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -74,15 +74,13 @@ void Heap::RegExpObject::init() Object::init(); Scope scope(internalClass->engine); Scoped o(scope, this); - o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false); - o->d()->global = false; + o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false, false); o->initProperties(); } -void Heap::RegExpObject::init(QV4::RegExp *value, bool global) +void Heap::RegExpObject::init(QV4::RegExp *value) { Object::init(); - this->global = global; this->value = value->d(); Scope scope(internalClass->engine); Scoped o(scope, this); @@ -95,7 +93,6 @@ void Heap::RegExpObject::init(QV4::RegExp *value, bool global) void Heap::RegExpObject::init(const QRegExp &re) { Object::init(); - global = false; // Convert the pattern to a ECMAScript pattern. QString pattern = QT_PREPEND_NAMESPACE(qt_regexp_toCanonical)(re.pattern(), re.patternSyntax()); @@ -246,7 +243,7 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) } Scoped regexp(scope, re->value()); - scope.result = Encode(scope.engine->newRegExpObject(regexp, re->global())); + scope.result = Encode(scope.engine->newRegExpObject(regexp)); return; } @@ -282,13 +279,13 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) } } - Scoped regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine)); + Scoped regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global)); if (!regexp->isValid()) { scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); return; } - scope.result = Encode(scope.engine->newRegExpObject(regexp, global)); + scope.result = Encode(scope.engine->newRegExpObject(regexp)); } void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) @@ -433,7 +430,6 @@ void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, Call Scoped re(scope, scope.result.asReturnedValue()); r->d()->value = re->value(); - r->d()->global = re->global(); RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 54731cef14..af49a1bf5e 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -60,6 +60,7 @@ #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4objectiterator_p.h" +#include "qv4regexp_p.h" #include #include @@ -75,11 +76,10 @@ namespace Heap { struct RegExpObject : Object { void init(); - void init(QV4::RegExp *value, bool global); + void init(QV4::RegExp *value); void init(const QRegExp &re); Pointer value; - bool global; }; struct RegExpCtor : FunctionObject { @@ -117,7 +117,7 @@ struct RegExpObject: Object { }; Heap::RegExp *value() const { return d()->value; } - bool global() const { return d()->global; } + bool global() const { return d()->value->global; } void initProperties(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index ecf2fd8b11..28b344d154 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -51,6 +51,8 @@ #include "qv4lookup_p.h" #include "qv4function_p.h" #include "qv4numberobject_p.h" +#include "qv4regexp_p.h" +#include "qv4regexpobject_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" #include @@ -1477,7 +1479,10 @@ ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine) ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { - return static_cast(engine->current->compilationUnit)->runtimeRegularExpressions[id].asReturnedValue(); + Heap::RegExpObject *ro = engine->newRegExpObject( + static_cast(engine->current->compilationUnit) + ->runtimeRegularExpressions[id].as()); + return ro->asReturnedValue(); } ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 1596f4b0fa..7ccb7a762f 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -542,7 +542,7 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call break; } nMatchOffsets += re->captureCount() * 2; - if (!regExp->d()->global) + if (!regExp->global()) break; offset = qMax(offset + 1, matchOffsets[oldSize + 1]); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 9d65f67f0f..3d95353fc0 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -50,6 +50,8 @@ #include #include #include +#include +#include #include #include @@ -443,7 +445,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) MOTH_BEGIN_INSTR(LoadRegExp) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = static_cast(engine->current->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; + Heap::RegExpObject *ro = engine->newRegExpObject( + static_cast(engine->current->compilationUnit) + ->runtimeRegularExpressions[instr.regExpId].as()); + VALUE(instr.result) = ro; MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 2642d10545..8b815f7a06 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -145,6 +145,7 @@ private slots: void array_join_QTBUG_53672(); void regexpLastMatch(); + void regexpLastIndex(); void indexedAccesses(); void prototypeChainGc(); @@ -3296,6 +3297,28 @@ void tst_QJSEngine::regexpLastMatch() } +void tst_QJSEngine::regexpLastIndex() +{ + QJSEngine eng; + QJSValue result; + result = eng.evaluate("function test(text, rx) {" + " var res;" + " while (res = rx.exec(text)) { " + " return true;" + " }" + " return false;" + " }" + "function tester(text) {" + " return test(text, /,\\s*/g);" + "}"); + QVERIFY(!result.isError()); + + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); + result = eng.evaluate("tester(\", \\n\");"); + QVERIFY(result.toBool()); +} + void tst_QJSEngine::indexedAccesses() { QJSEngine engine; -- cgit v1.2.3 From 0b73daa4f6858aa8d52e6b958d8f0b3209858b80 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 31 Aug 2017 16:53:21 -0500 Subject: Fix issue with programmatic flicking at bounds Ensure that the same flick consistently produces the same results, by making sure we don't use old timestamps from previous flicks. Task-number: QTBUG-62939 Change-Id: Ie738076abba66d38ff505292925e9441c38a3c95 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickflickable.cpp | 1 + .../data/programmaticFlickAtBounds3.qml | 19 ++++++++++++++ .../quick/qquicklistview/tst_qquicklistview.cpp | 30 ++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 tests/auto/quick/qquicklistview/data/programmaticFlickAtBounds3.qml diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 3bd647fed6..c92d370a48 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1773,6 +1773,7 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity) d->vData.reset(); d->hData.velocity = xVelocity; d->vData.velocity = yVelocity; + d->hData.vTime = d->vData.vTime = d->timeline.time(); bool flickedX = d->flickX(xVelocity); bool flickedY = d->flickY(yVelocity); diff --git a/tests/auto/quick/qquicklistview/data/programmaticFlickAtBounds3.qml b/tests/auto/quick/qquicklistview/data/programmaticFlickAtBounds3.qml new file mode 100644 index 0000000000..bd913b2ce1 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/programmaticFlickAtBounds3.qml @@ -0,0 +1,19 @@ +import QtQuick 2.9 + +ListView { + id: view + width: 200; height: 400 + + property real minOvershoot + onVerticalOvershootChanged: if (verticalOvershoot < minOvershoot) minOvershoot = verticalOvershoot + + highlightRangeMode: ListView.StrictlyEnforceRange + preferredHighlightBegin: 0 + preferredHighlightEnd: 0 + + model: 10 + delegate: Rectangle { + width: 200; height: 50 + color: index % 2 ? "red" : "green" + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 2eb87b9431..0d0f234d33 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -236,6 +236,7 @@ private slots: void QTBUG_38209(); void programmaticFlickAtBounds(); void programmaticFlickAtBounds2(); + void programmaticFlickAtBounds3(); void layoutChange(); @@ -8069,6 +8070,35 @@ void tst_QQuickListView::programmaticFlickAtBounds2() QTRY_COMPARE(listview->contentY(), qreal(100.0)); } +void tst_QQuickListView::programmaticFlickAtBounds3() +{ + QScopedPointer window(createView()); + window->setSource(testFileUrl("programmaticFlickAtBounds3.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = qobject_cast(window->rootObject()); + QVERIFY(listview); + + // flick down + listview->flick(0, 2000); + + // verify scope of the movement + QTRY_VERIFY(listview->property("minOvershoot").toReal() < qreal(-50.0)); + + // reset, and test a second time + listview->cancelFlick(); + listview->returnToBounds(); + QTRY_COMPARE(listview->contentY(), qreal(0.0)); + listview->setProperty("minOvershoot", qreal(0.0)); + + // flick down + listview->flick(0, 2000); + + // verify scope of the movement is the same + QTRY_VERIFY(listview->property("minOvershoot").toReal() < qreal(-50.0)); +} + void tst_QQuickListView::layoutChange() { RandomSortModel *model = new RandomSortModel; -- cgit v1.2.3 From 38ef59b386e5eb5063b9572d2dfc1a7d59624ef3 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 8 Sep 2017 15:20:02 +0200 Subject: Drain the mark stack while collecting roots This avoids overflows in the markStack for test cases where we have a huge amount of compilation units with many runtime strings that all want to get marked. Task-number: QTBUG-63063 Change-Id: I150c1f1a4065350cec59dd80c5c628920f70e3d0 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4engine.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a090ac7c47..d571337ecf 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -927,8 +927,18 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } +static void drainMarkStack(ExecutionEngine *engine, Value *markBase) +{ + while (engine->jsStackTop > markBase) { + Heap::Base *h = engine->popForGC(); + Q_ASSERT (h->vtable()->markObjects); + h->vtable()->markObjects(h, engine); + } +} + void ExecutionEngine::markObjects() { + Value *markBase = jsStackTop; identifierTable->mark(this); for (int i = 0; i < nArgumentsAccessors; ++i) { @@ -941,9 +951,13 @@ void ExecutionEngine::markObjects() classPool->markObjects(this); + drainMarkStack(this, markBase); + for (QSet::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); - it != end; ++it) + it != end; ++it) { (*it)->markObjects(this); + drainMarkStack(this, markBase); + } } ReturnedValue ExecutionEngine::throwError(const Value &value) -- cgit v1.2.3 From 71d32b4195b81c6786d8fef85960628b77ff6860 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 8 Sep 2017 13:42:24 +0200 Subject: Fix compilation with clang 4 on Linux Change-Id: I3f978d9638ce3f47dff0adfb5fccdc0b4816a690 Reviewed-by: Simon Hausmann --- src/quick/scenegraph/qsgrenderloop.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 2c73339b7a..df41fa8369 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -140,25 +140,25 @@ public: QSGGuiThreadRenderLoop(); ~QSGGuiThreadRenderLoop(); - void show(QQuickWindow *window); - void hide(QQuickWindow *window); + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; - void windowDestroyed(QQuickWindow *window); + void windowDestroyed(QQuickWindow *window) override; void renderWindow(QQuickWindow *window); - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); + void exposureChanged(QQuickWindow *window) override; + QImage grab(QQuickWindow *window) override; - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. - void handleUpdateRequest(QQuickWindow *); + void maybeUpdate(QQuickWindow *window) override; + void update(QQuickWindow *window) override{ maybeUpdate(window); } // identical for this implementation. + void handleUpdateRequest(QQuickWindow *) override; - void releaseResources(QQuickWindow *) Q_DECL_OVERRIDE; + void releaseResources(QQuickWindow *) override; - QAnimationDriver *animationDriver() const { return 0; } + QAnimationDriver *animationDriver() const override { return 0; } - QSGContext *sceneGraphContext() const; - QSGRenderContext *createRenderContext(QSGContext *) const { return rc; } + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; } struct WindowData { bool updatePending : 1; -- cgit v1.2.3 From 9fd39f34f7512d199a1f68dcff4f50e469897e31 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 11 Sep 2017 13:49:51 +0200 Subject: Update the expected size of QV4::CompiledData::Unit ebda8170a removed a member. Thanks to the padding, the size of the structure remained the same on 64-bit, but not on 32-bit. Removing the padding gives now the same size on both. Task-number: QTBUG-63109 Change-Id: If87ad21a1c94e63643b0cd52f95e244364f6e73d Reviewed-by: Lars Knoll --- src/qml/compiler/qv4compileddata_p.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 6e6d641d94..84777b3533 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -667,8 +667,6 @@ struct Unit LEUInt32 nObjects; LEUInt32 offsetToObjects; - LEUInt32 padding; - const Import *importAt(int idx) const { return reinterpret_cast((reinterpret_cast(this)) + offsetToImports + idx * sizeof(Import)); } @@ -733,7 +731,7 @@ struct Unit } }; -static_assert(sizeof(Unit) == 152, "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) == 144, "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 { -- cgit v1.2.3 From a763d6a88be732de4beb3c28dbe2cde7a063c83a Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Thu, 7 Sep 2017 22:20:14 -0700 Subject: Add missing math.h include for math functions On some platforms, math functions in the std namespace don't work even if cmath is included. Change-Id: Ia71d22b07f508e0584de5320f376fbf4b3a2887b Reviewed-by: Thiago Macieira --- src/qml/jsruntime/qv4mathobject.cpp | 1 + src/quick/items/qquickflickable.cpp | 1 + tests/auto/quick/qquickgridview/tst_qquickgridview.cpp | 2 ++ tests/auto/quick/qquickpathview/tst_qquickpathview.cpp | 2 ++ 4 files changed, 6 insertions(+) diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 2d9d81c64b..109d6c38b5 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -45,6 +45,7 @@ #include #include +#include #include using namespace QV4; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index c92d370a48..c75a682f4a 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -55,6 +55,7 @@ #include #include "qplatformdefs.h" +#include #include QT_BEGIN_NAMESPACE diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index b2d6584701..2b14842658 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -46,6 +46,8 @@ #include #include "qplatformdefs.h" +#include + Q_DECLARE_METATYPE(QQuickGridView::Flow) Q_DECLARE_METATYPE(Qt::LayoutDirection) Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection) diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index b01d0c3cec..cbef0fcc8d 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -48,6 +48,8 @@ #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" +#include + using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; -- cgit v1.2.3 From 78d574a909a4bcf7543fba8d921e8658dfbc0200 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Sep 2017 10:14:52 +0200 Subject: Fix Integrity build Disambiguate the QV4::Value parameter. Task-number: QTBUG-63135 Change-Id: Iae6bd209876336d58256aa94f89d146cadc62f08 Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index d571337ecf..54011e6bd2 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -927,7 +927,7 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } -static void drainMarkStack(ExecutionEngine *engine, Value *markBase) +static void drainMarkStack(ExecutionEngine *engine, QV4::Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); -- cgit v1.2.3 From 98358715930739ca8de172d88c5ce6941c275ff3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Sep 2017 15:13:33 +0200 Subject: Fix qml cache invalidation when changing dependent C++ registered QML singletons When a qml file uses a qml singleton, we need to reliably detect when the singleton changes and re-generate the cache of the qml file using it. This is a scenario covered and fixed by commit 5b94de09cc738837d1539e28b3c0dccd17c18d29, with the exception that currently QML singletons registered via qmlRegisterSingleton were not added to the list of dependent singletons for a qml file. We can fix this by extending findCompositeSingletons() to also cover the singletons that do not originate from a qmldir file. [ChangeLog][Qt][Qml] Fixed bug where sometimes changes to a qml singleton would not propagate to the users or cause crashes. Task-number: QTBUG-62243 Change-Id: I16c3d9ba65fd82e898a29b946c341907751135a9 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlimport.cpp | 11 +++++ src/qml/qml/qqmlmetatype.cpp | 12 +++++ src/qml/qml/qqmlmetatype_p.h | 2 + tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp | 61 ++++++++++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 0bd7317470..ccd287e1b6 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -471,6 +471,17 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QListuri, import->majversion)) { + module->walkCompositeSingletons([&resultList, &set](const QQmlType &singleton) { + QQmlImports::CompositeSingletonReference ref; + ref.typeName = singleton.elementName(); + ref.prefix = set.prefix; + ref.majorVersion = singleton.majorVersion(); + ref.minorVersion = singleton.minorVersion(); + resultList.append(ref); + }); + } } } diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 5bbc250d5e..8f5d11a96f 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1258,6 +1258,18 @@ QQmlType QQmlTypeModule::type(const QV4::String *name, int minor) const return QQmlType(); } +void QQmlTypeModule::walkCompositeSingletons(const std::function &callback) const +{ + QMutexLocker lock(metaTypeDataLock()); + for (auto typeCandidates = d->typeHash.begin(), end = d->typeHash.end(); + typeCandidates != end; ++typeCandidates) { + for (auto type: typeCandidates.value()) { + if (type->regType == QQmlType::CompositeSingletonType) + callback(QQmlType(type)); + } + } +} + QQmlTypeModuleVersion::QQmlTypeModuleVersion() : m_module(0), m_minor(0) { diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index ac2133ba30..9a7736ffcd 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -298,6 +298,8 @@ public: QQmlType type(const QHashedStringRef &, int) const; QQmlType type(const QV4::String *, int) const; + void walkCompositeSingletons(const std::function &callback) const; + QQmlTypeModulePrivate *priv() { return d; } private: //Used by register functions and creates the QQmlTypeModule for them diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 6ab84774f2..e75e51ed29 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -59,6 +59,7 @@ private slots: void cacheResources(); void stableOrderOfDependentCompositeTypes(); void singletonDependency(); + void cppRegisteredSingletonDependency(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -790,6 +791,66 @@ void tst_qmldiskcache::singletonDependency() } } +void tst_qmldiskcache::cppRegisteredSingletonDependency() +{ + qmlClearTypeRegistrations(); + QScopedPointer engine(new QQmlEngine); + + 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(); + }; + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }"); + + qmlRegisterSingletonType(QUrl::fromLocalFile(tempDir.path() + QLatin1String("/MySingleton.qml")), "CppRegisteredSingletonDependency", 1, 0, "Singly"); + + const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport CppRegisteredSingletonDependency 1.0\nQtObject {\n" + " function getValue() { return Singly.value; }\n" + "}"); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 42); + } + + const QString testFileCachePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + + engine.reset(new QQmlEngine); + waitForFileSystem(); + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }"); + waitForFileSystem(); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer obj(component.create()); + QVERIFY(!obj.isNull()); + + { + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString())); + } + + QVariant value; + QVERIFY(QMetaObject::invokeMethod(obj.data(), "getValue", Q_RETURN_ARG(QVariant, value))); + QCOMPARE(value.toInt(), 100); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" -- cgit v1.2.3 From fa00f0a0206f87b43cd1ee5448efe20cb6ff8e44 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Sep 2017 17:20:47 +0200 Subject: Error out when compiling signal handlers with arguments in qml files Ahead of time we cannot tell whether the use of "arguments" in a signal hander refers to the JS arguments object or a potential arguments signal parameter. Resolving that requires access to information we currently don't have. The QML engine has it at run-time (in SignalHandlerConverter) and that's why it works there accordingly. However when generating caches ahead of time, let's rather produce an error message with a hint how to work around it instead of producing differing behavior at run-time. Task-number: QTBUG-60011 Change-Id: I9e460bd467dbb5998f12a44c439223ea44e7bbad Reviewed-by: Lars Knoll --- tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp | 35 +++++++++++++++++++++++-- tools/qmlcachegen/qmlcachegen.cpp | 36 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp index b7e616a050..b69071dd59 100644 --- a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -43,6 +43,7 @@ private slots: void loadGeneratedFile(); void translationExpressionSupport(); + void errorOnArgumentsInSignalHandler(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -67,15 +68,20 @@ public: } }; -static bool generateCache(const QString &qmlFileName) +static bool generateCache(const QString &qmlFileName, QByteArray *capturedStderr = nullptr) { QProcess proc; - proc.setProcessChannelMode(QProcess::ForwardedChannels); + if (capturedStderr == nullptr) + proc.setProcessChannelMode(QProcess::ForwardedChannels); proc.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QLatin1String("qmlcachegen")); proc.setArguments(QStringList() << (QLatin1String("--target-architecture=") + QSysInfo::buildCpuArchitecture()) << (QLatin1String("--target-abi=") + QSysInfo::buildAbi()) << qmlFileName); proc.start(); if (!proc.waitForFinished()) return false; + + if (capturedStderr) + *capturedStderr = proc.readAllStandardError(); + if (proc.exitStatus() != QProcess::NormalExit) return false; return proc.exitCode() == 0; @@ -158,6 +164,31 @@ void tst_qmlcachegen::translationExpressionSupport() QCOMPARE(obj->property("text").toString(), QString("All Ok")); } +void tst_qmlcachegen::errorOnArgumentsInSignalHandler() +{ + 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 QtQml 2.2\n" + "QtObject {\n" + " signal mySignal(var arguments);\n" + " onMySignal: console.log(arguments);\n" + "}"); + + + QByteArray errorOutput; + QVERIFY(!generateCache(testFilePath, &errorOutput)); + QVERIFY2(errorOutput.contains("error: The use of the arguments object in signal handlers is"), errorOutput); +} + QTEST_GUILESS_MAIN(tst_qmlcachegen) #include "tst_qmlcachegen.moc" diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index b201176d5e..a62adc82f4 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -114,6 +114,37 @@ static void annotateListElements(QmlIR::Document *document) } } +static bool checkArgumentsObjectUseInSignalHandlers(const QmlIR::Document &doc, Error *error) +{ + for (QmlIR::Object *object: qAsConst(doc.objects)) { + for (auto binding = object->bindingsBegin(); binding != object->bindingsEnd(); ++binding) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + const QString propName = doc.stringAt(binding->propertyNameIndex); + if (!propName.startsWith(QLatin1String("on")) + || propName.length() < 3 + || !propName.at(2).isUpper()) + continue; + auto compiledFunction = doc.jsModule.functions.value(object->runtimeFunctionIndices.at(binding->value.compiledScriptIndex)); + if (!compiledFunction) + continue; + if (compiledFunction->usesArgumentsObject) { + error->message = QLatin1Char(':') + QString::number(compiledFunction->line) + QLatin1Char(':'); + 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."); + return false; + } + } + } + return true; +} + static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); @@ -173,6 +204,11 @@ static bool compileQmlFile(const QString &inputFileName, const QString &outputFi object->runtimeFunctionIndices.allocate(pool, runtimeFunctionIndices); } + if (!checkArgumentsObjectUseInSignalHandlers(irDocument, error)) { + *error = error->augment(inputFileName); + return false; + } + QmlIR::QmlUnitGenerator generator; { -- cgit v1.2.3 From bb02a577f0fbf3bd2a273129e6cb57cfc9a338c8 Mon Sep 17 00:00:00 2001 From: Pal Toth Date: Tue, 12 Sep 2017 12:49:40 +0300 Subject: Add NOTIFY signal for QQuickAnimatedImage::frameCount Task-number: QTBUG-62913 Change-Id: Ib561e0ab6582c1df41ae1c75ba304377c00d63f0 Reviewed-by: Shawn Rutledge --- src/quick/items/qquickanimatedimage.cpp | 22 ++++++++++++++++------ src/quick/items/qquickanimatedimage_p.h | 3 ++- src/quick/items/qquickanimatedimage_p_p.h | 1 + .../tst_qquickanimatedimage.cpp | 10 ++++++++++ 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index a30d71dd1e..5bc5b0faff 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -279,8 +279,7 @@ void QQuickAnimatedImage::setSource(const QUrl &url) d->oldPlaying = isPlaying(); if (d->_movie) { - delete d->_movie; - d->_movie = 0; + d->setMovie(nullptr); } d->url = url; @@ -320,7 +319,7 @@ void QQuickAnimatedImage::load() QString lf = QQmlFile::urlToLocalFileOrQrc(loadUrl); if (!lf.isEmpty()) { - d->_movie = new QMovie(lf); + d->setMovie(new QMovie(lf)); movieRequestFinished(); } else { #if QT_CONFIG(qml_network) @@ -366,14 +365,13 @@ void QQuickAnimatedImage::movieRequestFinished() } d->redirectCount=0; - d->_movie = new QMovie(d->reply); + d->setMovie(new QMovie(d->reply)); } #endif if (!d->_movie || !d->_movie->isValid()) { qmlWarning(this) << "Error Reading Animated Image File " << d->url.toString(); - delete d->_movie; - d->_movie = 0; + d->setMovie(nullptr); d->setImage(QImage()); if (d->progress != 0) { d->progress = 0; @@ -490,6 +488,18 @@ void QQuickAnimatedImage::componentComplete() load(); } +void QQuickAnimatedImagePrivate::setMovie(QMovie *movie) +{ + Q_Q(QQuickAnimatedImage); + const int oldFrameCount = q->frameCount(); + + delete _movie; + _movie = movie; + + if (oldFrameCount != q->frameCount()) + emit q->frameCountChanged(); +} + QT_END_NAMESPACE #include "moc_qquickanimatedimage_p.cpp" diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h index 143fe8904d..d0d8ec7c5c 100644 --- a/src/quick/items/qquickanimatedimage_p.h +++ b/src/quick/items/qquickanimatedimage_p.h @@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQuickAnimatedImage : public QQuickImage Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged) Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged) - Q_PROPERTY(int frameCount READ frameCount) + Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged) // read-only for AnimatedImage Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged) @@ -98,6 +98,7 @@ Q_SIGNALS: void pausedChanged(); void frameChanged(); void sourceSizeChanged(); + void frameCountChanged(); private Q_SLOTS: void movieUpdate(); diff --git a/src/quick/items/qquickanimatedimage_p_p.h b/src/quick/items/qquickanimatedimage_p_p.h index 9eff6a44e3..68c4f2d359 100644 --- a/src/quick/items/qquickanimatedimage_p_p.h +++ b/src/quick/items/qquickanimatedimage_p_p.h @@ -91,6 +91,7 @@ public: #endif QMap frameMap; QSize currentSourceSize; + void setMovie(QMovie *movie); }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index 34b9fb6b07..e303495944 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -132,6 +132,16 @@ void tst_qquickanimatedimage::frameCount() QVERIFY(anim->isPlaying()); QCOMPARE(anim->frameCount(), 3); + QSignalSpy frameCountChangedSpy(anim, &QQuickAnimatedImage::frameCountChanged); + + const QUrl origSource = anim->source(); + anim->setSource(QUrl()); + QCOMPARE(anim->frameCount(), 0); + QCOMPARE(frameCountChangedSpy.count(), 1); + anim->setSource(origSource); + QCOMPARE(anim->frameCount(), 3); + QCOMPARE(frameCountChangedSpy.count(), 2); + delete anim; } -- cgit v1.2.3 From 3604cae410f29597c97ba73df6d31a9b54e6d30d Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 12 Sep 2017 17:12:18 +0200 Subject: QQuickTextEdit: call implicitWidth() even if requireImplicitWidth is true Before once requireImplicitWidth was set to true by requesting the implicit width, the implicit (and width) was never updated again, even if the text was updated/changed. Adding also a test Task-number: QTBUG-63153 Change-Id: Ie3bac4baeb14c2e69acc43d11a351ac91d5400da Reviewed-by: Shawn Rutledge Reviewed-by: J-P Nurmi Reviewed-by: Alessandro Portale Reviewed-by: Thomas Hartmann --- src/quick/items/qquicktextedit.cpp | 2 +- tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 61d610520f..3714af2bc3 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2480,7 +2480,7 @@ void QQuickTextEdit::updateSize() if (d->isImplicitResizeEnabled()) { // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed. - if (!widthValid() && !d->requireImplicitWidth) + if (!widthValid()) setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding()); else setImplicitHeight(newHeight + topPadding() + bottomPadding()); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index ac57a05176..d58fc6c389 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -158,6 +158,7 @@ private slots: #endif void implicitSize_data(); void implicitSize(); + void implicitSize_QTBUG_63153(); void contentSize(); void boundingRect(); void clipRect(); @@ -3388,6 +3389,18 @@ void tst_qquicktextedit::implicitSize() QCOMPARE(textObject->height(), textObject->implicitHeight()); } +void tst_qquicktextedit::implicitSize_QTBUG_63153() +{ + QString componentStr = "import QtQuick 2.0\nTextEdit { }"; + QQmlComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QQuickTextEdit *textObject = qobject_cast(textComponent.create()); + textObject->setText("short"); + qreal shortImplicitWidth = textObject->implicitWidth(); + textObject->setText("in contrast to short this is long"); + QVERIFY2(shortImplicitWidth < textObject->implicitWidth(), qPrintable(QString("%1 < %2").arg(textObject->implicitWidth()).arg(shortImplicitWidth))); +} + void tst_qquicktextedit::contentSize() { QString componentStr = "import QtQuick 2.0\nTextEdit { width: 75; height: 16; font.pixelSize: 10 }"; -- cgit v1.2.3 From e29ffa179e9920443a23e2fcb3f0694df32e8a68 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 14 Sep 2017 13:47:14 +0200 Subject: Fix use-after-free when removing elements from a ListModel Detaching delegate instances from model items is done after the destruction of said model items. The problem is that after the model item is destroyed, it will emit a change/destroyed signal. As the delegate is still referencing the item, this will result in a use-after-free. To provent that, the items are kept around until after everyone (notably the delegate model) has been notified of the removal. [ChangeLog][Qt][Qml] Fix possible use-after-free when removing items from a ListModel through JavaScript. Task-number: QTBUG-59256 Change-Id: Iee182e2cf0b50d3dda2181fed95e38f1a60f22a9 Reviewed-by: Simon Hausmann --- src/qml/types/qqmldelegatemodel.cpp | 7 +++++++ src/qml/types/qqmllistmodel.cpp | 25 +++++++++++++++++++------ src/qml/types/qqmllistmodel_p_p.h | 2 +- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 9b1417eccd..26e6a81418 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1266,6 +1266,13 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count) d->emitChanges(); } +//### This method should be split in two. It will remove delegates, and it will re-render the list. +// When e.g. QQmlListModel::remove is called, the removal of the delegates should be done on +// QAbstractItemModel::rowsAboutToBeRemoved, and the re-rendering on +// QAbstractItemModel::rowsRemoved. Currently both are done on the latter signal. The problem is +// that the destruction of an item will emit a changed signal that ends up at the delegate, which +// in turn will try to load the data from the model (which should have already freed it), resulting +// in a use-after-free. See QTBUG-59256. void QQmlDelegateModelPrivate::itemsRemoved( const QVector &removes, QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 9d0f1afb32..b33be2b4fb 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -567,14 +567,20 @@ void ListModel::clear() elements.clear(); } -void ListModel::remove(int index, int count) +QVector> ListModel::remove(int index, int count) { + QVector> toDestroy; + auto layout = m_layout; for (int i=0 ; i < count ; ++i) { - elements[index+i]->destroy(m_layout); - delete elements[index+i]; + auto element = elements[index+i]; + toDestroy.append([element, layout](){ + element->destroy(layout); + delete element; + }); } elements.remove(index, count); updateCacheIndices(); + return toDestroy; } void ListModel::insert(int elementIndex, QV4::Object *object) @@ -2053,15 +2059,22 @@ void QQmlListModel::remove(QQmlV4Function *args) emitItemsAboutToBeRemoved(index, removeCount); + QVector> toDestroy; if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) - delete m_modelObjects[index+i]; + for (int i=0 ; i < removeCount ; ++i) { + auto modelObject = m_modelObjects[index+i]; + toDestroy.append([modelObject](){ + delete modelObject; + }); + } m_modelObjects.remove(index, removeCount); } else { - m_listModel->remove(index, removeCount); + toDestroy = m_listModel->remove(index, removeCount); } emitItemsRemoved(index, removeCount); + for (const auto &destroyer : toDestroy) + destroyer(); } else { qmlWarning(this) << tr("remove: incorrect number of arguments"); } diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index cdce78e542..4928ad3725 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -367,7 +367,7 @@ public: void insert(int elementIndex, QV4::Object *object); void clear(); - void remove(int index, int count); + Q_REQUIRED_RESULT QVector> remove(int index, int count); int appendElement(); void insertElement(int index); -- cgit v1.2.3 From bfab1db38167d31f0487f96d2d4b02d9243bc777 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 15 Sep 2017 13:56:36 +0200 Subject: Fall back to the ObjectWrapper for model advanceIterator When falling back to the QObjectWrapper it will add in the extra parts added when the roles were added to the object created by the model to hold the data being returned. This was causing the last entry to be duplicated and causing extra work too. Task-number: QTBUG-54285 Task-number: QTBUG-62156 Change-Id: I2907477277df8d16db4491a4999f004433e4205c Reviewed-by: Simon Hausmann --- src/qml/types/qqmllistmodel.cpp | 5 ++++- tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 23 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index b33be2b4fb..35ee30dccf 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1394,7 +1394,10 @@ void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, u p->value = v4->fromVariant(value); return; } - QV4::QObjectWrapper::advanceIterator(m, it, name, index, p, attributes); + // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add + // unnecessary entries that relate to the roles used. These just create extra work + // later on as they will just be ignored. + QV4::Object::advanceIterator(m, it, name, index, p, attributes); } DEFINE_OBJECT_VTABLE(ModelObject); diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 555ca5713e..f5c0e5ddf7 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -123,6 +123,7 @@ private slots: void about_to_be_signals(); void modify_through_delegate(); void bindingsOnGetResult(); + void stringifyModelEntry(); }; bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) @@ -1482,6 +1483,28 @@ void tst_qqmllistmodel::bindingsOnGetResult() QVERIFY(obj->property("success").toBool()); } +void tst_qqmllistmodel::stringifyModelEntry() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "Item {\n" + " ListModel {\n" + " id: testModel\n" + " objectName: \"testModel\"\n" + " ListElement { name: \"Joe\"; age: 22 }\n" + " }\n" + "}\n", QUrl()); + QScopedPointer scene(component.create()); + QQmlListModel *model = scene->findChild("testModel"); + QQmlExpression expr(engine.rootContext(), model, "JSON.stringify(get(0));"); + QVariant v = expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + const QString expectedString = QStringLiteral("{\"age\":22,\"name\":\"Joe\"}"); + QCOMPARE(v.toString(), expectedString); +} + QTEST_MAIN(tst_qqmllistmodel) #include "tst_qqmllistmodel.moc" -- cgit v1.2.3 From 44ae9cdb95b14813d339c7bbd00df608261c8b9d Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Thu, 14 Sep 2017 19:23:36 +0200 Subject: Fix compilation with -no-opengl Change-Id: I91aab9d78ff4dced55cb118ea8f88994bd1d2c20 Reviewed-by: Shawn Rutledge --- src/quick/items/context2d/qquickcontext2dtexture.cpp | 5 +++-- src/quick/scenegraph/coreapi/qsgrenderer.cpp | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 6136feb4ea..a8bf14ba9f 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -54,11 +54,12 @@ #include QT_BEGIN_NAMESPACE -#if QT_CONFIG(opengl) -#define QT_MINIMUM_FBO_SIZE 64 Q_LOGGING_CATEGORY(lcCanvas, "qt.quick.canvas") +#if QT_CONFIG(opengl) +#define QT_MINIMUM_FBO_SIZE 64 + static inline int qt_next_power_of_two(int v) { v--; diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 0458e2dead..67bd265801 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -50,7 +50,9 @@ QT_BEGIN_NAMESPACE +#if QT_CONFIG(opengl) static const bool qsg_sanity_check = qEnvironmentVariableIntValue("QSG_SANITY_CHECK"); +#endif static QElapsedTimer frameTimer; static qint64 preprocessTime; -- cgit v1.2.3 From 71a18fd64c93afa00f4d89de2ba47663f41eba2c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 18 Sep 2017 16:26:17 +0200 Subject: Fix qmlClearTypeRegistrations() not dropping all registrations In commit 48c09a85ce397979c7e706e3694c879ffe456e09 we added the undeletableTypes container to hold a reference on C++ registered types to keep the indices returned by the public qmlRegisterType() API stable. Since qmlClearTypeRegistrations() is API that also resets those indices, we must also clear the undeletableTypes container to avoid leaking memory. Change-Id: I2038c00913f894d58aca3714d64d497493585326 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlmetatype.cpp | 8 +++ src/qml/qml/qqmlmetatype_p.h | 1 + .../qml/qqmlenginecleanup/qqmlenginecleanup.pro | 2 +- .../qqmlenginecleanup/tst_qqmlenginecleanup.cpp | 59 +++++++++++++++++++--- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 8f5d11a96f..ac670bdabb 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1144,6 +1144,13 @@ void QQmlType::derefHandle(QQmlTypePrivate *priv) delete priv; } +int QQmlType::refCount(QQmlTypePrivate *priv) +{ + if (priv) + return priv->refCount; + return -1; +} + namespace { template void removeQQmlTypePrivate(QQmlTypeContainer &container, const QQmlTypePrivate *reference) @@ -1334,6 +1341,7 @@ void qmlClearTypeRegistrations() // Declared in qqml.h data->urlToNonFileImportType.clear(); data->metaObjectToType.clear(); data->uriToModule.clear(); + data->undeletableTypes.clear(); QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types #if QT_CONFIG(library) diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 9a7736ffcd..74b1cf0e06 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -245,6 +245,7 @@ public: QQmlTypePrivate *priv() const { return d; } static void refHandle(QQmlTypePrivate *priv); static void derefHandle(QQmlTypePrivate *priv); + static int refCount(QQmlTypePrivate *priv); enum RegistrationType { CppType = 0, diff --git a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro index 5bcec9f5b4..90508609a8 100644 --- a/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro +++ b/tests/auto/qml/qqmlenginecleanup/qqmlenginecleanup.pro @@ -6,4 +6,4 @@ include (../../shared/util.pri) SOURCES += tst_qqmlenginecleanup.cpp -QT += testlib qml +QT += testlib qml qml-private diff --git a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp index d0a8b6401f..7e9a1524b0 100644 --- a/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp +++ b/tests/auto/qml/qqmlenginecleanup/tst_qqmlenginecleanup.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include //Separate test, because if engine cleanup attempts fail they can easily break unrelated tests class tst_qqmlenginecleanup : public QQmlDataTest @@ -44,41 +46,82 @@ private slots: void test_valueTypeProviderModule(); // QTBUG-43004 }; +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + void tst_qqmlenginecleanup::test_qmlClearTypeRegistrations() { //Test for preventing memory leaks is in tests/manual/qmltypememory QQmlEngine* engine; - QQmlComponent* component; + CleanlyLoadingComponent* component; QUrl testFile = testFileUrl("types.qml"); + const auto qmlTypeForTestType = []() { + return QQmlMetaType::qmlType(QStringLiteral("TestTypeCpp"), QStringLiteral("Test"), 2, 0); + }; + + QVERIFY(!qmlTypeForTestType().isValid()); qmlRegisterType("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); + engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; - qmlClearTypeRegistrations(); + delete engine; + + { + auto cppType = qmlTypeForTestType(); + + qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); + + // cppType should hold the last ref, qmlClearTypeRegistration should have wiped + // all internal references. + QCOMPARE(QQmlType::refCount(cppType.priv()), 1); + } //2nd run verifies that types can reload after a qmlClearTypeRegistrations qmlRegisterType("Test", 2, 0, "TestTypeCpp"); + QVERIFY(qmlTypeForTestType().isValid()); engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isReady()); - delete engine; delete component; + delete engine; qmlClearTypeRegistrations(); + QVERIFY(!qmlTypeForTestType().isValid()); //3nd run verifies that TestTypeCpp is no longer registered engine = new QQmlEngine; - component = new QQmlComponent(engine, testFile); + component = new CleanlyLoadingComponent(engine, testFile); QVERIFY(component->isError()); QCOMPARE(component->errorString(), testFile.toString() +":33 module \"Test\" is not installed\n"); - delete engine; delete component; + delete engine; } static void cleanState(QQmlEngine **e) -- cgit v1.2.3 From 61716e2bbcc62d7447b4d9e8531ad98737407d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Thu, 7 Sep 2017 12:29:36 +0200 Subject: qmlimportscanner: Scan the root directory again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “qmlimportscanner -rootPath /path/to/foo” should scan QML files in the “foo” directory. Remove QDir::NoDot, which was added in commit 6ff0e9a6. Change-Id: I15cc4a289cf246786cdf8fe2020c7f3d2798b7a5 Reviewed-by: Andy Shaw --- tools/qmlimportscanner/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 352d385cfe..37d244c5e5 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -404,7 +404,7 @@ QVariantList findQmlImportsInDirectory(const QString &qmlDir) if (qmlDir.isEmpty()) return ret; - QDirIterator iterator(qmlDir, QDir::AllDirs | QDir::NoDot | QDir::NoDotDot, QDirIterator::Subdirectories); + QDirIterator iterator(qmlDir, QDir::AllDirs | QDir::NoDotDot, QDirIterator::Subdirectories); QStringList blacklist; while (iterator.hasNext()) { -- cgit v1.2.3