diff options
Diffstat (limited to 'src/qml/qml/qqmlpropertyvalidator.cpp')
-rw-r--r-- | src/qml/qml/qqmlpropertyvalidator.cpp | 509 |
1 files changed, 268 insertions, 241 deletions
diff --git a/src/qml/qml/qqmlpropertyvalidator.cpp b/src/qml/qml/qqmlpropertyvalidator.cpp index d7361ea2c6..ff22c3043a 100644 --- a/src/qml/qml/qqmlpropertyvalidator.cpp +++ b/src/qml/qml/qqmlpropertyvalidator.cpp @@ -1,57 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qqmlpropertyvalidator_p.h" #include <private/qqmlcustomparser_p.h> +#include <private/qqmlglobal_p.h> #include <private/qqmlirbuilder_p.h> -#include <private/qqmlstringconverters_p.h> #include <private/qqmlpropertycachecreator_p.h> #include <private/qqmlpropertyresolver_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmlsignalnames_p.h> #include <QtCore/qdatetime.h> QT_BEGIN_NAMESPACE -static bool isPrimitiveType(int typeId) +static bool isPrimitiveType(QMetaType metaType) { - switch (typeId) { + switch (metaType.id()) { #define HANDLE_PRIMITIVE(Type, id, T) \ case QMetaType::Type: QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE); @@ -62,7 +28,8 @@ QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(HANDLE_PRIMITIVE); } } -QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit) +QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports *imports, + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) : enginePrivate(enginePrivate) , compilationUnit(compilationUnit) , imports(imports) @@ -73,7 +40,7 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, c bindingPropertyDataPerObject->resize(compilationUnit->objectCount()); } -QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validate() +QVector<QQmlError> QQmlPropertyValidator::validate() { return validateObject(/*root object*/0, /*instantiatingBinding*/nullptr); } @@ -96,26 +63,39 @@ struct BindingFinder } }; -QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( +QVector<QQmlError> QQmlPropertyValidator::validateObject( int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const { const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex); + for (auto it = obj->inlineComponentsBegin(); it != obj->inlineComponentsEnd(); ++it) { + const auto errors = validateObject(it->objectIndex, /* instantiatingBinding*/ nullptr); + if (!errors.isEmpty()) + return errors; + } - if (obj->flags & QV4::CompiledData::Object::IsComponent) { + if (obj->hasFlag(QV4::CompiledData::Object::IsComponent) + && !obj->hasFlag(QV4::CompiledData::Object::IsInlineComponentRoot)) { Q_ASSERT(obj->nBindings == 1); const QV4::CompiledData::Binding *componentBinding = obj->bindingTable(); - Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object); + Q_ASSERT(componentBinding->type() == QV4::CompiledData::Binding::Type_Object); return validateObject(componentBinding->value.objectIndex, componentBinding); } - QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex); + QQmlPropertyCache::ConstPtr propertyCache = propertyCaches.at(objectIndex); if (!propertyCache) - return QVector<QQmlJS::DiagnosticMessage>(); + return QVector<QQmlError>(); QQmlCustomParser *customParser = nullptr; if (auto typeRef = resolvedType(obj->inheritedTypeNameIndex)) { - if (typeRef->type.isValid()) - customParser = typeRef->type.customParser(); + + // This binding instantiates a separate object. The separate object can have an ID and its + // own group properties even if it's then assigned to a value type, for example a 'var', or + // anything with an invokable ctor taking a QObject*. + populatingValueTypeGroupProperty = false; + + const auto type = typeRef->type(); + if (type.isValid()) + customParser = type.customParser(); } QList<const QV4::CompiledData::Binding*> customBindings; @@ -128,7 +108,7 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( if (!binding->isGroupProperty()) continue; - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) continue; if (populatingValueTypeGroupProperty) { @@ -142,9 +122,9 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( QQmlPropertyResolver propertyResolver(propertyCache); QString defaultPropertyName; - QQmlPropertyData *defaultProperty = nullptr; + const QQmlPropertyData *defaultProperty = nullptr; if (obj->indexOfDefaultPropertyOrAlias != -1) { - QQmlPropertyCache *cache = propertyCache->parent(); + const QQmlPropertyCache *cache = propertyCache->parent().data(); defaultPropertyName = cache->defaultPropertyName(); defaultProperty = cache->defaultProperty(); } else { @@ -152,19 +132,21 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( defaultProperty = propertyCache->defaultProperty(); } - QV4::BindingPropertyData collectedBindingPropertyData(obj->nBindings); + QV4::CompiledData::BindingPropertyData collectedBindingPropertyData(obj->nBindings); binding = obj->bindingTable(); for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { QString name = stringAt(binding->propertyNameIndex); + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + const QV4::CompiledData::Binding::Flags bindingFlags = binding->flags(); if (customParser) { - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { + if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) { customBindings << binding; continue; } - } else if (QmlIR::IRBuilder::isSignalPropertyName(name) + } else if (QQmlSignalNames::isHandlerName(name) && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) { customBindings << binding; continue; @@ -172,13 +154,14 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( } bool bindingToDefaultProperty = false; - bool isGroupProperty = instantiatingBinding && instantiatingBinding->type == QV4::CompiledData::Binding::Type_GroupProperty; + bool isGroupProperty = instantiatingBinding + && instantiatingBinding->type() == QV4::CompiledData::Binding::Type_GroupProperty; bool notInRevision = false; - QQmlPropertyData *pd = nullptr; + const QQmlPropertyData *pd = nullptr; if (!name.isEmpty()) { - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) { + if (bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerExpression + || bindingFlags & QV4::CompiledData::Binding::IsSignalHandlerObject) { pd = propertyResolver.signal(name, ¬InRevision); } else { pd = propertyResolver.property(name, ¬InRevision, @@ -187,9 +170,16 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( if (notInRevision) { QString typeName = stringAt(obj->inheritedTypeNameIndex); - auto *objectType = resolvedType(obj->inheritedTypeNameIndex); - if (objectType && objectType->type.isValid()) { - return recordError(binding->location, tr("\"%1.%2\" is not available in %3 %4.%5.").arg(typeName).arg(name).arg(objectType->type.module()).arg(objectType->majorVersion).arg(objectType->minorVersion)); + if (auto *objectType = resolvedType(obj->inheritedTypeNameIndex)) { + const auto type = objectType->type(); + if (type.isValid()) { + const auto version = objectType->version(); + return recordError(binding->location, + tr("\"%1.%2\" is not available in %3 %4.%5.") + .arg(typeName).arg(name).arg(type.module()) + .arg(version.majorVersion()) + .arg(version.minorVersion())); + } } else { return recordError(binding->location, tr("\"%1.%2\" is not available due to component versioning.").arg(typeName).arg(name)); } @@ -209,18 +199,21 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( if (name.constData()->isUpper() && !binding->isAttachedProperty()) { QQmlType type; QQmlImportNamespace *typeNamespace = nullptr; - imports.resolveType(stringAt(binding->propertyNameIndex), &type, nullptr, nullptr, &typeNamespace); + imports->resolveType( + QQmlTypeLoader::get(enginePrivate), stringAt(binding->propertyNameIndex), + &type, nullptr, &typeNamespace); if (typeNamespace) return recordError(binding->location, tr("Invalid use of namespace")); return recordError(binding->location, tr("Invalid attached object assignment")); } - if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) { + if (bindingType >= QV4::CompiledData::Binding::Type_Object + && (pd || binding->isAttachedProperty() || binding->isGroupProperty())) { const bool populatingValueTypeGroupProperty = pd - && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType()) - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment); - const QVector<QQmlJS::DiagnosticMessage> subObjectValidatorErrors + && QQmlMetaType::metaObjectForValueType(pd->propType()) + && !binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment); + const QVector<QQmlError> subObjectValidatorErrors = validateObject(binding->value.objectIndex, binding, populatingValueTypeGroupProperty); if (!subObjectValidatorErrors.isEmpty()) @@ -228,15 +221,25 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( } // Signal handlers were resolved and checked earlier in the signal handler conversion pass. - if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression - || binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject) + if (binding->flags() & (QV4::CompiledData::Binding::IsSignalHandlerExpression + | QV4::CompiledData::Binding::IsSignalHandlerObject + | QV4::CompiledData::Binding::IsPropertyObserver)) { continue; + } - if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) { - if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() || instantiatingBinding->isGroupProperty())) { - return recordError(binding->location, tr("Attached properties cannot be used here")); + if ((pd && bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) + || (!pd && bindingType == QV4::CompiledData::Binding::Type_GroupProperty)) { + if (instantiatingBinding && (instantiatingBinding->isAttachedProperty() + || instantiatingBinding->isGroupProperty())) { + return recordError( + binding->location, tr("%1 properties cannot be used here") + .arg(bindingType == QV4::CompiledData::Binding::Type_AttachedProperty + ? QStringLiteral("Attached") + : QStringLiteral("Group"))); } continue; + } else if (bindingType == QV4::CompiledData::Binding::Type_AttachedProperty) { + continue; } if (pd) { @@ -246,17 +249,17 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( if (!pd->isWritable() && !pd->isQList() && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) + && !(bindingFlags & QV4::CompiledData::Binding::InitializerForReadOnlyDeclaration) ) { - if (assigningToGroupProperty && binding->type < QV4::CompiledData::Binding::Type_Object) + if (assigningToGroupProperty && bindingType < QV4::CompiledData::Binding::Type_Object) return recordError(binding->valueLocation, tr("Cannot assign a value directly to a grouped property")); return recordError(binding->valueLocation, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } - if (!pd->isQList() && (binding->flags & QV4::CompiledData::Binding::IsListItem)) { + if (!pd->isQList() && (bindingFlags & QV4::CompiledData::Binding::IsListItem)) { QString error; - if (pd->propType() == qMetaTypeId<QQmlScriptString>()) + if (pd->propType() == QMetaType::fromType<QQmlScriptString>()) error = tr( "Cannot assign multiple values to a script property"); else error = tr( "Cannot assign multiple values to a singular property"); @@ -265,28 +268,28 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( if (!bindingToDefaultProperty && !binding->isGroupProperty() - && !(binding->flags & QV4::CompiledData::Binding::IsOnAssignment) + && !(bindingFlags & QV4::CompiledData::Binding::IsOnAssignment) && assigningToGroupProperty) { QV4::CompiledData::Location loc = binding->valueLocation; if (loc < (*assignedGroupProperty)->valueLocation) loc = (*assignedGroupProperty)->valueLocation; - if (pd && QQmlValueTypeFactory::isValueType(pd->propType())) + if (pd && QQmlMetaType::isValueType(pd->propType())) return recordError(loc, tr("Property has already been assigned a value")); return recordError(loc, tr("Cannot assign a value directly to a grouped property")); } - if (binding->type < QV4::CompiledData::Binding::Type_Script) { - QQmlJS::DiagnosticMessage bindingError = validateLiteralBinding(propertyCache, pd, binding); + if (bindingType < QV4::CompiledData::Binding::Type_Script) { + QQmlError bindingError = validateLiteralBinding(propertyCache, pd, binding); if (bindingError.isValid()) return recordError(bindingError); - } else if (binding->type == QV4::CompiledData::Binding::Type_Object) { - QQmlJS::DiagnosticMessage bindingError = validateObjectBinding(pd, name, binding); + } else if (bindingType == QV4::CompiledData::Binding::Type_Object) { + QQmlError bindingError = validateObjectBinding(pd, name, binding); if (bindingError.isValid()) return recordError(bindingError); } else if (binding->isGroupProperty()) { - if (QQmlValueTypeFactory::isValueType(pd->propType())) { - if (QQmlValueTypeFactory::metaObjectForMetaType(pd->propType())) { + if (QQmlMetaType::isValueType(pd->propType())) { + if (QQmlMetaType::metaObjectForValueType(pd->propType())) { if (!pd->isWritable()) { return recordError(binding->location, tr("Invalid property assignment: \"%1\" is a read-only property").arg(name)); } @@ -294,22 +297,30 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( return recordError(binding->location, tr("Invalid grouped property access")); } } else { - const int typeId = pd->propType(); - if (isPrimitiveType(typeId)) { + const QMetaType type = pd->propType(); + if (isPrimitiveType(type)) { return recordError( binding->location, tr("Invalid grouped property access: Property \"%1\" with primitive type \"%2\".") - .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(typeId))) + .arg(name, QString::fromUtf8(type.name())) ); } - if (!enginePrivate->propertyCacheForType(typeId)) { - return recordError(binding->location, - tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is not a value type") - .arg(name) - .arg(QString::fromLatin1(QMetaType::typeName(typeId))) - ); + if (!QQmlMetaType::propertyCacheForType(type)) { + auto mo = type.metaObject(); + if (!mo) { + return recordError(binding->location, + tr("Invalid grouped property access: Property \"%1\" with type \"%2\", which is neither a value nor an object type") + .arg(name, QString::fromUtf8(type.name())) + ); + } + if (QMetaObjectPrivate::get(mo)->flags & DynamicMetaObject) { + return recordError(binding->location, + tr("Unsupported grouped property access: Property \"%1\" with type \"%2\" has a dynamic meta-object.") + .arg(name, QString::fromUtf8(type.name())) + ); + } + // fall through, this is okay } } } @@ -338,32 +349,42 @@ QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::validateObject( customParser->clearErrors(); customParser->validator = this; customParser->engine = enginePrivate; - customParser->imports = &imports; - customParser->verifyBindings(compilationUnit, customBindings); + customParser->imports = imports; + customParser->verifyBindings( + enginePrivate->v4engine()->executableCompilationUnit( + QQmlRefPointer<QV4::CompiledData::CompilationUnit>(compilationUnit)), + customBindings); customParser->validator = nullptr; customParser->engine = nullptr; customParser->imports = (QQmlImports*)nullptr; - QVector<QQmlJS::DiagnosticMessage> parserErrors = customParser->errors(); + QVector<QQmlError> parserErrors = customParser->errors(); if (!parserErrors.isEmpty()) return parserErrors; } (*bindingPropertyDataPerObject)[objectIndex] = collectedBindingPropertyData; - QVector<QQmlJS::DiagnosticMessage> noError; + QVector<QQmlError> noError; return noError; } -QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCache, QQmlPropertyData *property, const QV4::CompiledData::Binding *binding) const +QQmlError QQmlPropertyValidator::validateLiteralBinding( + const QQmlPropertyCache::ConstPtr &propertyCache, const QQmlPropertyData *property, + const QV4::CompiledData::Binding *binding) const { if (property->isQList()) { return qQmlCompileError(binding->valueLocation, tr("Cannot assign primitives to lists")); } - QQmlJS::DiagnosticMessage noError; + QQmlError noError; if (property->isEnum()) { - if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum) + if (binding->hasFlag(QV4::CompiledData::Binding::IsResolvedEnum)) + return noError; + + // TODO: For historical reasons you can assign any number to an enum property alias + // This can be fixed with an opt-out mechanism, for example a pragma. + if (property->isAlias() && binding->isNumberBinding()) return noError; QString value = compilationUnit->bindingValueAsString(binding); @@ -381,49 +402,43 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp } auto warnOrError = [&](const QString &error) { - if (binding->type == QV4::CompiledData::Binding::Type_Null) { - QQmlError warning; - warning.setUrl(compilationUnit->url()); - warning.setLine(binding->valueLocation.line); - warning.setColumn(binding->valueLocation.column); - warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML " - "is deprecated. This will become a compile error in " - "future versions of Qt.")); - enginePrivate->warning(warning); - return noError; - } return qQmlCompileError(binding->valueLocation, error); }; - switch (property->propType()) { + const QV4::CompiledData::Binding::Type bindingType = binding->type(); + const auto isStringBinding = [&]() -> bool { + // validateLiteralBinding is not supposed to be used on scripts + Q_ASSERT(bindingType != QV4::CompiledData::Binding::Type_Script); + return bindingType == QV4::CompiledData::Binding::Type_String; + }; + + switch (property->propType().id()) { case QMetaType::QVariant: break; - case QVariant::String: { + case QMetaType::QString: { if (!binding->evaluatesToString()) { return warnOrError(tr("Invalid property assignment: string expected")); } } break; - case QVariant::StringList: { + case QMetaType::QStringList: { if (!binding->evaluatesToString()) { return warnOrError(tr("Invalid property assignment: string or string list expected")); } } break; - case QVariant::ByteArray: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + case QMetaType::QByteArray: { + if (bindingType != QV4::CompiledData::Binding::Type_String) return warnOrError(tr("Invalid property assignment: byte array expected")); - } } break; - case QVariant::Url: { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + case QMetaType::QUrl: { + if (bindingType != QV4::CompiledData::Binding::Type_String) return warnOrError(tr("Invalid property assignment: url expected")); - } } break; - case QVariant::UInt: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { + case QMetaType::UInt: { + if (bindingType == QV4::CompiledData::Binding::Type_Number) { double d = compilationUnit->bindingValueAsNumber(binding); if (double(uint(d)) == d) return noError; @@ -431,8 +446,8 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp return warnOrError(tr("Invalid property assignment: unsigned int expected")); } break; - case QVariant::Int: { - if (binding->type == QV4::CompiledData::Binding::Type_Number) { + case QMetaType::Int: { + if (bindingType == QV4::CompiledData::Binding::Type_Number) { double d = compilationUnit->bindingValueAsNumber(binding); if (double(int(d)) == d) return noError; @@ -441,162 +456,148 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp } break; case QMetaType::Float: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number expected")); } } break; - case QVariant::Double: { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + case QMetaType::Double: { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number expected")); } } break; - case QVariant::Color: { + case QMetaType::QColor: { bool ok = false; - QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::rgbaFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: color expected")); } } break; #if QT_CONFIG(datestring) - case QVariant::Date: { + case QMetaType::QDate: { bool ok = false; - QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::dateFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: date expected")); } } break; - case QVariant::Time: { + case QMetaType::QTime: { bool ok = false; - QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::timeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: time expected")); } } break; - case QVariant::DateTime: { + case QMetaType::QDateTime: { bool ok = false; - QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::dateTimeFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: datetime expected")); } } break; #endif // datestring - case QVariant::Point: { + case QMetaType::QPoint: { bool ok = false; - QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } } break; - case QVariant::PointF: { + case QMetaType::QPointF: { bool ok = false; - QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::pointFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } } break; - case QVariant::Size: { + case QMetaType::QSize: { bool ok = false; - QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } } break; - case QVariant::SizeF: { + case QMetaType::QSizeF: { bool ok = false; - QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::sizeFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: size expected")); } } break; - case QVariant::Rect: { + case QMetaType::QRect: { bool ok = false; - QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: rect expected")); } } break; - case QVariant::RectF: { + case QMetaType::QRectF: { bool ok = false; - QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); + if (isStringBinding()) + QQmlStringConverters::rectFFromString(compilationUnit->bindingValueAsString(binding), &ok); if (!ok) { return warnOrError(tr("Invalid property assignment: point expected")); } } break; - case QVariant::Bool: { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + case QMetaType::Bool: { + if (bindingType != QV4::CompiledData::Binding::Type_Boolean) { return warnOrError(tr("Invalid property assignment: boolean expected")); } } break; - case QVariant::Vector2D: { - struct { - float xp; - float yp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 2D vector expected")); - } - } - break; - case QVariant::Vector3D: { - struct { - float xp; - float yp; - float zy; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 3D vector expected")); - } - } - break; - case QVariant::Vector4D: { - struct { - float xp; - float yp; - float zy; - float wp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: 4D vector expected")); - } - } - break; - case QVariant::Quaternion: { - struct { - float wp; - float xp; - float yp; - float zp; - } vec; - if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, compilationUnit->bindingValueAsString(binding), &vec, sizeof(vec))) { - return warnOrError(tr("Invalid property assignment: quaternion expected")); + case QMetaType::QVector2D: + case QMetaType::QVector3D: + case QMetaType::QVector4D: + case QMetaType::QQuaternion: { + auto typeName = [&]() { + switch (property->propType().id()) { + case QMetaType::QVector2D: return QStringLiteral("2D vector"); + case QMetaType::QVector3D: return QStringLiteral("3D vector"); + case QMetaType::QVector4D: return QStringLiteral("4D vector"); + case QMetaType::QQuaternion: return QStringLiteral("quaternion"); + default: return QString(); + } + }; + const QVariant result = QQmlValueTypeProvider::createValueType( + compilationUnit->bindingValueAsString(binding), + property->propType()); + if (!result.isValid()) { + return warnOrError(tr("Invalid property assignment: %1 expected") + .arg(typeName())); } } break; - case QVariant::RegExp: - case QVariant::RegularExpression: + case QMetaType::QRegularExpression: return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required - if (property->propType() == qMetaTypeId<QList<qreal> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Number) { + if (property->propType() == QMetaType::fromType<QList<qreal> >()) { + if (bindingType != QV4::CompiledData::Binding::Type_Number) { return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); } break; - } else if (property->propType() == qMetaTypeId<QList<int> >()) { - bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number); + } else if (property->propType() == QMetaType::fromType<QList<int> >()) { + bool ok = (bindingType == QV4::CompiledData::Binding::Type_Number); if (ok) { double n = compilationUnit->bindingValueAsNumber(binding); if (double(int(n)) != n) @@ -605,35 +606,33 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp if (!ok) return warnOrError(tr("Invalid property assignment: int or array of ints expected")); break; - } else if (property->propType() == qMetaTypeId<QList<bool> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { + } else if (property->propType() == QMetaType::fromType<QList<bool> >()) { + if (bindingType != QV4::CompiledData::Binding::Type_Boolean) { return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); } break; - } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { - if (binding->type != QV4::CompiledData::Binding::Type_String) { + } else if (property->propType() == QMetaType::fromType<QList<QUrl> >()) { + if (bindingType != QV4::CompiledData::Binding::Type_String) { return warnOrError(tr("Invalid property assignment: url or array of urls expected")); } break; - } else if (property->propType() == qMetaTypeId<QList<QString> >()) { + } else if (property->propType() == QMetaType::fromType<QList<QString> >()) { if (!binding->evaluatesToString()) { return warnOrError(tr("Invalid property assignment: string or array of strings expected")); } break; - } else if (property->propType() == qMetaTypeId<QJSValue>()) { + } else if (property->propType() == QMetaType::fromType<QJSValue>()) { break; - } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { + } else if (property->propType() == QMetaType::fromType<QQmlScriptString>()) { break; } else if (property->isQObject() - && binding->type == QV4::CompiledData::Binding::Type_Null) { + && bindingType == QV4::CompiledData::Binding::Type_Null) { + break; + } else if (QQmlMetaType::qmlType(property->propType()).canConstructValueType()) { break; } - // otherwise, try a custom type assignment - QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); - if (!converter) { - return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); - } + return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QLatin1StringView(property->propType().name()))); } break; } @@ -644,9 +643,22 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateLiteralBinding(QQmlProp Returns true if from can be assigned to a (QObject) property of type to. */ -bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const +bool QQmlPropertyValidator::canCoerce(QMetaType to, QQmlPropertyCache::ConstPtr fromMo) const { - QQmlPropertyCache *toMo = enginePrivate->rawPropertyCacheForType(to); + QQmlPropertyCache::ConstPtr toMo = QQmlMetaType::rawPropertyCacheForType(to); + + if (toMo.isNull()) { + // if we have an inline component from the current file, + // it is not properly registered at this point, as registration + // only occurs after the whole file has been validated + // Therefore we need to check the ICs here + for (const auto& icDatum : compilationUnit->inlineComponentData) { + if (icDatum.qmlType.typeId() == to) { + toMo = compilationUnit->propertyCaches.at(icDatum.objectIndex); + break; + } + } + } while (fromMo) { if (fromMo == toMo) @@ -656,40 +668,39 @@ bool QQmlPropertyValidator::canCoerce(int to, QQmlPropertyCache *fromMo) const return false; } -QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const +QVector<QQmlError> QQmlPropertyValidator::recordError(const QV4::CompiledData::Location &location, const QString &description) const { - QVector<QQmlJS::DiagnosticMessage> errors; + QVector<QQmlError> errors; errors.append(qQmlCompileError(location, description)); return errors; } -QVector<QQmlJS::DiagnosticMessage> QQmlPropertyValidator::recordError(const QQmlJS::DiagnosticMessage &error) const +QVector<QQmlError> QQmlPropertyValidator::recordError(const QQmlError &error) const { - QVector<QQmlJS::DiagnosticMessage> errors; + QVector<QQmlError> errors; errors.append(error); return errors; } -QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const +QQmlError QQmlPropertyValidator::validateObjectBinding(const QQmlPropertyData *property, const QString &propertyName, const QV4::CompiledData::Binding *binding) const { - QQmlJS::DiagnosticMessage noError; + QQmlError noError; - if (binding->flags & QV4::CompiledData::Binding::IsOnAssignment) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Object); + if (binding->hasFlag(QV4::CompiledData::Binding::IsOnAssignment)) { + Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Object); bool isValueSource = false; bool isPropertyInterceptor = false; const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex); if (auto *typeRef = resolvedType(targetObject->inheritedTypeNameIndex)) { - QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate)); - const QMetaObject *mo = cache->firstCppMetaObject(); + QQmlPropertyCache::ConstPtr cache = typeRef->createPropertyCache(); + const QMetaObject *mo = cache ? cache->firstCppMetaObject() : nullptr; QQmlType qmlType; while (mo && !qmlType.isValid()) { qmlType = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } - Q_ASSERT(qmlType.isValid()); isValueSource = qmlType.propertyValueSourceCast() != -1; isPropertyInterceptor = qmlType.propertyValueInterceptorCast() != -1; @@ -702,7 +713,7 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope return noError; } - const int propType = property->propType(); + const QMetaType propType = property->propType(); const auto rhsType = [&]() { return stringAt(compilationUnit->objectAt(binding->value.objectIndex) ->inheritedTypeNameIndex); @@ -712,43 +723,59 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope // Can only check at instantiation time if the created sub-object successfully casts to the // target interface. return noError; - } else if (propType == QMetaType::QVariant || propType == qMetaTypeId<QJSValue>()) { + } else if (propType == QMetaType::fromType<QVariant>() + || propType == QMetaType::fromType<QJSValue>()) { // We can convert everything to QVariant :) return noError; } else if (property->isQList()) { - const int listType = enginePrivate->listType(propType); + const QMetaType listType = QQmlMetaType::listValueType(property->propType()); if (!QQmlMetaType::isInterface(listType)) { - QQmlPropertyCache *source = propertyCaches.at(binding->value.objectIndex); + QQmlPropertyCache::ConstPtr source = propertyCaches.at(binding->value.objectIndex); if (!canCoerce(listType, source)) { return qQmlCompileError(binding->valueLocation, tr("Cannot assign object to list property \"%1\"").arg(propertyName)); } } return noError; - } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { + } else if (binding->hasFlag(QV4::CompiledData::Binding::IsSignalHandlerObject) + && property->isFunction()) { return noError; } else if (isPrimitiveType(propType)) { - auto typeName = QString::fromUtf8(QMetaType::typeName(propType)); - return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting \"%3\"") + auto typeName = QString::fromUtf8(QMetaType(propType).name()); + return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting \"%3\"") .arg(rhsType()) .arg(propertyName) .arg(typeName)); - } else if (QQmlValueTypeFactory::isValueType(propType)) { - return qQmlCompileError(binding->location, tr("Can not assign value of type \"%1\" to property \"%2\", expecting an object") - .arg(rhsType()).arg(propertyName)); - } else if (propType == qMetaTypeId<QQmlScriptString>()) { + } else if (propType == QMetaType::fromType<QQmlScriptString>()) { return qQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); + } else if (QQmlMetaType::isValueType(property->propType())) { + return qQmlCompileError(binding->location, tr("Cannot assign value of type \"%1\" to property \"%2\", expecting an object") + .arg(rhsType()).arg(propertyName)); } else { // We want to use the raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - // Using -1 for the minor version ensures that we get the raw metaObject. - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(propType, -1); + // Not passing a version ensures that we get the raw metaObject. + QQmlPropertyCache::ConstPtr propertyMetaObject + = QQmlMetaType::rawPropertyCacheForType(propType); + if (!propertyMetaObject) { + // if we have an inline component from the current file, + // it is not properly registered at this point, as registration + // only occurs after the whole file has been validated + // Therefore we need to check the ICs here + for (const auto& icDatum: compilationUnit->inlineComponentData) { + if (icDatum.qmlType.typeId() == property->propType()) { + propertyMetaObject + = compilationUnit->propertyCaches.at(icDatum.objectIndex); + break; + } + } + } if (propertyMetaObject) { // Will be true if the assigned type inherits propertyMetaObject // Determine isAssignable value bool isAssignable = false; - QQmlPropertyCache *c = propertyCaches.at(binding->value.objectIndex); + QQmlPropertyCache::ConstPtr c = propertyCaches.at(binding->value.objectIndex); while (c && !isAssignable) { isAssignable |= c == propertyMetaObject; c = c->parent(); @@ -756,11 +783,11 @@ QQmlJS::DiagnosticMessage QQmlPropertyValidator::validateObjectBinding(QQmlPrope if (!isAssignable) { return qQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.") - .arg(rhsType()).arg(QLatin1String(QMetaType::typeName(propType)))); + .arg(rhsType()).arg(QLatin1String(property->propType().name()))); } } else { return qQmlCompileError(binding->valueLocation, tr("Cannot assign to property of unknown type \"%1\".") - .arg(QLatin1String(QMetaType::typeName(propType)))); + .arg(QLatin1String(property->propType().name()))); } } |