aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTarja Sundqvist <[email protected]>2024-11-08 15:36:12 +0200
committerTarja Sundqvist <[email protected]>2024-11-08 15:36:12 +0200
commitabe4729ea8db32124c36dc33fc32eb629df03043 (patch)
tree6c02a1174b4abbcec9a127758b2c406975661312
parent7c32569ad27b743b6cb50e2bcb67c9ca1674f238 (diff)
parent7550f26e156b3bca5f4b6a9711352c2aa0ba8463 (diff)
Merge tag 'v5.15.16-lts' into tqtc/lts-5.15-opensourcev5.15.16-lts-lgpl5.15
Qt 5.15.16-lts release Change-Id: I2892ad4097deaec565b10357ca61be10048a7c81
-rw-r--r--.qmake.conf2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp7
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp7
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob.cpp4
-rw-r--r--src/qml/animations/qparallelanimationgroupjob.cpp4
-rw-r--r--src/qml/jsruntime/qv4context.cpp18
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp4
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp24
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h5
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp12
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h2
-rw-r--r--src/qml/jsruntime/qv4module.cpp9
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp28
-rw-r--r--src/qml/jsruntime/qv4string_p.h22
-rw-r--r--src/qml/qml/qqmldata_p.h64
-rw-r--r--src/qml/qml/qqmlengine.cpp84
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp3
-rw-r--r--src/qmltest/doc/src/qtquicktest.qdoc1
-rw-r--r--src/quick/accessible/qaccessiblequickitem.cpp2
-rw-r--r--src/quick/doc/snippets/qml/pathview/pathview.qml9
-rw-r--r--src/quick/items/qquickgridview.cpp22
-rw-r--r--src/quick/items/qquickitem.cpp17
-rw-r--r--src/quick/items/qquickitem_p.h3
-rw-r--r--src/quick/items/qquickitemview.cpp2
-rw-r--r--src/quick/items/qquickitemview_p_p.h2
-rw-r--r--src/quick/items/qquicklistview.cpp45
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp5
-rw-r--r--src/quick/util/qquickpixmapcache.cpp6
-rw-r--r--src/quick/util/qquickstategroup.cpp12
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml4
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module1.js5
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module2.mjs7
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module3.mjs0
-rw-r--r--tests/auto/qml/debugger/qv4debugger/data/module4.mjs0
-rw-r--r--tests/auto/qml/debugger/qv4debugger/qv4debugger.pro3
-rw-r--r--tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp49
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp10
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp20
-rw-r--r--tests/auto/quick/qquickgridview/data/qtbug86255.qml55
-rw-r--r--tests/auto/quick/qquickgridview/tst_qquickgridview.cpp13
-rw-r--r--tests/auto/quick/qquickstates/data/jsValueWhen.qml18
-rw-r--r--tests/auto/quick/qquickstates/tst_qquickstates.cpp11
42 files changed, 466 insertions, 154 deletions
diff --git a/.qmake.conf b/.qmake.conf
index e78597cf64..0374cc1858 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -4,4 +4,4 @@ CONFIG += warning_clean
DEFINES += QT_NO_LINKED_LIST
DEFINES += QT_NO_JAVA_STYLE_ITERATORS
-MODULE_VERSION = 5.15.15
+MODULE_VERSION = 5.15.16
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index b50490e831..48092b1591 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -224,9 +224,10 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
QV4::ScopedValue v(scope);
QV4::Heap::InternalClass *ic = ctxt->internalClass();
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->keyAt(i);
- names.append(name);
- v = static_cast<QV4::Heap::CallContext *>(ctxt->d())->locals[i];
+ QV4::ScopedValue stringOrSymbol(scope, ic->keyAt(i));
+ QV4::ScopedString propName(scope, stringOrSymbol->toString(scope.engine));
+ names.append(propName->toQString());
+ v = ctxt->getProperty(propName);
collectedRefs.append(addValueRef(v));
}
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index dceaab9f6d..2fd395ce6c 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -493,9 +493,10 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
QV4::Heap::InternalClass *ic = callContext->internalClass();
QV4::ScopedValue v(scope);
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->keyAt(i);
- v = callContext->d()->locals[i];
- collector.collect(&output, QString(), name, v);
+ QV4::ScopedValue stringOrSymbol(scope, ic->keyAt(i));
+ QV4::ScopedString propName(scope, stringOrSymbol->toString(scope.engine));
+ v = callContext->getProperty(propName);
+ collector.collect(&output, QString(), propName->toQString(), v);
}
}
diff --git a/src/qml/animations/qcontinuinganimationgroupjob.cpp b/src/qml/animations/qcontinuinganimationgroupjob.cpp
index 88c0e9e60e..61a9dc36f8 100644
--- a/src/qml/animations/qcontinuinganimationgroupjob.cpp
+++ b/src/qml/animations/qcontinuinganimationgroupjob.cpp
@@ -82,9 +82,9 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS
return;
}
for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
- resetUncontrolledAnimationFinishTime(animation);
+ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation));
animation->setDirection(m_direction);
- animation->start();
+ RETURN_IF_DELETED(animation->start());
}
break;
}
diff --git a/src/qml/animations/qparallelanimationgroupjob.cpp b/src/qml/animations/qparallelanimationgroupjob.cpp
index 420a934ba2..a828d0e234 100644
--- a/src/qml/animations/qparallelanimationgroupjob.cpp
+++ b/src/qml/animations/qparallelanimationgroupjob.cpp
@@ -144,10 +144,10 @@ void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newSta
animation->stop();
m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1;
}
- resetUncontrolledAnimationFinishTime(animation);
+ RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation));
animation->setDirection(m_direction);
if (shouldAnimationStart(animation, oldState == Stopped))
- animation->start();
+ RETURN_IF_DELETED(animation->start());
}
break;
}
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 018571e325..c547c0692d 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -334,9 +334,14 @@ ReturnedValue ExecutionContext::getProperty(String *name)
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->indexOfValueOrGetter(id);
- if (index < UINT_MAX)
+ const uint index = c->internalClass->indexOfValueOrGetter(id);
+ if (index < c->locals.alloc)
return c->locals[index].asReturnedValue();
+
+ // TODO: We should look up the module imports here, but those are part of the CU:
+ // imports[index - c->locals.size];
+ // See QTBUG-118478
+
Q_FALLTHROUGH();
}
case Heap::ExecutionContext::Type_WithContext:
@@ -384,9 +389,14 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->indexOfValueOrGetter(id);
- if (index < UINT_MAX)
+ const uint index = c->internalClass->indexOfValueOrGetter(id);
+ if (index < c->locals.alloc)
return c->locals[index].asReturnedValue();
+
+ // TODO: We should look up the module imports here, but those are part of the CU:
+ // imports[index - c->locals.size];
+ // See QTBUG-118478
+
Q_FALLTHROUGH();
}
case Heap::ExecutionContext::Type_GlobalContext: {
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index c3d7165f71..266ed17c18 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -152,7 +152,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
if (!d)
return nullptr;
- PropertyKey id = d->identifierTable->asPropertyKey(str);
+ PropertyKey id = d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
return lookup(id);
}
@@ -169,7 +169,7 @@ const IdentifierHashEntry *IdentifierHash::lookup(String *str) const
const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->asPropertyKey(str);
+ return d->identifierTable->asPropertyKey(str, IdentifierTable::ForceConversionToId);
}
const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index 21b47c3909..ad6b39e0b1 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -132,16 +132,23 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
-Heap::String *IdentifierTable::insertString(const QString &s)
+Heap::String *IdentifierTable::insertString(
+ const QString &s, IdentifierTable::KeyConversionBehavior conversionBehavior)
{
uint subtype;
- uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+
+ uint hash = String::createHashValue(s.constData(), s.size(), &subtype);
if (subtype == Heap::String::StringType_ArrayIndex) {
- Heap::String *str = engine->newString(s);
- str->stringHash = hash;
- str->subtype = subtype;
- return str;
+ if (Q_UNLIKELY(conversionBehavior == ForceConversionToId)) {
+ hash = String::createHashValueDisallowingArrayIndex(s.constData(), s.size(), &subtype);
+ } else {
+ Heap::String *str = engine->newString(s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ return str;
+ }
}
+
uint idx = hash % alloc;
while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == s)
@@ -278,9 +285,10 @@ void IdentifierTable::sweep()
size -= freed;
}
-PropertyKey IdentifierTable::asPropertyKey(const QString &s)
+PropertyKey IdentifierTable::asPropertyKey(
+ const QString &s, IdentifierTable::KeyConversionBehavior conversionBehavior)
{
- return insertString(s)->identifier;
+ return insertString(s, conversionBehavior)->identifier;
}
PropertyKey IdentifierTable::asPropertyKey(const char *s, int len)
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index 78e2b6620e..1dda65f2ed 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -75,11 +75,12 @@ struct Q_QML_PRIVATE_EXPORT IdentifierTable
void addEntry(Heap::StringOrSymbol *str);
public:
+ enum KeyConversionBehavior { Default, ForceConversionToId };
IdentifierTable(ExecutionEngine *engine, int numBits = 8);
~IdentifierTable();
- Heap::String *insertString(const QString &s);
+ Heap::String *insertString(const QString &s, KeyConversionBehavior conversionBehavior);
Heap::Symbol *insertSymbol(const QString &s);
PropertyKey asPropertyKey(const Heap::String *str) {
@@ -91,7 +92,7 @@ public:
return asPropertyKey(str->d());
}
- PropertyKey asPropertyKey(const QString &s);
+ PropertyKey asPropertyKey(const QString &s, KeyConversionBehavior conversionBehavior = Default);
PropertyKey asPropertyKey(const char *s, int len);
PropertyKey asPropertyKeyImpl(const Heap::String *str);
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 904c6a5eaf..d6211bab1a 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -329,9 +329,15 @@ void InternalClass::destroy()
Base::destroy();
}
-QString InternalClass::keyAt(uint index) const
-{
- return nameMap.at(index).toQString();
+ReturnedValue InternalClass::keyAt(uint index) const
+{
+ PropertyKey key = nameMap.at(index);
+ if (!key.isValid())
+ return Encode::undefined();
+ if (key.isArrayIndex())
+ return Encode(key.asArrayIndex());
+ Q_ASSERT(key.isStringOrSymbol());
+ return key.asStringOrSymbol()->asReturnedValue();
}
void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index a5a1471cf1..5fe97e8029 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -344,7 +344,7 @@ struct InternalClass : Base {
void init(InternalClass *other);
void destroy();
- Q_QML_PRIVATE_EXPORT QString keyAt(uint index) const;
+ Q_QML_PRIVATE_EXPORT ReturnedValue keyAt(uint index) const;
Q_REQUIRED_RESULT InternalClass *nonExtensible();
static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry);
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
index 08a1900383..8f148994e3 100644
--- a/src/qml/jsruntime/qv4module.cpp
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -258,9 +258,12 @@ OwnPropertyKeyIterator *Module::virtualOwnPropertyKeys(const Object *o, Value *t
if (module->d()->unit->isESModule()) {
names = module->d()->unit->exportedNames();
} else {
- Heap::InternalClass *scopeClass = module->d()->scope->internalClass;
- for (uint i = 0; i < scopeClass->size; ++i)
- names << scopeClass->keyAt(i);
+ QV4::Scope scope(module->engine());
+ QV4::Scoped<InternalClass> scopeClass(scope, module->d()->scope->internalClass);
+ for (uint i = 0, end = scopeClass->d()->size; i < end; ++i) {
+ QV4::ScopedValue key(scope, scopeClass->d()->keyAt(i));
+ names << key->toQString();
+ }
}
return new ModuleNamespaceIterator(names);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index f84718b48f..04b6cfc921 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -261,7 +261,33 @@ struct QQmlSequence : Object {
template <typename Container>
struct QQmlSequence : public QV4::Object
{
- V4_OBJECT2(QQmlSequence<Container>, QV4::Object)
+ // Unroll V4_OBJECT2(QQmlSequence<Container>, QV4::Object) here.
+ // Newer C++ versions don't tolerate the template argument in Q_DISABLE_COPY.
+private:
+ QQmlSequence() Q_DECL_EQ_DELETE;
+ Q_DISABLE_COPY(QQmlSequence)
+public:
+ Q_MANAGED_CHECK
+ typedef QV4::Heap::QQmlSequence<Container> Data;
+ typedef QV4::Object SuperClass;
+ static const QV4::VTable static_vtbl;
+ static inline const QV4::VTable *staticVTable() { return &static_vtbl; }
+ V4_MANAGED_SIZE_TEST
+
+ QV4::Heap::QQmlSequence<Container> *d_unchecked() const
+ {
+ return static_cast<QV4::Heap::QQmlSequence<Container> *>(m());
+ }
+
+ QV4::Heap::QQmlSequence<Container> *d() const
+ {
+ QV4::Heap::QQmlSequence<Container> *dptr = d_unchecked();
+ dptr->_checkIsInitialized();
+ return dptr;
+ }
+
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::QQmlSequence<Container>>::value);
+
Q_MANAGED_TYPE(QmlSequence)
V4_PROTOTYPE(sequencePrototype)
V4_NEEDS_DESTROY
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 52fe09cd72..55a0ff4316 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -222,6 +222,12 @@ struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
return calculateHashValue(ch, end, subtype);
}
+ static uint createHashValueDisallowingArrayIndex(const QChar *ch, int length, uint *subtype)
+ {
+ const QChar *end = ch + length;
+ return calculateHashValue<String::DisallowArrayIndex>(ch, end, subtype);
+ }
+
static uint createHashValue(const char *ch, int length, uint *subtype)
{
const char *end = ch + length;
@@ -235,15 +241,19 @@ protected:
static qint64 virtualGetLength(const Managed *m);
public:
- template <typename T>
+ enum IndicesBehavior {Default, DisallowArrayIndex};
+ template <IndicesBehavior Behavior = Default, typename T>
static inline uint calculateHashValue(const T *ch, const T* end, uint *subtype)
{
// array indices get their number as hash value
- uint h = stringToArrayIndex(ch, end);
- if (h != UINT_MAX) {
- if (subtype)
- *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
- return h;
+ uint h = UINT_MAX;
+ if (Behavior != DisallowArrayIndex) {
+ h = stringToArrayIndex(ch, end);
+ if (h != UINT_MAX) {
+ if (subtype)
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
+ return h;
+ }
}
while (ch < end) {
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index ee31cb38d9..c44fb8608e 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -176,24 +176,24 @@ public:
};
struct NotifyList {
- quint64 connectionMask;
-
- quint16 maximumTodoIndex;
- quint16 notifiesSize;
-
- QQmlNotifierEndpoint *todo;
- QQmlNotifierEndpoint**notifies;
+ quint64 connectionMask = 0;
+ QQmlNotifierEndpoint *todo = nullptr;
+ QQmlNotifierEndpoint**notifies = nullptr;
+ quint16 maximumTodoIndex = 0;
+ quint16 notifiesSize = 0;
void layout();
private:
void layout(QQmlNotifierEndpoint*);
};
- NotifyList *notifyList;
+ QAtomicPointer<NotifyList> notifyList;
- inline QQmlNotifierEndpoint *notify(int index);
+ inline QQmlNotifierEndpoint *notify(int index) const;
void addNotify(int index, QQmlNotifierEndpoint *);
int endpointCount(int index);
bool signalHasEndpoint(int index) const;
- void disconnectNotifiers();
+
+ enum class DeleteNotifyList { Yes, No };
+ void disconnectNotifiers(DeleteNotifyList doDelete);
// The context that created the C++ object
QQmlContextData *context = nullptr;
@@ -342,23 +342,31 @@ bool QQmlData::wasDeleted(const QObject *object)
return ddata && ddata->isQueuedForDeletion;
}
-QQmlNotifierEndpoint *QQmlData::notify(int index)
+inline bool isIndexInConnectionMask(quint64 connectionMask, int index)
+{
+ return connectionMask & (1ULL << quint64(index % 64));
+}
+
+QQmlNotifierEndpoint *QQmlData::notify(int index) const
{
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
Q_ASSERT(index <= 0xFFFF);
- if (!notifyList || !(notifyList->connectionMask & (1ULL << quint64(index % 64)))) {
+ NotifyList *list = notifyList.loadRelaxed();
+ if (!list || !isIndexInConnectionMask(list->connectionMask, index))
return nullptr;
- } else if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else if (index <= notifyList->maximumTodoIndex) {
- notifyList->layout();
- }
- if (index < notifyList->notifiesSize) {
- return notifyList->notifies[index];
- } else {
- return nullptr;
+ if (index < list->notifiesSize)
+ return list->notifies[index];
+
+ if (index <= list->maximumTodoIndex) {
+ list->layout();
+ if (index < list->notifiesSize)
+ return list->notifies[index];
}
+
+ return nullptr;
}
/*
@@ -367,7 +375,19 @@ QQmlNotifierEndpoint *QQmlData::notify(int index)
*/
inline bool QQmlData::signalHasEndpoint(int index) const
{
- return notifyList && (notifyList->connectionMask & (1ULL << quint64(index % 64)));
+ // This can be called from any thread.
+ // We still use relaxed semantics. If we're on a thread different from the "home" thread
+ // of the QQmlData, two interesting things might happen:
+ //
+ // 1. The list might go away while we hold it. In that case we are dealing with an object whose
+ // QObject dtor is being executed concurrently. This is UB already without the notify lists.
+ // Therefore, we don't need to consider it.
+ // 2. The connectionMask may be amended or zeroed while we are looking at it. In that case
+ // we "misreport" the endpoint. Since ordering of events across threads is inherently
+ // nondeterministic, either result is correct in that case. We can accept it.
+
+ NotifyList *list = notifyList.loadRelaxed();
+ return list && isIndexInConnectionMask(list->connectionMask, index);
}
bool QQmlData::hasBindingBit(int coreIndex) const
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 852a673ebd..2325c4c1e0 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -718,7 +718,7 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o)
// Disconnect the notifiers now - during object destruction this would be too late, since
// the disconnect call wouldn't be able to call disconnectNotify(), as it isn't possible to
// get the metaobject anymore.
- d->disconnectNotifiers();
+ d->disconnectNotifiers(QQmlData::DeleteNotifyList::No);
}
}
@@ -789,7 +789,10 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in
// QQmlEngine to emit signals from a different thread. These signals are then automatically
// marshalled back onto the QObject's thread and handled by QML from there. This is tested
// by the qqmlecmascript::threadSignal() autotest.
- if (!ddata->notifyList)
+
+ // Relaxed semantics here. If we're on a different thread we might schedule a useless event,
+ // but that should be rare.
+ if (!ddata->notifyList.loadRelaxed())
return;
auto objectThreadData = QObjectPrivate::get(object)->threadData.loadRelaxed();
@@ -1835,49 +1838,72 @@ void QQmlData::releaseDeferredData()
void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint)
{
- if (!notifyList) {
- notifyList = (NotifyList *)malloc(sizeof(NotifyList));
- notifyList->connectionMask = 0;
- notifyList->maximumTodoIndex = 0;
- notifyList->notifiesSize = 0;
- notifyList->todo = nullptr;
- notifyList->notifies = nullptr;
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+
+ NotifyList *list = notifyList.loadRelaxed();
+
+ if (!list) {
+ list = new NotifyList;
+ // We don't really care when this change takes effect on other threads. The notifyList can
+ // only become non-null once in the life time of a QQmlData. It becomes null again when the
+ // underlying QObject is deleted. At that point any interaction with the QQmlData is UB
+ // anyway. So, for all intents and purposese, the list becomes non-null once and then stays
+ // non-null "forever". We can apply relaxed semantics.
+ notifyList.storeRelaxed(list);
}
Q_ASSERT(!endpoint->isConnected());
index = qMin(index, 0xFFFF - 1);
- notifyList->connectionMask |= (1ULL << quint64(index % 64));
- if (index < notifyList->notifiesSize) {
+ // Likewise, we don't really care _when_ the change in the connectionMask is propagated to other
+ // threads. Cross-thread event ordering is inherently nondeterministic. Therefore, when querying
+ // the conenctionMask in the presence of concurrent modification, any result is correct.
+ list->connectionMask |= (1ULL << quint64(index % 64));
- endpoint->next = notifyList->notifies[index];
+ if (index < list->notifiesSize) {
+ endpoint->next = list->notifies[index];
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->notifies[index];
- notifyList->notifies[index] = endpoint;
-
+ endpoint->prev = &list->notifies[index];
+ list->notifies[index] = endpoint;
} else {
- notifyList->maximumTodoIndex = qMax(int(notifyList->maximumTodoIndex), index);
+ list->maximumTodoIndex = qMax(int(list->maximumTodoIndex), index);
- endpoint->next = notifyList->todo;
+ endpoint->next = list->todo;
if (endpoint->next) endpoint->next->prev = &endpoint->next;
- endpoint->prev = &notifyList->todo;
- notifyList->todo = endpoint;
+ endpoint->prev = &list->todo;
+ list->todo = endpoint;
}
}
-void QQmlData::disconnectNotifiers()
+void QQmlData::disconnectNotifiers(QQmlData::DeleteNotifyList doDelete)
{
- if (notifyList) {
- while (notifyList->todo)
- notifyList->todo->disconnect();
- for (int ii = 0; ii < notifyList->notifiesSize; ++ii) {
- while (QQmlNotifierEndpoint *ep = notifyList->notifies[ii])
+ // Can only happen on "home" thread. We apply relaxed semantics when loading the atomics.
+ if (NotifyList *list = notifyList.loadRelaxed()) {
+ while (QQmlNotifierEndpoint *todo = list->todo)
+ todo->disconnect();
+ for (int ii = 0; ii < list->notifiesSize; ++ii) {
+ while (QQmlNotifierEndpoint *ep = list->notifies[ii])
ep->disconnect();
}
- free(notifyList->notifies);
- free(notifyList);
- notifyList = nullptr;
+ free(list->notifies);
+
+ if (doDelete == DeleteNotifyList::Yes) {
+ // We can only get here from QQmlData::destroyed(), and that can only come from the
+ // the QObject dtor. If you're still sending signals at that point you have UB already
+ // without any threads. Therefore, it's enough to apply relaxed semantics.
+ notifyList.storeRelaxed(nullptr);
+ delete list;
+ } else {
+ // We can use relaxed semantics here. The worst thing that can happen is that some
+ // signal is falsely reported as connected. Signal connectedness across threads
+ // is not quite deterministic anyway.
+ list->connectionMask = 0;
+ list->maximumTodoIndex = 0;
+ list->notifiesSize = 0;
+ list->notifies = nullptr;
+
+ }
}
}
@@ -1961,7 +1987,7 @@ void QQmlData::destroyed(QObject *object)
guard->objectDestroyed(object);
}
- disconnectNotifiers();
+ disconnectNotifiers(DeleteNotifyList::Yes);
if (extendedData)
delete extendedData;
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 1e0e4e419f..4fd2092fd3 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -231,6 +231,9 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
const QV4::CompiledData::Alias *aliasData = &metaObject->compiledObject->aliasTable()[aliasId];
if (!aliasData->isObjectAlias()) {
QQmlContextData *ctxt = metaObject->ctxt;
+ if (!ctxt->engine)
+ return;
+
QObject *target = ctxt->idValues[aliasData->targetObjectId()].data();
if (!target)
return;
diff --git a/src/qmltest/doc/src/qtquicktest.qdoc b/src/qmltest/doc/src/qtquicktest.qdoc
index 31c097ed76..d93aa8160e 100644
--- a/src/qmltest/doc/src/qtquicktest.qdoc
+++ b/src/qmltest/doc/src/qtquicktest.qdoc
@@ -27,6 +27,7 @@
/*!
\namespace QQuickTest
+ \inheaderfile QtQuickTest
\inmodule QtQuickTest
\brief The QQuickTest namespace contains all the functions and
diff --git a/src/quick/accessible/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp
index ae1954ae8d..36b65f906c 100644
--- a/src/quick/accessible/qaccessiblequickitem.cpp
+++ b/src/quick/accessible/qaccessiblequickitem.cpp
@@ -217,7 +217,7 @@ QAccessible::Role QAccessibleQuickItem::role() const
QAccessible::Role role = QAccessible::NoRole;
if (item())
- role = QQuickItemPrivate::get(item())->accessibleRole();
+ role = QQuickItemPrivate::get(item())->effectiveAccessibleRole();
if (role == QAccessible::NoRole) {
if (qobject_cast<QQuickText*>(const_cast<QQuickItem *>(item())))
role = QAccessible::StaticText;
diff --git a/src/quick/doc/snippets/qml/pathview/pathview.qml b/src/quick/doc/snippets/qml/pathview/pathview.qml
index 58d19b1a0c..0c3b96bb26 100644
--- a/src/quick/doc/snippets/qml/pathview/pathview.qml
+++ b/src/quick/doc/snippets/qml/pathview/pathview.qml
@@ -59,15 +59,20 @@ Rectangle {
id: delegate
Column {
id: wrapper
+
+ required property url icon
+ required property string name
+
opacity: PathView.isCurrentItem ? 1 : 0.5
+
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
- source: icon
+ source: wrapper.icon
}
Text {
id: nameText
- text: name
+ text: wrapper.name
font.pointSize: 16
}
}
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index 0a1f7681d6..7105b507a3 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -400,7 +400,7 @@ int QQuickGridViewPrivate::snapIndex() const
if (item->index == -1)
continue;
qreal itemTop = item->position();
- FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight);
+ FxGridItemSG *hItem = static_cast<FxGridItemSG*>(highlight.get());
if (itemTop >= hItem->rowPos()-rowSize()/2 && itemTop < hItem->rowPos()+rowSize()/2) {
FxGridItemSG *gridItem = static_cast<FxGridItemSG*>(item);
index = gridItem->index;
@@ -700,10 +700,9 @@ void QQuickGridViewPrivate::createHighlight(bool onDestruction)
{
bool changed = false;
if (highlight) {
- if (trackedItem == highlight)
+ if (trackedItem == highlight.get())
trackedItem = nullptr;
- delete highlight;
- highlight = nullptr;
+ highlight.reset();
delete highlightXAnimator;
delete highlightYAnimator;
@@ -720,7 +719,8 @@ void QQuickGridViewPrivate::createHighlight(bool onDestruction)
if (currentItem) {
QQuickItem *item = createHighlightItem();
if (item) {
- FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true);
+ std::unique_ptr<FxGridItemSG> newHighlight
+ = std::make_unique<FxGridItemSG>(item, q, true);
newHighlight->trackGeometry(true);
if (autoHighlight)
resetHighlightPosition();
@@ -731,7 +731,7 @@ void QQuickGridViewPrivate::createHighlight(bool onDestruction)
highlightYAnimator->target = QQmlProperty(item, QLatin1String("y"));
highlightYAnimator->userDuration = highlightMoveDuration;
- highlight = newHighlight;
+ highlight = std::move(newHighlight);
changed = true;
}
}
@@ -762,7 +762,7 @@ void QQuickGridViewPrivate::resetHighlightPosition()
{
if (highlight && currentItem) {
FxGridItemSG *cItem = static_cast<FxGridItemSG*>(currentItem);
- static_cast<FxGridItemSG*>(highlight)->setPosition(cItem->colPos(), cItem->rowPos());
+ static_cast<FxGridItemSG *>(highlight.get())->setPosition(cItem->colPos(), cItem->rowPos());
}
}
@@ -2101,7 +2101,8 @@ void QQuickGridView::viewportMoved(Qt::Orientations orient)
if (pos != d->highlight->position()) {
d->highlightXAnimator->stop();
d->highlightYAnimator->stop();
- static_cast<FxGridItemSG*>(d->highlight)->setPosition(static_cast<FxGridItemSG*>(d->highlight)->colPos(), pos);
+ FxGridItemSG *sgHighlight = static_cast<FxGridItemSG *>(d->highlight.get());
+ sgHighlight->setPosition(sgHighlight->colPos(), pos);
} else {
d->updateHighlight();
}
@@ -2110,7 +2111,10 @@ void QQuickGridView::viewportMoved(Qt::Orientations orient)
int idx = d->snapIndex();
if (idx >= 0 && idx != d->currentIndex) {
d->updateCurrent(idx);
- if (d->currentItem && static_cast<FxGridItemSG*>(d->currentItem)->colPos() != static_cast<FxGridItemSG*>(d->highlight)->colPos() && d->autoHighlight) {
+ if (d->currentItem
+ && static_cast<FxGridItemSG*>(d->currentItem)->colPos()
+ != static_cast<FxGridItemSG*>(d->highlight.get())->colPos()
+ && d->autoHighlight) {
if (d->flow == FlowLeftToRight)
d->highlightXAnimator->to = d->currentItem->itemX();
else
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 540d12c84a..c655b4c327 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -2400,7 +2400,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole();
if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
return true;
} else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
@@ -8989,13 +8989,20 @@ QQuickItemPrivate::ExtraData::ExtraData()
#if QT_CONFIG(accessibility)
-QAccessible::Role QQuickItemPrivate::accessibleRole() const
+QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const
{
Q_Q(const QQuickItem);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false));
- if (accessibleAttached)
- return accessibleAttached->role();
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false);
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+ return role;
+}
+QAccessible::Role QQuickItemPrivate::accessibleRole() const
+{
return QAccessible::NoRole;
}
#endif
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 841d91bb40..d48b551064 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -574,7 +574,10 @@ public:
virtual void implicitHeightChanged();
#if QT_CONFIG(accessibility)
+ QAccessible::Role effectiveAccessibleRole() const;
+private:
virtual QAccessible::Role accessibleRole() const;
+public:
#endif
void setImplicitAntialiasing(bool antialiasing);
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 29afaae93b..6ffbdf96d5 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -2513,7 +2513,7 @@ void QQuickItemViewPrivate::updateTrackedItem()
Q_Q(QQuickItemView);
FxViewItem *item = currentItem;
if (highlight)
- item = highlight;
+ item = highlight.get();
trackedItem = item;
if (trackedItem)
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 0bc2a6b768..eac185ac7a 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -279,7 +279,7 @@ public:
QPauseAnimationJob bufferPause;
QQmlComponent *highlightComponent;
- FxViewItem *highlight;
+ std::unique_ptr<FxViewItem> highlight;
int highlightRange; // enum value
qreal highlightRangeStart;
qreal highlightRangeEnd;
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 1001b34c3c..56cb4e4507 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -157,9 +157,9 @@ public:
QQuickListView::HeaderPositioning headerPositioning;
QQuickListView::FooterPositioning footerPositioning;
- QSmoothedAnimation *highlightPosAnimator;
- QSmoothedAnimation *highlightWidthAnimator;
- QSmoothedAnimation *highlightHeightAnimator;
+ std::unique_ptr<QSmoothedAnimation> highlightPosAnimator;
+ std::unique_ptr<QSmoothedAnimation> highlightWidthAnimator;
+ std::unique_ptr<QSmoothedAnimation> highlightHeightAnimator;
qreal highlightMoveVelocity;
qreal highlightResizeVelocity;
int highlightResizeDuration;
@@ -202,11 +202,6 @@ public:
{
highlightMoveDuration = -1; //override default value set in base class
}
- ~QQuickListViewPrivate() {
- delete highlightPosAnimator;
- delete highlightWidthAnimator;
- delete highlightHeightAnimator;
- }
friend class QQuickViewSection;
@@ -979,14 +974,13 @@ void QQuickListViewPrivate::createHighlight(bool onDestruction)
{
bool changed = false;
if (highlight) {
- if (trackedItem == highlight)
+ if (trackedItem == highlight.get())
trackedItem = nullptr;
- delete highlight;
- highlight = nullptr;
+ highlight.reset();
- delete highlightPosAnimator;
- delete highlightWidthAnimator;
- delete highlightHeightAnimator;
+ highlightPosAnimator.reset();
+ highlightWidthAnimator.reset();
+ highlightHeightAnimator.reset();
highlightPosAnimator = nullptr;
highlightWidthAnimator = nullptr;
highlightHeightAnimator = nullptr;
@@ -1001,7 +995,8 @@ void QQuickListViewPrivate::createHighlight(bool onDestruction)
if (currentItem) {
QQuickItem *item = createHighlightItem();
if (item) {
- FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
+ std::unique_ptr<FxListItemSG> newHighlight
+ = std::make_unique<FxListItemSG>(item, q, true);
newHighlight->trackGeometry(true);
if (autoHighlight) {
@@ -1009,22 +1004,22 @@ void QQuickListViewPrivate::createHighlight(bool onDestruction)
newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
}
const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
- highlightPosAnimator = new QSmoothedAnimation;
+ highlightPosAnimator = std::make_unique<QSmoothedAnimation>();
highlightPosAnimator->target = QQmlProperty(item, posProp);
highlightPosAnimator->velocity = highlightMoveVelocity;
highlightPosAnimator->userDuration = highlightMoveDuration;
- highlightWidthAnimator = new QSmoothedAnimation;
+ highlightWidthAnimator = std::make_unique<QSmoothedAnimation>();
highlightWidthAnimator->velocity = highlightResizeVelocity;
highlightWidthAnimator->userDuration = highlightResizeDuration;
highlightWidthAnimator->target = QQmlProperty(item, QStringLiteral("width"));
- highlightHeightAnimator = new QSmoothedAnimation;
+ highlightHeightAnimator = std::make_unique<QSmoothedAnimation>();
highlightHeightAnimator->velocity = highlightResizeVelocity;
highlightHeightAnimator->userDuration = highlightResizeDuration;
highlightHeightAnimator->target = QQmlProperty(item, QStringLiteral("height"));
- highlight = newHighlight;
+ highlight = std::move(newHighlight);
changed = true;
}
}
@@ -1064,8 +1059,10 @@ void QQuickListViewPrivate::updateHighlight()
void QQuickListViewPrivate::resetHighlightPosition()
{
- if (highlight && currentItem)
- static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
+ if (highlight && currentItem) {
+ static_cast<FxListItemSG*>(highlight.get())->setPosition(
+ static_cast<FxListItemSG*>(currentItem)->itemPosition());
+ }
}
bool QQuickListViewPrivate::movingFromHighlight()
@@ -3393,7 +3390,7 @@ void QQuickListView::viewportMoved(Qt::Orientations orient)
pos = viewPos + d->highlightRangeStart;
if (pos != d->highlight->position()) {
d->highlightPosAnimator->stop();
- static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
+ static_cast<FxListItemSG*>(d->highlight.get())->setPosition(pos);
} else {
d->updateHighlight();
}
@@ -3711,8 +3708,10 @@ bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Change &ch
item = createItem(it.index, QQmlIncubator::Synchronous);
if (!item)
return false;
- if (it.removedAtIndex)
+ if (it.removedAtIndex) {
+ releaseItem(item, reusableFlag);
continue;
+ }
visibleItems.insert(index, item);
if (index == 0)
diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp
index 61a4529530..7e522406a0 100644
--- a/src/quick/items/qquicktextnodeengine.cpp
+++ b/src/quick/items/qquicktextnodeengine.cpp
@@ -206,10 +206,7 @@ void QQuickTextNodeEngine::addTextDecorations(const QVarLengthArray<TextDecorati
{
QRectF &rect = textDecoration.rect;
- rect.setY(qRound(rect.y()
- + m_currentLine.ascent()
- + (m_currentLine.leadingIncluded() ? m_currentLine.leading() : qreal(0.0f))
- + offset));
+ rect.setY(qRound(rect.y() + m_currentLine.ascent() + offset));
rect.setHeight(thickness);
}
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index bc1b267797..8885b6162d 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -698,9 +698,9 @@ void QQuickPixmapReader::processJobs()
// Clean cancelled jobs
if (!cancelled.isEmpty()) {
-#if QT_CONFIG(qml_network)
for (int i = 0; i < cancelled.count(); ++i) {
QQuickPixmapReply *job = cancelled.at(i);
+#if QT_CONFIG(qml_network)
QNetworkReply *reply = networkJobs.key(job, 0);
if (reply) {
networkJobs.remove(reply);
@@ -709,6 +709,9 @@ void QQuickPixmapReader::processJobs()
reply->close();
}
} else {
+#else
+ {
+#endif
QQuickImageResponse *asyncResponse = asyncResponses.key(job);
if (asyncResponse) {
asyncResponses.remove(asyncResponse);
@@ -720,7 +723,6 @@ void QQuickPixmapReader::processJobs()
job->deleteLater();
}
cancelled.clear();
-#endif
}
if (!jobs.isEmpty()) {
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp
index 7cb3138618..e0356a0173 100644
--- a/src/quick/util/qquickstategroup.cpp
+++ b/src/quick/util/qquickstategroup.cpp
@@ -381,8 +381,16 @@ bool QQuickStateGroupPrivate::updateAutoState()
const auto potentialWhenBinding = QQmlPropertyPrivate::binding(whenProp);
// if there is a binding, the value in when might not be up-to-date at this point
// so we manually reevaluate the binding
- if (auto abstractBinding = dynamic_cast<QQmlBinding *>(potentialWhenBinding))
- whenValue = abstractBinding->evaluate().toBool();
+ if (auto binding = dynamic_cast<QQmlBinding *>(potentialWhenBinding)) {
+ if (binding->context() && binding->context()->isValid()) {
+ QVariant evalResult = binding->evaluate();
+ if (evalResult.userType() == qMetaTypeId<QJSValue>())
+ whenValue = evalResult.value<QJSValue>().toBool();
+ else
+ whenValue = evalResult.toBool();
+ }
+ }
+
if (whenValue) {
if (stateChangeDebug())
qWarning() << "Setting auto state due to expression";
diff --git a/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml
new file mode 100644
index 0000000000..2582a23ec5
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/breakPointInJSModule.qml
@@ -0,0 +1,4 @@
+import QtQml 2.15
+import "module1.js" as Module1
+
+QtObject {}
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module1.js b/tests/auto/qml/debugger/qv4debugger/data/module1.js
new file mode 100644
index 0000000000..9ce1f1e6b1
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module1.js
@@ -0,0 +1,5 @@
+.pragma library
+
+.import "module2.mjs" as Module2
+
+Module2.crashMe();
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module2.mjs b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs
new file mode 100644
index 0000000000..80f82af953
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module2.mjs
@@ -0,0 +1,7 @@
+import * as Module3 from "module3.mjs"
+import * as Module4 from "module4.mjs"
+
+export function crashMe()
+{
+ console.log("Hello world!");
+}
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module3.mjs b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module3.mjs
diff --git a/tests/auto/qml/debugger/qv4debugger/data/module4.mjs b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/auto/qml/debugger/qv4debugger/data/module4.mjs
diff --git a/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro b/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro
index e25b4260e5..63cdfc2394 100644
--- a/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro
+++ b/tests/auto/qml/debugger/qv4debugger/qv4debugger.pro
@@ -16,4 +16,7 @@ HEADERS += \
INCLUDEPATH += \
$$PWD/../../../../../src/plugins/qmltooling/qmldbg_debugger
+include (../../../shared/util.pri)
+TESTDATA = data/*
+
QT += core-private gui-private qml-private network testlib
diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
index e4e7728508..486e617f1e 100644
--- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
+++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp
@@ -41,6 +41,8 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugservice_p.h>
+#include "../../../shared/util.h"
+
using namespace QV4;
using namespace QV4::Debugging;
@@ -224,10 +226,14 @@ public:
QJsonArray scopes = frameObj.value(QLatin1String("scopes")).toArray();
int nscopes = scopes.size();
int s = 0;
- for (s = 0; s < nscopes; ++s) {
- QJsonObject o = scopes.at(s).toObject();
- if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext
- break;
+ if (m_targetScope != -1) {
+ s = m_targetScope;
+ } else {
+ for (s = 0; s < nscopes; ++s) {
+ QJsonObject o = scopes.at(s).toObject();
+ if (o.value(QLatin1String("type")).toInt(-2) == 1) // CallContext
+ break;
+ }
}
if (s == nscopes)
return;
@@ -257,6 +263,7 @@ public:
bool m_wasPaused;
QV4Debugger::PauseReason m_pauseReason;
bool m_captureContextInfo;
+ int m_targetScope = -1;
QList<QV4Debugger::ExecutionState> m_statesWhenPaused;
QList<TestBreakPoint> m_breakPointsToAddWhenPaused;
QVector<QV4::StackFrame> m_stackTrace;
@@ -284,11 +291,12 @@ public:
}
};
-class tst_qv4debugger : public QObject
+class tst_qv4debugger : public QQmlDataTest
{
Q_OBJECT
private slots:
+ void initTestCase() { QQmlDataTest::initTestCase(); }
void init();
void cleanup();
@@ -323,6 +331,8 @@ private slots:
void readThis();
void signalParameters();
+ void breakPointInJSModule();
+
private:
QV4Debugger *debugger() const
{
@@ -939,6 +949,35 @@ void tst_qv4debugger::signalParameters()
QCOMPARE(obj->property("resultCallbackExternal").toString(), QLatin1String("unset"));
}
+void tst_qv4debugger::breakPointInJSModule()
+{
+ QQmlEngine engine;
+ QV4::ExecutionEngine *v4 = engine.handle();
+ QPointer<QV4Debugger> v4Debugger = new QV4Debugger(v4);
+ v4->setDebugger(v4Debugger.data());
+
+ QScopedPointer<QThread> debugThread(new QThread);
+ debugThread->start();
+ QScopedPointer<TestAgent> debuggerAgent(new TestAgent(v4));
+ debuggerAgent->addDebugger(v4Debugger);
+ debuggerAgent->moveToThread(debugThread.data());
+
+ QQmlComponent component(&engine, testFileUrl("breakPointInJSModule.qml"));
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ debuggerAgent->m_captureContextInfo = true;
+ debuggerAgent->m_targetScope = 1;
+ v4Debugger->addBreakPoint("module2.mjs", 6);
+
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY(!obj.isNull());
+
+ QVERIFY(!debuggerAgent->m_capturedScope.isEmpty());
+
+ debugThread->quit();
+ debugThread->wait();
+}
+
QTEST_MAIN(tst_qv4debugger)
#include "tst_qv4debugger.moc"
diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
index 6754f22049..2866988a1a 100644
--- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
+++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp
@@ -73,6 +73,8 @@ private slots:
void contextObjectHierarchy();
void destroyContextProperty();
+ void numericContextProperty();
+
private:
QQmlEngine engine;
};
@@ -918,6 +920,14 @@ void tst_qqmlcontext::destroyContextProperty()
// TODO: Or are we?
}
+void tst_qqmlcontext::numericContextProperty()
+{
+ QQmlEngine engine;
+ auto context = engine.rootContext();
+ context->setContextProperty(QLatin1String("11"), 42);
+ QCOMPARE(context->contextProperty(QLatin1String("11")).toInt(), 42);
+}
+
QTEST_MAIN(tst_qqmlcontext)
#include "tst_qqmlcontext.moc"
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index d5587432de..7d53762efa 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -59,7 +59,8 @@ void tst_qqmlinstantiator::createNone()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createNone.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QCOMPARE(instantiator->isActive(), true);
QCOMPARE(instantiator->count(), 0);
@@ -71,7 +72,8 @@ void tst_qqmlinstantiator::createSingle()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createSingle.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QCOMPARE(instantiator->isActive(), true);
QCOMPARE(instantiator->count(), 1);
@@ -88,7 +90,8 @@ void tst_qqmlinstantiator::createMultiple()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QCOMPARE(instantiator->isActive(), true);
QCOMPARE(instantiator->count(), 10);
@@ -106,7 +109,8 @@ void tst_qqmlinstantiator::stringModel()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("stringModel.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QCOMPARE(instantiator->isActive(), true);
QCOMPARE(instantiator->count(), 4);
@@ -123,7 +127,8 @@ void tst_qqmlinstantiator::activeProperty()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("inactive.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged()));
QSignalSpy countSpy(instantiator, SIGNAL(countChanged()));
@@ -178,7 +183,8 @@ void tst_qqmlinstantiator::intModelChange()
{
QQmlEngine engine;
QQmlComponent component(&engine, testFileUrl("createMultiple.qml"));
- QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(component.create());
+ QScopedPointer<QObject> o(component.create());
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator*>(o.data());
QVERIFY(instantiator != nullptr);
QSignalSpy activeSpy(instantiator, SIGNAL(activeChanged()));
QSignalSpy countSpy(instantiator, SIGNAL(countChanged()));
@@ -214,7 +220,7 @@ void tst_qqmlinstantiator::createAndRemove()
QScopedPointer<StringModel> model {new StringModel("model1")};
qmlRegisterSingletonInstance("Test", 1, 0, "Model1", model.get());
QQmlComponent component(&engine, testFileUrl("createAndRemove.qml"));
- QObject *rootObject = component.create();
+ QScopedPointer<QObject> rootObject(component.create());
QVERIFY(rootObject != nullptr);
QQmlInstantiator *instantiator =
diff --git a/tests/auto/quick/qquickgridview/data/qtbug86255.qml b/tests/auto/quick/qquickgridview/data/qtbug86255.qml
new file mode 100644
index 0000000000..20688b1967
--- /dev/null
+++ b/tests/auto/quick/qquickgridview/data/qtbug86255.qml
@@ -0,0 +1,55 @@
+import QtQuick 2.15
+
+Item {
+ width: 240
+ height: 320
+
+ GridView {
+ id: grid
+ objectName: "view"
+ anchors.fill: parent
+ cellWidth: 64
+ cellHeight: 64
+ model: ListModel {
+ id: listModel
+
+ Component.onCompleted: reload()
+
+ function reload() {
+ clear();
+ for (let i = 0; i < 1000; i++) {
+ let magic = Math.random();
+ append( { magic } );
+ }
+ }
+ }
+ clip: true
+ delegate: Item {
+ id: d
+ property string val: magic
+ Loader {
+ property alias value: d.val
+ asynchronous: true
+ sourceComponent: cmp
+ }
+ }
+ }
+
+ Timer {
+ running: true
+ interval: 1000
+ onTriggered: listModel.reload()
+ }
+ Timer {
+ running: true
+ interval: 500
+ onTriggered: grid.flick(0, -4000)
+ }
+
+ Component {
+ id: cmp
+ Text {
+ text: value
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
index 94ec4f44d5..7d0d9fa7a7 100644
--- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp
@@ -213,6 +213,7 @@ private slots:
void QTBUG_45640();
void QTBUG_49218();
void QTBUG_48870_fastModelUpdates();
+ void QTBUG_86255();
void keyNavigationEnabled();
void resizeDynamicCellWidthRtL();
@@ -6814,6 +6815,18 @@ void tst_QQuickGridView::resizeDynamicCellWidthRtL()
QTRY_COMPARE(gridview->contentX(), 0.f);
}
+void tst_QQuickGridView::QTBUG_86255()
+{
+ QScopedPointer<QQuickView> window(createView());
+ window->setSource(testFileUrl("qtbug86255.qml"));
+ window->show();
+ QVERIFY(QTest::qWaitForWindowExposed(window.data()));
+ QQuickGridView *view = findItem<QQuickGridView>(window->rootObject(), "view");
+ QVERIFY(view != nullptr);
+ QTRY_COMPARE(view->isFlicking(), true);
+ QTRY_COMPARE(view->isFlicking(), false);
+}
+
void tst_QQuickGridView::releaseItems()
{
QScopedPointer<QQuickView> view(createView());
diff --git a/tests/auto/quick/qquickstates/data/jsValueWhen.qml b/tests/auto/quick/qquickstates/data/jsValueWhen.qml
new file mode 100644
index 0000000000..6d5eb1600c
--- /dev/null
+++ b/tests/auto/quick/qquickstates/data/jsValueWhen.qml
@@ -0,0 +1,18 @@
+import QtQuick 2.15
+
+Item {
+ id: root
+ property var prop: null
+ property bool works: false
+ states: [
+ State {
+ name: "mystate"
+ when: root.prop
+ PropertyChanges {
+ target: root
+ works: "works"
+ }
+ }
+ ]
+ Component.onCompleted: root.prop = new Object
+}
diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
index aa55b42935..26e86672b0 100644
--- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp
+++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp
@@ -188,6 +188,7 @@ private slots:
void revertListMemoryLeak();
void duplicateStateName();
void trivialWhen();
+ void jsValueWhen();
void noStateOsciallation();
void parentChangeCorrectReversal();
void revertNullObjectBinding();
@@ -1734,6 +1735,16 @@ void tst_qquickstates::trivialWhen()
QVERIFY(c.create());
}
+void tst_qquickstates::jsValueWhen()
+{
+ QQmlEngine engine;
+
+ QQmlComponent c(&engine, testFileUrl("jsValueWhen.qml"));
+ QScopedPointer<QObject> root(c.create());
+ QVERIFY(root);
+ QVERIFY(root->property("works").toBool());
+}
+
void tst_qquickstates::noStateOsciallation()
{
QQmlEngine engine;