diff options
Diffstat (limited to 'src/qml/qml/qqmlmetatype.cpp')
-rw-r--r-- | src/qml/qml/qqmlmetatype.cpp | 1674 |
1 files changed, 1113 insertions, 561 deletions
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index c21247bb95..1175bde3db 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1,49 +1,15 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module 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 "qqmlmetatype_p.h" +#include <private/qqmlextensionplugin_p.h> #include <private/qqmlmetatypedata_p.h> -#include <private/qqmltypemodule_p_p.h> +#include <private/qqmlpropertycachecreator_p.h> #include <private/qqmltype_p_p.h> #include <private/qqmltypeloader_p.h> -#include <private/qqmlextensionplugin_p.h> +#include <private/qqmltypemodule_p.h> +#include <private/qqmlvaluetype_p.h> #include <private/qv4executablecompilationunit_p.h> #include <QtCore/qcoreapplication.h> @@ -51,6 +17,7 @@ #include <QtCore/qloggingcategory.h> Q_DECLARE_LOGGING_CATEGORY(DBG_DISK_CACHE) +Q_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration") QT_BEGIN_NAMESPACE @@ -62,6 +29,12 @@ struct LockedData : private QQmlMetaTypeData Q_GLOBAL_STATIC(LockedData, metaTypeData) Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) +struct ModuleUri : public QString +{ + ModuleUri(const QString &string) : QString(string) {} + ModuleUri(const std::unique_ptr<QQmlTypeModule> &module) : QString(module->module()) {} +}; + class QQmlMetaTypeDataPtr { Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) @@ -80,7 +53,7 @@ public: bool isValid() const { return data != nullptr; } private: - QMutexLocker locker; + QMutexLocker<QRecursiveMutex> locker; LockedData *data = nullptr; }; @@ -88,45 +61,35 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &type) { auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); - d->iid = type.iid; + d->extraData.interfaceTypeData = type.iid; d->typeId = type.typeId; d->listId = type.listId; - d->isSetup = true; - d->version_maj = 0; - d->version_min = 0; + d->module = QString::fromUtf8(type.uri); + d->version = type.version; data->registerType(d); return d; } -static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, - const QQmlPrivate::RegisterSingletonType &type) +static QQmlTypePrivate *createQQmlType( + QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterSingletonType &type, + const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) { auto *d = new QQmlTypePrivate(QQmlType::SingletonType); data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; - - if (type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi)) { - if (type.version >= 1) // static metaobject added in version 1 - d->baseMetaObject = type.instanceMetaObject; - if (type.version >= 2) // typeId added in version 2 - d->typeId = type.typeId; - if (type.version >= 2) // revisions added in version 2 - d->revision = type.revision; - } + d->version = type.version; - d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; - d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; - if (type.version >= 3) { - d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.generalizedQobjectApi; - } else { - d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + if (type.qObjectApi) { + d->baseMetaObject = type.instanceMetaObject; + d->typeId = type.typeId; + d->revision = type.revision; } - d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); - d->extraData.sd->singletonInstanceInfo->instanceMetaObject - = ((type.qobjectApi || (type.version >= 3 && type.generalizedQobjectApi) ) && type.version >= 1) ? type.instanceMetaObject : nullptr; + + d->extraData.singletonTypeData->singletonInstanceInfo = siinfo; + d->extraData.singletonTypeData->extFunc = type.extensionObjectCreate; + d->extraData.singletonTypeData->extMetaObject = type.extensionMetaObject; return d; } @@ -138,72 +101,111 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; - if (type.version >= 1) // revisions added in version 1 - d->revision = type.revision; + d->version = type.version; + d->revision = type.revision; d->typeId = type.typeId; d->listId = type.listId; - d->extraData.cd->allocationSize = type.objectSize; - d->extraData.cd->newFunc = type.create; - d->extraData.cd->noCreationReason = type.noCreationReason; + d->extraData.cppTypeData->allocationSize = type.objectSize; + d->extraData.cppTypeData->userdata = type.userdata; + d->extraData.cppTypeData->newFunc = type.create; + d->extraData.cppTypeData->noCreationReason = type.noCreationReason; + d->extraData.cppTypeData->createValueTypeFunc = type.createValueType; d->baseMetaObject = type.metaObject; - d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; - d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; - d->extraData.cd->parserStatusCast = type.parserStatusCast; - d->extraData.cd->propertyValueSourceCast = type.valueSourceCast; - d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; - d->extraData.cd->extFunc = type.extensionObjectCreate; - d->extraData.cd->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); - d->extraData.cd->registerEnumClassesUnscoped = true; + d->extraData.cppTypeData->attachedPropertiesFunc = type.attachedPropertiesFunction; + d->extraData.cppTypeData->attachedPropertiesType = type.attachedPropertiesMetaObject; + d->extraData.cppTypeData->parserStatusCast = type.parserStatusCast; + d->extraData.cppTypeData->propertyValueSourceCast = type.valueSourceCast; + d->extraData.cppTypeData->propertyValueInterceptorCast = type.valueInterceptorCast; + d->extraData.cppTypeData->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast) + ? type.finalizerCast + : -1; + d->extraData.cppTypeData->extFunc = type.extensionObjectCreate; + d->extraData.cppTypeData->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); + d->extraData.cppTypeData->registerEnumClassesUnscoped = true; + d->extraData.cppTypeData->registerEnumsFromRelatedTypes = true; + d->extraData.cppTypeData->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod) + && type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None; + d->extraData.cppTypeData->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod) + && type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured; if (type.extensionMetaObject) - d->extraData.cd->extMetaObject = type.extensionMetaObject; + d->extraData.cppTypeData->extMetaObject = type.extensionMetaObject; // Check if the user wants only scoped enum classes if (d->baseMetaObject) { - auto indexOfClassInfo = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped"); - if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false")) - d->extraData.cd->registerEnumClassesUnscoped = false; + auto indexOfUnscoped = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped"); + if (indexOfUnscoped != -1 + && qstrcmp(d->baseMetaObject->classInfo(indexOfUnscoped).value(), "false") == 0) { + d->extraData.cppTypeData->registerEnumClassesUnscoped = false; + } + + auto indexOfRelated = d->baseMetaObject->indexOfClassInfo("RegisterEnumsFromRelatedTypes"); + if (indexOfRelated != -1 + && qstrcmp(d->baseMetaObject->classInfo(indexOfRelated).value(), "false") == 0) { + d->extraData.cppTypeData->registerEnumsFromRelatedTypes = false; + } } return d; } +static void addQQmlMetaTypeInterfaces(QQmlTypePrivate *priv, const QByteArray &className) +{ + Q_ASSERT(!className.isEmpty()); + QByteArray ptr = className + '*'; + QByteArray lst = "QQmlListProperty<" + className + '>'; + + QMetaType ptr_type(new QQmlMetaTypeInterface(ptr)); + QMetaType lst_type(new QQmlListMetaTypeInterface(lst, ptr_type.iface())); + + // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry. + ptr_type.id(); + lst_type.id(); + + priv->typeId = ptr_type; + priv->listId = lst_type; +} + static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) { + // This is a procedurally registered composite type. It's evil. It doesn't get any metatypes + // because we never want to find it in the compositeTypes. Otherwise we might mix it up with an + // actually compiled version of the same type. + auto *d = new QQmlTypePrivate(QQmlType::CompositeType); data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; - - d->extraData.fd->url = QQmlTypeLoader::normalize(type.url); + d->version = type.version; + d->extraData.compositeTypeData = QQmlTypeLoader::normalize(type.url); return d; } -static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, - const QQmlPrivate::RegisterCompositeSingletonType &type) +static QQmlTypePrivate *createQQmlType( + QQmlMetaTypeData *data, const QString &elementName, + const QQmlPrivate::RegisterCompositeSingletonType &type, + const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) { + // This is a procedurally registered composite singleton. It's evil. It doesn't get any + // metatypes because we never want to find it in the compositeTypes. Otherwise we might mix it + // up with an actually compiled version of the same type. + auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); data->registerType(d); d->setName(QString::fromUtf8(type.uri), elementName); - d->version_maj = type.versionMajor; - d->version_min = type.versionMinor; + d->version = type.version; - d->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; - d->extraData.sd->singletonInstanceInfo->url = QQmlTypeLoader::normalize(type.url); - d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + d->extraData.singletonTypeData->singletonInstanceInfo = siinfo; return d; } void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, - const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd) + const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd, + QQmlMetaType::ClonePolicy policy) { // Set classname - builder.setClassName(ignoreEnd->className()); + builder.setClassName(mo->className()); // Clone Q_CLASSINFO for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { @@ -217,44 +219,45 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } - // Clone Q_PROPERTY - for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { - QMetaProperty property = mo->property(ii); + if (policy != QQmlMetaType::CloneEnumsOnly) { + // Clone Q_METHODS - do this first to avoid duplicating the notify signals. + for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { + QMetaMethod method = mo->method(ii); - int otherIndex = ignoreEnd->indexOfProperty(property.name()); - if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { - builder.addProperty(QByteArray("__qml_ignore__") + property.name(), QByteArray("void")); - // Skip - } else { - builder.addProperty(property); - } - } + // More complex - need to search name + QByteArray name = method.name(); - // Clone Q_METHODS - for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { - QMetaMethod method = mo->method(ii); + bool found = false; - // More complex - need to search name - QByteArray name = method.name(); + for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); + !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { + QMetaMethod other = ignoreEnd->method(ii); - bool found = false; + found = name == other.name(); + } - for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); - !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); - ++ii) { + QMetaMethodBuilder m = builder.addMethod(method); + if (found) // SKIP + m.setAccess(QMetaMethod::Private); + } - QMetaMethod other = ignoreEnd->method(ii); + // Clone Q_PROPERTY + for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { + QMetaProperty property = mo->property(ii); - found = name == other.name(); + int otherIndex = ignoreEnd->indexOfProperty(property.name()); + if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { + builder.addProperty(QByteArray("__qml_ignore__") + property.name(), + QByteArray("void")); + // Skip + } else { + builder.addProperty(property); + } } - - QMetaMethodBuilder m = builder.addMethod(method); - if (found) // SKIP - m.setAccess(QMetaMethod::Private); } - // Clone Q_ENUMS + // Clone enums registered with the metatype system for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { QMetaEnum enumerator = mo->enumerator(ii); @@ -267,21 +270,32 @@ void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, } } -void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, int majorVersion, - void (*registerFunction)()) +void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)()) { - const QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); QQmlMetaTypeDataPtr data; - if (data->moduleTypeRegistrationFunctions.contains(versionedUri)) - qFatal("Canot add multiple registrations for %s %d", qPrintable(uri), majorVersion); + if (data->moduleTypeRegistrationFunctions.contains(uri)) + qFatal("Cannot add multiple registrations for %s", qPrintable(uri)); else - data->moduleTypeRegistrationFunctions.insert(versionedUri, registerFunction); + data->moduleTypeRegistrationFunctions.insert(uri, registerFunction); } -void QQmlMetaType::qmlRegisterModuleTypes(const QString &uri, int majorVersion) +void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri) { QQmlMetaTypeDataPtr data; - data->registerModuleTypes(QQmlMetaTypeData::VersionedUri(uri, majorVersion)); + + if (!data.isValid()) + return; // shutdown/deletion race. Not a problem. + + if (!data->moduleTypeRegistrationFunctions.contains(uri)) + qFatal("Cannot remove multiple registrations for %s", qPrintable(uri)); + else + data->moduleTypeRegistrationFunctions.remove(uri); +} + +bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri) +{ + QQmlMetaTypeDataPtr data; + return data->registerModuleTypes(uri); } void QQmlMetaType::clearTypeRegistrations() @@ -289,9 +303,7 @@ void QQmlMetaType::clearTypeRegistrations() //Only cleans global static, assumed no running engine QQmlMetaTypeDataPtr data; - for (QQmlMetaTypeData::TypeModules::const_iterator i = data->uriToModule.constBegin(), cend = data->uriToModule.constEnd(); i != cend; ++i) - delete *i; - + data->uriToModule.clear(); data->types.clear(); data->idToType.clear(); data->nameToType.clear(); @@ -299,17 +311,33 @@ void QQmlMetaType::clearTypeRegistrations() data->typePropertyCaches.clear(); data->urlToNonFileImportType.clear(); data->metaObjectToType.clear(); - data->uriToModule.clear(); data->undeletableTypes.clear(); + data->propertyCaches.clear(); + data->inlineComponentTypes.clear(); + + // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first. + QQmlMetaTypeData::CompositeTypes emptyComposites; + emptyComposites.swap(data->compositeTypes); +} + +void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name) +{ + QQmlMetaTypeDataPtr data; + const QQmlType type = data->types.value(typeIndex); + const QQmlTypePrivate *priv = type.priv(); + data->nameToType.insert(name, priv); } -int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &autoparent) +int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function) { + if (function.structVersion > 1) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; - data->parentFunctions.append(autoparent.function); + data->parentFunctions.append(function.function); - return data->parentFunctions.count() - 1; + return data->parentFunctions.size() - 1; } void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) @@ -320,30 +348,23 @@ void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFun QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) { - if (type.version > 0) + if (type.structVersion > 1) qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); QQmlMetaTypeDataPtr data; QQmlTypePrivate *priv = createQQmlType(data, type); Q_ASSERT(priv); - data->idToType.insert(priv->typeId, priv); - data->idToType.insert(priv->listId, priv); - // XXX No insertMulti, so no multi-version interfaces? - if (!priv->elementName.isEmpty()) - data->nameToType.insert(priv->elementName, priv); - if (data->interfaces.size() <= type.typeId) - data->interfaces.resize(type.typeId + 16); - if (data->lists.size() <= type.listId) - data->lists.resize(type.listId + 16); - data->interfaces.setBit(type.typeId, true); - data->lists.setBit(type.listId, true); + data->idToType.insert(priv->typeId.id(), priv); + data->idToType.insert(priv->listId.id(), priv); + + data->interfaces.insert(type.typeId.id()); return QQmlType(priv); } -QString registrationTypeString(QQmlType::RegistrationType typeType) +static QString registrationTypeString(QQmlType::RegistrationType typeType) { QString typeStr; if (typeType == QQmlType::CppType) @@ -352,27 +373,43 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) typeStr = QStringLiteral("singleton type"); else if (typeType == QQmlType::CompositeSingletonType) typeStr = QStringLiteral("composite singleton type"); + else if (typeType == QQmlType::SequentialContainerType) + typeStr = QStringLiteral("sequential container type"); else typeStr = QStringLiteral("type"); return typeStr; } // NOTE: caller must hold a QMutexLocker on "data" -bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, - const char *uri, const QString &typeName, int majorVersion) +static bool checkRegistration( + QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, + const QString &typeName, QTypeRevision version, QMetaType::TypeFlags flags) { if (!typeName.isEmpty()) { - if (typeName.at(0).isLower()) { + if (typeName.at(0).isLower() && (flags & QMetaType::PointerToQObject)) { QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter")); - data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName)); + data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType), typeName)); return false; } - int typeNameLen = typeName.length(); + if (typeName.at(0).isUpper() + && (flags & (QMetaType::IsGadget | QMetaType::PointerToGadget))) { + qCWarning(lcTypeRegistration).noquote() + << QCoreApplication::translate( + "qmlRegisterType", + "Invalid QML %1 name \"%2\"; " + "value type names should begin with a lowercase letter") + .arg(registrationTypeString(typeType), typeName); + } + + // There can also be types that aren't even gadgets, and there can be types for namespaces. + // We cannot check those, but namespaces should be uppercase. + + int typeNameLen = typeName.size(); for (int ii = 0; ii < typeNameLen; ++ii) { - if (!(typeName.at(ii).isLetterOrNumber() || typeName.at(ii) == '_')) { + if (!(typeName.at(ii).isLetterOrNumber() || typeName.at(ii) == u'_')) { QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"")); - data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType)).arg(typeName)); + data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType), typeName)); return false; } } @@ -380,16 +417,15 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da if (uri && !typeName.isEmpty()) { QString nameSpace = QString::fromUtf8(uri); - 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; - } + QQmlTypeModule *qqtm = data->findTypeModule(nameSpace, version); + if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) { + QString failure(QCoreApplication::translate( + "qmlRegisterType", + "Cannot install %1 '%2' into protected module '%3' version '%4'")); + data->recordTypeRegFailure(failure + .arg(registrationTypeString(typeType), typeName, nameSpace) + .arg(version.majorVersion())); + return false; } } @@ -397,46 +433,40 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da } // NOTE: caller must hold a QMutexLocker on "data" -QQmlTypeModule *getTypeModule(const QHashedString &uri, int majorVersion, QQmlMetaTypeData *data) +static QQmlTypeModule *getTypeModule( + const QHashedString &uri, QTypeRevision version, QQmlMetaTypeData *data) { - QQmlMetaTypeData::VersionedUri versionedUri(uri, majorVersion); - QQmlTypeModule *module = data->uriToModule.value(versionedUri); - if (!module) { - module = new QQmlTypeModule(versionedUri.uri, versionedUri.majorVersion); - data->uriToModule.insert(versionedUri, module); - } - return module; + if (QQmlTypeModule *module = data->findTypeModule(uri, version)) + return module; + return data->addTypeModule(std::make_unique<QQmlTypeModule>(uri, version.majorVersion())); } // NOTE: caller must hold a QMutexLocker on "data" -void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) +static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) { Q_ASSERT(type); if (!type->elementName.isEmpty()) - data->nameToType.insertMulti(type->elementName, type); + data->nameToType.insert(type->elementName, type); if (type->baseMetaObject) - data->metaObjectToType.insertMulti(type->baseMetaObject, type); + data->metaObjectToType.insert(type->baseMetaObject, type); - if (type->typeId) { - data->idToType.insert(type->typeId, type); - if (data->objects.size() <= type->typeId) - data->objects.resize(type->typeId + 16); - data->objects.setBit(type->typeId, true); - } + if (type->regType == QQmlType::SequentialContainerType) { + if (type->listId.isValid()) + data->idToType.insert(type->listId.id(), type); + } else { + if (type->typeId.isValid()) + data->idToType.insert(type->typeId.id(), type); - if (type->listId) { - if (data->lists.size() <= type->listId) - data->lists.resize(type->listId + 16); - data->lists.setBit(type->listId, true); - data->idToType.insert(type->listId, type); + if (type->listId.flags().testFlag(QMetaType::IsQmlList)) + data->idToType.insert(type->listId.id(), type); } if (!type->module.isEmpty()) { const QHashedString &mod = type->module; - QQmlTypeModule *module = getTypeModule(mod, type->version_maj, data); + QQmlTypeModule *module = getTypeModule(mod, type->version, data); Q_ASSERT(module); module->add(type); } @@ -444,38 +474,52 @@ void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) { + if (type.structVersion > int(QQmlPrivate::RegisterType::CurrentVersion)) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; QString elementName = QString::fromUtf8(type.elementName); - if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.versionMajor)) + if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.version, + QMetaType(type.typeId).flags())) { return QQmlType(); + } QQmlTypePrivate *priv = createQQmlType(data, elementName, type); - addTypeToData(priv, data); - if (!type.typeId) - data->idToType.insert(priv->typeId, priv); return QQmlType(priv); } -QQmlType QQmlMetaType::registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) +QQmlType QQmlMetaType::registerSingletonType( + const QQmlPrivate::RegisterSingletonType &type, + const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) { + if (type.structVersion > 1) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; QString typeName = QString::fromUtf8(type.typeName); - if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.versionMajor)) + if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.version, + QMetaType(type.typeId).flags())) { return QQmlType(); + } - QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo); addTypeToData(priv, data); return QQmlType(priv); } -QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::RegisterCompositeSingletonType &type) +QQmlType QQmlMetaType::registerCompositeSingletonType( + const QQmlPrivate::RegisterCompositeSingletonType &type, + const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) { + if (type.structVersion > 1) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. QQmlMetaTypeDataPtr data; @@ -484,21 +528,24 @@ QQmlType QQmlMetaType::registerCompositeSingletonType(const QQmlPrivate::Registe if (*(type.uri) == '\0') fileImport = true; if (!checkRegistration(QQmlType::CompositeSingletonType, data, fileImport ? nullptr : type.uri, - typeName, type.versionMajor)) { + typeName, type.version, {})) { return QQmlType(); } - QQmlTypePrivate *priv = createQQmlType(data, typeName, type); + QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo); addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); + files->insert(siinfo->url, priv); return QQmlType(priv); } QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) { + if (type.structVersion > 1) + qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); + // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. QQmlMetaTypeDataPtr data; @@ -506,61 +553,209 @@ QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterComposit bool fileImport = false; if (*(type.uri) == '\0') fileImport = true; - if (!checkRegistration(QQmlType::CompositeType, data, fileImport?nullptr:type.uri, typeName, type.versionMajor)) + if (!checkRegistration(QQmlType::CompositeType, data, fileImport?nullptr:type.uri, typeName, + type.version, {})) { return QQmlType(); + } QQmlTypePrivate *priv = createQQmlType(data, typeName, type); addTypeToData(priv, data); QQmlMetaTypeData::Files *files = fileImport ? &(data->urlToType) : &(data->urlToNonFileImportType); - files->insertMulti(QQmlTypeLoader::normalize(type.url), priv); + files->insert(QQmlTypeLoader::normalize(type.url), priv); return QQmlType(priv); } -void QQmlMetaType::registerInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) +class QQmlMetaTypeRegistrationFailureRecorder +{ + Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) +public: + QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) + : data(data) + { + data->setTypeRegistrationFailures(failures); + } + + ~QQmlMetaTypeRegistrationFailureRecorder() + { + data->setTypeRegistrationFailures(nullptr); + } + + QQmlMetaTypeData *data = nullptr; +}; + + +static QQmlType createTypeForUrl( + QQmlMetaTypeData *data, const QUrl &url, const QHashedStringRef &qualifiedType, + QQmlMetaType::CompositeTypeLookupMode mode, QList<QQmlError> *errors, QTypeRevision version) { - QByteArray name = compilationUnit->rootPropertyCache()->className(); + const int dot = qualifiedType.indexOf(QLatin1Char('.')); + const QString typeName = dot < 0 + ? qualifiedType.toString() + : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); + + QStringList failures; + QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); + + // Register the type. Note that the URI parameters here are empty; for + // file type imports, we do not place them in a URI as we don't + // necessarily have a good and unique one (picture a library import, + // which may be found in multiple plugin locations on disk), but there + // are other reasons for this too. + // + // By not putting them in a URI, we prevent the types from being + // registered on a QQmlTypeModule; this is important, as once types are + // placed on there, they cannot be easily removed, meaning if the + // developer subsequently loads a different import (meaning different + // types) with the same URI (using, say, a different plugin path), it is + // very undesirable that we continue to associate the types from the + // "old" URI with that new module. + // + // Not having URIs also means that the types cannot be found by name + // etc, the only way to look them up is through QQmlImports -- for + // better or worse. + const QQmlType::RegistrationType registrationType = mode == QQmlMetaType::Singleton + ? QQmlType::CompositeSingletonType + : QQmlType::CompositeType; + if (checkRegistration(registrationType, data, nullptr, typeName, version, {})) { + + // TODO: Ideally we should defer most of this work using some lazy/atomic mechanism + // that creates the details on first use. We must not observably modify + // QQmlTypePrivate after it has been created since it is supposed to be immutable + // and shared across threads. - QByteArray ptr = name + '*'; - QByteArray lst = "QQmlListProperty<" + name + '>'; + auto *priv = new QQmlTypePrivate(registrationType); + addQQmlMetaTypeInterfaces(priv, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url)); + + priv->setName(QString(), typeName); + priv->version = version; + + if (mode == QQmlMetaType::Singleton) { + QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create(); + siinfo->url = url; + siinfo->typeName = typeName.toUtf8(); + priv->extraData.singletonTypeData->singletonInstanceInfo = + QQmlType::SingletonInstanceInfo::ConstPtr( + siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt); + } else { + priv->extraData.compositeTypeData = url; + } - int ptr_type = QMetaType::registerNormalizedType(ptr, - QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject*>::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper<QObject*>::Construct, - sizeof(QObject*), - static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QObject*>::Flags), - nullptr); - int lst_type = QMetaType::registerNormalizedType(lst, - QtMetaTypePrivate::QMetaTypeFunctionHelper<QQmlListProperty<QObject> >::Destruct, - QtMetaTypePrivate::QMetaTypeFunctionHelper<QQmlListProperty<QObject> >::Construct, - sizeof(QQmlListProperty<QObject>), - static_cast<QFlags<QMetaType::TypeFlag> >(QtPrivate::QMetaTypeTypeFlags<QQmlListProperty<QObject> >::Flags), - static_cast<QMetaObject*>(nullptr)); + data->registerType(priv); + addTypeToData(priv, data); + return QQmlType(priv); + } - compilationUnit->metaTypeId = ptr_type; - compilationUnit->listMetaTypeId = lst_type; + // This means that the type couldn't be found by URL, but could not be + // registered either, meaning we most likely were passed some kind of bad + // data. + if (errors) { + QQmlError error; + error.setDescription(failures.join(u'\n')); + errors->prepend(error); + } else { + qWarning("%s", failures.join(u'\n').toLatin1().constData()); + } + return QQmlType(); +} +QQmlType QQmlMetaType::findCompositeType( + const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, + CompositeTypeLookupMode mode) +{ + const QUrl normalized = QQmlTypeLoader::normalize(url); QQmlMetaTypeDataPtr data; - data->qmlLists.insert(lst_type, ptr_type); + + bool urlExists = true; + auto found = data->urlToType.constFind(normalized); + if (found == data->urlToType.cend()) { + found = data->urlToNonFileImportType.constFind(normalized); + if (found == data->urlToNonFileImportType.cend()) + urlExists = false; + } + + if (const QtPrivate::QMetaTypeInterface *iface = urlExists + ? found.value()->typeId.iface() + : nullptr) { + if (compilationUnit.isNull()) + return QQmlType(*found); + + const auto composite = data->compositeTypes.constFind(iface); + if (composite == data->compositeTypes.constEnd() || composite.value() == compilationUnit) + return QQmlType(*found); + } + + const QQmlType type = createTypeForUrl( + data, normalized, QHashedStringRef(), mode, nullptr, QTypeRevision()); + + if (!urlExists && type.isValid()) + data->urlToType.insert(normalized, type.priv()); + + return type; } -void QQmlMetaType::unregisterInternalCompositeType(QV4::ExecutableCompilationUnit *compilationUnit) +static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url) { - int ptr_type = compilationUnit->metaTypeId; - int lst_type = compilationUnit->listMetaTypeId; + QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::InlineComponentType); + priv->setName(QString(), url.fragment()); + + priv->extraData.inlineComponentTypeData = url; + + const QByteArray className + = QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(url, url.fragment()); + + addQQmlMetaTypeInterfaces(priv, className); + const QQmlType result(priv); + priv->release(); + + data->inlineComponentTypes.insert(url, result); + + return result; +} +QQmlType QQmlMetaType::findInlineComponentType( + const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +{ QQmlMetaTypeDataPtr data; - data->qmlLists.remove(lst_type); - QMetaType::unregisterType(ptr_type); - QMetaType::unregisterType(lst_type); + // If there is an "unclaimed" inline component type, we can "claim" it now. Otherwise + // we have to create a new one. + const auto it = data->inlineComponentTypes.constFind(url); + if (it != data->inlineComponentTypes.constEnd()) { + const auto jt = data->compositeTypes.constFind(it->typeId().iface()); + if (jt == data->compositeTypes.constEnd() || *jt == compilationUnit) + return *it; + } + + return doRegisterInlineComponentType(data, url); +} + +void QQmlMetaType::unregisterInternalCompositeType(QMetaType metaType, QMetaType listMetaType) +{ + // This may be called from delayed dtors on shutdown when the data is already gone. + QQmlMetaTypeDataPtr data; + if (data.isValid()) { + if (QQmlValueType *vt = data->metaTypeToValueType.take(metaType.id())) + delete vt; + if (QQmlValueType *vt = data->metaTypeToValueType.take(listMetaType.id())) + delete vt; + + auto it = data->compositeTypes.constFind(metaType.iface()); + if (it != data->compositeTypes.constEnd()) + data->compositeTypes.erase(it); + } + + QMetaType::unregisterMetaType(metaType); + QMetaType::unregisterMetaType(listMetaType); + delete static_cast<const QQmlMetaTypeInterface *>(metaType.iface()); + delete static_cast<const QQmlListMetaTypeInterface *>(listMetaType.iface()); } int QQmlMetaType::registerUnitCacheHook( const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) { - if (hookRegistration.version > 0) + if (hookRegistration.structVersion > 1) qFatal("qmlRegisterType(): Cannot mix incompatible QML versions."); QQmlMetaTypeDataPtr data; @@ -568,40 +763,135 @@ int QQmlMetaType::registerUnitCacheHook( return 0; } -bool QQmlMetaType::protectModule(const QString &uri, int majVersion) +QQmlType QQmlMetaType::registerSequentialContainer( + const QQmlPrivate::RegisterSequentialContainer &container) { + if (container.structVersion > 1) + qFatal("qmlRegisterSequenceContainer(): Cannot mix incompatible QML versions."); + QQmlMetaTypeDataPtr data; - QQmlMetaTypeData::VersionedUri versionedUri; - versionedUri.uri = uri; - versionedUri.majorVersion = majVersion; + if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, QString(), + container.version, {})) { + return QQmlType(); + } + + QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType); - if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) { - qqtm->lock(); - return true; + data->registerType(priv); + priv->setName(QString::fromUtf8(container.uri), QString()); + priv->version = container.version; + priv->revision = container.revision; + priv->typeId = container.metaSequence.valueMetaType(); + priv->listId = container.typeId; + priv->extraData.sequentialContainerTypeData = container.metaSequence; + + addTypeToData(priv, data); + + return QQmlType(priv); +} + +void QQmlMetaType::unregisterSequentialContainer(int id) +{ + unregisterType(id); +} + +bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, + bool weakProtectAllVersions) +{ + QQmlMetaTypeDataPtr data; + if (version.hasMajorVersion()) { + if (QQmlTypeModule *module = data->findTypeModule(uri, version)) { + if (!weakProtectAllVersions) { + module->setLockLevel(QQmlTypeModule::LockLevel::Strong); + return true; + } + } else { + return false; + } } - return false; + + const auto range = std::equal_range( + data->uriToModule.begin(), data->uriToModule.end(), uri, + std::less<ModuleUri>()); + + for (auto it = range.first; it != range.second; ++it) + (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak); + + return range.first != range.second; +} + +void QQmlMetaType::registerModuleImport(const QString &uri, QTypeRevision moduleVersion, + const QQmlDirParser::Import &import) +{ + QQmlMetaTypeDataPtr data; + + data->moduleImports.insert(QQmlMetaTypeData::VersionedUri(uri, moduleVersion), import); } -void QQmlMetaType::registerModule(const char *uri, int versionMajor, int versionMinor) +void QQmlMetaType::unregisterModuleImport(const QString &uri, QTypeRevision moduleVersion, + const QQmlDirParser::Import &import) { QQmlMetaTypeDataPtr data; + data->moduleImports.remove(QQmlMetaTypeData::VersionedUri(uri, moduleVersion), import); +} - QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); +QList<QQmlDirParser::Import> QQmlMetaType::moduleImports( + const QString &uri, QTypeRevision version) +{ + QQmlMetaTypeDataPtr data; + QList<QQmlDirParser::Import> result; + + const auto unrevisioned = data->moduleImports.equal_range( + QQmlMetaTypeData::VersionedUri(uri, QTypeRevision())); + for (auto it = unrevisioned.second; it != unrevisioned.first;) + result.append(*(--it)); + + if (version.hasMajorVersion()) { + const auto revisioned = data->moduleImports.equal_range( + QQmlMetaTypeData::VersionedUri(uri, version)); + for (auto it = revisioned.second; it != revisioned.first;) + result.append(*(--it)); + return result; + } + + // Use latest module available with that URI. + const auto begin = data->moduleImports.begin(); + auto it = unrevisioned.first; + if (it == begin) + return result; + + const QQmlMetaTypeData::VersionedUri latestVersion = (--it).key(); + if (latestVersion.uri != uri) + return result; + + do { + result += *it; + } while (it != begin && (--it).key() == latestVersion); + + return result; +} + +void QQmlMetaType::registerModule(const char *uri, QTypeRevision version) +{ + QQmlMetaTypeDataPtr data; + + QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), version, data); Q_ASSERT(module); - module->addMinorVersion(versionMinor); + if (version.hasMinorVersion()) + module->addMinorVersion(version.minorVersion()); } -int QQmlMetaType::typeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName) +int QQmlMetaType::typeId(const char *uri, QTypeRevision version, const char *qmlName) { QQmlMetaTypeDataPtr data; - QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data); + QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), version, data); if (!module) return -1; - QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), versionMinor); + QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), version); if (!type.isValid()) return -1; @@ -615,40 +905,21 @@ void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) } static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, - int majorVersion) + QTypeRevision version) { // Has any type previously been installed to this namespace? QHashedString nameSpace(uri); for (const QQmlType &type : data->types) { - if (type.module() == nameSpace && type.majorVersion() == majorVersion) + if (type.module() == nameSpace && type.version().majorVersion() == version.majorVersion()) return true; } return false; } -class QQmlMetaTypeRegistrationFailureRecorder -{ - Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) -public: - QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) - : data(data) - { - data->setTypeRegistrationFailures(failures); - } - - ~QQmlMetaTypeRegistrationFailureRecorder() - { - data->setTypeRegistrationFailures(nullptr); - } - - QQmlMetaTypeData *data = nullptr; -}; - - -bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePath, - const QString &uri, const QString &typeNamespace, int vmaj, - QList<QQmlError> *errors) +QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes( + QObject *instance, const QString &basePath, const QString &uri, + const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors) { if (!typeNamespace.isEmpty() && typeNamespace != uri) { // This is an 'identified' module @@ -657,10 +928,10 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat QQmlError error; error.setDescription( QStringLiteral("Module namespace '%1' does not match import URI '%2'") - .arg(typeNamespace).arg(uri)); + .arg(typeNamespace, uri)); errors->prepend(error); } - return false; + return RegistrationResult::Failure; } QStringList failures; @@ -669,7 +940,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); if (!typeNamespace.isEmpty()) { // This is an 'identified' module - if (namespaceContainsRegistrations(data, typeNamespace, vmaj)) { + if (namespaceContainsRegistrations(data, typeNamespace, version)) { // Other modules have already installed to this namespace if (errors) { QQmlError error; @@ -678,7 +949,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat .arg(typeNamespace)); errors->prepend(error); } - return false; + return RegistrationResult::Failure; } } else { // This is not an identified module - provide a warning @@ -687,7 +958,7 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat "it cannot be protected from external registrations.").arg(uri)); } - if (!qobject_cast<QQmlEngineExtensionInterface *>(instance)) { + if (instance && !qobject_cast<QQmlEngineExtensionInterface *>(instance)) { QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance); if (!iface) { if (errors) { @@ -697,35 +968,40 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat "QQmlEngineExtensionInterface").arg(typeNamespace)); errors->prepend(error); } - return false; + return RegistrationResult::Failure; } +#if QT_DEPRECATED_SINCE(6, 3) if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) { // basepath should point to the directory of the module, not the plugin file itself: QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); } +#else + Q_UNUSED(basePath) +#endif const QByteArray bytes = uri.toUtf8(); const char *moduleId = bytes.constData(); iface->registerTypes(moduleId); } - data->registerModuleTypes(QQmlMetaTypeData::VersionedUri(uri, vmaj)); + if (failures.isEmpty() && !data->registerModuleTypes(uri)) + return RegistrationResult::NoRegistrationFunction; if (!failures.isEmpty()) { if (errors) { - for (const QString &failure : qAsConst(failures)) { + for (const QString &failure : std::as_const(failures)) { QQmlError error; error.setDescription(failure); errors->prepend(error); } } - return false; + return RegistrationResult::Failure; } } - return true; + return RegistrationResult::Success; } /* @@ -741,8 +1017,8 @@ bool QQmlMetaType::registerPluginTypes(QObject *instance, const QString &basePat */ QQmlType QQmlMetaType::typeForUrl(const QString &urlString, const QHashedStringRef &qualifiedType, - bool isCompositeSingleton, QList<QQmlError> *errors, - int majorVersion, int minorVersion) + CompositeTypeLookupMode mode, QList<QQmlError> *errors, + QTypeRevision version) { // ### unfortunate (costly) conversion const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString)); @@ -759,128 +1035,80 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString, return ret; } - const int dot = qualifiedType.indexOf(QLatin1Char('.')); - const QString typeName = dot < 0 - ? qualifiedType.toString() - : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); - - QStringList failures; - QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); - - // Register the type. Note that the URI parameters here are empty; for - // file type imports, we do not place them in a URI as we don't - // necessarily have a good and unique one (picture a library import, - // which may be found in multiple plugin locations on disk), but there - // are other reasons for this too. - // - // By not putting them in a URI, we prevent the types from being - // registered on a QQmlTypeModule; this is important, as once types are - // placed on there, they cannot be easily removed, meaning if the - // developer subsequently loads a different import (meaning different - // types) with the same URI (using, say, a different plugin path), it is - // very undesirable that we continue to associate the types from the - // "old" URI with that new module. - // - // Not having URIs also means that the types cannot be found by name - // etc, the only way to look them up is through QQmlImports -- for - // better or worse. - const QQmlType::RegistrationType registrationType = isCompositeSingleton - ? QQmlType::CompositeSingletonType - : QQmlType::CompositeType; - if (checkRegistration(registrationType, data, nullptr, typeName, majorVersion)) { - auto *priv = new QQmlTypePrivate(registrationType); - priv->setName(QString(), typeName); - priv->version_maj = majorVersion; - priv->version_min = minorVersion; - - if (isCompositeSingleton) { - priv->extraData.sd->singletonInstanceInfo = new QQmlType::SingletonInstanceInfo; - priv->extraData.sd->singletonInstanceInfo->url = url; - priv->extraData.sd->singletonInstanceInfo->typeName = typeName; - } else { - priv->extraData.fd->url = url; - } - - data->registerType(priv); - addTypeToData(priv, data); - data->urlToType.insertMulti(url, priv); - return QQmlType(priv); - } - - // This means that the type couldn't be found by URL, but could not be - // registered either, meaning we most likely were passed some kind of bad - // data. - if (errors) { - QQmlError error; - error.setDescription(failures.join('\n')); - errors->prepend(error); - } else { - qWarning("%s", failures.join('\n').toLatin1().constData()); - } - return QQmlType(); -} - -QRecursiveMutex *QQmlMetaType::typeRegistrationLock() -{ - return metaTypeDataLock(); + const QQmlType type = createTypeForUrl( + data, url, qualifiedType, mode, errors, version); + data->urlToType.insert(url, type.priv()); + return type; } /* - Returns true if a module \a uri of any version is installed. + Returns the latest version of \a uri installed, or an in valid QTypeRevision(). */ -bool QQmlMetaType::isAnyModule(const QString &uri) +QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri) { QQmlMetaTypeDataPtr data; + auto upper = std::upper_bound(data->uriToModule.begin(), data->uriToModule.end(), uri, + std::less<ModuleUri>()); + if (upper == data->uriToModule.begin()) + return QTypeRevision(); - for (QQmlMetaTypeData::TypeModules::ConstIterator iter = data->uriToModule.cbegin(); - iter != data->uriToModule.cend(); ++iter) { - if ((*iter)->module() == uri) - return true; - } - - return false; + const auto module = (--upper)->get(); + return (module->module() == uri) + ? QTypeRevision::fromVersion(module->majorVersion(), module->maximumMinorVersion()) + : QTypeRevision(); } /* Returns true if a module \a uri of this version is installed and locked; */ -bool QQmlMetaType::isLockedModule(const QString &uri, int majVersion) +bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version) { QQmlMetaTypeDataPtr data; - QQmlMetaTypeData::VersionedUri versionedUri; - versionedUri.uri = uri; - versionedUri.majorVersion = majVersion; - if (QQmlTypeModule* qqtm = data->uriToModule.value(versionedUri, 0)) - return qqtm->isLocked(); + if (QQmlTypeModule* qqtm = data->findTypeModule(uri, version)) + return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong; return false; } /* - Returns true if any type or API has been registered for the given \a module with at least - versionMajor.versionMinor, or if types have been registered for \a module with at most - versionMajor.versionMinor. - - So if only 4.7 and 4.9 have been registered, 4.7,4.8, and 4.9 are valid, but not 4.6 nor 4.10. + Returns the best matching registered version for the given \a module. If \a version is + the does not have a major version, returns the latest registered version. Otherwise + chooses the same major version and checks if the minor version is within the range + of registered minor versions. If so, retuens the original version, otherwise returns + an invalid version. If \a version does not have a minor version, chooses the latest one. */ -bool QQmlMetaType::isModule(const QString &module, int versionMajor, int versionMinor) +QTypeRevision QQmlMetaType::matchingModuleVersion(const QString &module, QTypeRevision version) { - Q_ASSERT(versionMajor >= 0 && versionMinor >= 0); + if (!version.hasMajorVersion()) + return latestModuleVersion(module); + QQmlMetaTypeDataPtr data; // first, check Types - QQmlTypeModule *tm = - data->uriToModule.value(QQmlMetaTypeData::VersionedUri(module, versionMajor)); - if (tm && tm->minimumMinorVersion() <= versionMinor && tm->maximumMinorVersion() >= versionMinor) - return true; + if (QQmlTypeModule *tm = data->findTypeModule(module, version)) { + if (!version.hasMinorVersion()) + return QTypeRevision::fromVersion(version.majorVersion(), tm->maximumMinorVersion()); - return false; + if (tm->minimumMinorVersion() <= version.minorVersion() + && tm->maximumMinorVersion() >= version.minorVersion()) { + return version; + } + } + + return QTypeRevision(); } -QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, int majorVersion) +QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, QTypeRevision version) { QQmlMetaTypeDataPtr data; - return data->uriToModule.value(QQmlMetaTypeData::VersionedUri(uri, majorVersion)); + + if (version.hasMajorVersion()) + return data->findTypeModule(uri, version); + + auto range = std::equal_range(data->uriToModule.begin(), data->uriToModule.end(), + uri, std::less<ModuleUri>()); + + return range.first == range.second ? nullptr : (--range.second)->get(); } QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() @@ -891,7 +1119,7 @@ QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) { - if (!isQObject(v.userType())) { + if (!v.metaType().flags().testFlag(QMetaType::PointerToQObject)) { if (ok) *ok = false; return nullptr; } @@ -901,54 +1129,28 @@ QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) return *(QObject *const *)v.constData(); } -bool QQmlMetaType::isQObject(int userType) -{ - if (userType == QMetaType::QObjectStar) - return true; - - QQmlMetaTypeDataPtr data; - return userType >= 0 && userType < data->objects.size() && data->objects.testBit(userType); -} - /* Returns the item type for a list of type \a id. */ -int QQmlMetaType::listType(int id) -{ - QQmlMetaTypeDataPtr data; - QHash<int, int>::ConstIterator iter = data->qmlLists.constFind(id); - if (iter != data->qmlLists.cend()) - return *iter; - QQmlTypePrivate *type = data->idToType.value(id); - if (type && type->listId == id) - return type->typeId; - else - return 0; -} - -#if QT_DEPRECATED_SINCE(5, 14) -int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) -{ - QQmlMetaTypeDataPtr data; - - for (auto it = data->metaObjectToType.constFind(mo), end = data->metaObjectToType.constEnd(); - it != end && it.key() == mo; ++it) { - const QQmlType type(it.value()); - if (type.attachedPropertiesFunction(engine)) - return type.attachedPropertiesId(engine); +QMetaType QQmlMetaType::listValueType(QMetaType metaType) +{ + if (isList(metaType)) { + const auto iface = metaType.iface(); + if (iface && iface->metaObjectFn == &dynamicQmlListMarker) + return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType); + } else if (metaType.flags() & QMetaType::PointerToQObject) { + return QMetaType(); } - return -1; -} - -QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePrivate *engine, int id) -{ - if (id < 0) - return nullptr; QQmlMetaTypeDataPtr data; - return data->types.at(id).attachedPropertiesFunction(engine); + Q_ASSERT(data); + QQmlTypePrivate *type = data->idToType.value(metaType.id()); + + if (type && type->listId == metaType) + return type->typeId; + else + return QMetaType {}; } -#endif QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, const QMetaObject *mo) @@ -1011,120 +1213,64 @@ QMetaMethod QQmlMetaType::defaultMethod(QObject *obj) return defaultMethod(metaObject); } -QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) -{ - if (userType < 0) - return Unknown; - if (userType == QMetaType::QObjectStar) - return Object; - - QQmlMetaTypeDataPtr data; - if (data->qmlLists.contains(userType)) - return List; - else if (userType < data->objects.size() && data->objects.testBit(userType)) - return Object; - else if (userType < data->lists.size() && data->lists.testBit(userType)) - return List; - else - return Unknown; -} - /*! See qmlRegisterInterface() for information about when this will return true. */ -bool QQmlMetaType::isInterface(int userType) +bool QQmlMetaType::isInterface(QMetaType type) { const QQmlMetaTypeDataPtr data; - return userType >= 0 && userType < data->interfaces.size() && data->interfaces.testBit(userType); + return data->interfaces.contains(type.id()); } -const char *QQmlMetaType::interfaceIId(int userType) -{ - - QQmlTypePrivate *typePrivate = nullptr; - { - QQmlMetaTypeDataPtr data; - typePrivate = data->idToType.value(userType); - } - - QQmlType type(typePrivate); - if (type.isInterface() && type.typeId() == userType) - return type.interfaceIId(); - else - return nullptr; -} - -bool QQmlMetaType::isList(int userType) +const char *QQmlMetaType::interfaceIId(QMetaType metaType) { const QQmlMetaTypeDataPtr data; - if (data->qmlLists.contains(userType)) - return true; - return userType >= 0 && userType < data->lists.size() && data->lists.testBit(userType); -} - -/*! - A custom string convertor allows you to specify a function pointer that - returns a variant of \a type. For example, if you have written your own icon - class that you want to support as an object property assignable in QML: - - \code - int type = qRegisterMetaType<SuperIcon>("SuperIcon"); - QML::addCustomStringConvertor(type, &SuperIcon::pixmapFromString); - \endcode - - The function pointer must be of the form: - \code - QVariant (*StringConverter)(const QString &); - \endcode - */ -void QQmlMetaType::registerCustomStringConverter(int type, StringConverter converter) -{ - QQmlMetaTypeDataPtr data; - if (data->stringConverters.contains(type)) - return; - data->stringConverters.insert(type, converter); + const QQmlType type(data->idToType.value(metaType.id())); + return (type.isInterface() && type.typeId() == metaType) ? type.interfaceIId() : nullptr; } -/*! - Return the custom string converter for \a type, previously installed through - registerCustomStringConverter() - */ -QQmlMetaType::StringConverter QQmlMetaType::customStringConverter(int type) +bool QQmlMetaType::isList(QMetaType type) { - const QQmlMetaTypeDataPtr data; - return data->stringConverters.value(type); + if (type.flags().testFlag(QMetaType::IsQmlList)) + return true; + else + return false; } /*! Returns the type (if any) of URI-qualified named \a qualifiedName and version specified by \a version_major and \a version_minor. */ -QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, QTypeRevision version) { int slash = qualifiedName.indexOf(QLatin1Char('/')); if (slash <= 0) return QQmlType(); QHashedStringRef module(qualifiedName.constData(), slash); - QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.length() - slash - 1); + QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.size() - slash - 1); - return qmlType(name, module, version_major, version_minor); + return qmlType(name, module, version); } /*! - Returns the type (if any) of \a name in \a module and version specified - by \a version_major and \a version_minor. + \internal + Returns the type (if any) of \a name in \a module and the specified \a version. + + If \a version has no major version, accept any version. + If \a version has no minor version, accept any minor version. + If \a module is empty, search in all modules and accept any version. */ -QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, + QTypeRevision version) { - Q_ASSERT(version_major >= 0 && version_minor >= 0); const QQmlMetaTypeDataPtr data; - QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); + const QHashedString key(QString::fromRawData(name.constData(), name.length()), name.hash()); + QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(key); while (it != data->nameToType.cend() && it.key() == name) { QQmlType t(*it); - // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty - if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, version_major,version_minor)) + if (module.isEmpty() || t.availableInVersion(module, version)) return t; ++it; } @@ -1133,8 +1279,8 @@ QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedString } /*! - Returns the type (if any) that corresponds to the \a metaObject. Returns null if no - type is registered. + Returns the type (if any) that corresponds to the \a metaObject. Returns an invalid type if no + such type is registered. */ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) { @@ -1147,44 +1293,53 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) by \a version_major and \a version_minor in module specified by \a uri. Returns null if no type is registered. */ -QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor) +QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, + QTypeRevision version) { - Q_ASSERT(version_major >= 0 && version_minor >= 0); const QQmlMetaTypeDataPtr data; - QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); - while (it != data->metaObjectToType.cend() && it.key() == metaObject) { + const auto range = data->metaObjectToType.equal_range(metaObject); + for (auto it = range.first; it != range.second; ++it) { QQmlType t(*it); - if (version_major < 0 || module.isEmpty() || t.availableInVersion(module, version_major,version_minor)) + if (module.isEmpty() || t.availableInVersion(module, version)) return t; - ++it; } return QQmlType(); } /*! - Returns the type (if any) that corresponds to \a typeId. Depending on \a category, the - \a typeId is interpreted either as QVariant::Type or as QML type id returned by one of the - qml type registration functions. Returns null if no type is registered. + Returns the type (if any) that corresponds to \a qmlTypeId. + Returns an invalid QQmlType if no such type is registered. */ -QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category) +QQmlType QQmlMetaType::qmlTypeById(int qmlTypeId) { const QQmlMetaTypeDataPtr data; - - if (category == TypeIdCategory::MetaType) { - QQmlTypePrivate *type = data->idToType.value(typeId); - if (type && type->typeId == typeId) - return QQmlType(type); - } else if (category == TypeIdCategory::QmlType) { - QQmlType type = data->types.value(typeId); - if (type.isValid()) - return type; - } + QQmlType type = data->types.value(qmlTypeId); + if (type.isValid()) + return type; return QQmlType(); } /*! + Returns the type (if any) that corresponds to \a metaType. + Returns an invalid QQmlType if no such type is registered. +*/ +QQmlType QQmlMetaType::qmlType(QMetaType metaType) +{ + const QQmlMetaTypeDataPtr data; + QQmlTypePrivate *type = data->idToType.value(metaType.id()); + return (type && type->typeId == metaType) ? QQmlType(type) : QQmlType(); +} + +QQmlType QQmlMetaType::qmlListType(QMetaType metaType) +{ + const QQmlMetaTypeDataPtr data; + QQmlTypePrivate *type = data->idToType.value(metaType.id()); + return (type && type->listId == metaType) ? QQmlType(type) : QQmlType(); +} + +/*! Returns the type (if any) that corresponds to the given \a url in the set of composite types added through file imports. @@ -1205,16 +1360,149 @@ QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl, bool includeNonFileI return QQmlType(); } -QQmlPropertyCache *QQmlMetaType::propertyCache(const QMetaObject *metaObject, int minorVersion) +QQmlType QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(const QUrl &url) +{ + QQmlMetaTypeDataPtr data; + const auto it = data->inlineComponentTypes.constFind(url); + if (it != data->inlineComponentTypes.constEnd()) + return *it; + + return doRegisterInlineComponentType(data, url); +} + +/*! +Returns a QQmlPropertyCache for \a obj if one is available. + +If \a obj is null, being deleted or contains a dynamic meta object, +nullptr is returned. +*/ +QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version) +{ + if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted) + return QQmlPropertyCache::ConstPtr(); + return QQmlMetaType::propertyCache(obj->metaObject(), version); +} + +QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( + const QMetaObject *metaObject, QTypeRevision version) { QQmlMetaTypeDataPtr data; // not const: the cache is created on demand - return data->propertyCache(metaObject, minorVersion); + return data->propertyCache(metaObject, version); } -QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVersion) +QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( + const QQmlType &type, QTypeRevision version) { QQmlMetaTypeDataPtr data; // not const: the cache is created on demand - return data->propertyCache(type, minorVersion); + return data->propertyCache(type, version); +} + +/*! + * \internal + * + * Look up by type's baseMetaObject. + */ +QQmlMetaObject QQmlMetaType::rawMetaObjectForType(QMetaType metaType) +{ + const QQmlMetaTypeDataPtr data; + if (auto composite = data->findPropertyCacheInCompositeTypes(metaType)) + return QQmlMetaObject(composite); + + const QQmlTypePrivate *type = data->idToType.value(metaType.id()); + return (type && type->typeId == metaType) ? type->baseMetaObject : nullptr; +} + +/*! + * \internal + * + * Look up by type's metaObject. + */ +QQmlMetaObject QQmlMetaType::metaObjectForType(QMetaType metaType) +{ + const QQmlMetaTypeDataPtr data; + if (auto composite = data->findPropertyCacheInCompositeTypes(metaType)) + return QQmlMetaObject(composite); + + const QQmlTypePrivate *type = data->idToType.value(metaType.id()); + return (type && type->typeId == metaType) + ? QQmlType(type).metaObject() + : nullptr; +} + +/*! + * \internal + * + * Look up by type's metaObject and version. + */ +QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaType) +{ + QQmlMetaTypeDataPtr data; + if (auto composite = data->findPropertyCacheInCompositeTypes(metaType)) + return composite; + + const QQmlTypePrivate *type = data->idToType.value(metaType.id()); + if (type && type->typeId == metaType) { + if (const QMetaObject *mo = QQmlType(type).metaObject()) + return data->propertyCache(mo, type->version); + } + + return QQmlPropertyCache::ConstPtr(); +} + +/*! + * \internal + * + * Look up by type's baseMetaObject and unspecified/any version. + * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or + * the actual type's version seems strange. The behavior has been in place for a while. + */ +QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType metaType) +{ + QQmlMetaTypeDataPtr data; + if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(metaType)) + return composite; + + const QQmlTypePrivate *type = data->idToType.value(metaType.id()); + if (!type || type->typeId != metaType) + return QQmlPropertyCache::ConstPtr(); + + const QMetaObject *metaObject = type->isValueType() + ? type->metaObjectForValueType() + : type->baseMetaObject; + + return metaObject + ? data->propertyCache(metaObject, QTypeRevision()) + : QQmlPropertyCache::ConstPtr(); +} + +/*! + * \internal + * + * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type + * has no revisiononed attributes here. Unspecified versions are interpreted as "any". + */ +QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType( + QMetaType metaType, QTypeRevision version) +{ + QQmlMetaTypeDataPtr data; + if (auto composite = data->findPropertyCacheInCompositeTypes(metaType)) + return composite; + + const QQmlTypePrivate *typePriv = data->idToType.value(metaType.id()); + if (!typePriv || typePriv->typeId != metaType) + return QQmlPropertyCache::ConstPtr(); + + const QQmlType type(typePriv); + if (type.containsRevisionedAttributes()) { + // It can only have (revisioned) properties or methods if it has a metaobject + Q_ASSERT(type.metaObject()); + return data->propertyCache(type, version); + } + + if (const QMetaObject *metaObject = type.metaObject()) + return data->propertyCache(metaObject, version); + + return QQmlPropertyCache::ConstPtr(); } void QQmlMetaType::unregisterType(int typeIndex) @@ -1222,6 +1510,8 @@ void QQmlMetaType::unregisterType(int typeIndex) QQmlMetaTypeDataPtr data; const QQmlType type = data->types.value(typeIndex); if (const QQmlTypePrivate *d = type.priv()) { + if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType) + removeFromInlineComponents(data->inlineComponentTypes, d); removeQQmlTypePrivate(data->idToType, d); removeQQmlTypePrivate(data->nameToType, d); removeQQmlTypePrivate(data->urlToType, d); @@ -1229,12 +1519,55 @@ void QQmlMetaType::unregisterType(int typeIndex) removeQQmlTypePrivate(data->metaObjectToType, d); for (auto & module : data->uriToModule) module->remove(d); - data->clearPropertyCachesForMinorVersion(typeIndex); + data->clearPropertyCachesForVersion(typeIndex); data->types[typeIndex] = QQmlType(); data->undeletableTypes.remove(type); } } +void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, QQmlTypePrivate *type) +{ + Q_ASSERT(type); + + QQmlMetaTypeDataPtr data; + data->metaObjectToType.insert(metaobject, type); +} + +static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d) +{ + for (auto it = data->inlineComponentTypes.begin(), end = data->inlineComponentTypes.end(); + it != end; ++it) { + if (!QQmlMetaType::equalBaseUrls(it.key(), d->sourceUrl())) + continue; + + const QQmlTypePrivate *icPriv = it->priv(); + if (icPriv && icPriv->count() > 1) + return true; + } + return false; +} + +static int doCountInternalCompositeTypeSelfReferences( + QQmlMetaTypeData *data, + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +{ + int result = 0; + auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) { + if (!iface) + return; + + const auto it = data->compositeTypes.constFind(iface); + if (it != data->compositeTypes.constEnd() && *it == compilationUnit) + ++result; + }; + + doCheck(compilationUnit->metaType().iface()); + for (auto &&inlineData: compilationUnit->inlineComponentData) + doCheck(inlineData.qmlType.typeId().iface()); + + return result; +} + void QQmlMetaType::freeUnusedTypesAndCaches() { QQmlMetaTypeDataPtr data; @@ -1243,15 +1576,33 @@ void QQmlMetaType::freeUnusedTypesAndCaches() if (!data.isValid()) return; + bool droppedAtLeastOneComposite; + do { + droppedAtLeastOneComposite = false; + auto it = data->compositeTypes.begin(); + while (it != data->compositeTypes.end()) { + if ((*it)->count() <= doCountInternalCompositeTypeSelfReferences(data, *it)) { + it = data->compositeTypes.erase(it); + droppedAtLeastOneComposite = true; + } else { + ++it; + } + } + } while (droppedAtLeastOneComposite); + bool deletedAtLeastOneType; do { deletedAtLeastOneType = false; QList<QQmlType>::Iterator it = data->types.begin(); while (it != data->types.end()) { const QQmlTypePrivate *d = (*it).priv(); - if (d && d->count() == 1) { + if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) { deletedAtLeastOneType = true; + if (d->regType == QQmlType::CompositeType + || d->regType == QQmlType::CompositeSingletonType) { + removeFromInlineComponents(data->inlineComponentTypes, d); + } removeQQmlTypePrivate(data->idToType, d); removeQQmlTypePrivate(data->nameToType, d); removeQQmlTypePrivate(data->urlToType, d); @@ -1261,7 +1612,7 @@ void QQmlMetaType::freeUnusedTypesAndCaches() for (auto &module : data->uriToModule) module->remove(d); - data->clearPropertyCachesForMinorVersion(d->index); + data->clearPropertyCachesForVersion(d->index); *it = QQmlType(); } else { ++it; @@ -1272,14 +1623,10 @@ void QQmlMetaType::freeUnusedTypesAndCaches() bool deletedAtLeastOneCache; do { deletedAtLeastOneCache = false; - QHash<const QMetaObject *, QQmlPropertyCache *>::Iterator it = data->propertyCaches.begin(); + auto it = data->propertyCaches.begin(); while (it != data->propertyCaches.end()) { - if ((*it)->count() == 1) { - QQmlPropertyCache *pc = nullptr; - qSwap(pc, *it); it = data->propertyCaches.erase(it); - pc->release(); deletedAtLeastOneCache = true; } else { ++it; @@ -1296,7 +1643,7 @@ QList<QString> QQmlMetaType::qmlTypeNames() const QQmlMetaTypeDataPtr data; QList<QString> names; - names.reserve(data->nameToType.count()); + names.reserve(data->nameToType.size()); QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); while (it != data->nameToType.cend()) { QQmlType t(*it); @@ -1315,7 +1662,7 @@ QList<QQmlType> QQmlMetaType::qmlTypes() const QQmlMetaTypeDataPtr data; QList<QQmlType> types; - for (QQmlTypePrivate *t : data->nameToType) + for (const QQmlTypePrivate *t : data->nameToType) types.append(QQmlType(t)); return types; @@ -1338,7 +1685,7 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() const QQmlMetaTypeDataPtr data; QList<QQmlType> retn; - for (const auto t : qAsConst(data->nameToType)) { + for (const auto t : std::as_const(data->nameToType)) { QQmlType type(t); if (type.isSingleton()) retn.append(type); @@ -1346,22 +1693,47 @@ QList<QQmlType> QQmlMetaType::qmlSingletonTypes() return retn; } -const QV4::CompiledData::Unit *QQmlMetaType::findCachedCompilationUnit(const QUrl &uri, CachedUnitLookupError *status) +static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit) { + quint32 numTypedFunctions = 0; + for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions; + function; ++function) { + if (function->functionPtr) + ++numTypedFunctions; + else + return false; + } + return numTypedFunctions == unit->qmlData->functionTableSize; +} + +const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit( + const QUrl &uri, QQmlMetaType::CacheMode mode, CachedUnitLookupError *status) +{ + Q_ASSERT(mode != RejectAll); const QQmlMetaTypeDataPtr data; - for (const auto lookup : qAsConst(data->lookupCachedQmlUnit)) { + for (const auto lookup : std::as_const(data->lookupCachedQmlUnit)) { if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { QString error; - if (!QV4::ExecutableCompilationUnit::verifyHeader(unit->qmlData, QDateTime(), &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 (mode == RequireFullyTyped && !isFullyTyped(unit)) { + qCDebug(DBG_DISK_CACHE) + << "Error loading pre-compiled file " << uri + << ": compilation unit contains functions not compiled to native code."; + if (status) + *status = CachedUnitLookupError::NotFullyTyped; + return nullptr; + } + if (status) *status = CachedUnitLookupError::NoError; - return unit->qmlData; + return unit; } } @@ -1409,8 +1781,8 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) marker = typeName.indexOf(QLatin1String("_QML_")); if (marker != -1) { - typeName = typeName.leftRef(marker) + QLatin1Char('*'); - type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); + typeName = QStringView{typeName}.left(marker) + QLatin1Char('*'); + type = QQmlMetaType::qmlType(QMetaType::fromName(typeName.toUtf8())); if (type.isValid()) { QString qmlTypeName = type.qmlTypeName(); const int lastSlash = qmlTypeName.lastIndexOf(QLatin1Char('/')); @@ -1432,31 +1804,211 @@ QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject QList<QQmlProxyMetaObject::ProxyData> metaObjects; mo = mo->d.superdata; - const QQmlMetaTypeDataPtr data; - - while (mo) { - QQmlTypePrivate *t = data->metaObjectToType.value(mo); - if (t) { + if (!mo) + return metaObjects; + + auto createProxyMetaObject = [&](QQmlTypePrivate *This, + const QMetaObject *superdataBaseMetaObject, + const QMetaObject *extMetaObject, + QObject *(*extFunc)(QObject *)) { + if (!extMetaObject) + return; + + QMetaObjectBuilder builder; + clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject, + extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = baseMetaObject; + if (!metaObjects.isEmpty()) + metaObjects.constLast().metaObject->d.superdata = mmo; + else if (lastMetaObject) + lastMetaObject->d.superdata = mmo; + QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 }; + metaObjects << data; + registerMetaObjectForType(mmo, This); + }; + + for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) { + // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*. + // This algorithm only accounts for the most recently inserted one. That's pretty + // random. However, the availability of types depends on what documents you have + // loaded before. Just adding all possible extensions would also be pretty random. + // The right way to do this would be to take the relations between the QML modules + // into account. For this we would need proper module dependency information. + if (QQmlTypePrivate *t = data->metaObjectToType.value(mo)) { if (t->regType == QQmlType::CppType) { - if (t->extraData.cd->extFunc) { - QMetaObjectBuilder builder; - clone(builder, t->extraData.cd->extMetaObject, t->baseMetaObject, baseMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = baseMetaObject; - if (!metaObjects.isEmpty()) - metaObjects.constLast().metaObject->d.superdata = mmo; - else if (lastMetaObject) - lastMetaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->extraData.cd->extFunc, 0, 0 }; - metaObjects << data; - } + createProxyMetaObject( + t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject, + t->extraData.cppTypeData->extFunc); + } else if (t->regType == QQmlType::SingletonType) { + createProxyMetaObject( + t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject, + t->extraData.singletonTypeData->extFunc); } } - mo = mo->d.superdata; - } + }; return metaObjects; } +static bool isInternalType(int idx) +{ + // Qt internal types + switch (idx) { + case QMetaType::UnknownType: + case QMetaType::QStringList: + case QMetaType::QObjectStar: + case QMetaType::VoidStar: + case QMetaType::Nullptr: + case QMetaType::QVariant: + case QMetaType::QLocale: + case QMetaType::QImage: // scarce type, keep as QVariant + case QMetaType::QPixmap: // scarce type, keep as QVariant + return true; + default: + return false; + } +} + +bool QQmlMetaType::isValueType(QMetaType type) +{ + if (!type.isValid() || isInternalType(type.id())) + return false; + + return valueType(type) != nullptr; +} + +const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType) +{ + switch (metaType.id()) { + case QMetaType::QPoint: + return &QQmlPointValueType::staticMetaObject; + case QMetaType::QPointF: + return &QQmlPointFValueType::staticMetaObject; + case QMetaType::QSize: + return &QQmlSizeValueType::staticMetaObject; + case QMetaType::QSizeF: + return &QQmlSizeFValueType::staticMetaObject; + case QMetaType::QRect: + return &QQmlRectValueType::staticMetaObject; + case QMetaType::QRectF: + return &QQmlRectFValueType::staticMetaObject; +#if QT_CONFIG(easingcurve) + case QMetaType::QEasingCurve: + return &QQmlEasingValueType::staticMetaObject; +#endif + default: + break; + } + + // It doesn't have to be a gadget for a QML type to exist, but we don't want to + // call QObject pointers value types. Explicitly registered types also override + // the implicit use of gadgets. + if (!(metaType.flags() & QMetaType::PointerToQObject)) { + const QQmlMetaTypeDataPtr data; + const QQmlTypePrivate *type = data->idToType.value(metaType.id()); + if (type && type->regType == QQmlType::CppType && type->typeId == metaType) { + if (const QMetaObject *mo = type->metaObjectForValueType()) + return mo; + } + } + + // If it _is_ a gadget, we can just use it. + if (metaType.flags() & QMetaType::IsGadget) + return metaType.metaObject(); + + return nullptr; +} + +QQmlValueType *QQmlMetaType::valueType(QMetaType type) +{ + QQmlMetaTypeDataPtr data; + + const auto it = data->metaTypeToValueType.constFind(type.id()); + if (it != data->metaTypeToValueType.constEnd()) + return *it; + + if (const QMetaObject *mo = metaObjectForValueType(type)) + return *data->metaTypeToValueType.insert(type.id(), new QQmlValueType(type, mo)); + return *data->metaTypeToValueType.insert(type.id(), nullptr); +} + +QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t) +{ + const QQmlMetaTypeDataPtr data; + return data->findPropertyCacheInCompositeTypes(t); +} + +void QQmlMetaType::registerInternalCompositeType( + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +{ + QQmlMetaTypeDataPtr data; + + auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) { + Q_ASSERT(iface); + Q_ASSERT(compilationUnit); + + // We can't assert on anything else here. We may get a completely new type as exposed + // by the qmldiskcache test that changes a QML file in place during the execution + // of the test. + data->compositeTypes.insert(iface, compilationUnit); + }; + + doInsert(compilationUnit->metaType().iface()); + for (auto &&inlineData: compilationUnit->inlineComponentData) + doInsert(inlineData.qmlType.typeId().iface()); +} + +void QQmlMetaType::unregisterInternalCompositeType( + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +{ + QQmlMetaTypeDataPtr data; + + auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) { + if (!iface) + return; + + const auto it = data->compositeTypes.constFind(iface); + if (it != data->compositeTypes.constEnd() && *it == compilationUnit) + data->compositeTypes.erase(it); + }; + + doRemove(compilationUnit->metaType().iface()); + for (auto &&inlineData: compilationUnit->inlineComponentData) + doRemove(inlineData.qmlType.typeId().iface()); +} + +int QQmlMetaType::countInternalCompositeTypeSelfReferences( + const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) +{ + QQmlMetaTypeDataPtr data; + return doCountInternalCompositeTypeSelfReferences(data, compilationUnit); +} + +QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit( + QMetaType type) +{ + const QQmlMetaTypeDataPtr data; + return data->compositeTypes.value(type.iface()); +} + +QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit( + const QUrl &url) +{ + const QUrl normalized = QQmlTypeLoader::normalize(url); + QQmlMetaTypeDataPtr data; + + auto found = data->urlToType.constFind(normalized); + if (found == data->urlToType.constEnd()) { + found = data->urlToNonFileImportType.constFind(normalized); + if (found == data->urlToNonFileImportType.constEnd()) + return QQmlRefPointer<QV4::CompiledData::CompilationUnit>(); + } + + const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface()); + return composite == data->compositeTypes.constEnd() + ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>() + : composite.value(); +} + QT_END_NAMESPACE |