aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-06-14 01:01:31 +0200
committerUlf Hermann <ulf.hermann@qt.io>2019-06-14 09:50:21 +0200
commit82ca44ca1a4abb2bcfee5f8ff41df72d02cf4491 (patch)
tree1795638fbf3ad02c16a69dd69efb8738976f7a07
parent6d5a0a55d862c78b185a077171a0b46a94d5a599 (diff)
parent15b90204fa5126f339db551cf7429534f1359322 (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.cpp1
-rw-r--r--src/qml/jsapi/qjsvalue_p.h21
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp5
-rw-r--r--src/qmlmodels/qqmldelegatecomponent.cpp36
-rw-r--r--src/qmlmodels/qqmldelegatemodel.cpp17
-rw-r--r--src/quick/items/qquickshadereffect.cpp2
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp18
-rw-r--r--tests/auto/qml/qjsvalue/qjsvalue.pro2
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp37
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h4
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()
{