diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-06-14 01:01:31 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-06-14 09:50:21 +0200 |
commit | 82ca44ca1a4abb2bcfee5f8ff41df72d02cf4491 (patch) | |
tree | 1795638fbf3ad02c16a69dd69efb8738976f7a07 | |
parent | 6d5a0a55d862c78b185a077171a0b46a94d5a599 (diff) | |
parent | 15b90204fa5126f339db551cf7429534f1359322 (diff) |
Merge remote-tracking branch 'origin/5.13' into dev
Conflicts:
tests/auto/qml/qjsengine/tst_qjsengine.cpp
Change-Id: I34df194046a91ee8a076ce28022eb99d68e7f362
-rw-r--r-- | src/qml/jit/qv4baselinejit.cpp | 1 | ||||
-rw-r--r-- | src/qml/jsapi/qjsvalue_p.h | 21 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4memberdata.cpp | 5 | ||||
-rw-r--r-- | src/qmlmodels/qqmldelegatecomponent.cpp | 36 | ||||
-rw-r--r-- | src/qmlmodels/qqmldelegatemodel.cpp | 17 | ||||
-rw-r--r-- | src/quick/items/qquickshadereffect.cpp | 2 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 18 | ||||
-rw-r--r-- | tests/auto/qml/qjsvalue/qjsvalue.pro | 2 | ||||
-rw-r--r-- | tests/auto/qml/qjsvalue/tst_qjsvalue.cpp | 37 | ||||
-rw-r--r-- | tests/auto/qml/qjsvalue/tst_qjsvalue.h | 4 |
10 files changed, 133 insertions, 10 deletions
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 9dd2cdc43b..906cc30e67 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -897,7 +897,6 @@ void BaselineJIT::generate_ThrowOnNullOrUndefined() void BaselineJIT::generate_GetTemplateObject(int index) { - STORE_ACC(); as->prepareCallWithArgCount(2); as->passInt32AsArg(index, 1); as->passFunctionAsArg(0); diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index bcf0a9d12d..2faffffbae 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -60,6 +60,8 @@ #include <private/qv4mm_p.h> #include <private/qv4persistent_p.h> +#include <QtCore/qthread.h> + QT_BEGIN_NAMESPACE class Q_AUTOTEST_EXPORT QJSValuePrivate @@ -79,6 +81,11 @@ public: return nullptr; } + static inline void setRawValue(QJSValue *jsval, QV4::Value *v) + { + jsval->d = reinterpret_cast<quintptr>(v); + } + static inline void setVariant(QJSValue *jsval, const QVariant &v) { QVariant *val = new QVariant(v); jsval->d = reinterpret_cast<quintptr>(val) | 1; @@ -169,10 +176,20 @@ public: } static inline void free(QJSValue *jsval) { - if (QV4::Value *v = QJSValuePrivate::getValue(jsval)) + if (QV4::Value *v = QJSValuePrivate::getValue(jsval)) { + if (QV4::ExecutionEngine *e = engine(jsval)) { + if (QJSEngine *jsEngine = e->jsEngine()) { + if (jsEngine->thread() != QThread::currentThread()) { + QMetaObject::invokeMethod( + jsEngine, [v](){ QV4::PersistentValueStorage::free(v); }); + return; + } + } + } QV4::PersistentValueStorage::free(v); - else if (QVariant *v = QJSValuePrivate::getVariant(jsval)) + } else if (QVariant *v = QJSValuePrivate::getVariant(jsval)) { delete v; + } } }; diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index ffebe1b5da..34b0c38ae6 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -72,8 +72,9 @@ Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberD // The above code can overflow in a number of interesting ways. All of those are unsigned, // and therefore defined behavior. Still, apply some sane bounds. - if (alloc > size_t(std::numeric_limits<int>::max())) - alloc = size_t(std::numeric_limits<int>::max()); + const size_t intMax = std::numeric_limits<int>::max(); + if (alloc > intMax) + alloc = intMax; Heap::MemberData *m; if (old) { diff --git a/src/qmlmodels/qqmldelegatecomponent.cpp b/src/qmlmodels/qqmldelegatecomponent.cpp index a7e9536917..ccb0d60053 100644 --- a/src/qmlmodels/qqmldelegatecomponent.cpp +++ b/src/qmlmodels/qqmldelegatecomponent.cpp @@ -199,11 +199,45 @@ bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required by a view and used as a delegate. DelegateChooser encapsulates a set of \l {DelegateChoice}s. - These choices are used determine the delegate that will be instantiated for each + These choices are used to determine the delegate that will be instantiated for each item in the model. The selection of the choice is performed based on the value that a model item has for \l role, and also based on index. + DelegateChooser is commonly used when a view needs to display a set of delegates that are significantly + different from each other. For example, a typical phone settings view might include toggle switches, + sliders, radio buttons, and other visualizations based on the type of each setting. In this case, DelegateChooser + could provide an easy way to associate a different type of delegate with each setting: + + \qml \QtMinorVersion + import QtQuick 2.\1 + import QtQuick.Controls 2.\1 + import Qt.labs.qmlmodels 1.0 + + ListView { + width: 200; height: 400 + + ListModel { + id: listModel + ListElement { type: "info"; ... } + ListElement { type: "switch"; ... } + ListElement { type: "swipe"; ... } + ListElement { type: "switch"; ... } + } + + DelegateChooser { + id: chooser + role: "type" + DelegateChoice { roleValue: "info"; ItemDelegate { ... } } + DelegateChoice { roleValue: "switch"; SwitchDelegate { ... } } + DelegateChoice { roleValue: "swipe"; SwipeDelegate { ... } } + } + + model: listModel + delegate: chooser + } + \endqml + \note This type is intended to transparently work only with TableView and any DelegateModel-based view. Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support this type of component to make it function as described. diff --git a/src/qmlmodels/qqmldelegatemodel.cpp b/src/qmlmodels/qqmldelegatemodel.cpp index 2216e5fb50..859fbae187 100644 --- a/src/qmlmodels/qqmldelegatemodel.cpp +++ b/src/qmlmodels/qqmldelegatemodel.cpp @@ -1344,6 +1344,11 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); + // layout change triggered by changing the modelIndex might have + // already invalidated this item in d->m_cache and deleted it. + if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) + continue; + if (item->modelIndex() >= index) { const int newIndex = item->modelIndex() + count; const int row = newIndex; @@ -1487,7 +1492,7 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count) QQmlDelegateModelItem *item = cache.at(i); // layout change triggered by removal of a previous item might have // already invalidated this item in d->m_cache and deleted it - if (!d->m_cache.contains(item)) + if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) continue; if (item->modelIndex() >= index + count) { @@ -1542,6 +1547,11 @@ void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); + // layout change triggered by changing the modelIndex might have + // already invalidated this item in d->m_cache and deleted it. + if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) + continue; + if (item->modelIndex() >= from && item->modelIndex() < from + count) { const int newIndex = item->modelIndex() - from + to; const int row = newIndex; @@ -1634,6 +1644,11 @@ void QQmlDelegateModel::_q_modelReset() const QList<QQmlDelegateModelItem *> cache = d->m_cache; for (int i = 0, c = cache.count(); i < c; ++i) { QQmlDelegateModelItem *item = cache.at(i); + // layout change triggered by changing the modelIndex might have + // already invalidated this item in d->m_cache and deleted it. + if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) + continue; + if (item->modelIndex() != -1) item->setModelIndex(-1, -1, -1); } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 3721731f68..eb5256fa4e 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -879,6 +879,8 @@ QString QQuickShaderEffect::parseLog() // for OpenGL-based autotests void QQuickShaderEffectPrivate::updatePolish() { Q_Q(QQuickShaderEffect); + if (!qmlEngine(q)) + return; #if QT_CONFIG(opengl) if (q->m_glImpl) { q->m_glImpl->maybeUpdateShaders(); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index c2459955b2..e08a1cc37e 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -244,6 +244,7 @@ private slots: void equality(); void aggressiveGc(); + void noAccumulatorInTemplateLiteral(); void interrupt_data(); void interrupt(); @@ -4840,6 +4841,23 @@ void tst_QJSEngine::aggressiveGc() qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); } +void tst_QJSEngine::noAccumulatorInTemplateLiteral() +{ + const QByteArray origAggressiveGc = qgetenv("QV4_MM_AGGRESSIVE_GC"); + qputenv("QV4_MM_AGGRESSIVE_GC", "true"); + { + QJSEngine engine; + + // getTemplateLiteral should not save the accumulator as it's garbage and trashes + // the next GC run. Instead, we want to see the stack overflow error. + QJSValue value = engine.evaluate("function a(){\nS=o=>s\nFunction``\na()}a()"); + + QVERIFY(value.isError()); + QCOMPARE(value.toString(), "RangeError: Maximum call stack size exceeded."); + } + qputenv("QV4_MM_AGGRESSIVE_GC", origAggressiveGc); +} + void tst_QJSEngine::interrupt_data() { QTest::addColumn<int>("jitThreshold"); diff --git a/tests/auto/qml/qjsvalue/qjsvalue.pro b/tests/auto/qml/qjsvalue/qjsvalue.pro index 3bbbbd4787..a01cffa100 100644 --- a/tests/auto/qml/qjsvalue/qjsvalue.pro +++ b/tests/auto/qml/qjsvalue/qjsvalue.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qjsvalue macx:CONFIG -= app_bundle -QT += qml widgets testlib gui-private +QT += qml widgets testlib gui-private qml-private SOURCES += tst_qjsvalue.cpp HEADERS += tst_qjsvalue.h diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index a57cd3113c..4de72ae7a1 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -27,7 +27,14 @@ ****************************************************************************/ #include "tst_qjsvalue.h" + +#include <private/qv4engine_p.h> +#include <private/qjsvalue_p.h> + #include <QtWidgets/QPushButton> +#include <QtCore/qthread.h> + +#include <memory> tst_QJSValue::tst_QJSValue() : engine(nullptr) @@ -2650,4 +2657,34 @@ void tst_QJSValue::nestedObjectToVariant() QCOMPARE(o.toVariant(), expected); } +void tst_QJSValue::deleteFromDifferentThread() +{ +#if !QT_CONFIG(thread) + QSKIP("Need thread support to destroy QJSValues from different threads"); +#else + QV4::PersistentValueStorage storage(engine->handle()); + QCOMPARE(storage.firstPage, nullptr); + QJSValue jsval; + QJSValuePrivate::setRawValue(&jsval, storage.allocate()); + QVERIFY(storage.firstPage != nullptr); + + QMutex mutex; + QWaitCondition condition; + + std::unique_ptr<QThread> thread(QThread::create([&]() { + QMutexLocker locker(&mutex); + QJSValuePrivate::free(&jsval); + QJSValuePrivate::setRawValue(&jsval, nullptr); + QVERIFY(storage.firstPage != nullptr); + condition.wakeOne(); + })); + + QMutexLocker locker(&mutex); + thread->start(); + condition.wait(&mutex); + QTRY_VERIFY(thread->isFinished()); + QTRY_COMPARE(storage.firstPage, nullptr); +#endif +} + QTEST_MAIN(tst_QJSValue) diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h index 9532b1f10e..f704169d43 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h @@ -35,8 +35,6 @@ #include <qjsvalue.h> #include <QtTest/QtTest> -Q_DECLARE_METATYPE(QVariant) - class tst_QJSValue : public QObject { Q_OBJECT @@ -144,6 +142,8 @@ private slots: void nestedObjectToVariant_data(); void nestedObjectToVariant(); + void deleteFromDifferentThread(); + private: void newEngine() { |