From 4e21daa0210b2aa385792e1c4cb01f57804c5dcb Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 2 Oct 2019 14:06:56 +0200 Subject: Re-add the warnings about deprecated default binding restore mode We _really_ want people to port away from this. And yes, we are going to change it. Amends commit c273175ffec925a4164de41a79c21d785a1761a7. Task-number: QTBUG-33444 Task-number: QTBUG-78566 Change-Id: Iab50b8c7bad043528602e2e617c772de06d18b1e Reviewed-by: Simon Hausmann Reviewed-by: Pierre-Yves Siret --- src/qml/types/qqmlbind.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/qml') diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 9d305cd24c..8f40f4f704 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -359,7 +359,8 @@ void QQmlBind::setDelayed(bool delayed) \li Binding.RestoreBindingOrValue The original value is always restored. \endlist - The default value is Binding.RestoreBinding. + \warning The default value is Binding.RestoreBinding. This will change in + Qt 5.15 to Binding.RestoreBindingOrValue. If you rely on any specific behavior regarding the restoration of plain values when bindings get disabled you should migrate to explicitly set the @@ -456,11 +457,23 @@ void QQmlBind::eval() Q_ASSERT(vmemo); vmemo->setVMEProperty(propPriv->core.coreIndex(), *d->v4Value.valueRef()); d->clearPrev(); + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 5.15 the default is Binding.RestoreBinding." + << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue."; } } else if (d->prevIsVariant) { if (d->restoreValue) { d->prop.write(d->prevValue); d->clearPrev(); + } else if (!d->restoreModeExplicit) { + qmlWarning(this) + << "Not restoring previous value because restoreMode has not been set." + << "This behavior is deprecated." + << "In Qt < 5.15 the default is Binding.RestoreBinding." + << "In Qt >= 5.15 the default is Binding.RestoreBindingOrValue."; } } return; -- cgit v1.2.3 From 138f09bd63b8070686ed8a282375dbcf3ce7674f Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 4 Oct 2019 12:18:50 +0200 Subject: Convert a few sizeof(array)/sizeof(element0) fors to range fors Increases readability Change-Id: I46d82fac83e538988cea79a053d70b954a3cb9f1 Reviewed-by: Fabian Kosmale Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4dateobject.cpp | 3 +-- src/qml/qml/qqmlimport.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index 5616b1450b..520b715189 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -601,8 +601,7 @@ static inline double ParseString(const QString &s, double localTZA) QStringLiteral("d MMMM, yyyy hh:mm:ss"), }; - for (uint i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { - const QString &format(formats[i]); + for (const QString &format : formats) { dt = format.indexOf(QLatin1String("hh:mm")) < 0 ? QDateTime(QDate::fromString(s, format), QTime(0, 0, 0), Qt::UTC) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 5feea9daa8..8b5e11c890 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -780,12 +780,12 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt typeStr + dotqml_string, // Type -> Type.qml typeStr + dotuidotqml_string // Type -> Type.ui.qml }; - for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) { - exists = typeLoader->fileExists(localDirectoryPath, urlsToTry[i]); + for (const QString &urlToTry : urlsToTry) { + exists = typeLoader->fileExists(localDirectoryPath, urlToTry); if (exists) { #if defined(Q_OS_MACOS) || defined(Q_OS_WIN) // don't let function.qml confuse the use of "new Function(...)" for example. - if (!QQml_isFileCaseCorrect(localDirectoryPath + urlsToTry[i])) { + if (!QQml_isFileCaseCorrect(localDirectoryPath + urlToTry)) { exists = false; if (errors) { QQmlError caseError; @@ -797,7 +797,7 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt #else Q_UNUSED(errors); #endif - qmlUrl = url + urlsToTry[i]; + qmlUrl = url + urlToTry; break; } } -- cgit v1.2.3 From 2b371a50cbc1f54284fcea3834834ca58fc28fd0 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 4 Oct 2019 16:20:29 +0200 Subject: qmlRegisterSingletonInstance: Do not crash if instance gets deleted Use a QPointer, so that we notice if the object has been deleted. Also ensure that in the documentation the function is in a single line, as qdoc will otherwise silently omit it. Change-Id: Idecd370d00089997cd18b3247ad2290a561b2b69 Reviewed-by: Simon Hausmann Reviewed-by: Ulf Hermann --- src/qml/doc/src/qmlfunctions.qdoc | 3 +-- src/qml/qml/qqml.cpp | 7 +++++++ src/qml/qml/qqmlprivate.h | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/qml') diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index b083012eab..b2d322465d 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -628,8 +628,7 @@ */ /*! - \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int - versionMinor, const char *typeName, QObject* cppObject) + \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject* cppObject) \relates QQmlEngine \since 5.14 diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 8a50b51b5d..5b16a3c9e2 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -79,6 +79,13 @@ int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *q // From qqmlprivate.h QObject *QQmlPrivate::RegisterSingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *) { + if (!m_object) { + QQmlError error; + error.setDescription(QLatin1String("The registered singleton has already been deleted. Ensure that it outlives the engine.")); + QQmlEnginePrivate::get(qeng)->warning(qeng, error); + return nullptr; + } + if (qeng->thread() != m_object->thread()) { QQmlError error; error.setDescription(QLatin1String("Registered object must live in the same thread as the engine it was registered with")); diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index e6dd5e0b16..bfd3611fb8 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -58,6 +58,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -326,7 +327,7 @@ namespace QQmlPrivate { QObject *operator()(QQmlEngine *, QJSEngine *); - QObject *m_object; + QPointer m_object; bool alreadyCalled = false; }; } -- cgit v1.2.3 From ce94b847c87e2606c5f189d2ad3715c1d025b9cf Mon Sep 17 00:00:00 2001 From: Albert Astals Cid Date: Fri, 4 Oct 2019 14:13:36 +0200 Subject: Small performance improvements suggested by clang-tidy mostly add const &, a few std::move and in particular case, remove const so the std::move being done over the variable actually has effect Change-Id: Id611cd31bc012f219d7a17d4626b1c2a5fbddd66 Reviewed-by: Fabian Kosmale Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlirbuilder.cpp | 2 +- src/qml/compiler/qqmlirbuilder_p.h | 2 +- src/qml/qml/qqmlcontext.cpp | 2 +- src/qml/qml/qqmldatablob.cpp | 2 +- src/qml/qml/qqmldirdata.cpp | 2 +- src/qml/qml/qqmlmetatypedata.cpp | 2 +- src/qml/qml/qqmltype.cpp | 2 +- src/qml/qml/qqmltypeloader.cpp | 2 +- src/qml/qml/qqmltypeloaderqmldircontent.cpp | 3 ++- 9 files changed, 10 insertions(+), 9 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 171dc641d3..665d2633a7 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -964,7 +964,7 @@ QStringRef IRBuilder::asStringRef(QQmlJS::AST::Node *node) return textRefAt(node->firstSourceLocation(), node->lastSourceLocation()); } -void IRBuilder::extractVersion(QStringRef string, int *maj, int *min) +void IRBuilder::extractVersion(const QStringRef &string, int *maj, int *min) { *maj = -1; *min = -1; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index c366c8e459..4279f5b768 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -453,7 +453,7 @@ public: static QString asString(QQmlJS::AST::UiQualifiedId *node); QStringRef asStringRef(QQmlJS::AST::Node *node); - static void extractVersion(QStringRef string, int *maj, int *min); + static void extractVersion(const QStringRef &string, int *maj, int *min); QStringRef textRefAt(const QQmlJS::AST::SourceLocation &loc) const { return QStringRef(&sourceCode, loc.offset, loc.length); } QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first, diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 14892bd6ad..af8deb085c 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -358,7 +358,7 @@ void QQmlContext::setContextProperties(const QVector &properties) data->expressions = nullptr; data->childContexts = nullptr; - for (auto property : properties) + for (const auto &property : properties) setContextProperty(property.name, property.value); data->expressions = expressions; diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp index 2183721d32..750fc6de50 100644 --- a/src/qml/qml/qqmldatablob.cpp +++ b/src/qml/qml/qqmldatablob.cpp @@ -351,7 +351,7 @@ void QQmlDataBlob::addDependency(QQmlDataBlob *blob) status() == Error || status() == Complete || m_isDone) return; - for (auto existingDep: qAsConst(m_waitingFor)) + for (const auto &existingDep: qAsConst(m_waitingFor)) if (existingDep.data() == blob) return; diff --git a/src/qml/qml/qqmldirdata.cpp b/src/qml/qml/qqmldirdata.cpp index 7652cec322..de74dfdf9b 100644 --- a/src/qml/qml/qqmldirdata.cpp +++ b/src/qml/qml/qqmldirdata.cpp @@ -61,7 +61,7 @@ QQmlTypeLoader::Blob::PendingImportPtr QQmlQmldirData::import(QQmlTypeLoader::Bl void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, QQmlTypeLoader::Blob::PendingImportPtr import) { - m_imports[blob] = import; + m_imports[blob] = std::move(import); } int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const diff --git a/src/qml/qml/qqmlmetatypedata.cpp b/src/qml/qml/qqmlmetatypedata.cpp index 41a7778da3..775bc8bdb4 100644 --- a/src/qml/qml/qqmlmetatypedata.cpp +++ b/src/qml/qml/qqmlmetatypedata.cpp @@ -150,7 +150,7 @@ QQmlPropertyCache *QQmlMetaTypeData::propertyCache(const QQmlType &type, int min bool hasCopied = false; for (int ii = 0; ii < types.count(); ++ii) { - QQmlType currentType = types.at(ii); + const QQmlType ¤tType = types.at(ii); if (!currentType.isValid()) continue; diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 3615749da1..2db3ad6b2a 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -393,7 +393,7 @@ void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, co qWarning().noquote() << QLatin1String("Possible conflicting items:"); // find items with conflicting key - for (const auto i : enumInfoList) { + for (const auto &i : qAsConst(enumInfoList)) { if (i.enumKey == conflictingKey) qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope " << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->")); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 42e7d2c4b4..62007effdd 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -491,7 +491,7 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, PendingImportPtr import, { QQmlRefPointer data = typeLoader()->getQmldir(url); - data->setImport(this, import); + data->setImport(this, std::move(import)); data->setPriority(this, priority); if (data->status() == Error) { diff --git a/src/qml/qml/qqmltypeloaderqmldircontent.cpp b/src/qml/qml/qqmltypeloaderqmldircontent.cpp index 238af9b710..8e983db756 100644 --- a/src/qml/qml/qqmltypeloaderqmldircontent.cpp +++ b/src/qml/qml/qqmltypeloaderqmldircontent.cpp @@ -55,7 +55,8 @@ QList QQmlTypeLoaderQmldirContent::errors(const QString &uri) const { QList errors; const QUrl url(uri); - for (const auto parseError : m_parser.errors(uri)) { + const auto parseErrors = m_parser.errors(uri); + for (const auto &parseError : parseErrors) { QQmlError error; error.setUrl(url); error.setLine(parseError.line); -- cgit v1.2.3 From 00f903f3b4cd46ddf8361876401e5405030f97f1 Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 7 Oct 2019 10:01:46 +0200 Subject: QML Binding: do not convert strings The root cause for the issue is that QQmlObjectCreator::setPropertyValue calls QQmlStringConverters::variantFromString on strings if the property is of type QVariant. Unfortunately, this cannot be changed easily as the current behavior is explicitly documented and tested in tst_qqmllanguage, thus making it a breaking change. As a workaround, QML Binding does now take a QJSValue instead of a QVariant (making value a var property), which does not trigger the conversion path. Fixes: QTBUG-78943 Change-Id: I0b64dffdb6b84b2bab2bb85a8cb263e530c18570 Reviewed-by: Ulf Hermann --- src/qml/qml/qqmlobjectcreator.cpp | 2 ++ src/qml/types/qqmlbind.cpp | 8 ++++---- src/qml/types/qqmlbind_p.h | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'src/qml') diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index f89608cd5d..a4270628e8 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -445,6 +445,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QV4::ScopedString s(scope, v4->newString(stringValue)); _vmeMetaObject->setVMEProperty(property->coreIndex(), s); } else { + // ### Qt 6: Doing the conversion here where we don't know the eventual target type is rather strange + // and caused for instance QTBUG-78943 QVariant value = QQmlStringConverters::variantFromString(stringValue); property->writeProperty(_qobject, &value, propertyWriteFlags); } diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp index 8f40f4f704..36de16a818 100644 --- a/src/qml/types/qqmlbind.cpp +++ b/src/qml/types/qqmlbind.cpp @@ -78,7 +78,7 @@ public: QQmlNullableValue when; QPointer obj; QString propName; - QQmlNullableValue value; + QQmlNullableValue value; QQmlProperty prop; QQmlAbstractBinding::Ptr prevBind; QV4::PersistentValue v4Value; @@ -293,13 +293,13 @@ void QQmlBind::setProperty(const QString &p) The value to be set on the target object and property. This can be a constant (which isn't very useful), or a bound expression. */ -QVariant QQmlBind::value() const +QJSValue QQmlBind::value() const { Q_D(const QQmlBind); return d->value.value; } -void QQmlBind::setValue(const QVariant &v) +void QQmlBind::setValue(const QJSValue &v) { Q_D(QQmlBind); d->value = v; @@ -502,7 +502,7 @@ void QQmlBind::eval() QQmlPropertyPrivate::removeBinding(d->prop); } - d->prop.write(d->value.value); + d->prop.write(d->value.value.toVariant()); } QT_END_NAMESPACE diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h index 22007a3d25..ba040d2a0b 100644 --- a/src/qml/types/qqmlbind_p.h +++ b/src/qml/types/qqmlbind_p.h @@ -75,7 +75,7 @@ private: Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(QObject *target READ object WRITE setObject) Q_PROPERTY(QString property READ property WRITE setProperty) - Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(QJSValue value READ value WRITE setValue) Q_PROPERTY(bool when READ when WRITE setWhen) Q_PROPERTY(bool delayed READ delayed WRITE setDelayed REVISION 8) Q_PROPERTY(RestorationMode restoreMode READ restoreMode WRITE setRestoreMode @@ -95,8 +95,8 @@ public: QString property() const; void setProperty(const QString &); - QVariant value() const; - void setValue(const QVariant &); + QJSValue value() const; + void setValue(const QJSValue &); bool delayed() const; void setDelayed(bool); -- cgit v1.2.3 From e5f3e7149b7e130c375c1419f1f804dfeaf578aa Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Mon, 30 Sep 2019 08:32:02 +0200 Subject: Add initial support for deep aliases This patch reuses the existing support for value types to support aliases of depth 2. This covers the initial use case in QTBUG-48150. Adding support for "deeper" aliases would require storing the complete "property path", which in turn would require increasing the size of the Alias data. This is currently considered out of scope, at least until a clear use-case appears. Fixes: QTBUG-48150 Change-Id: Id2ac4dd175003a37eba2919e7604d0a3be54d29f Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlpropertycachecreator_p.h | 76 +++++++++++++++++++------------- src/qml/qml/qqmltypecompiler.cpp | 51 ++++++++++++++------- src/qml/qml/qqmltypedata.cpp | 2 +- src/qml/qml/qqmlvmemetaobject.cpp | 33 ++++++++++---- 4 files changed, 107 insertions(+), 55 deletions(-) (limited to 'src/qml') diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 94bf3cbdc3..39778aa328 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -585,13 +585,13 @@ public: QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer); - void appendAliasPropertiesToMetaObjects(); + void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv); - QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex); + QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv); private: - void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags); + void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv); + QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv); void collectObjectsWithAliasesRecursively(int objectIndex, QVector *objectsWithAliases) const; @@ -610,7 +610,7 @@ inline QQmlPropertyCacheAliasCreator::QQmlPropertyCacheAliasCre } template -inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesToMetaObjects() +inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv) { // skip the root object (index 0) as that one does not have a first object index originating // from a binding. @@ -620,15 +620,15 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie continue; const auto rootBinding = component.bindingsBegin(); - appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex); + appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv); } const int rootObjectIndex = 0; - appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex); + appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv); } template -inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex) +inline void QQmlPropertyCacheAliasCreator::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv) { QVector objectsWithAliases; collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases); @@ -668,7 +668,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasPropertie const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (allAliasTargetsExist(object)) { - appendAliasesToPropertyCache(component, objectIndex); + appendAliasesToPropertyCache(component, objectIndex, enginePriv); } else { pendingObjects.append(objectIndex); } @@ -702,9 +702,8 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl } template -inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator::propertyDataForAlias( - const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, - QQmlPropertyData::Flags *propertyFlags) +inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, + QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv) { *type = 0; bool writable = false; @@ -736,7 +735,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator: lastAlias = targetAlias; } while (lastAlias->aliasToLocalAlias); - return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags); + return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv); } const int targetObjectIndex = objectForId(component, alias.targetObjectId); @@ -768,29 +767,46 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator: QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex); Q_ASSERT(targetCache); + QQmlPropertyData *targetProperty = targetCache->property(coreIndex); Q_ASSERT(targetProperty); - *type = targetProperty->propType(); + // for deep aliases, valueTypeIndex is always set + if (!QQmlValueTypeFactory::isValueType(targetProperty->propType()) && valueTypeIndex != -1) { + // deep alias property + *type = targetProperty->propType(); + targetCache = enginePriv->propertyCacheForType(*type); + Q_ASSERT(targetCache); + targetProperty = targetCache->property(valueTypeIndex); - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QVariant::Int; - else - *type = valueTypeMetaObject->property(valueTypeIndex).userType(); + *type = targetProperty->propType(); + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + } else { - if (targetProperty->isEnum()) { - *type = QVariant::Int; + // value type or primitive type or enum + *type = targetProperty->propType(); + + writable = targetProperty->isWritable(); + resettable = targetProperty->isResettable(); + + if (valueTypeIndex != -1) { + const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type); + if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) + *type = QVariant::Int; + else + *type = valueTypeMetaObject->property(valueTypeIndex).userType(); } else { - // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); + if (targetProperty->isEnum()) { + *type = QVariant::Int; + } else { + // Copy type flags + propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); - if (targetProperty->isVarProperty()) - propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + if (targetProperty->isVarProperty()) + propertyFlags->type = QQmlPropertyData::Flags::QVariantType; + } } } } @@ -802,7 +818,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator: template inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator::appendAliasesToPropertyCache( - const CompiledObject &component, int objectIndex) + const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv) { const CompiledObject &object = *objectContainer->objectAt(objectIndex); if (!object.aliasCount()) @@ -823,7 +839,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator: int type = 0; int minorVersion = 0; QQmlPropertyData::Flags propertyFlags; - QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); + QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv); if (error.isValid()) return error; diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index bbeaf7be9a..9ff0e3fb9e 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -1016,7 +1016,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex) } if (result == AllAliasesResolved) { - QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex); + QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate); if (error.isValid()) { recordError(error); return false; @@ -1143,23 +1143,42 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, if (!subProperty.isEmpty()) { const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType()); if (!valueTypeMetaObject) { - *error = qQmlCompileError( - alias->referenceLocation, - tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } + // could be a deep alias + bool isDeepAlias = subProperty.at(0).isLower(); + if (isDeepAlias) { + isDeepAlias = false; + for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) { + auto binding = *it; + if (compiler->stringAt(binding.propertyNameIndex) == property) { + resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex)); + QQmlPropertyData *actualProperty = resolver.property(subProperty.toString()); + if (actualProperty) { + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex()); + isDeepAlias = true; + } + } + } + } + if (!isDeepAlias) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + } else { - int valueTypeIndex = - valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); - if (valueTypeIndex == -1) { - *error = qQmlCompileError( - alias->referenceLocation, - tr("Invalid alias target location: %1").arg(subProperty.toString())); - break; - } - Q_ASSERT(valueTypeIndex <= 0x0000FFFF); + int valueTypeIndex = + valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData()); + if (valueTypeIndex == -1) { + *error = qQmlCompileError( + alias->referenceLocation, + tr("Invalid alias target location: %1").arg(subProperty.toString())); + break; + } + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); - propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); + propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex); + } } else { if (targetProperty->isQObject()) alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject; diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp index 99fe069685..cfdcf6aad5 100644 --- a/src/qml/qml/qqmltypedata.cpp +++ b/src/qml/qml/qqmltypedata.cpp @@ -209,7 +209,7 @@ void QQmlTypeData::createTypeAndPropertyCaches( QQmlPropertyCacheAliasCreator aliasCreator( &m_compiledData->propertyCaches, m_compiledData.data()); - aliasCreator.appendAliasPropertiesToMetaObjects(); + aliasCreator.appendAliasPropertiesToMetaObjects(engine); pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches); } diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index b9d8fed243..abdc686ec2 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -164,8 +164,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect() QQmlData *targetDData = QQmlData::get(target, /*create*/false); if (!targetDData) return; - int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex(); + QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex); + int coreIndex = encodedIndex.coreIndex(); + int valueTypeIndex = encodedIndex.valueTypeIndex(); const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); + if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) { + // deep alias + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine()); + auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType()); + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + Q_ASSERT(newPropertyCache); + pd = newPropertyCache->property(valueTypeIndex); + } if (!pd) return; @@ -858,17 +869,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * if (!targetDData->propertyCache) return -1; const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex); - // Value type property + // Value type property or deep alias QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType()); - Q_ASSERT(valueType); + if (valueType) { - valueType->read(target, coreIndex); - int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); + valueType->read(target, coreIndex); + int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a); - if (c == QMetaObject::WriteProperty) - valueType->write(target, coreIndex, nullptr); + if (c == QMetaObject::WriteProperty) + valueType->write(target, coreIndex, nullptr); - return rv; + return rv; + } else { + // deep alias + void *argv[1] = { &target }; + QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv); + return QMetaObject::metacall(target, c, valueTypePropertyIndex, a); + } } else { return QMetaObject::metacall(target, c, coreIndex, a); -- cgit v1.2.3 From 914e0300792856ddac9b99b20a8d88dd6f087fa6 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 1 Oct 2019 12:37:55 +0200 Subject: Use qmlProtectModule to protect a module from further modification We don't need two mechanisms to do essentially the same thing. QQmlTypeLoader::Blob::addImport() had an "optimization" to never check for qmldir files of locked imports. This meant the first time you imported a module with a plugin that locked the module you could use the qmldir file to load additional .qml files afterwards. The second time you imported the same thing, you couldn't. As this is not a great example of consistent behavior, we drop this optimization and always allow the qmldir files of plugins that lock the module to specify additional QML files. As a side effect of this, additional plugins listed in a qmldir file can also now be loaded after the module has been locked by some other means. However, any qmlRegisterFooBar() called from the module will be prevented. Change-Id: Idabb2bd5f75fc85b62f42625173672b4ae84382e Reviewed-by: Fabian Kosmale Reviewed-by: Simon Hausmann --- src/qml/qml/qqml.cpp | 2 +- src/qml/qml/qqmlimport.cpp | 19 ++++++++++++++++--- src/qml/qml/qqmlimport_p.h | 2 -- src/qml/qml/qqmlmetatype.cpp | 39 +++++++++++++-------------------------- src/qml/qml/qqmlmetatype_p.h | 2 +- src/qml/qml/qqmlmetatypedata_p.h | 4 ---- src/qml/qml/qqmltypeloader.cpp | 8 +------- 7 files changed, 32 insertions(+), 44 deletions(-) (limited to 'src/qml') diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index a3ca48d6d9..0f81d83f13 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -61,7 +61,7 @@ void qmlClearTypeRegistrations() // Declared in qqml.h //From qqml.h bool qmlProtectModule(const char *uri, int majVersion) { - return QQmlMetaType::protectModule(uri, majVersion); + return QQmlMetaType::protectModule(QString::fromUtf8(uri), majVersion); } //From qqml.h diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 5feea9daa8..73c195cd13 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1997,12 +1997,25 @@ void QQmlImportDatabase::setImportPathList(const QStringList &paths) /*! \internal */ -bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &basePath, - const QString &uri, const QString &typeNamespace, int vmaj, QList *errors) +static bool registerPluginTypes(QObject *instance, const QString &basePath, const QString &uri, + const QString &typeNamespace, int vmaj, QList *errors) { if (qmlImportTrace()) qDebug().nospace() << "QQmlImportDatabase::registerPluginTypes: " << uri << " from " << basePath; - return QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors); + + if (!QQmlMetaType::registerPluginTypes(instance, basePath, uri, typeNamespace, vmaj, errors)) + return false; + + if (vmaj >= 0 && !typeNamespace.isEmpty() && !QQmlMetaType::protectModule(uri, vmaj)) { + QQmlError error; + error.setDescription( + QString::fromLatin1("Cannot protect module %1 %2 as it was never registered") + .arg(uri).arg(vmaj)); + errors->append(error); + return false; + } + + return true; } /*! diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index ea9c2eafb5..e24b3c447c 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -235,8 +235,6 @@ private: const QString &baseName); bool importStaticPlugin(QObject *instance, const QString &basePath, const QString &uri, const QString &typeNamespace, int vmaj, QList *errors); - bool registerPluginTypes(QObject *instance, const QString &basePath, - const QString &uri, const QString &typeNamespace, int vmaj, QList *errors); void clearDirCache(); struct QmldirCache { diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 268d58a856..1a5affb0ad 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -342,7 +342,7 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) // NOTE: caller must hold a QMutexLocker on "data" bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, - const char *uri, const QString &typeName, int majorVersion = -1) + const char *uri, const QString &typeName, int majorVersion) { if (!typeName.isEmpty()) { if (typeName.at(0).isLower()) { @@ -363,27 +363,16 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da if (uri && !typeName.isEmpty()) { QString nameSpace = QString::fromUtf8(uri); - - if (data->typeRegistrationNamespace.isEmpty() && !nameSpace.isEmpty()) { - // Is the target namespace protected against further registrations? - if (data->protectedNamespaces.contains(nameSpace)) { + QQmlMetaTypeData::VersionedUri versionedUri; + versionedUri.uri = nameSpace; + versionedUri.majorVersion = majorVersion; + if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){ + if (qqtm->isLocked()){ QString failure(QCoreApplication::translate("qmlRegisterType", - "Cannot install %1 '%2' into protected namespace '%3'")); - data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace)); + "Cannot install %1 '%2' into protected module '%3' version '%4'")); + data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion)); return false; } - } else if (majorVersion >= 0) { - QQmlMetaTypeData::VersionedUri versionedUri; - versionedUri.uri = nameSpace; - versionedUri.majorVersion = majorVersion; - if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)){ - if (qqtm->isLocked()){ - QString failure(QCoreApplication::translate("qmlRegisterType", - "Cannot install %1 '%2' into protected module '%3' version '%4'")); - data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace).arg(majorVersion)); - return false; - } - } } } @@ -477,8 +466,10 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe bool fileImport = false; if (*(type.uri) == '\0') fileImport = true; - if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, typeName)) + if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, + typeName, type.versionMajor)) { return QQmlType(); + } QQmlTypePrivate *priv = createQQmlType(data, typeName, type); addTypeToData(priv, data); @@ -560,12 +551,12 @@ int QQmlMetaType::registerUnitCacheHook( return 0; } -bool QQmlMetaType::protectModule(const char *uri, int majVersion) +bool QQmlMetaType::protectModule(const QString &uri, int majVersion) { QQmlMetaTypeDataPtr data; QQmlMetaTypeData::VersionedUri versionedUri; - versionedUri.uri = QString::fromUtf8(uri); + versionedUri.uri = uri; versionedUri.majorVersion = majVersion; if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) { @@ -683,8 +674,6 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat } return false; } - - data->protectedNamespaces.insert(uri); } else { // This is not an identified module - provide a warning qWarning().nospace() << qPrintable( @@ -698,11 +687,9 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); } - data->typeRegistrationNamespace = typeNamespace; const QByteArray bytes = uri.toUtf8(); const char *moduleId = bytes.constData(); iface->registerTypes(moduleId); - data->typeRegistrationNamespace.clear(); } if (!failures.isEmpty()) { diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 6c2b0bb2a6..35d5386e1f 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -84,7 +84,7 @@ public: static void unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit); static void registerModule(const char *uri, int versionMajor, int versionMinor); - static bool protectModule(const char *uri, int majVersion); + static bool protectModule(const QString &uri, int majVersion); static int typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName); diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h index 5239b635ce..ea796ee7c6 100644 --- a/src/qml/qml/qqmlmetatypedata_p.h +++ b/src/qml/qml/qqmlmetatypedata_p.h @@ -106,10 +106,6 @@ struct QQmlMetaTypeData QList parentFunctions; QVector lookupCachedQmlUnit; - QSet protectedNamespaces; - - QString typeRegistrationNamespace; - QHash qmlLists; QHash propertyCaches; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 42e7d2c4b4..5ab404f743 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -563,13 +563,7 @@ bool QQmlTypeLoader::Blob::addImport(QQmlTypeLoader::Blob::PendingImportPtr impo QString qmldirFilePath; QString qmldirUrl; - if (QQmlMetaType::isLockedModule(import->uri, import->majorVersion)) { - //Locked modules are checked first, to save on filesystem checks - if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, - import->minorVersion, QString(), QString(), false, errors)) - return false; - - } else if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion, + if (m_importCache.locateQmldir(importDatabase, import->uri, import->majorVersion, import->minorVersion, &qmldirFilePath, &qmldirUrl)) { // This is a local library import if (!m_importCache.addLibraryImport(importDatabase, import->uri, import->qualifier, import->majorVersion, -- cgit v1.2.3