From 4838687eaa63de6e4d6821f14cf865a13af9ebc1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 8 Mar 2018 16:04:26 +0100 Subject: Initialize all members of QQmlPropertyData This lead to quite a few valgrind warnings in test cases. Change-Id: Icef0fc5f93a68e4fe67e1ecd4755b456ad4778a9 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycache_p.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 51a191a41f..b78a2ddd20 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -81,6 +81,7 @@ template class QQmlPropertyCacheAliasCreator; // We have this somewhat awful split between RawData and Data so that RawData can be // used in unions. In normal code, you should always use Data which initializes RawData // to an invalid state on construction. +// ### We should be able to remove this split nowadays class QQmlPropertyRawData { public: @@ -273,20 +274,20 @@ public: private: Flags _flags; - qint16 _coreIndex; - quint16 _propType; + qint16 _coreIndex = 0; + quint16 _propType = 0; // The notify index is in the range returned by QObjectPrivate::signalIndex(). // This is different from QMetaMethod::methodIndex(). - qint16 _notifyIndex; - qint16 _overrideIndex; + qint16 _notifyIndex = 0; + qint16 _overrideIndex = 0; - quint8 _revision; - quint8 _typeMinorVersion; - qint16 _metaObjectOffset; + quint8 _revision = 0; + quint8 _typeMinorVersion = 0; + qint16 _metaObjectOffset = 0; - QQmlPropertyCacheMethodArguments *_arguments; - StaticMetaCallFunction _staticMetaCallFunction; + QQmlPropertyCacheMethodArguments *_arguments = nullptr; + StaticMetaCallFunction _staticMetaCallFunction = nullptr; friend class QQmlPropertyData; friend class QQmlPropertyCache; -- cgit v1.2.3 From f514451cc2e3610e160b5dc8ccd1e390730ecc67 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2018 16:18:15 +0100 Subject: Fix unnecessary evaluation of dependent bindings Given two simple bindings in this order property int firstVar: secondVar property int secondVar: ... then the binding expression for "secondVar" ends up being evaluated twice at run-time. The first time happens when enabling the binding expression for "firstVar", which results in the engine detecting that there is a dependency onto another binding that has not been enabled yet. This is when QQmlData::flushPendingBinding(Impl) enables the expression for secondVar and does an initial evaluation. Afterwards the QQmlObjectCreator continues enabling the next binding in ::finalize(), which will end up evaluating secondVar a second time, unnecessarily. We can detect this case inside setEnabled and only call update() if we transition from disabled to enabled state. This should also cover the case of bindings created and assigned dynamically through QtQuick PropertyChanges / States, as those call setEnabled(false) before removing the binding (to replace it with something else) and setEnabled(true) when reverting the state (in QQmlPropertyPrivate::setBinding). Change-Id: I447432891eabff2c4393f5abfee1092992746fa0 Task-number: QTBUG-66945 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlbinding.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index ca3bff43a4..bc7fccb2c0 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -490,6 +490,7 @@ void QQmlBinding::refresh() void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { + const bool wasEnabled = enabledFlag(); setEnabledFlag(e); setNotifyOnValueChanged(e); @@ -499,7 +500,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) m_nextBinding.clearFlag2(); } - if (e) + if (e && !wasEnabled) update(flags); } -- cgit v1.2.3 From aa94c6c0469b0595f483f13ac88459f0035deef9 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 22 Feb 2018 12:14:34 +0100 Subject: Fix issue with bindings to aliases that cannot yet be resolved When an alias points to a child object which has not yet been initialized, it's id won't have been registered yet, so setting up a binding to it will result in a crash. The fix is: when setting a binding target fails, and its target property is an alias, queue them until all bindings have been set up, and try again. Task-number: QTBUG-57041 Change-Id: I4dc5a6d25c0a32fed9fd952c955e2006c76be45a Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 11 ++-- src/qml/qml/qqmlbinding_p.h | 4 +- src/qml/qml/qqmlobjectcreator.cpp | 135 +++++++++++++++++++++++--------------- src/qml/qml/qqmlobjectcreator_p.h | 3 + src/qml/qml/qqmlproperty.cpp | 1 + 5 files changed, 97 insertions(+), 57 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index bc7fccb2c0..c314833304 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -515,13 +515,13 @@ void QQmlBinding::setTarget(const QQmlProperty &prop) setTarget(prop.object(), pd->core, &pd->valueTypeData); } -void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) +bool QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const QQmlPropertyData *valueType) { m_target = object; if (!object) { m_targetIndex = QQmlPropertyIndex(); - return; + return false; } int coreIndex = core.coreIndex(); @@ -531,9 +531,10 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const int aValueTypeIndex; if (!vme->aliasTarget(coreIndex, &object, &coreIndex, &aValueTypeIndex)) { + // can't resolve id (yet) m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } if (valueTypeIndex == -1) valueTypeIndex = aValueTypeIndex; @@ -542,7 +543,7 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const if (!data || !data->propertyCache) { m_target = nullptr; m_targetIndex = QQmlPropertyIndex(); - return; + return false; } QQmlPropertyData *propertyData = data->propertyCache->property(coreIndex); Q_ASSERT(propertyData); @@ -558,6 +559,8 @@ void QQmlBinding::setTarget(QObject *object, const QQmlPropertyData &core, const data->propertyCache = QQmlEnginePrivate::get(context()->engine)->cache(m_target->metaObject()); data->propertyCache->addref(); } + + return true; } void QQmlBinding::getPropertyData(QQmlPropertyData **propertyData, QQmlPropertyData *valueTypeData) const diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index 19ec3f5d4f..b67b02975f 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -72,6 +72,8 @@ class Q_QML_PRIVATE_EXPORT QQmlBinding : public QQmlJavaScriptExpression, { friend class QQmlAbstractBinding; public: + typedef QExplicitlySharedDataPointer Ptr; + static QQmlBinding *create(const QQmlPropertyData *, const QQmlScriptString &, QObject *, QQmlContext *); static QQmlBinding *create(const QQmlPropertyData *, const QString &, QObject *, QQmlContextData *, const QString &url = QString(), quint16 lineNumber = 0); @@ -82,7 +84,7 @@ public: ~QQmlBinding() override; void setTarget(const QQmlProperty &); - void setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); + bool setTarget(QObject *, const QQmlPropertyData &, const QQmlPropertyData *valueType); void setNotifyOnValueChanged(bool); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 36e56a01f8..90f3beb40b 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -780,7 +780,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings) qSwap(_currentList, savedList); } -bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) +bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding) { if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty()); @@ -802,7 +802,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } // ### resolve this at compile time - if (property && property->propType() == qMetaTypeId()) { + if (bindingProperty && bindingProperty->propType() == qMetaTypeId()) { QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject); ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid; ss.d.data()->lineNumber = binding->location.line; @@ -815,7 +815,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QQmlPropertyData::RemoveBindingOnAliasWrite; int propertyWriteStatus = -1; void *argv[] = { &ss, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); return true; } @@ -826,7 +826,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - if (!property) // ### error + if (!bindingProperty) // ### error return true; if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) { @@ -838,20 +838,20 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con const QQmlPropertyData *valueTypeProperty = nullptr; QObject *bindingTarget = _bindingTarget; - if (QQmlValueTypeFactory::isValueType(property->propType())) { - valueType = QQmlValueTypeFactory::valueType(property->propType()); + if (QQmlValueTypeFactory::isValueType(bindingProperty->propType())) { + valueType = QQmlValueTypeFactory::valueType(bindingProperty->propType()); if (!valueType) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; } - valueType->read(_qobject, property->coreIndex()); + valueType->read(_qobject, bindingProperty->coreIndex()); groupObject = valueType; - valueTypeProperty = property; + valueTypeProperty = bindingProperty; } else { void *argv[1] = { &groupObject }; - QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, bindingProperty->coreIndex(), argv); if (!groupObject) { recordError(binding->location, tr("Cannot set properties on %1 as it is null").arg(stringAt(binding->propertyNameIndex))); return false; @@ -864,21 +864,21 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; if (valueType) - valueType->write(_qobject, property->coreIndex(), QQmlPropertyData::BypassInterceptor); + valueType->write(_qobject, bindingProperty->coreIndex(), QQmlPropertyData::BypassInterceptor); return true; } } - if (_ddata->hasBindingBit(property->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) + if (_ddata->hasBindingBit(bindingProperty->coreIndex()) && !(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) && !_valueTypeProperty) - QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(property->coreIndex())); + QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex())); if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - int signalIndex = _propertyCache->methodIndexToSignalIndex(property->coreIndex()); + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, context, _scopeObject, runtimeFunction, currentQmlContext()); @@ -890,34 +890,44 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // the point property (_qobjectForBindings) and after evaluating the expression, // the result is written to a value type virtual property, that contains the sub-index // of the "x" property. - QQmlBinding *qmlBinding; - const QQmlPropertyData *prop = property; + QQmlBinding::Ptr qmlBinding; + const QQmlPropertyData *targetProperty = bindingProperty; const QQmlPropertyData *subprop = nullptr; if (_valueTypeProperty) { - prop = _valueTypeProperty; - subprop = property; + targetProperty = _valueTypeProperty; + subprop = bindingProperty; } if (binding->containsTranslations()) { qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context); } else { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - qmlBinding = QQmlBinding::create(prop, runtimeFunction, _scopeObject, context, currentQmlContext()); + qmlBinding = QQmlBinding::create(targetProperty, runtimeFunction, _scopeObject, context, currentQmlContext()); } - qmlBinding->setTarget(_bindingTarget, *prop, subprop); - sharedState->allCreatedBindings.push(QQmlAbstractBinding::Ptr(qmlBinding)); + auto bindingTarget = _bindingTarget; + auto valueTypeProperty = _valueTypeProperty; + auto assignBinding = [qmlBinding, bindingTarget, targetProperty, subprop, bindingProperty, valueTypeProperty](QQmlObjectCreatorSharedState *sharedState) -> bool { + if (!qmlBinding->setTarget(bindingTarget, *targetProperty, subprop) && targetProperty->isAlias()) + return false; + + sharedState->allCreatedBindings.push(qmlBinding); - if (property->isAlias()) { - QQmlPropertyPrivate::setBinding(qmlBinding, QQmlPropertyPrivate::DontEnable); - } else { - qmlBinding->addToObject(); + if (bindingProperty->isAlias()) { + QQmlPropertyPrivate::setBinding(qmlBinding.data(), QQmlPropertyPrivate::DontEnable); + } else { + qmlBinding->addToObject(); - if (!_valueTypeProperty) { - QQmlData *targetDeclarativeData = QQmlData::get(_bindingTarget); - Q_ASSERT(targetDeclarativeData); - targetDeclarativeData->setPendingBindingBit(_bindingTarget, property->coreIndex()); + if (!valueTypeProperty) { + QQmlData *targetDeclarativeData = QQmlData::get(bindingTarget); + Q_ASSERT(targetDeclarativeData); + targetDeclarativeData->setPendingBindingBit(bindingTarget, bindingProperty->coreIndex()); + } } - } + + return true; + }; + if (!assignBinding(sharedState.data())) + pendingAliasBindings.push_back(assignBinding); } return true; } @@ -934,9 +944,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vs->setTarget(prop); return true; } @@ -946,8 +956,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con QObject *target = createdSubObject->parent(); QQmlPropertyIndex propertyIndex; - if (property->isAlias()) { - QQmlPropertyIndex originalIndex(property->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); + if (bindingProperty->isAlias()) { + QQmlPropertyIndex originalIndex(bindingProperty->coreIndex(), _valueTypeProperty ? _valueTypeProperty->coreIndex() : -1); QQmlPropertyIndex propIndex; QQmlPropertyPrivate::findAliasTarget(target, originalIndex, &target, &propIndex); QQmlData *data = QQmlData::get(target); @@ -964,9 +974,9 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { QQmlProperty prop; if (_valueTypeProperty) - prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, property, context); + prop = QQmlPropertyPrivate::restore(target, *_valueTypeProperty, bindingProperty, context); else - prop = QQmlPropertyPrivate::restore(target, *property, nullptr, context); + prop = QQmlPropertyPrivate::restore(target, *bindingProperty, nullptr, context); vi->setTarget(prop); propertyIndex = QQmlPropertyPrivate::propertyIndex(prop); } @@ -982,8 +992,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con // Assigning object to signal property? if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { - if (!property->isFunction()) { - recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(property->name(_qobject))); + if (!bindingProperty->isFunction()) { + recordError(binding->valueLocation, tr("Cannot assign an object to signal property %1").arg(bindingProperty->name(_qobject))); return false; } QMetaMethod method = QQmlMetaType::defaultMethod(createdSubObject); @@ -992,7 +1002,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QMetaMethod signalMethod = _qobject->metaObject()->method(property->coreIndex()); + QMetaMethod signalMethod = _qobject->metaObject()->method(bindingProperty->coreIndex()); if (!QMetaObject::checkConnectArgs(signalMethod, method)) { recordError(binding->valueLocation, tr("Cannot connect mismatched signal/slot %1 %vs. %2") @@ -1001,7 +1011,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con return false; } - QQmlPropertyPrivate::connect(_qobject, property->coreIndex(), createdSubObject, method.methodIndex()); + QQmlPropertyPrivate::connect(_qobject, bindingProperty->coreIndex(), createdSubObject, method.methodIndex()); return true; } @@ -1010,32 +1020,32 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con int propertyWriteStatus = -1; void *argv[] = { nullptr, nullptr, &propertyWriteStatus, &propertyWriteFlags }; - if (const char *iid = QQmlMetaType::interfaceIId(property->propType())) { + if (const char *iid = QQmlMetaType::interfaceIId(bindingProperty->propType())) { void *ptr = createdSubObject->qt_metacast(iid); if (ptr) { argv[0] = &ptr; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } else { recordError(binding->location, tr("Cannot assign object to interface property")); return false; } - } else if (property->propType() == QMetaType::QVariant) { - if (property->isVarProperty()) { + } else if (bindingProperty->propType() == QMetaType::QVariant) { + if (bindingProperty->isVarProperty()) { QV4::Scope scope(v4); QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); - _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); argv[0] = &value; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } - } else if (property->isQList()) { + } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); void *itemToAdd = createdSubObject; const char *iid = nullptr; - int listItemType = QQmlEnginePrivate::get(engine)->listType(property->propType()); + int listItemType = QQmlEnginePrivate::get(engine)->listType(bindingProperty->propType()); if (listItemType != -1) iid = QQmlMetaType::interfaceIId(listItemType); if (iid) @@ -1051,17 +1061,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else { // pointer compatibility was tested in QQmlPropertyValidator at type compile time argv[0] = &createdSubObject; - QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, property->coreIndex(), argv); + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } return true; } - if (property->isQList()) { + if (bindingProperty->isQList()) { recordError(binding->location, tr("Cannot assign primitives to lists")); return false; } - setPropertyValue(property, binding); + setPropertyValue(bindingProperty, binding); return true; } @@ -1274,12 +1284,33 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo qSwap(_qmlContext, qmlContext); - bool result = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + bool ok = populateInstance(index, instance, /*binding target*/instance, /*value type property*/nullptr); + if (ok) { + if (isContextObject && !pendingAliasBindings.empty()) { + bool processedAtLeastOneBinding = false; + do { + processedAtLeastOneBinding = false; + for (std::vector::iterator it = pendingAliasBindings.begin(); + it != pendingAliasBindings.end(); ) { + if ((*it)(sharedState.data())) { + it = pendingAliasBindings.erase(it); + processedAtLeastOneBinding = true; + } else { + ++it; + } + } + } while (processedAtLeastOneBinding && pendingAliasBindings.empty()); + Q_ASSERT(pendingAliasBindings.empty()); + } + } else { + // an error occurred, so we can't setup the pending alias bindings + pendingAliasBindings.clear(); + } qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); - return result ? instance : nullptr; + return ok ? instance : nullptr; } QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interrupt) diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 399a5f6d4a..67a5bdd827 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -161,6 +161,9 @@ private: QV4::QmlContext *_qmlContext; friend struct QQmlObjectCreatorRecursionWatcher; + + typedef std::function PendingAliasBinding; + std::vector pendingAliasBindings; }; struct QQmlObjectCreatorRecursionWatcher diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ebf296b29d..c4487f91a3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -877,6 +877,7 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, QQmlPropertyIndex bin void QQmlPropertyPrivate::setBinding(QQmlAbstractBinding *binding, BindingFlags flags, QQmlPropertyData::WriteFlags writeFlags) { Q_ASSERT(binding); + Q_ASSERT(binding->targetObject()); QObject *object = binding->targetObject(); const QQmlPropertyIndex index = binding->targetPropertyIndex(); -- cgit v1.2.3 From 3339bf866a68ba129e88fd7d27a2abbea0d1c6ba Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 8 Mar 2018 15:02:45 +0100 Subject: When deactivating a loader, do not immediately clear its context In 2eb2d6386da304cd1164264ae0bff685c796d89c, deactivating/clearing the loader would now prevent any subsequent bindings from being evaluated. The problem there was that the item created by the loader wouldn't have a parent item (among things) anymore, so references to it in the bindings would result in errors. The way to prevent it was done by invalidating the context of the item, which in turn would detach it from the root context. This is a problem if objects in the root context are referenced after deactivating/clearing the loader: onSomethingChanged: { loader.source = "" objectInRootContext.doIt() } This would result in a ReferenceError when resolving objectInRootContext and break the behavior present before the fix mentioned above. The correct way is to recursively clear the context set on all bindings, but leave everything in place. This way, no subsequent bindings will be evaluated, but the currently "running" scripts will still be able to reach the root context. Task-number: QTBUG-66822 Change-Id: Ic9c2ab0a752093a26967da4783cb4c29cf83d2ca Reviewed-by: Simon Hausmann Reviewed-by: Michael Brasser --- src/qml/qml/qqmlcontext.cpp | 8 ++++++++ src/qml/qml/qqmlcontext_p.h | 1 + 2 files changed, 9 insertions(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 6e43bc735f..5dd3278b4c 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -593,6 +593,14 @@ void QQmlContextData::invalidate() parent = nullptr; } +void QQmlContextData::clearContextRecursively() +{ + clearContext(); + + for (auto ctxIt = childContexts; ctxIt; ctxIt = ctxIt->nextChild) + ctxIt->clearContextRecursively(); +} + void QQmlContextData::clearContext() { emitDestruction(); diff --git a/src/qml/qml/qqmlcontext_p.h b/src/qml/qml/qqmlcontext_p.h index ff36d6c9a8..5dfee48848 100644 --- a/src/qml/qml/qqmlcontext_p.h +++ b/src/qml/qml/qqmlcontext_p.h @@ -115,6 +115,7 @@ public: QQmlContextData(QQmlContext *); void emitDestruction(); void clearContext(); + void clearContextRecursively(); void invalidate(); inline bool isValid() const { -- cgit v1.2.3 From d7b361bc33992ed61310b709df476cc4fa9f67e5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2018 17:15:41 +0100 Subject: Reduce the overhead of type loader locking Allow pulling the shared mutex out of the QQmlThread for the type loader so that the lock and unlock calls can be inlined. We do a lot more of those now. Task-number: QTBUG-41465 Change-Id: I42f3d17feb08863f51b003b061d89f49c5a6d574 Reviewed-by: Michael Brasser --- src/qml/qml/ftw/qqmlthread.cpp | 6 ++++++ src/qml/qml/ftw/qqmlthread_p.h | 2 ++ src/qml/qml/qqmltypeloader.cpp | 16 ++++------------ src/qml/qml/qqmltypeloader_p.h | 5 +++-- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp index 78b477b472..96313d7627 100644 --- a/src/qml/qml/ftw/qqmlthread.cpp +++ b/src/qml/qml/ftw/qqmlthread.cpp @@ -57,6 +57,7 @@ public: void run() override; + inline QMutex &mutex() { return _mutex; } inline void lock() { _mutex.lock(); } inline void unlock() { _mutex.unlock(); } inline void wait() { _wait.wait(&_mutex); } @@ -263,6 +264,11 @@ bool QQmlThread::isShutdown() const return d->m_shutdown; } +QMutex &QQmlThread::mutex() +{ + return d->mutex(); +} + void QQmlThread::lock() { d->lock(); diff --git a/src/qml/qml/ftw/qqmlthread_p.h b/src/qml/qml/ftw/qqmlthread_p.h index 0ed12a2972..b5c580fe8b 100644 --- a/src/qml/qml/ftw/qqmlthread_p.h +++ b/src/qml/qml/ftw/qqmlthread_p.h @@ -59,6 +59,7 @@ QT_BEGIN_NAMESPACE class QThread; +class QMutex; class QQmlThreadPrivate; class QQmlThread @@ -71,6 +72,7 @@ public: void shutdown(); bool isShutdown() const; + QMutex &mutex(); void lock(); void unlock(); void wakeOne(); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 61208d1c4a..5b954605e0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -983,16 +983,6 @@ void QQmlTypeLoader::setProfiler(QQmlProfiler *profiler) } #endif -void QQmlTypeLoader::lock() -{ - m_thread->lock(); -} - -void QQmlTypeLoader::unlock() -{ - m_thread->unlock(); -} - struct PlainLoader { void loadThread(QQmlTypeLoader *loader, QQmlDataBlob *blob) const { @@ -1632,8 +1622,10 @@ bool QQmlTypeLoaderQmldirContent::designerSupported() const Constructs a new type loader that uses the given \a engine. */ QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine) - : m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)), - m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) + : m_engine(engine) + , m_thread(new QQmlTypeLoaderThread(this)) + , m_mutex(m_thread->mutex()) + , m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD) { } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 713f707387..df79d13f1c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -313,8 +313,8 @@ public: bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; - void lock(); - void unlock(); + void lock() { m_mutex.lock(); } + void unlock() { m_mutex.unlock(); } void load(QQmlDataBlob *, Mode = PreferSynchronous); void loadWithStaticData(QQmlDataBlob *, const QByteArray &, Mode = PreferSynchronous); @@ -381,6 +381,7 @@ private: QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; + QMutex &m_mutex; #if QT_CONFIG(qml_debug) QScopedPointer m_profiler; -- cgit v1.2.3 From a3ad52526f79c1528f170c8affe5af00b68ca61d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2018 17:08:21 +0100 Subject: Fix crash when calling QQmlEngine::clearComponentCache() We must protect various resources in the type loader with our existing lock. The QQmlTypeLoaderQmldirContent is now value based, so that we can release the lock on the shared cache early. Copying it involves adjusting the refcount of the QHash and QString instances in the QQmlDirParser. The safety of this was verified with a TSAN build and the example supplied in the task. It crashed reliably with TASN errors first and with this patch it runs without errors. Task-number: QTBUG-41465 Change-Id: I616843c4b8bdfd65d1277d4faa8cb884d8e77df8 Reviewed-by: Lars Knoll --- src/qml/qml/qqmldirparser_p.h | 2 -- src/qml/qml/qqmlengine.cpp | 2 ++ src/qml/qml/qqmlimport.cpp | 62 +++++++++++++++++++++--------------------- src/qml/qml/qqmlimport_p.h | 2 +- src/qml/qml/qqmltypeloader.cpp | 55 ++++++++++++++++++++----------------- src/qml/qml/qqmltypeloader_p.h | 9 ++++-- 6 files changed, 71 insertions(+), 61 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h index 95370398ad..820c40238d 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qml/qqmldirparser_p.h @@ -63,8 +63,6 @@ class QQmlError; class QQmlEngine; class Q_QML_PRIVATE_EXPORT QQmlDirParser { - Q_DISABLE_COPY(QQmlDirParser) - public: QQmlDirParser(); ~QQmlDirParser(); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4054d2f0be..49f25e89fe 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1104,7 +1104,9 @@ QQmlEngine::~QQmlEngine() void QQmlEngine::clearComponentCache() { Q_D(QQmlEngine); + d->typeLoader.lock(); d->typeLoader.clearCache(); + d->typeLoader.unlock(); } /*! diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 8e6cbcbd7e..005db4248e 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -318,17 +318,17 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList *errors); + QQmlTypeLoaderQmldirContent *qmldir, QList *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); @@ -668,14 +668,14 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; - qmlDirComponents = qmldir->components(); + qmlDirComponents = qmldir.components(); - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already for (QList::const_iterator it = nameSpace->imports.constBegin(); @@ -1068,26 +1068,26 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoaderQmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent &qmldir, QList *errors) { - Q_ASSERT(qmldir); + Q_ASSERT(qmldir.hasContent()); if (qmlImportTrace()) qDebug().nospace() << "QQmlImports(" << qPrintable(base) << ")::importExtension: " << "loaded " << qmldirFilePath; - if (designerSupportRequired && !qmldir->designerSupported()) { + if (designerSupportRequired && !qmldir.designerSupported()) { if (errors) { QQmlError error; - error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir->typeNamespace())); + error.setDescription(QQmlImportDatabase::tr("module does not support the designer \"%1\"").arg(qmldir.typeNamespace())); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } return false; } - int qmldirPluginCount = qmldir->plugins().count(); + int qmldirPluginCount = qmldir.plugins().count(); if (qmldirPluginCount == 0) return true; @@ -1098,7 +1098,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, // listed plugin inside qmldir. And for this reason, mixing dynamic and static plugins inside a // single module is not recommended. - QString typeNamespace = qmldir->typeNamespace(); + QString typeNamespace = qmldir.typeNamespace(); QString qmldirPath = qmldirFilePath; int slash = qmldirPath.lastIndexOf(Slash); if (slash > 0) @@ -1108,7 +1108,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, int staticPluginsFound = 0; #if defined(QT_SHARED) - const auto qmldirPlugins = qmldir->plugins(); + const auto qmldirPlugins = qmldir.plugins(); for (const QQmlDirParser::Plugin &plugin : qmldirPlugins) { QString resolvedFilePath = database->resolvePlugin(typeLoader, qmldirPath, plugin.path, plugin.name); if (!resolvedFilePath.isEmpty()) { @@ -1174,7 +1174,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, if (qmldirPluginCount > 1 && staticPluginsFound > 0) error.setDescription(QQmlImportDatabase::tr("could not resolve all plugins for module \"%1\"").arg(uri)); else - error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir->plugins()[dynamicPluginsFound].name)); + error.setDescription(QQmlImportDatabase::tr("module \"%1\" plugin \"%2\" not found").arg(uri).arg(qmldir.plugins()[dynamicPluginsFound].name)); error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); errors->prepend(error); } @@ -1187,17 +1187,17 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoaderQmldirContent **qmldir, QList *errors) + QQmlTypeLoaderQmldirContent *qmldir, QList *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); *qmldir = typeLoader->qmldirContent(qmldirIdentifier); - if (*qmldir) { + if ((*qmldir).hasContent()) { // Ensure that parsing was successful - if ((*qmldir)->hasError()) { + if ((*qmldir).hasError()) { QUrl url = QUrl::fromLocalFile(qmldirIdentifier); - const QList qmldirErrors = (*qmldir)->errors(uri); + const QList qmldirErrors = (*qmldir).errors(uri); for (int i = 0; i < qmldirErrors.size(); ++i) { QQmlError error = qmldirErrors.at(i); error.setUrl(url); @@ -1323,14 +1323,14 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent &qmldir, const QString &uri, int vmaj, int vmin, QList *errors) { int lowest_min = INT_MAX; int highest_min = INT_MIN; typedef QQmlDirComponents::const_iterator ConstIterator; - const QQmlDirComponents &components = qmldir->components(); + const QQmlDirComponents &components = qmldir.components(); ConstIterator cend = components.constEnd(); for (ConstIterator cit = components.constBegin(); cit != cend; ++cit) { @@ -1354,7 +1354,7 @@ bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent } typedef QList::const_iterator SConstIterator; - const QQmlDirScripts &scripts = qmldir->scripts(); + const QQmlDirScripts &scripts = qmldir.scripts(); SConstIterator send = scripts.constEnd(); for (SConstIterator sit = scripts.constBegin(); sit != send; ++sit) { @@ -1446,14 +1446,14 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) @@ -1471,7 +1471,7 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre error.setDescription(QQmlImportDatabase::tr("module \"%1\" is not installed").arg(uri)); errors->prepend(error); return false; - } else if ((vmaj >= 0) && (vmin >= 0) && qmldir) { + } else if ((vmaj >= 0) && (vmin >= 0) && qmldir.hasContent()) { // Verify that the qmldir content is valid for this version if (!validateQmldirVersion(qmldir, uri, vmaj, vmin, errors)) return false; @@ -1565,12 +1565,12 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; - if (qmldir) { - if (!importExtension(qmldir->pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) + if (qmldir.hasContent()) { + if (!importExtension(qmldir.pluginLocation(), importUri, vmaj, vmin, database, qmldir, errors)) return false; if (!inserted->setQmldirContent(url, qmldir, nameSpace, errors)) @@ -1589,14 +1589,14 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & Q_ASSERT(nameSpace); if (QQmlImportInstance *import = nameSpace->findImport(uri)) { - const QQmlTypeLoaderQmldirContent *qmldir = nullptr; + QQmlTypeLoaderQmldirContent qmldir; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; - if (qmldir) { + if (qmldir.hasContent()) { int vmaj = import->majversion; int vmin = import->minversion; - if (!importExtension(qmldir->pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) + if (!importExtension(qmldir.pluginLocation(), uri, vmaj, vmin, database, qmldir, errors)) return false; if (import->setQmldirContent(qmldirUrl, qmldir, nameSpace, errors)) { diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index b70bb5253c..2437979ef8 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -85,7 +85,7 @@ struct QQmlImportInstance QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent &qmldir, QQmlImportNamespace *nameSpace, QList *errors); static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5b954605e0..2b778b0b63 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1370,8 +1370,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1418,8 +1418,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); - const auto qmldirScripts = qmldir->scripts(); + const QQmlTypeLoaderQmldirContent qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const auto qmldirScripts = qmldir.scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl); @@ -1584,6 +1584,7 @@ QString QQmlTypeLoaderQmldirContent::typeNamespace() const void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { + m_hasContent = true; m_location = location; m_parser.parse(content); } @@ -1808,6 +1809,7 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) int lastSlash = path.lastIndexOf(QLatin1Char('/')); QString dirPath(path.left(lastSlash)); + LockHolder holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache *entry = exists ? new QCache : nullptr; @@ -1871,6 +1873,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) --length; QString dirPath(path.left(length)); + LockHolder holder(this); if (!m_importDirCache.contains(dirPath)) { bool exists = QDir(dirPath).exists(); QCache *files = exists ? new QCache : nullptr; @@ -1889,8 +1892,10 @@ Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQm It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent QQmlTypeLoader::qmldirContent(const QString &filePathIn) { + LockHolder holder(this); + QString filePath; // Try to guess if filePathIn is already a URL. This is necessarily fragile, because @@ -1904,39 +1909,39 @@ const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString & filePath = filePathIn; } else { filePath = QQmlFile::urlToLocalFileOrQrc(url); - if (filePath.isEmpty()) // Can't load the remote here, but should be cached - return *(m_importQmlDirCache.value(filePathIn)); + if (filePath.isEmpty()) { // Can't load the remote here, but should be cached + if (auto entry = m_importQmlDirCache.value(filePathIn)) + return **entry; + else + return QQmlTypeLoaderQmldirContent(); + } } - QQmlTypeLoaderQmldirContent *qmldir; QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); - if (!val) { - qmldir = new QQmlTypeLoaderQmldirContent; + if (val) + return **val; + QQmlTypeLoaderQmldirContent *qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) #define CASE_MISMATCH_ERROR QString(QLatin1String("cannot load module \"$$URI$$\": File name case mismatch for \"%1\"")) - QFile file(filePath); - if (!QQml_isFileCaseCorrect(filePath)) { - ERROR(CASE_MISMATCH_ERROR.arg(filePath)); - } else if (file.open(QFile::ReadOnly)) { - QByteArray data = file.readAll(); - qmldir->setContent(filePath, QString::fromUtf8(data)); - } else { - ERROR(NOT_READABLE_ERROR.arg(filePath)); - } + QFile file(filePath); + if (!QQml_isFileCaseCorrect(filePath)) { + ERROR(CASE_MISMATCH_ERROR.arg(filePath)); + } else if (file.open(QFile::ReadOnly)) { + QByteArray data = file.readAll(); + qmldir->setContent(filePath, QString::fromUtf8(data)); + } else { + ERROR(NOT_READABLE_ERROR.arg(filePath)); + } #undef ERROR #undef NOT_READABLE_ERROR #undef CASE_MISMATCH_ERROR - m_importQmlDirCache.insert(filePath, qmldir); - } else { - qmldir = *val; - } - - return qmldir; + m_importQmlDirCache.insert(filePath, qmldir); + return *qmldir; } void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index df79d13f1c..4665d342c1 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -228,12 +228,16 @@ class QQmlTypeLoaderQmldirContent { private: friend class QQmlTypeLoader; - QQmlTypeLoaderQmldirContent(); void setContent(const QString &location, const QString &content); void setError(const QQmlError &); public: + QQmlTypeLoaderQmldirContent(); + QQmlTypeLoaderQmldirContent(const QQmlTypeLoaderQmldirContent &) = default; + QQmlTypeLoaderQmldirContent &operator=(const QQmlTypeLoaderQmldirContent &) = default; + + bool hasContent() const { return m_hasContent; } bool hasError() const; QList errors(const QString &uri) const; @@ -250,6 +254,7 @@ public: private: QQmlDirParser m_parser; QString m_location; + bool m_hasContent = false; }; class Q_QML_PRIVATE_EXPORT QQmlTypeLoader @@ -304,7 +309,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); -- cgit v1.2.3 From 1de4d413c9dd5c8ad5859f944d9ded7d8778f777 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2018 17:01:21 +0100 Subject: Fix assigning objects to QJSValue properties We support simple object bindings such as someProperty: Rectangle { ... } when the type of "someProperty" is QVariant, but we produce an error when it's QJSValue. There is no good reason for that, and the fix for QTBUG-67118 requires this. Change-Id: Ia5dc88749bcba0b5c781a6ab2b4a9fb92299e0ac Reviewed-by: Mitch Curtis Reviewed-by: Lars Knoll --- src/qml/qml/qqmlobjectcreator.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 90f3beb40b..7051fb51da 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -55,6 +55,7 @@ #include #include #include +#include QT_USE_NAMESPACE @@ -1039,6 +1040,17 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper argv[0] = &value; QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } + } else if (bindingProperty->propType() == qMetaTypeId()) { + QV4::Scope scope(v4); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); + if (bindingProperty->isVarProperty()) { + _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); + } else { + QJSValue value; + QJSValuePrivate::setValue(&value, v4, wrappedObject); + argv[0] = &value; + QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); + } } else if (bindingProperty->isQList()) { Q_ASSERT(_currentList.object); -- cgit v1.2.3 From 22b13921f8067f8a93164875a4ad59bed85b0400 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 19 Mar 2018 13:14:13 +0100 Subject: Handle function expressions as signal handlers There are two ways to use function expressions on the right-hand side of bindings: property var somethingPressed somethingPressed: function() { /* ..press something else.. */ } signal buttonPressed onButtonPressed: function() { /* ..handle buttonPress.. */ } In the former case, it declares a property that holds a function. So on initialization, the right-hand side of the binding returns a closure that gets assigned to the property 'somethingPressed'. In the latter case, the signal handler is explicitly marked as a function for clarity. So, the handler should not be returning the closure, but the handler should *be* the closure. In general, it is not possible to detect if the left-hand side is a property or a signal handler when generating QML cache files ahead of time. So for this case, we mark the function as only returning a closure. Then when instantiating the object, we check if it is a signal handler, and if the handler is marked as only returning a closure. If so, we set that closure to be the signal handler. Task-number: QTBUG-57043 Task-number: QTBUG-50328 Change-Id: I3008ddd847e30b7d0adef07344a326f84d85f1ba Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlobjectcreator.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7051fb51da..7c36f59035 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -879,6 +879,14 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + + // When a user writes the following: + // onSignal: function() { doSomethingUsefull } + // then do not run the binding that returns the closure, but run the closure + // instead. + if (auto closure = runtimeFunction->nestedFunction()) + runtimeFunction = closure; + int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, -- cgit v1.2.3 From 64e90f393146dadb382d154e7d67a9109ab2492a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 9 Mar 2018 16:40:38 +0100 Subject: Fix QML data structure version checking for ahead-of-time generated files We must also do version checking for QML and JS files that were compiled ahead of time and are embedded in resources. If the lookup for the original source code fails, then we must generate an appropriate error message. As an upside we get better error reporting when trying to load an empty file and Qt.include() now reports the error message in the statusText field. The error reporting for imported scripts was not changed as importing an empty script is (oddly) allowed. Task-number: QTBUG-66986 Change-Id: Ie0ef81af371a51ecf8c66ae7954d43f5cc6c12de Reviewed-by: Erik Verbruggen --- src/qml/qml/qqmlmetatype.cpp | 35 +++++++++++++++++++++++++++++++++-- src/qml/qml/qqmlmetatype_p.h | 12 +++++++++++- src/qml/qml/qqmltypeloader.cpp | 38 +++++++++++++++++++++++++++++++------- src/qml/qml/qqmltypeloader_p.h | 5 +++++ 4 files changed, 80 insertions(+), 10 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 7754f0fddc..8fda7f6f77 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,8 @@ #include #include "qqmlcomponent.h" +Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) + QT_BEGIN_NAMESPACE struct QQmlMetaTypeData @@ -2539,18 +2542,46 @@ QList QQmlMetaType::qmlSingletonTypes() return retn; } -const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri) +const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) { QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { - if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) + if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { + QString error; + if (!unit->qmlData->verifyHeader(QDateTime(), &error)) { + qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; + if (status) + *status = CachedUnitLookupError::VersionMismatch; + return nullptr; + } + if (status) + *status = CachedUnitLookupError::NoError; return unit->qmlData; + } } + + if (status) + *status = CachedUnitLookupError::NoUnitFound; + return nullptr; } +void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.prepend(handler); +} + +void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + data->lookupCachedQmlUnit.removeAll(handler); +} + /*! Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 07bef526ba..cd7afc8a01 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -133,7 +133,17 @@ public: static QList parentFunctions(); - static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri); + enum class CachedUnitLookupError { + NoError, + NoUnitFound, + VersionMismatch + }; + + static const QV4::CompiledData::Unit *findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status); + + // used by tst_qqmlcachegen.cpp + static void prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); + static void removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler); static bool namespaceContainsRegistrations(const QString &, int majorVersion); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 2b778b0b63..5572fdad44 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1259,6 +1259,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) QML_MEMORY_SCOPE_URL(blob->url()); QQmlDataBlob::SourceCodeData d; d.inlineSourceCode = QString::fromUtf8(data); + d.hasInlineSourceCode = true; setData(blob, d); } @@ -1670,9 +1671,11 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode) typeData = new QQmlTypeData(url, this); // TODO: if (compiledData == 0), is it safe to omit this insertion? m_typeCache.insert(url, typeData); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url())) { + QQmlMetaType::CachedUnitLookupError error = QQmlMetaType::CachedUnitLookupError::NoError; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(typeData->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(typeData, cachedUnit, mode); } else { + typeData->setCachedUnitStatus(error); QQmlTypeLoader::load(typeData, mode); } } else if ((mode == PreferSynchronous || mode == Synchronous) && QQmlFile::isSynchronous(url)) { @@ -1727,9 +1730,11 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &url) scriptBlob = new QQmlScriptBlob(url, this); m_scriptCache.insert(url, scriptBlob); - if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url())) { + QQmlMetaType::CachedUnitLookupError error; + if (const QV4::CompiledData::Unit *cachedUnit = QQmlMetaType::findCachedCompilationUnit(scriptBlob->url(), &error)) { QQmlTypeLoader::loadWithCachedUnit(scriptBlob, cachedUnit); } else { + scriptBlob->setCachedUnitStatus(error); QQmlTypeLoader::load(scriptBlob); } } @@ -2428,8 +2433,13 @@ void QQmlTypeData::dataReceived(const SourceCodeData &data) if (isError()) return; - if (!m_backupSourceCode.exists()) { - setError(QQmlTypeLoader::tr("No such file or directory")); + if (!m_backupSourceCode.exists() || m_backupSourceCode.isEmpty()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else if (!m_backupSourceCode.exists()) + setError(QQmlTypeLoader::tr("No such file or directory")); + else + setError(QQmlTypeLoader::tr("File is empty")); return; } @@ -2981,6 +2991,13 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data) } } + if (!data.exists()) { + if (m_cachedUnitStatus == QQmlMetaType::CachedUnitLookupError::VersionMismatch) + setError(QQmlTypeLoader::tr("File was compiled ahead of time with an incompatible version of Qt and the original file cannot be found. Please recompile")); + else + setError(QQmlTypeLoader::tr("No such file or directory")); + return; + } QmlIR::Document irUnit(isDebugging()); @@ -3178,7 +3195,7 @@ void QQmlQmldirData::initializeFromCachedUnit(const QV4::CompiledData::Unit *) QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { error->clear(); - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return inlineSourceCode; QFile f(fileInfo.absoluteFilePath()); @@ -3205,7 +3222,7 @@ QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return QDateTime(); QDateTime timeStamp = fileInfo.lastModified(); @@ -3220,11 +3237,18 @@ QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const bool QQmlDataBlob::SourceCodeData::exists() const { - if (!inlineSourceCode.isEmpty()) + if (hasInlineSourceCode) return true; return fileInfo.exists(); } +bool QQmlDataBlob::SourceCodeData::isEmpty() const +{ + if (hasInlineSourceCode) + return inlineSourceCode.isEmpty(); + return fileInfo.size() == 0; +} + QT_END_NAMESPACE #include "qqmltypeloader.moc" diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 4665d342c1..5988632547 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -140,11 +140,13 @@ public: QString readAll(QString *error) const; QDateTime sourceTimeStamp() const; bool exists() const; + bool isEmpty() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; QString inlineSourceCode; QFileInfo fileInfo; + bool hasInlineSourceCode = false; }; protected: @@ -271,6 +273,8 @@ public: const QQmlImports &imports() const { return m_importCache; } + void setCachedUnitStatus(QQmlMetaType::CachedUnitLookupError status) { m_cachedUnitStatus = status; } + protected: bool addImport(const QV4::CompiledData::Import *import, QList *errors); @@ -293,6 +297,7 @@ public: QQmlImports m_importCache; QHash m_unresolvedImports; QList m_qmldirs; + QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError; }; QQmlTypeLoader(QQmlEngine *); -- cgit v1.2.3 From 21301c1dbb00f4a2cd991e520423ed039b297ffb Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 20 Mar 2018 09:44:30 -0500 Subject: Simplify handling of function expressions as signal handlers Change-Id: I4bfa05b4619c248119c78d05e64270e6627f6065 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlboundsignal.cpp | 12 ++++++++++++ src/qml/qml/qqmlobjectcreator.cpp | 8 -------- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 060706ac50..d5117c8cec 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -110,6 +110,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, m_index(index), m_target(target) { + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = function->nestedFunction()) + function = closure; + setupFunction(scope, function); init(ctxt, scopeObject); } @@ -122,6 +128,12 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); + // If the function is marked as having a nested function, then the user wrote: + // onSomeSignal: function() { /*....*/ } + // So take that nested function: + if (auto closure = runtimeFunction->nestedFunction()) + runtimeFunction = closure; + QV4::ExecutionEngine *engine = ctxt->engine->handle(); QList signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames(); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7c36f59035..7051fb51da 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -879,14 +879,6 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) { if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) { QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex]; - - // When a user writes the following: - // onSignal: function() { doSomethingUsefull } - // then do not run the binding that returns the closure, but run the closure - // instead. - if (auto closure = runtimeFunction->nestedFunction()) - runtimeFunction = closure; - int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex()); QQmlBoundSignal *bs = new QQmlBoundSignal(_bindingTarget, signalIndex, _scopeObject, engine); QQmlBoundSignalExpression *expr = new QQmlBoundSignalExpression(_bindingTarget, signalIndex, -- cgit v1.2.3 From 8629682663adb0de5f91d2bd545b5d68e6afb7cd Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 18 Dec 2017 17:40:48 +0100 Subject: Add a feature for DelegateModel Change-Id: Ia24767b33a20bd70096bbb8b4f27729c788eb331 Reviewed-by: Oswald Buddenhagen --- src/qml/qml/qqmlengine.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 49f25e89fe..ebd354d003 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -88,7 +88,9 @@ #include #include #include +#if QT_CONFIG(qml_delegate_model) #include +#endif #include #include #include @@ -240,8 +242,10 @@ void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility qmlRegisterType(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType(uri, versionMajor, versionMinor, "Package"); +#if QT_CONFIG(qml_delegate_model) qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataModel"); qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataGroup"); +#endif qmlRegisterType(uri, versionMajor, versionMinor, "VisualItemModel"); } -- cgit v1.2.3 From 214fbaa57b73296a0a191b5ff2b1fbc8bf0aaa7a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 26 Feb 2018 09:16:06 +0100 Subject: add qmlRegisterModule to all QML plugins (QUIP 99) Now it should always be possible to do import QtQuick.Module x.m where x is the module's major version and m is Qt's minor version. [ChangeLog][QtQuick][Important Behavior Changes] In Qt 5.11 and newer versions, QML plugin modules are available with the same minor version as the Qt release minor version number. For example it's possible to import QtQuick.Window 2.11 or import QtQuick.Layouts 1.11 even though there haven't been any API changes in these modules for Qt 5.11, and the maximum possible import version will automatically increment in future Qt versions. This is intended to reduce confusion. Change-Id: I0d28ed04d186bcdd5acde95b8ed0b66c1c4697e3 Reviewed-by: J-P Nurmi --- src/qml/qml/qqmlengine.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ebd354d003..54a7c5130d 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -257,6 +257,9 @@ void QQmlEnginePrivate::defineQtQuick2Module() // register the QtQuick2 types which are implemented in the QtQml module. registerQtQuick2Types("QtQuick",2,0); qmlRegisterUncreatableType("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + + // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward + qmlRegisterModule("QtQuick", 2, QT_VERSION_MINOR); } bool QQmlEnginePrivate::designerMode() @@ -956,6 +959,9 @@ void QQmlEnginePrivate::init() registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. qmlRegisterUncreatableType("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); + // Auto-increment the import to stay in sync with ALL future QtQml minor versions from 5.11 onward + qmlRegisterModule("QtQml", 2, QT_VERSION_MINOR); + QQmlData::init(); baseModulesUninitialized = false; } -- cgit v1.2.3 From 27bf1eafbf6b3b16e0256feff4f6fce3f4fddc72 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2018 12:44:25 +0200 Subject: Fix QQmlExpression::hasError()/error() on syntax errors Record errors that happen during QV4::Script::parse() time in the same way as we record errors during binding evaluation, in order to correctly set the error state of QQmlExpression. This also removes dead code about setting line, description, etc. which is taken care of by ExecutionEngine::catchExceptionAsQmlError. Task-number: QTBUG-67240 Change-Id: I2d586e16803d0883cdd2d1d262b4c67202c00562 Reviewed-by: Michael Brasser --- src/qml/qml/qqmlexpression.cpp | 5 +++++ src/qml/qml/qqmljavascriptexpression.cpp | 14 +++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 59cc9bb09f..27d3acb9b7 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -252,6 +252,11 @@ QV4::ReturnedValue QQmlExpressionPrivate::v4value(bool *isUndefined) if (!expressionFunctionValid) { createQmlBinding(context(), scopeObject(), expression, url, line); expressionFunctionValid = true; + if (hasError()) { + if (isUndefined) + *isUndefined = true; + return QV4::Encode::undefined(); + } } return evaluate(isUndefined); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 74148e3ca4..3daa107b64 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -451,15 +451,11 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * QV4::Script script(v4, qmlContext, code, filename, line); script.parse(); if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - if (error.description().isEmpty()) - error.setDescription(QLatin1String("Exception occurred during function evaluation")); - if (error.line() == -1) - error.setLine(line); - if (error.url().isEmpty()) - error.setUrl(QUrl::fromLocalFile(filename)); - error.setObject(qmlScope); - ep->warning(error); + QQmlDelayedError *error = delayedError(); + error->catchJavaScriptException(v4); + error->setErrorObject(qmlScope); + if (!error->addError(ep)) + ep->warning(error); return; } setupFunction(qmlContext, script.vmFunction); -- cgit v1.2.3 From b780aeb0ce30b040b9057f1e2b447b08b24b45cb Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 17 Jan 2018 15:44:18 +0100 Subject: Allow for QQmlData::set*Bit and clear*Bit to be inlined Change-Id: I216adf12e7ec402f3ccb4f846165171c9833f23b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmldata_p.h | 57 ++++++++++++++++++++++++++++++--- src/qml/qml/qqmlengine.cpp | 79 ++++++++++------------------------------------ 2 files changed, 70 insertions(+), 66 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 20b96d2c4b..59fefde893 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -208,12 +208,12 @@ public: QQmlData**prevContextObject; inline bool hasBindingBit(int) const; - void clearBindingBit(int); - void setBindingBit(QObject *obj, int); + inline void setBindingBit(QObject *obj, int); + inline void clearBindingBit(int); inline bool hasPendingBindingBit(int index) const; - void setPendingBindingBit(QObject *obj, int); - void clearPendingBindingBit(int); + inline void setPendingBindingBit(QObject *obj, int); + inline void clearPendingBindingBit(int); quint16 lineNumber; quint16 columnNumber; @@ -304,6 +304,27 @@ private: const BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; return bits[offset] & bitFlagForBit(bit); } + + Q_ALWAYS_INLINE void clearBit(int bit) + { + uint offset = QQmlData::offsetForBit(bit); + if (bindingBitsArraySize > offset) { + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + bits[offset] &= ~QQmlData::bitFlagForBit(bit); + } + } + + Q_ALWAYS_INLINE void setBit(QObject *obj, int bit) + { + uint offset = QQmlData::offsetForBit(bit); + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + if (Q_UNLIKELY(bindingBitsArraySize <= offset)) + bits = growBits(obj, bit); + bits[offset] |= QQmlData::bitFlagForBit(bit); + } + + Q_NEVER_INLINE BindingBitsType *growBits(QObject *obj, int bit); + Q_DISABLE_COPY(QQmlData); }; @@ -356,6 +377,20 @@ bool QQmlData::hasBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2); } +void QQmlData::setBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2); +} + +void QQmlData::clearBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2); +} + bool QQmlData::hasPendingBindingBit(int coreIndex) const { Q_ASSERT(coreIndex >= 0); @@ -364,6 +399,20 @@ bool QQmlData::hasPendingBindingBit(int coreIndex) const return hasBitSet(coreIndex * 2 + 1); } +void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + setBit(obj, coreIndex * 2 + 1); +} + +void QQmlData::clearPendingBindingBit(int coreIndex) +{ + Q_ASSERT(coreIndex >= 0); + Q_ASSERT(coreIndex <= 0xffff); + clearBit(coreIndex * 2 + 1); +} + void QQmlData::flushPendingBinding(QObject *o, QQmlPropertyIndex propertyIndex) { QQmlData *data = QQmlData::get(o, false); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 54a7c5130d..613f9b4fe5 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -111,12 +111,6 @@ Q_DECLARE_METATYPE(QQmlProperty) QT_BEGIN_NAMESPACE -typedef QQmlData::BindingBitsType BindingBitsType; -enum { - BitsPerType = QQmlData::BitsPerType, - InlineBindingArraySize = QQmlData::InlineBindingArraySize -}; - void qmlRegisterBaseTypes(const char *uri, int versionMajor, int versionMinor) { QQmlEnginePrivate::registerBaseTypes(uri, versionMajor, versionMinor); @@ -1896,66 +1890,27 @@ void QQmlData::parentChanged(QObject *object, QObject *parent) } } -static void QQmlData_setBit(QQmlData *data, QObject *obj, int bit) -{ - uint offset = QQmlData::offsetForBit(bit); - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - if (Q_UNLIKELY(data->bindingBitsArraySize <= offset)) { - int props = QQmlMetaObject(obj).propertyCount(); - Q_ASSERT(bit < 2 * props); - - uint arraySize = (2 * static_cast(props) + BitsPerType - 1) / BitsPerType; - Q_ASSERT(arraySize > InlineBindingArraySize && arraySize > data->bindingBitsArraySize); - - BindingBitsType *newBits = static_cast(malloc(arraySize*sizeof(BindingBitsType))); - memcpy(newBits, bits, data->bindingBitsArraySize * sizeof(BindingBitsType)); - memset(newBits + data->bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - data->bindingBitsArraySize)); - - if (data->bindingBitsArraySize > InlineBindingArraySize) - free(bits); - data->bindingBits = newBits; - bits = newBits; - data->bindingBitsArraySize = arraySize; - } - Q_ASSERT(offset < data->bindingBitsArraySize); - bits[offset] |= QQmlData::bitFlagForBit(bit); -} - -static void QQmlData_clearBit(QQmlData *data, int bit) -{ - uint offset = QQmlData::offsetForBit(bit); - if (data->bindingBitsArraySize > offset) { - BindingBitsType *bits = (data->bindingBitsArraySize == InlineBindingArraySize) ? data->bindingBitsValue : data->bindingBits; - bits[offset] &= ~QQmlData::bitFlagForBit(bit); - } -} - -void QQmlData::clearBindingBit(int coreIndex) +QQmlData::BindingBitsType *QQmlData::growBits(QObject *obj, int bit) { - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2); -} + BindingBitsType *bits = (bindingBitsArraySize == InlineBindingArraySize) ? bindingBitsValue : bindingBits; + int props = QQmlMetaObject(obj).propertyCount(); + Q_ASSERT(bit < 2 * props); + Q_UNUSED(bit); // .. for Q_NO_DEBUG mode when the assert above expands to empty -void QQmlData::setBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2); -} + uint arraySize = (2 * static_cast(props) + BitsPerType - 1) / BitsPerType; + Q_ASSERT(arraySize > 1); + Q_ASSERT(arraySize <= 0xffff); // max for bindingBitsArraySize -void QQmlData::clearPendingBindingBit(int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_clearBit(this, coreIndex * 2 + 1); -} + BindingBitsType *newBits = static_cast(malloc(arraySize*sizeof(BindingBitsType))); + memcpy(newBits, bits, bindingBitsArraySize * sizeof(BindingBitsType)); + memset(newBits + bindingBitsArraySize, 0, sizeof(BindingBitsType) * (arraySize - bindingBitsArraySize)); -void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) -{ - Q_ASSERT(coreIndex >= 0); - Q_ASSERT(coreIndex <= 0xffff); - QQmlData_setBit(this, obj, coreIndex * 2 + 1); + if (bindingBitsArraySize > InlineBindingArraySize) + free(bits); + bindingBits = newBits; + bits = newBits; + bindingBitsArraySize = arraySize; + return bits; } QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) -- cgit v1.2.3 From 740462a87480e1b347d833321cdcf316840c1c13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2018 15:31:29 +0200 Subject: Fix crash when QML engine warning handler feeds errors back into QML When a QQmlEngine warning handler that's called during component instantiation results in subsequent component instantiations, either via the signal or via a Qt message handler like in the bug report, then we might end up modifying the linked list of errored bindings before returning from the QQmlEnginePrivate::warning() call. The easy fix is to extract the QQmlError, unlink the delayed error from the linked list and then deliver the error to the QQmlEngine. Change-Id: I6b7be61b57b35636282595937046ff76091144a3 Task-number: QTBUG-53293 Reviewed-by: Lars Knoll --- src/qml/qml/qqmlcomponent.cpp | 3 +-- src/qml/qml/qqmlengine.cpp | 13 ------------- src/qml/qml/qqmlengine_p.h | 2 -- src/qml/qml/qqmlincubator.cpp | 12 ++++-------- src/qml/qml/qqmljavascriptexpression.cpp | 2 +- src/qml/qml/qqmljavascriptexpression_p.h | 16 +++++++++------- 6 files changed, 15 insertions(+), 33 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 3174bbecd3..fe4768db15 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -929,8 +929,7 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS if (0 == enginePriv->inProgressCreations) { while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 613f9b4fe5..7e11177caa 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1996,11 +1996,6 @@ void QQmlEnginePrivate::warning(const QList &errors) dumpwarning(errors); } -void QQmlEnginePrivate::warning(QQmlDelayedError *error) -{ - warning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error) { if (engine) @@ -2017,14 +2012,6 @@ void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList &erro dumpwarning(error); } -void QQmlEnginePrivate::warning(QQmlEngine *engine, QQmlDelayedError *error) -{ - if (engine) - QQmlEnginePrivate::get(engine)->warning(error); - else - dumpwarning(error->error()); -} - void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error) { if (engine) diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index d6110c6699..da52e01793 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -231,10 +231,8 @@ public: void sendExit(int retCode = 0); void warning(const QQmlError &); void warning(const QList &); - void warning(QQmlDelayedError *); static void warning(QQmlEngine *, const QQmlError &); static void warning(QQmlEngine *, const QList &); - static void warning(QQmlEngine *, QQmlDelayedError *); static void warning(QQmlEnginePrivate *, const QQmlError &); static void warning(QQmlEnginePrivate *, const QList &); diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index 4546a4423f..df168960c6 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -367,10 +367,8 @@ finishIncubate: enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } else if (!creator.isNull()) { vmeGuard.guard(creator.data()); @@ -575,10 +573,8 @@ void QQmlIncubator::clear() enginePriv->inProgressCreations--; if (0 == enginePriv->inProgressCreations) { - while (enginePriv->erroredBindings) { - enginePriv->warning(enginePriv->erroredBindings); - enginePriv->erroredBindings->removeError(); - } + while (enginePriv->erroredBindings) + enginePriv->warning(enginePriv->erroredBindings->removeError()); } } diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 3daa107b64..40cf1417d0 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -455,7 +455,7 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * error->catchJavaScriptException(v4); error->setErrorObject(qmlScope); if (!error->addError(ep)) - ep->warning(error); + ep->warning(error->error()); return; } setupFunction(qmlContext, script.vmFunction); diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index a028850074..bff8866011 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -63,16 +63,18 @@ class QQmlDelayedError { public: inline QQmlDelayedError() : nextError(nullptr), prevError(nullptr) {} - inline ~QQmlDelayedError() { removeError(); } + inline ~QQmlDelayedError() { (void)removeError(); } bool addError(QQmlEnginePrivate *); - inline void removeError() { - if (!prevError) return; - if (nextError) nextError->prevError = prevError; - *prevError = nextError; - nextError = nullptr; - prevError = nullptr; + Q_REQUIRED_RESULT inline QQmlError removeError() { + if (prevError) { + if (nextError) nextError->prevError = prevError; + *prevError = nextError; + nextError = nullptr; + prevError = nullptr; + } + return m_error; } inline bool isValid() const { return m_error.isValid(); } -- cgit v1.2.3 From 51b73e0bb68812d78315af032546750d04656c02 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2018 16:49:19 +0200 Subject: Fix XMLHttpRequest when used with QQmlEngine::evaluate Our XHR implementation insists on a valid QQmlContext when processing callbacks. This is to protect against callbacks being triggered after dynamic QML contexts such as delegates have been destroyed. Unfortunately those checks are too strict and make it impossible to use XHR from within plain JS scripts (where v4->callingQmlContext() will return a null pointer). Dispatching the callbacks in functions that are directly called from QML/JS is safe and something we can do unconditionally. This applies to the callbacks triggered from abort() and open() for example. When we're called from QNetworkAccessManager we should enforce the continued existence of a QML context only if it was present at send() time. Task-number: QTBUG-67337 Change-Id: I8235f6ef407adc3eaeeff4eee72238ba6750afb2 Reviewed-by: Michael Brasser Reviewed-by: Valery Kotov Reviewed-by: Lars Knoll --- src/qml/qml/qqmlxmlhttprequest.cpp | 62 ++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 30 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 5673acec89..567d83f3ee 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1019,7 +1019,7 @@ public: Opened = 1, HeadersReceived = 2, Loading = 3, Done = 4 }; - QQmlXMLHttpRequest(QNetworkAccessManager *manager); + QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4); virtual ~QQmlXMLHttpRequest(); bool sendFlag() const; @@ -1028,9 +1028,9 @@ public: int replyStatus() const; QString replyStatusText() const; - ReturnedValue open(Object *thisObject, QQmlContextData *context, const QString &, const QUrl &, LoadType); + ReturnedValue open(Object *thisObject, const QString &, const QUrl &, LoadType); ReturnedValue send(Object *thisObject, QQmlContextData *context, const QByteArray &); - ReturnedValue abort(Object *thisObject, QQmlContextData *context); + ReturnedValue abort(Object *thisObject); void addHeader(const QString &, const QString &); QString header(const QString &name) const; @@ -1078,9 +1078,10 @@ private: PersistentValue m_thisObject; QQmlContextDataRef m_qmlContext; + bool m_wasConstructedWithQmlContext = true; - static void dispatchCallback(Object *thisObj, QQmlContextData *context); - void dispatchCallback(); + static void dispatchCallbackNow(Object *thisObj); + void dispatchCallbackSafely(); int m_status; QString m_statusText; @@ -1096,12 +1097,13 @@ private: QV4::PersistentValue m_parsedDocument; }; -QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager) +QQmlXMLHttpRequest::QQmlXMLHttpRequest(QNetworkAccessManager *manager, QV4::ExecutionEngine *v4) : m_state(Unsent), m_errorFlag(false), m_sendFlag(false) , m_redirectCount(0), m_gotXml(false), m_textCodec(nullptr), m_network(nullptr), m_nam(manager) , m_responseType() , m_parsedDocument() { + m_wasConstructedWithQmlContext = v4->callingQmlContext() != nullptr; } QQmlXMLHttpRequest::~QQmlXMLHttpRequest() @@ -1134,7 +1136,7 @@ QString QQmlXMLHttpRequest::replyStatusText() const return m_statusText; } -ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *context, const QString &method, const QUrl &url, LoadType loadType) +ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, const QString &method, const QUrl &url, LoadType loadType) { destroyNetwork(); m_sendFlag = false; @@ -1145,7 +1147,7 @@ ReturnedValue QQmlXMLHttpRequest::open(Object *thisObject, QQmlContextData *cont m_request.setAttribute(QNetworkRequest::SynchronousRequestAttribute, loadType == SynchronousLoad); m_state = Opened; m_addedHeaders.clear(); - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); return Encode::undefined(); } @@ -1297,7 +1299,7 @@ ReturnedValue QQmlXMLHttpRequest::send(Object *thisObject, QQmlContextData *cont return Encode::undefined(); } -ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *context) +ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject) { destroyNetwork(); m_responseEntityBody = QByteArray(); @@ -1310,7 +1312,7 @@ ReturnedValue QQmlXMLHttpRequest::abort(Object *thisObject, QQmlContextData *con m_state = Done; m_sendFlag = false; - dispatchCallback(thisObject, context); + dispatchCallbackNow(thisObject); } m_state = Unsent; @@ -1329,7 +1331,7 @@ void QQmlXMLHttpRequest::readyRead() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } bool wasEmpty = m_responseEntityBody.isEmpty(); @@ -1337,7 +1339,7 @@ void QQmlXMLHttpRequest::readyRead() if (wasEmpty && !m_responseEntityBody.isEmpty()) m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } static const char *errorToString(QNetworkReply::NetworkError error) @@ -1380,14 +1382,14 @@ void QQmlXMLHttpRequest::error(QNetworkReply::NetworkError error) error == QNetworkReply::ServiceUnavailableError || error == QNetworkReply::UnknownServerError) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } else { m_errorFlag = true; m_responseEntityBody = QByteArray(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); } #define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 @@ -1419,7 +1421,7 @@ void QQmlXMLHttpRequest::finished() if (m_state < HeadersReceived) { m_state = HeadersReceived; fillHeadersList (); - dispatchCallback(); + dispatchCallbackSafely(); } m_responseEntityBody.append(m_network->readAll()); readEncoding(); @@ -1436,11 +1438,11 @@ void QQmlXMLHttpRequest::finished() destroyNetwork(); if (m_state < Loading) { m_state = Loading; - dispatchCallback(); + dispatchCallbackSafely(); } m_state = Done; - dispatchCallback(); + dispatchCallbackSafely(); m_thisObject.clear(); m_qmlContext.setContextData(nullptr); @@ -1557,17 +1559,10 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const return m_responseEntityBody; } -void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *context) +void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj) { Q_ASSERT(thisObj); - if (!context) - // if the calling context object is no longer valid, then it has been - // deleted explicitly (e.g., by a Loader deleting the itemContext when - // the source is changed). We do nothing in this case, as the evaluation - // cannot succeed. - return; - QV4::Scope scope(thisObj->engine()); ScopedString s(scope, scope.engine->newString(QStringLiteral("onreadystatechange"))); ScopedFunctionObject callback(scope, thisObj->get(s)); @@ -1585,9 +1580,16 @@ void QQmlXMLHttpRequest::dispatchCallback(Object *thisObj, QQmlContextData *cont } } -void QQmlXMLHttpRequest::dispatchCallback() +void QQmlXMLHttpRequest::dispatchCallbackSafely() { - dispatchCallback(m_thisObject.as(), m_qmlContext.contextData()); + if (m_wasConstructedWithQmlContext && !m_qmlContext.contextData()) + // if the calling context object is no longer valid, then it has been + // deleted explicitly (e.g., by a Loader deleting the itemContext when + // the source is changed). We do nothing in this case, as the evaluation + // cannot succeed. + return; + + dispatchCallbackNow(m_thisObject.as()); } void QQmlXMLHttpRequest::destroyNetwork() @@ -1640,7 +1642,7 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject Scope scope(f->engine()); const QQmlXMLHttpRequestCtor *ctor = static_cast(f); - QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager()); + QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine); Scoped w(scope, scope.engine->memoryManager->allocObject(r)); ScopedObject proto(scope, ctor->d()->proto); w->setPrototype(proto); @@ -1778,7 +1780,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_open(const FunctionObject *b, const if (!username.isNull()) url.setUserName(username); if (!password.isNull()) url.setPassword(password); - return r->open(w, scope.engine->callingQmlContext(), method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); + return r->open(w, method, url, async ? QQmlXMLHttpRequest::AsynchronousLoad : QQmlXMLHttpRequest::SynchronousLoad); } ReturnedValue QQmlXMLHttpRequestCtor::method_setRequestHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) @@ -1860,7 +1862,7 @@ ReturnedValue QQmlXMLHttpRequestCtor::method_abort(const FunctionObject *b, cons V4THROW_REFERENCE("Not an XMLHttpRequest object"); QQmlXMLHttpRequest *r = w->d()->request; - return r->abort(w, scope.engine->callingQmlContext()); + return r->abort(w); } ReturnedValue QQmlXMLHttpRequestCtor::method_getResponseHeader(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) -- cgit v1.2.3 From 4909773f8162de49830d65e886747c11fff72934 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 20 Mar 2018 12:46:58 +0100 Subject: Fix calling Qt.binding() on bound functions Calling Qt.binding() on a bound function object is a valid use case and used to work until Qt 5.8. The problem was that we optimized the code in QQmlBinding and QQmlJavascriptExpression to directly work on a QV4::Function, so this wouldn't work anymore. To fix this make sure recursive calls to Function.bind() are unrolled (so that the BoundFunction's target is never a bound function itself), then add the bound function as an optional member to the QQmlBinding and use it's bound arguments if present. Task-number: QTBUG-61927 Change-Id: I472214ddd82fc2a1212efd9b769861fc43d2ddaf Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlbinding.cpp | 48 ++++++++++++++++++++++++++++++-- src/qml/qml/qqmlbinding_p.h | 11 ++++++++ src/qml/qml/qqmljavascriptexpression.cpp | 14 +--------- src/qml/qml/qqmljavascriptexpression_p.h | 4 +-- src/qml/qml/qqmlvaluetypewrapper.cpp | 8 ++++-- src/qml/qml/v8/qqmlbuiltinfunctions.cpp | 9 +++--- src/qml/qml/v8/qqmlbuiltinfunctions_p.h | 8 ++++-- 7 files changed, 76 insertions(+), 26 deletions(-) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index c314833304..30a18440a8 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr return b; } +QQmlSourceLocation QQmlBinding::sourceLocation() const +{ + if (m_sourceLocation) + return *m_sourceLocation; + return QQmlJavaScriptExpression::sourceLocation(); +} + +void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location) +{ + if (m_sourceLocation) + delete m_sourceLocation; + m_sourceLocation = new QQmlSourceLocation(location); +} + + QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj, QQmlContextData *ctxt, const QString &url, quint16 lineNumber) { @@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function QQmlBinding::~QQmlBinding() { + delete m_sourceLocation; } void QQmlBinding::setNotifyOnValueChanged(bool v) @@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) setUpdatingFlag(false); } +QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined) +{ + QV4::ExecutionEngine *v4 = context()->engine->handle(); + int argc = 0; + const QV4::Value *argv = nullptr; + const QV4::Value *thisObject = nullptr; + QV4::BoundFunction *b = nullptr; + if ((b = static_cast(m_boundFunction.valueRef()))) { + QV4::Heap::MemberData *args = b->boundArgs(); + if (args) { + argc = args->values.size; + argv = args->values.data(); + } + thisObject = &b->d()->boundThis; + } + QV4::Scope scope(v4); + QV4::JSCallData jsCall(scope, argc, argv, thisObject); + + return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined); +} + + // QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or // double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant // expression for the switch for the compiler to generate the optimal code, but @@ -203,7 +242,7 @@ protected: bool isUndefined = false; - QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); + QV4::ScopedValue result(scope, evaluate(&isUndefined)); bool error = false; if (!watcher.wasDeleted() && isAddedToObject() && !hasError()) @@ -302,9 +341,14 @@ public: { setCompilationUnit(compilationUnit); m_binding = binding; - setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column)); } + QQmlSourceLocation sourceLocation() const override final + { + return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column); + } + + void doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final { diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h index b67b02975f..a1295bd0ac 100644 --- a/src/qml/qml/qqmlbinding_p.h +++ b/src/qml/qml/qqmlbinding_p.h @@ -104,6 +104,12 @@ public: QString expressionIdentifier() const override; void expressionChanged() override; + QQmlSourceLocation sourceLocation() const override; + void setSourceLocation(const QQmlSourceLocation &location); + void setBoundFunction(QV4::BoundFunction *boundFunction) { + m_boundFunction.set(boundFunction->engine(), *boundFunction); + } + /** * This method returns a snapshot of the currently tracked dependencies of * this binding. The dependencies can change upon reevaluation. This method is @@ -123,6 +129,8 @@ protected: bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData, const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags); + QV4::ReturnedValue evaluate(bool *isUndefined); + private: inline bool updatingFlag() const; inline void setUpdatingFlag(bool); @@ -130,6 +138,9 @@ private: inline void setEnabledFlag(bool); static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property); + + QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions + QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object }; bool QQmlBinding::updatingFlag() const diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 40cf1417d0..93ec9421ed 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression() m_context(nullptr), m_prevExpression(nullptr), m_nextExpression(nullptr), - m_v4Function(nullptr), - m_sourceLocation(nullptr) + m_v4Function(nullptr) { } @@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; - - delete m_sourceLocation; } void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) @@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged() QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const { - if (m_sourceLocation) - return *m_sourceLocation; if (m_v4Function) return m_v4Function->sourceLocation(); return QQmlSourceLocation(); } -void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location) -{ - if (m_sourceLocation) - delete m_sourceLocation; - m_sourceLocation = new QQmlSourceLocation(location); -} - void QQmlJavaScriptExpression::setContext(QQmlContextData *context) { if (m_prevExpression) { diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index bff8866011..01af3b89ca 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -116,8 +116,7 @@ public: inline QObject *scopeObject() const; inline void setScopeObject(QObject *v); - QQmlSourceLocation sourceLocation() const; - void setSourceLocation(const QQmlSourceLocation &location); + virtual QQmlSourceLocation sourceLocation() const; bool isValid() const { return context() != nullptr; } @@ -188,7 +187,6 @@ private: QV4::PersistentValue m_qmlScope; QQmlRefPointer m_compilationUnit; QV4::Function *m_v4Function; - QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions }; class QQmlPropertyCapture diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index a28115d192..6196a09d94 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QV4::Scoped bindingFunction(scope, (const Value &)f); - QV4::ScopedContext ctx(scope, bindingFunction->scope()); - QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx); + QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction()); + QV4::ScopedContext ctx(scope, f->scope()); + QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx); + newBinding->setSourceLocation(bindingFunction->currentLocation()); + if (f->isBoundFunction()) + newBinding->setBoundFunction(static_cast(f.getPointer())); newBinding->setSourceLocation(bindingFunction->currentLocation()); newBinding->setTarget(referenceObject, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 1371f1f041..9e7c84011b 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1344,11 +1344,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co return QQmlLocale::locale(scope.engine, code); } -void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) +void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction) { - Scope scope(originalFunction->engine()); - ScopedContext context(scope, originalFunction->scope()); - FunctionObject::init(context, originalFunction->function()); + Scope scope(bindingFunction->engine()); + ScopedContext context(scope, bindingFunction->scope()); + FunctionObject::init(context, bindingFunction->function()); + this->bindingFunction.set(internalClass->engine, bindingFunction->d()); } QQmlSourceLocation QQmlBindingFunction::currentLocation() const diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 104dae5d79..ee3b5f7d6e 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -80,8 +80,11 @@ struct ConsoleObject : Object { void init(); }; -struct QQmlBindingFunction : FunctionObject { - void init(const QV4::FunctionObject *originalFunction); +#define QQmlBindingFunctionMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, bindingFunction) +DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) { + DECLARE_MARKOBJECTS(QQmlBindingFunction) + void init(const QV4::FunctionObject *bindingFunction); }; } @@ -179,6 +182,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject { V4_OBJECT2(QQmlBindingFunction, FunctionObject) + Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; } QQmlSourceLocation currentLocation() const; // from caller stack trace }; -- cgit v1.2.3 From 0b394e30bba4f6bb7e6f7dbe5585a2e15aa0f21d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 13 Apr 2018 15:43:40 +0200 Subject: Fix memory leak when calling instanceof on QML items The return value of getType() has the ref count increased and needs handling on the caller side. Change-Id: I05ffa4dae221f2502f87b76762164bba1389db32 Reviewed-by: Lars Knoll --- src/qml/qml/qqmltypewrapper.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/qml/qml') diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 6dbf6ad8c1..ce35e966aa 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -388,6 +388,7 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl()); CompiledData::CompilationUnit *cu = td->compilationUnit(); myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + td->release(); } else { myQmlType = qenginepriv->metaObjectForType(myTypeId); } -- cgit v1.2.3