diff options
author | Tarja Sundqvist <[email protected]> | 2024-11-08 15:36:12 +0200 |
---|---|---|
committer | Tarja Sundqvist <[email protected]> | 2024-11-08 15:36:12 +0200 |
commit | abe4729ea8db32124c36dc33fc32eb629df03043 (patch) | |
tree | 6c02a1174b4abbcec9a127758b2c406975661312 | |
parent | 7c32569ad27b743b6cb50e2bcb67c9ca1674f238 (diff) | |
parent | 7550f26e156b3bca5f4b6a9711352c2aa0ba8463 (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
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 = ¬ifyList->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 = ¬ifyList->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; |