| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qqmlmetatype_p.h" |
| 5 | |
| 6 | #include <private/qqmlextensionplugin_p.h> |
| 7 | #include <private/qqmlmetatypedata_p.h> |
| 8 | #include <private/qqmlpropertycachecreator_p.h> |
| 9 | #include <private/qqmlscriptblob_p.h> |
| 10 | #include <private/qqmltype_p_p.h> |
| 11 | #include <private/qqmltypeloader_p.h> |
| 12 | #include <private/qqmltypemodule_p.h> |
| 13 | #include <private/qqmlvaluetype_p.h> |
| 14 | #include <private/qv4executablecompilationunit_p.h> |
| 15 | |
| 16 | #include <QtCore/qcoreapplication.h> |
| 17 | #include <QtCore/qmutex.h> |
| 18 | #include <QtCore/qloggingcategory.h> |
| 19 | |
| 20 | Q_STATIC_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration" ) |
| 21 | |
| 22 | QT_BEGIN_NAMESPACE |
| 23 | |
| 24 | struct LockedData : private QQmlMetaTypeData |
| 25 | { |
| 26 | friend class QQmlMetaTypeDataPtr; |
| 27 | }; |
| 28 | |
| 29 | Q_GLOBAL_STATIC(LockedData, metaTypeData) |
| 30 | Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock) |
| 31 | |
| 32 | struct ModuleUri : public QString |
| 33 | { |
| 34 | ModuleUri(const QString &string) : QString(string) {} |
| 35 | ModuleUri(const std::unique_ptr<QQmlTypeModule> &module) : QString(module->module()) {} |
| 36 | }; |
| 37 | |
| 38 | class QQmlMetaTypeDataPtr |
| 39 | { |
| 40 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeDataPtr) |
| 41 | public: |
| 42 | QQmlMetaTypeDataPtr() : locker(metaTypeDataLock()), data(metaTypeData()) {} |
| 43 | ~QQmlMetaTypeDataPtr() = default; |
| 44 | |
| 45 | QQmlMetaTypeData &operator*() { return *data; } |
| 46 | QQmlMetaTypeData *operator->() { return data; } |
| 47 | operator QQmlMetaTypeData *() { return data; } |
| 48 | |
| 49 | const QQmlMetaTypeData &operator*() const { return *data; } |
| 50 | const QQmlMetaTypeData *operator->() const { return data; } |
| 51 | operator const QQmlMetaTypeData *() const { return data; } |
| 52 | |
| 53 | bool isValid() const { return data != nullptr; } |
| 54 | |
| 55 | private: |
| 56 | QMutexLocker<QRecursiveMutex> locker; |
| 57 | LockedData *data = nullptr; |
| 58 | }; |
| 59 | |
| 60 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, |
| 61 | const QQmlPrivate::RegisterInterface &type) |
| 62 | { |
| 63 | auto *d = new QQmlTypePrivate(QQmlType::InterfaceType); |
| 64 | d->extraData.interfaceTypeData = type.iid; |
| 65 | d->typeId = type.typeId; |
| 66 | d->listId = type.listId; |
| 67 | d->module = QString::fromUtf8(utf8: type.uri); |
| 68 | d->version = type.version; |
| 69 | data->registerType(priv: d); |
| 70 | return d; |
| 71 | } |
| 72 | |
| 73 | static QQmlTypePrivate *createQQmlType( |
| 74 | QQmlMetaTypeData *data, const QString &elementName, |
| 75 | const QQmlPrivate::RegisterSingletonType &type, |
| 76 | const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) |
| 77 | { |
| 78 | auto *d = new QQmlTypePrivate(QQmlType::SingletonType); |
| 79 | data->registerType(priv: d); |
| 80 | |
| 81 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
| 82 | d->version = type.version; |
| 83 | |
| 84 | if (type.qObjectApi) { |
| 85 | d->baseMetaObject = type.instanceMetaObject; |
| 86 | d->typeId = type.typeId; |
| 87 | d->revision = type.revision; |
| 88 | } |
| 89 | |
| 90 | d->extraData.singletonTypeData->singletonInstanceInfo = siinfo; |
| 91 | d->extraData.singletonTypeData->extFunc = type.extensionObjectCreate; |
| 92 | d->extraData.singletonTypeData->extMetaObject = type.extensionMetaObject; |
| 93 | |
| 94 | return d; |
| 95 | } |
| 96 | |
| 97 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
| 98 | const QQmlPrivate::RegisterType &type) |
| 99 | { |
| 100 | QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType); |
| 101 | data->registerType(priv: d); |
| 102 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
| 103 | |
| 104 | d->version = type.version; |
| 105 | d->revision = type.revision; |
| 106 | d->typeId = type.typeId; |
| 107 | d->listId = type.listId; |
| 108 | d->extraData.cppTypeData->allocationSize = type.objectSize; |
| 109 | d->extraData.cppTypeData->userdata = type.userdata; |
| 110 | d->extraData.cppTypeData->newFunc = type.create; |
| 111 | d->extraData.cppTypeData->noCreationReason = type.noCreationReason; |
| 112 | d->extraData.cppTypeData->createValueTypeFunc = type.createValueType; |
| 113 | d->baseMetaObject = type.metaObject; |
| 114 | d->extraData.cppTypeData->attachedPropertiesFunc = type.attachedPropertiesFunction; |
| 115 | d->extraData.cppTypeData->attachedPropertiesType = type.attachedPropertiesMetaObject; |
| 116 | d->extraData.cppTypeData->parserStatusCast = type.parserStatusCast; |
| 117 | d->extraData.cppTypeData->propertyValueSourceCast = type.valueSourceCast; |
| 118 | d->extraData.cppTypeData->propertyValueInterceptorCast = type.valueInterceptorCast; |
| 119 | d->extraData.cppTypeData->finalizerCast = type.has(v: QQmlPrivate::RegisterType::FinalizerCast) |
| 120 | ? type.finalizerCast |
| 121 | : -1; |
| 122 | d->extraData.cppTypeData->extFunc = type.extensionObjectCreate; |
| 123 | d->extraData.cppTypeData->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser); |
| 124 | d->extraData.cppTypeData->registerEnumClassesUnscoped = true; |
| 125 | d->extraData.cppTypeData->registerEnumsFromRelatedTypes = true; |
| 126 | d->extraData.cppTypeData->constructValueType = type.has(v: QQmlPrivate::RegisterType::CreationMethod) |
| 127 | && type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None; |
| 128 | d->extraData.cppTypeData->populateValueType = type.has(v: QQmlPrivate::RegisterType::CreationMethod) |
| 129 | && type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured; |
| 130 | |
| 131 | if (type.extensionMetaObject) |
| 132 | d->extraData.cppTypeData->extMetaObject = type.extensionMetaObject; |
| 133 | |
| 134 | // Check if the user wants only scoped enum classes |
| 135 | if (d->baseMetaObject) { |
| 136 | auto indexOfUnscoped = d->baseMetaObject->indexOfClassInfo(name: "RegisterEnumClassesUnscoped" ); |
| 137 | if (indexOfUnscoped != -1 |
| 138 | && qstrcmp(str1: d->baseMetaObject->classInfo(index: indexOfUnscoped).value(), str2: "false" ) == 0) { |
| 139 | d->extraData.cppTypeData->registerEnumClassesUnscoped = false; |
| 140 | } |
| 141 | |
| 142 | auto indexOfRelated = d->baseMetaObject->indexOfClassInfo(name: "RegisterEnumsFromRelatedTypes" ); |
| 143 | if (indexOfRelated != -1 |
| 144 | && qstrcmp(str1: d->baseMetaObject->classInfo(index: indexOfRelated).value(), str2: "false" ) == 0) { |
| 145 | d->extraData.cppTypeData->registerEnumsFromRelatedTypes = false; |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | return d; |
| 150 | } |
| 151 | |
| 152 | static void addQQmlMetaTypeInterfaces( |
| 153 | QQmlMetaTypeData *data, const QUrl &url, QQmlTypePrivate *priv, const QByteArray &className) |
| 154 | { |
| 155 | Q_ASSERT(!className.isEmpty()); |
| 156 | QByteArray ptr = className + '*'; |
| 157 | QByteArray lst = "QQmlListProperty<" + className + '>'; |
| 158 | |
| 159 | QQmlMetaTypeData::CompositeMetaTypes &types = data->compositeMetaTypes[url]; |
| 160 | if (types.type) { |
| 161 | Q_ASSERT(types.listType); |
| 162 | |
| 163 | QMetaType::unregisterMetaType(type: QMetaType(types.type)); |
| 164 | QMetaType::unregisterMetaType(type: QMetaType(types.listType)); |
| 165 | |
| 166 | types.type->name = std::move(ptr); |
| 167 | types.type->QMetaTypeInterface::name = types.type->name.constData(); |
| 168 | types.listType->name = std::move(lst); |
| 169 | types.listType->QMetaTypeInterface::name = types.listType->name.constData(); |
| 170 | } else { |
| 171 | types.type = new QQmlMetaTypeInterface(std::move(ptr)); |
| 172 | types.listType = new QQmlListMetaTypeInterface(std::move(lst), types.type); |
| 173 | } |
| 174 | |
| 175 | QMetaType ptr_type(types.type); |
| 176 | QMetaType lst_type(types.listType); |
| 177 | |
| 178 | // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry. |
| 179 | ptr_type.id(); |
| 180 | lst_type.id(); |
| 181 | |
| 182 | priv->typeId = ptr_type; |
| 183 | priv->listId = lst_type; |
| 184 | } |
| 185 | |
| 186 | static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName, |
| 187 | const QQmlPrivate::RegisterCompositeType &type) |
| 188 | { |
| 189 | auto *d = new QQmlTypePrivate(QQmlType::CompositeType); |
| 190 | data->registerType(priv: d); |
| 191 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
| 192 | d->version = type.version; |
| 193 | |
| 194 | const QUrl normalized = QQmlTypeLoader::normalize(unNormalizedUrl: type.url); |
| 195 | d->extraData.compositeTypeData = normalized; |
| 196 | addQQmlMetaTypeInterfaces( |
| 197 | data, url: normalized, priv: d, className: QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url: normalized)); |
| 198 | return d; |
| 199 | } |
| 200 | |
| 201 | static QQmlTypePrivate *createQQmlType( |
| 202 | QQmlMetaTypeData *data, const QString &elementName, |
| 203 | const QQmlPrivate::RegisterCompositeSingletonType &type, |
| 204 | const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) |
| 205 | { |
| 206 | auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType); |
| 207 | data->registerType(priv: d); |
| 208 | d->setName(uri: QString::fromUtf8(utf8: type.uri), element: elementName); |
| 209 | |
| 210 | d->version = type.version; |
| 211 | |
| 212 | d->extraData.singletonTypeData->singletonInstanceInfo = siinfo; |
| 213 | const QUrl &url = siinfo->url; |
| 214 | addQQmlMetaTypeInterfaces( |
| 215 | data, url, priv: d, className: QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url)); |
| 216 | return d; |
| 217 | } |
| 218 | |
| 219 | void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo, |
| 220 | const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd, |
| 221 | QQmlMetaType::ClonePolicy policy) |
| 222 | { |
| 223 | // Set classname |
| 224 | builder.setClassName(mo->className()); |
| 225 | |
| 226 | // Clone Q_CLASSINFO |
| 227 | for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) { |
| 228 | QMetaClassInfo info = mo->classInfo(index: ii); |
| 229 | |
| 230 | int otherIndex = ignoreEnd->indexOfClassInfo(name: info.name()); |
| 231 | if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) { |
| 232 | // Skip |
| 233 | } else { |
| 234 | builder.addClassInfo(name: info.name(), value: info.value()); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | if (policy != QQmlMetaType::CloneEnumsOnly) { |
| 239 | // Clone Q_METHODS - do this first to avoid duplicating the notify signals. |
| 240 | for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) { |
| 241 | QMetaMethod method = mo->method(index: ii); |
| 242 | |
| 243 | // More complex - need to search name |
| 244 | QByteArray name = method.name(); |
| 245 | |
| 246 | bool found = false; |
| 247 | |
| 248 | for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount(); |
| 249 | !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) { |
| 250 | |
| 251 | QMetaMethod other = ignoreEnd->method(index: ii); |
| 252 | |
| 253 | found = name == other.name(); |
| 254 | } |
| 255 | |
| 256 | QMetaMethodBuilder m = builder.addMethod(prototype: method); |
| 257 | if (found) // SKIP |
| 258 | m.setAccess(QMetaMethod::Private); |
| 259 | } |
| 260 | |
| 261 | // Clone Q_PROPERTY |
| 262 | for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) { |
| 263 | QMetaProperty property = mo->property(index: ii); |
| 264 | |
| 265 | int otherIndex = ignoreEnd->indexOfProperty(name: property.name()); |
| 266 | if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) { |
| 267 | builder.addProperty(name: QByteArray("__qml_ignore__" ) + property.name(), |
| 268 | type: QByteArray("void" )); |
| 269 | // Skip |
| 270 | } else { |
| 271 | builder.addProperty(prototype: property); |
| 272 | } |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | // Clone enums registered with the metatype system |
| 277 | for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) { |
| 278 | QMetaEnum enumerator = mo->enumerator(index: ii); |
| 279 | |
| 280 | int otherIndex = ignoreEnd->indexOfEnumerator(name: enumerator.name()); |
| 281 | if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) { |
| 282 | // Skip |
| 283 | } else { |
| 284 | builder.addEnumerator(prototype: enumerator); |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)()) |
| 290 | { |
| 291 | QQmlMetaTypeDataPtr data; |
| 292 | if (data->moduleTypeRegistrationFunctions.contains(key: uri)) |
| 293 | qFatal(msg: "Cannot add multiple registrations for %s" , qPrintable(uri)); |
| 294 | else |
| 295 | data->moduleTypeRegistrationFunctions.insert(key: uri, value: registerFunction); |
| 296 | } |
| 297 | |
| 298 | void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri) |
| 299 | { |
| 300 | QQmlMetaTypeDataPtr data; |
| 301 | |
| 302 | if (!data.isValid()) |
| 303 | return; // shutdown/deletion race. Not a problem. |
| 304 | |
| 305 | if (!data->moduleTypeRegistrationFunctions.contains(key: uri)) |
| 306 | qFatal(msg: "Cannot remove multiple registrations for %s" , qPrintable(uri)); |
| 307 | else |
| 308 | data->moduleTypeRegistrationFunctions.remove(key: uri); |
| 309 | } |
| 310 | |
| 311 | bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri) |
| 312 | { |
| 313 | QQmlMetaTypeDataPtr data; |
| 314 | return data->registerModuleTypes(uri); |
| 315 | } |
| 316 | |
| 317 | void QQmlMetaType::clearTypeRegistrations() |
| 318 | { |
| 319 | //Only cleans global static, assumed no running engine |
| 320 | QQmlMetaTypeDataPtr data; |
| 321 | |
| 322 | data->uriToModule.clear(); |
| 323 | data->types.clear(); |
| 324 | data->idToType.clear(); |
| 325 | data->nameToType.clear(); |
| 326 | data->urlToType.clear(); |
| 327 | data->typePropertyCaches.clear(); |
| 328 | data->metaObjectToType.clear(); |
| 329 | data->undeletableTypes.clear(); |
| 330 | data->propertyCaches.clear(); |
| 331 | |
| 332 | qDeleteAll(c: data->metaTypeToValueType); |
| 333 | data->metaTypeToValueType.clear(); |
| 334 | |
| 335 | data->moduleImports.clear(); |
| 336 | |
| 337 | // Avoid deletion recursion (via QQmlTypePrivate dtor) by moving them out of the way first. |
| 338 | QQmlMetaTypeData::CompositeTypes emptyComposites; |
| 339 | emptyComposites.swap(other&: data->compositeTypes); |
| 340 | |
| 341 | qDeleteAll(c: data->metaTypeToValueType); |
| 342 | data->metaTypeToValueType.clear(); |
| 343 | |
| 344 | data->clearCompositeMetaTypes(); |
| 345 | } |
| 346 | |
| 347 | void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name) |
| 348 | { |
| 349 | QQmlMetaTypeDataPtr data; |
| 350 | const QQmlType type = data->types.value(i: typeIndex); |
| 351 | const QQmlTypePrivate *priv = type.priv(); |
| 352 | data->nameToType.insert(key: name, value: priv); |
| 353 | } |
| 354 | |
| 355 | int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function) |
| 356 | { |
| 357 | if (function.structVersion > 1) |
| 358 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 359 | |
| 360 | QQmlMetaTypeDataPtr data; |
| 361 | |
| 362 | data->parentFunctions.append(t: function.function); |
| 363 | |
| 364 | return data->parentFunctions.size() - 1; |
| 365 | } |
| 366 | |
| 367 | void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function) |
| 368 | { |
| 369 | QQmlMetaTypeDataPtr data; |
| 370 | data->parentFunctions.removeOne(t: function); |
| 371 | } |
| 372 | |
| 373 | QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type) |
| 374 | { |
| 375 | if (type.structVersion > 1) |
| 376 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 377 | |
| 378 | QQmlMetaTypeDataPtr data; |
| 379 | QQmlTypePrivate *priv = createQQmlType(data, type); |
| 380 | Q_ASSERT(priv); |
| 381 | |
| 382 | |
| 383 | data->idToType.insert(key: priv->typeId.id(), value: priv); |
| 384 | data->idToType.insert(key: priv->listId.id(), value: priv); |
| 385 | |
| 386 | data->interfaces.insert(value: type.typeId.id()); |
| 387 | |
| 388 | return QQmlType(priv); |
| 389 | } |
| 390 | |
| 391 | static QString registrationTypeString(QQmlType::RegistrationType typeType) |
| 392 | { |
| 393 | QString typeStr; |
| 394 | if (typeType == QQmlType::CppType) |
| 395 | typeStr = QStringLiteral("element" ); |
| 396 | else if (typeType == QQmlType::SingletonType) |
| 397 | typeStr = QStringLiteral("singleton type" ); |
| 398 | else if (typeType == QQmlType::CompositeSingletonType) |
| 399 | typeStr = QStringLiteral("composite singleton type" ); |
| 400 | else if (typeType == QQmlType::SequentialContainerType) |
| 401 | typeStr = QStringLiteral("sequential container type" ); |
| 402 | else |
| 403 | typeStr = QStringLiteral("type" ); |
| 404 | return typeStr; |
| 405 | } |
| 406 | |
| 407 | // NOTE: caller must hold a QMutexLocker on "data" |
| 408 | static bool checkRegistration( |
| 409 | QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, |
| 410 | const QString &typeName, QTypeRevision version, QMetaType::TypeFlags flags) |
| 411 | { |
| 412 | if (!typeName.isEmpty()) { |
| 413 | if (typeName.at(i: 0).isLower() && (flags & QMetaType::PointerToQObject)) { |
| 414 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter" )); |
| 415 | data->recordTypeRegFailure(message: failure.arg(args: registrationTypeString(typeType), args: typeName)); |
| 416 | return false; |
| 417 | } |
| 418 | |
| 419 | if (typeName.at(i: 0).isUpper() |
| 420 | && (flags & (QMetaType::IsGadget | QMetaType::PointerToGadget))) { |
| 421 | qCWarning(lcTypeRegistration).noquote() |
| 422 | << QCoreApplication::translate( |
| 423 | context: "qmlRegisterType" , |
| 424 | key: "Invalid QML %1 name \"%2\"; " |
| 425 | "value type names should begin with a lowercase letter" ) |
| 426 | .arg(args: registrationTypeString(typeType), args: typeName); |
| 427 | } |
| 428 | |
| 429 | // There can also be types that aren't even gadgets, and there can be types for namespaces. |
| 430 | // We cannot check those, but namespaces should be uppercase. |
| 431 | |
| 432 | int typeNameLen = typeName.size(); |
| 433 | for (int ii = 0; ii < typeNameLen; ++ii) { |
| 434 | if (!(typeName.at(i: ii).isLetterOrNumber() || typeName.at(i: ii) == u'_')) { |
| 435 | QString failure(QCoreApplication::translate(context: "qmlRegisterType" , key: "Invalid QML %1 name \"%2\"" )); |
| 436 | data->recordTypeRegFailure(message: failure.arg(args: registrationTypeString(typeType), args: typeName)); |
| 437 | return false; |
| 438 | } |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | if (uri && !typeName.isEmpty()) { |
| 443 | QString nameSpace = QString::fromUtf8(utf8: uri); |
| 444 | QQmlTypeModule *qqtm = data->findTypeModule(module: nameSpace, version); |
| 445 | if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) { |
| 446 | QString failure(QCoreApplication::translate( |
| 447 | context: "qmlRegisterType" , |
| 448 | key: "Cannot install %1 '%2' into protected module '%3' version '%4'" )); |
| 449 | data->recordTypeRegFailure(message: failure |
| 450 | .arg(args: registrationTypeString(typeType), args: typeName, args&: nameSpace) |
| 451 | .arg(a: version.majorVersion())); |
| 452 | return false; |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | return true; |
| 457 | } |
| 458 | |
| 459 | // NOTE: caller must hold a QMutexLocker on "data" |
| 460 | static QQmlTypeModule *getTypeModule( |
| 461 | const QHashedString &uri, QTypeRevision version, QQmlMetaTypeData *data) |
| 462 | { |
| 463 | if (QQmlTypeModule *module = data->findTypeModule(module: uri, version)) |
| 464 | return module; |
| 465 | return data->addTypeModule(module: std::make_unique<QQmlTypeModule>(args: uri, args: version.majorVersion())); |
| 466 | } |
| 467 | |
| 468 | // NOTE: caller must hold a QMutexLocker on "data" |
| 469 | static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data) |
| 470 | { |
| 471 | Q_ASSERT(type); |
| 472 | |
| 473 | if (!type->elementName.isEmpty()) |
| 474 | data->nameToType.insert(key: type->elementName, value: type); |
| 475 | |
| 476 | if (type->baseMetaObject) |
| 477 | data->metaObjectToType.insert(key: type->baseMetaObject, value: type); |
| 478 | |
| 479 | if (type->regType == QQmlType::SequentialContainerType) { |
| 480 | if (type->listId.isValid()) |
| 481 | data->idToType.insert(key: type->listId.id(), value: type); |
| 482 | } else { |
| 483 | if (type->typeId.isValid()) |
| 484 | data->idToType.insert(key: type->typeId.id(), value: type); |
| 485 | |
| 486 | if (type->listId.flags().testFlag(flag: QMetaType::IsQmlList)) |
| 487 | data->idToType.insert(key: type->listId.id(), value: type); |
| 488 | } |
| 489 | |
| 490 | if (!type->module.isEmpty()) { |
| 491 | const QHashedString &mod = type->module; |
| 492 | |
| 493 | QQmlTypeModule *module = getTypeModule(uri: mod, version: type->version, data); |
| 494 | Q_ASSERT(module); |
| 495 | module->add(type); |
| 496 | } |
| 497 | } |
| 498 | |
| 499 | QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type) |
| 500 | { |
| 501 | if (type.structVersion > int(QQmlPrivate::RegisterType::CurrentVersion)) |
| 502 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 503 | |
| 504 | QQmlMetaTypeDataPtr data; |
| 505 | |
| 506 | QString elementName = QString::fromUtf8(utf8: type.elementName); |
| 507 | if (!checkRegistration(typeType: QQmlType::CppType, data, uri: type.uri, typeName: elementName, version: type.version, |
| 508 | flags: QMetaType(type.typeId).flags())) { |
| 509 | return QQmlType(); |
| 510 | } |
| 511 | |
| 512 | QQmlTypePrivate *priv = createQQmlType(data, elementName, type); |
| 513 | addTypeToData(type: priv, data); |
| 514 | |
| 515 | return QQmlType(priv); |
| 516 | } |
| 517 | |
| 518 | QQmlType QQmlMetaType::registerSingletonType( |
| 519 | const QQmlPrivate::RegisterSingletonType &type, |
| 520 | const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) |
| 521 | { |
| 522 | if (type.structVersion > 1) |
| 523 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 524 | |
| 525 | QQmlMetaTypeDataPtr data; |
| 526 | |
| 527 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
| 528 | if (!checkRegistration(typeType: QQmlType::SingletonType, data, uri: type.uri, typeName, version: type.version, |
| 529 | flags: QMetaType(type.typeId).flags())) { |
| 530 | return QQmlType(); |
| 531 | } |
| 532 | |
| 533 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type, siinfo); |
| 534 | |
| 535 | addTypeToData(type: priv, data); |
| 536 | |
| 537 | return QQmlType(priv); |
| 538 | } |
| 539 | |
| 540 | QQmlType QQmlMetaType::registerCompositeSingletonType( |
| 541 | const QQmlPrivate::RegisterCompositeSingletonType &type, |
| 542 | const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo) |
| 543 | { |
| 544 | if (type.structVersion > 1) |
| 545 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 546 | |
| 547 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
| 548 | QQmlMetaTypeDataPtr data; |
| 549 | |
| 550 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
| 551 | if (!checkRegistration( |
| 552 | typeType: QQmlType::CompositeSingletonType, data, uri: type.uri, typeName, version: type.version, flags: {})) { |
| 553 | return QQmlType(); |
| 554 | } |
| 555 | |
| 556 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type, siinfo); |
| 557 | addTypeToData(type: priv, data); |
| 558 | |
| 559 | data->urlToType.insert(key: siinfo->url, value: priv); |
| 560 | |
| 561 | return QQmlType(priv); |
| 562 | } |
| 563 | |
| 564 | QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) |
| 565 | { |
| 566 | if (type.structVersion > 1) |
| 567 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 568 | |
| 569 | // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. |
| 570 | QQmlMetaTypeDataPtr data; |
| 571 | |
| 572 | QString typeName = QString::fromUtf8(utf8: type.typeName); |
| 573 | if (!checkRegistration(typeType: QQmlType::CompositeType, data, uri: type.uri, typeName, version: type.version, flags: {})) |
| 574 | return QQmlType(); |
| 575 | |
| 576 | QQmlTypePrivate *priv = createQQmlType(data, elementName: typeName, type); |
| 577 | addTypeToData(type: priv, data); |
| 578 | |
| 579 | data->urlToType.insert(key: QQmlTypeLoader::normalize(unNormalizedUrl: type.url), value: priv); |
| 580 | |
| 581 | return QQmlType(priv); |
| 582 | } |
| 583 | |
| 584 | class QQmlMetaTypeRegistrationFailureRecorder |
| 585 | { |
| 586 | Q_DISABLE_COPY_MOVE(QQmlMetaTypeRegistrationFailureRecorder) |
| 587 | public: |
| 588 | QQmlMetaTypeRegistrationFailureRecorder(QQmlMetaTypeData *data, QStringList *failures) |
| 589 | : data(data) |
| 590 | { |
| 591 | data->setTypeRegistrationFailures(failures); |
| 592 | } |
| 593 | |
| 594 | ~QQmlMetaTypeRegistrationFailureRecorder() |
| 595 | { |
| 596 | data->setTypeRegistrationFailures(nullptr); |
| 597 | } |
| 598 | |
| 599 | QQmlMetaTypeData *data = nullptr; |
| 600 | }; |
| 601 | |
| 602 | |
| 603 | static QQmlType createTypeForUrl( |
| 604 | QQmlMetaTypeData *data, const QUrl &url, const QHashedStringRef &qualifiedType, |
| 605 | QQmlMetaType::CompositeTypeLookupMode mode, QList<QQmlError> *errors, QTypeRevision version) |
| 606 | { |
| 607 | const int dot = qualifiedType.indexOf(QLatin1Char('.')); |
| 608 | const QString typeName = dot < 0 |
| 609 | ? qualifiedType.toString() |
| 610 | : QString(qualifiedType.constData() + dot + 1, qualifiedType.length() - dot - 1); |
| 611 | |
| 612 | QStringList failures; |
| 613 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
| 614 | |
| 615 | // Register the type. Note that the URI parameters here are empty; for |
| 616 | // file type imports, we do not place them in a URI as we don't |
| 617 | // necessarily have a good and unique one (picture a library import, |
| 618 | // which may be found in multiple plugin locations on disk), but there |
| 619 | // are other reasons for this too. |
| 620 | // |
| 621 | // By not putting them in a URI, we prevent the types from being |
| 622 | // registered on a QQmlTypeModule; this is important, as once types are |
| 623 | // placed on there, they cannot be easily removed, meaning if the |
| 624 | // developer subsequently loads a different import (meaning different |
| 625 | // types) with the same URI (using, say, a different plugin path), it is |
| 626 | // very undesirable that we continue to associate the types from the |
| 627 | // "old" URI with that new module. |
| 628 | // |
| 629 | // Not having URIs also means that the types cannot be found by name |
| 630 | // etc, the only way to look them up is through QQmlImports -- for |
| 631 | // better or worse. |
| 632 | QQmlType::RegistrationType registrationType; |
| 633 | switch (mode) { |
| 634 | case QQmlMetaType::Singleton: |
| 635 | registrationType = QQmlType::CompositeSingletonType; |
| 636 | break; |
| 637 | case QQmlMetaType::NonSingleton: |
| 638 | registrationType = QQmlType::CompositeType; |
| 639 | break; |
| 640 | case QQmlMetaType::JavaScript: |
| 641 | registrationType = QQmlType::JavaScriptType; |
| 642 | break; |
| 643 | default: |
| 644 | Q_UNREACHABLE_RETURN(QQmlType()); |
| 645 | } |
| 646 | |
| 647 | if (checkRegistration(typeType: registrationType, data, uri: nullptr, typeName, version, flags: {})) { |
| 648 | |
| 649 | // TODO: Ideally we should defer most of this work using some lazy/atomic mechanism |
| 650 | // that creates the details on first use. We must not observably modify |
| 651 | // QQmlTypePrivate after it has been created since it is supposed to be immutable |
| 652 | // and shared across threads. |
| 653 | |
| 654 | auto *priv = new QQmlTypePrivate(registrationType); |
| 655 | addQQmlMetaTypeInterfaces( |
| 656 | data, url, priv, className: QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url)); |
| 657 | |
| 658 | priv->setName(uri: QString(), element: typeName); |
| 659 | priv->version = version; |
| 660 | |
| 661 | switch (mode) { |
| 662 | case QQmlMetaType::Singleton: { |
| 663 | QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create(); |
| 664 | siinfo->url = url; |
| 665 | siinfo->typeName = typeName.toUtf8(); |
| 666 | priv->extraData.singletonTypeData->singletonInstanceInfo = |
| 667 | QQmlType::SingletonInstanceInfo::ConstPtr( |
| 668 | siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt); |
| 669 | break; |
| 670 | } |
| 671 | case QQmlMetaType::NonSingleton: { |
| 672 | priv->extraData.compositeTypeData = url; |
| 673 | break; |
| 674 | } |
| 675 | case QQmlMetaType::JavaScript: { |
| 676 | priv->extraData.javaScriptTypeData = url; |
| 677 | break; |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | data->registerType(priv); |
| 682 | addTypeToData(type: priv, data); |
| 683 | return QQmlType(priv); |
| 684 | } |
| 685 | |
| 686 | // This means that the type couldn't be found by URL, but could not be |
| 687 | // registered either, meaning we most likely were passed some kind of bad |
| 688 | // data. |
| 689 | if (errors) { |
| 690 | QQmlError error; |
| 691 | error.setDescription(failures.join(sep: u'\n')); |
| 692 | errors->prepend(t: error); |
| 693 | } else { |
| 694 | qWarning(msg: "%s" , failures.join(sep: u'\n').toLatin1().constData()); |
| 695 | } |
| 696 | return QQmlType(); |
| 697 | } |
| 698 | |
| 699 | QQmlType QQmlMetaType::findCompositeType( |
| 700 | const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, |
| 701 | CompositeTypeLookupMode mode) |
| 702 | { |
| 703 | const QUrl normalized = QQmlTypeLoader::normalize(unNormalizedUrl: url); |
| 704 | QQmlMetaTypeDataPtr data; |
| 705 | |
| 706 | bool urlExists = true; |
| 707 | auto found = data->urlToType.constFind(key: normalized); |
| 708 | if (found == data->urlToType.cend()) |
| 709 | urlExists = false; |
| 710 | |
| 711 | if (urlExists) { |
| 712 | if (compilationUnit.isNull()) |
| 713 | return QQmlType(*found); |
| 714 | const auto composite = data->compositeTypes.constFind(key: found.value()->typeId.iface()); |
| 715 | if (composite == data->compositeTypes.constEnd() || composite.value() == compilationUnit) |
| 716 | return QQmlType(*found); |
| 717 | } |
| 718 | |
| 719 | const QQmlType type = createTypeForUrl( |
| 720 | data, url: normalized, qualifiedType: QHashedStringRef(), mode, errors: nullptr, version: QTypeRevision()); |
| 721 | |
| 722 | if (!urlExists && type.isValid()) |
| 723 | data->urlToType.insert(key: normalized, value: type.priv()); |
| 724 | |
| 725 | return type; |
| 726 | } |
| 727 | |
| 728 | static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url) |
| 729 | { |
| 730 | QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::InlineComponentType); |
| 731 | priv->setName(uri: QString(), element: url.fragment()); |
| 732 | |
| 733 | priv->extraData.inlineComponentTypeData = url; |
| 734 | data->registerType(priv); |
| 735 | |
| 736 | addQQmlMetaTypeInterfaces( |
| 737 | data, url, priv, className: QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(url)); |
| 738 | data->urlToType.insert(key: url, value: priv); |
| 739 | |
| 740 | data->idToType.insert(key: priv->typeId.id(), value: priv); |
| 741 | data->idToType.insert(key: priv->listId.id(), value: priv); |
| 742 | |
| 743 | return QQmlType(priv); |
| 744 | } |
| 745 | |
| 746 | QQmlType QQmlMetaType::findInlineComponentType( |
| 747 | const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) |
| 748 | { |
| 749 | QQmlMetaTypeDataPtr data; |
| 750 | |
| 751 | // If there is an "unclaimed" inline component type, we can "claim" it now. Otherwise |
| 752 | // we have to create a new one. |
| 753 | const auto it = data->urlToType.constFind(key: url); |
| 754 | if (it != data->urlToType.constEnd()) { |
| 755 | const auto jt = data->compositeTypes.constFind(key: (*it)->typeId.iface()); |
| 756 | if (jt == data->compositeTypes.constEnd() || *jt == compilationUnit) |
| 757 | return QQmlType(*it); |
| 758 | } |
| 759 | |
| 760 | return doRegisterInlineComponentType(data, url); |
| 761 | } |
| 762 | |
| 763 | int QQmlMetaType::registerUnitCacheHook( |
| 764 | const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration) |
| 765 | { |
| 766 | if (hookRegistration.structVersion > 1) |
| 767 | qFatal(msg: "qmlRegisterType(): Cannot mix incompatible QML versions." ); |
| 768 | |
| 769 | QQmlMetaTypeDataPtr data; |
| 770 | data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit; |
| 771 | return 0; |
| 772 | } |
| 773 | |
| 774 | QQmlType QQmlMetaType::registerSequentialContainer( |
| 775 | const QQmlPrivate::RegisterSequentialContainer &container) |
| 776 | { |
| 777 | if (container.structVersion > 1) |
| 778 | qFatal(msg: "qmlRegisterSequenceContainer(): Cannot mix incompatible QML versions." ); |
| 779 | |
| 780 | QQmlMetaTypeDataPtr data; |
| 781 | |
| 782 | if (!checkRegistration(typeType: QQmlType::SequentialContainerType, data, uri: container.uri, typeName: QString(), |
| 783 | version: container.version, flags: {})) { |
| 784 | return QQmlType(); |
| 785 | } |
| 786 | |
| 787 | QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType); |
| 788 | |
| 789 | data->registerType(priv); |
| 790 | priv->setName(uri: QString::fromUtf8(utf8: container.uri), element: QString()); |
| 791 | priv->version = container.version; |
| 792 | priv->revision = container.revision; |
| 793 | priv->typeId = container.metaSequence.valueMetaType(); |
| 794 | priv->listId = container.typeId; |
| 795 | priv->extraData.sequentialContainerTypeData = container.metaSequence; |
| 796 | |
| 797 | addTypeToData(type: priv, data); |
| 798 | |
| 799 | return QQmlType(priv); |
| 800 | } |
| 801 | |
| 802 | void QQmlMetaType::unregisterSequentialContainer(int id) |
| 803 | { |
| 804 | unregisterType(type: id); |
| 805 | } |
| 806 | |
| 807 | bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version, |
| 808 | bool weakProtectAllVersions) |
| 809 | { |
| 810 | QQmlMetaTypeDataPtr data; |
| 811 | if (version.hasMajorVersion()) { |
| 812 | if (QQmlTypeModule *module = data->findTypeModule(module: uri, version)) { |
| 813 | if (!weakProtectAllVersions) { |
| 814 | module->setLockLevel(QQmlTypeModule::LockLevel::Strong); |
| 815 | return true; |
| 816 | } |
| 817 | } else { |
| 818 | return false; |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | const auto range = std::equal_range( |
| 823 | first: data->uriToModule.begin(), last: data->uriToModule.end(), val: uri, |
| 824 | comp: std::less<ModuleUri>()); |
| 825 | |
| 826 | for (auto it = range.first; it != range.second; ++it) |
| 827 | (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak); |
| 828 | |
| 829 | return range.first != range.second; |
| 830 | } |
| 831 | |
| 832 | void QQmlMetaType::registerModuleImport(const QString &uri, QTypeRevision moduleVersion, |
| 833 | const QQmlDirParser::Import &import) |
| 834 | { |
| 835 | QQmlMetaTypeDataPtr data; |
| 836 | |
| 837 | data->moduleImports.insert(key: QQmlMetaTypeData::VersionedUri(uri, moduleVersion), value: import); |
| 838 | } |
| 839 | |
| 840 | void QQmlMetaType::unregisterModuleImport(const QString &uri, QTypeRevision moduleVersion, |
| 841 | const QQmlDirParser::Import &import) |
| 842 | { |
| 843 | QQmlMetaTypeDataPtr data; |
| 844 | data->moduleImports.remove(key: QQmlMetaTypeData::VersionedUri(uri, moduleVersion), value: import); |
| 845 | } |
| 846 | |
| 847 | QList<QQmlDirParser::Import> QQmlMetaType::moduleImports( |
| 848 | const QString &uri, QTypeRevision version) |
| 849 | { |
| 850 | QQmlMetaTypeDataPtr data; |
| 851 | QList<QQmlDirParser::Import> result; |
| 852 | |
| 853 | const auto unrevisioned = data->moduleImports.equal_range( |
| 854 | akey: QQmlMetaTypeData::VersionedUri(uri, QTypeRevision())); |
| 855 | for (auto it = unrevisioned.second; it != unrevisioned.first;) |
| 856 | result.append(t: *(--it)); |
| 857 | |
| 858 | if (version.hasMajorVersion()) { |
| 859 | const auto revisioned = data->moduleImports.equal_range( |
| 860 | akey: QQmlMetaTypeData::VersionedUri(uri, version)); |
| 861 | for (auto it = revisioned.second; it != revisioned.first;) |
| 862 | result.append(t: *(--it)); |
| 863 | return result; |
| 864 | } |
| 865 | |
| 866 | // Use latest module available with that URI. |
| 867 | const auto begin = data->moduleImports.begin(); |
| 868 | auto it = unrevisioned.first; |
| 869 | if (it == begin) |
| 870 | return result; |
| 871 | |
| 872 | const QQmlMetaTypeData::VersionedUri latestVersion = (--it).key(); |
| 873 | if (latestVersion.uri != uri) |
| 874 | return result; |
| 875 | |
| 876 | do { |
| 877 | result += *it; |
| 878 | } while (it != begin && (--it).key() == latestVersion); |
| 879 | |
| 880 | return result; |
| 881 | } |
| 882 | |
| 883 | void QQmlMetaType::registerModule(const char *uri, QTypeRevision version) |
| 884 | { |
| 885 | QQmlMetaTypeDataPtr data; |
| 886 | |
| 887 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(utf8: uri), version, data); |
| 888 | Q_ASSERT(module); |
| 889 | |
| 890 | if (version.hasMinorVersion()) |
| 891 | module->addMinorVersion(minorVersion: version.minorVersion()); |
| 892 | } |
| 893 | |
| 894 | int QQmlMetaType::typeId(const char *uri, QTypeRevision version, const char *qmlName) |
| 895 | { |
| 896 | QQmlMetaTypeDataPtr data; |
| 897 | |
| 898 | QQmlTypeModule *module = getTypeModule(uri: QString::fromUtf8(utf8: uri), version, data); |
| 899 | if (!module) |
| 900 | return -1; |
| 901 | |
| 902 | QQmlType type = module->type(name: QHashedStringRef(QString::fromUtf8(utf8: qmlName)), version); |
| 903 | if (!type.isValid()) |
| 904 | return -1; |
| 905 | |
| 906 | return type.index(); |
| 907 | } |
| 908 | |
| 909 | void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) |
| 910 | { |
| 911 | QQmlMetaTypeDataPtr data; |
| 912 | data->undeletableTypes.insert(value: dtype); |
| 913 | } |
| 914 | |
| 915 | static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, |
| 916 | QTypeRevision version) |
| 917 | { |
| 918 | // Has any type previously been installed to this namespace? |
| 919 | QHashedString nameSpace(uri); |
| 920 | for (const QQmlType &type : data->types) { |
| 921 | if (type.module() == nameSpace && type.version().majorVersion() == version.majorVersion()) |
| 922 | return true; |
| 923 | } |
| 924 | |
| 925 | return false; |
| 926 | } |
| 927 | |
| 928 | QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes( |
| 929 | QObject *instance, const QString &basePath, const QString &uri, |
| 930 | const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors) |
| 931 | { |
| 932 | if (!typeNamespace.isEmpty() && typeNamespace != uri) { |
| 933 | // This is an 'identified' module |
| 934 | // The namespace for type registrations must match the URI for locating the module |
| 935 | if (errors) { |
| 936 | QQmlError error; |
| 937 | error.setDescription( |
| 938 | QStringLiteral("Module namespace '%1' does not match import URI '%2'" ) |
| 939 | .arg(args: typeNamespace, args: uri)); |
| 940 | errors->prepend(t: error); |
| 941 | } |
| 942 | return RegistrationResult::Failure; |
| 943 | } |
| 944 | |
| 945 | QStringList failures; |
| 946 | QQmlMetaTypeDataPtr data; |
| 947 | { |
| 948 | QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures); |
| 949 | if (!typeNamespace.isEmpty()) { |
| 950 | // This is an 'identified' module |
| 951 | if (namespaceContainsRegistrations(data, uri: typeNamespace, version)) { |
| 952 | // Other modules have already installed to this namespace |
| 953 | if (errors) { |
| 954 | QQmlError error; |
| 955 | error.setDescription(QStringLiteral("Namespace '%1' has already been used " |
| 956 | "for type registration" ) |
| 957 | .arg(a: typeNamespace)); |
| 958 | errors->prepend(t: error); |
| 959 | } |
| 960 | return RegistrationResult::Failure; |
| 961 | } |
| 962 | } else { |
| 963 | // This is not an identified module - provide a warning |
| 964 | qWarning().nospace() << qPrintable( |
| 965 | QStringLiteral("Module '%1' does not contain a module identifier directive - " |
| 966 | "it cannot be protected from external registrations." ).arg(uri)); |
| 967 | } |
| 968 | |
| 969 | if (instance && !qobject_cast<QQmlEngineExtensionInterface *>(object: instance)) { |
| 970 | QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(object: instance); |
| 971 | if (!iface) { |
| 972 | if (errors) { |
| 973 | QQmlError error; |
| 974 | // Also does not implement QQmlTypesExtensionInterface, but we want to discourage that. |
| 975 | error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement " |
| 976 | "QQmlEngineExtensionInterface" ).arg(a: typeNamespace)); |
| 977 | errors->prepend(t: error); |
| 978 | } |
| 979 | return RegistrationResult::Failure; |
| 980 | } |
| 981 | |
| 982 | #if QT_DEPRECATED_SINCE(6, 3) |
| 983 | if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(object: instance)) { |
| 984 | // basepath should point to the directory of the module, not the plugin file itself: |
| 985 | QQmlExtensionPluginPrivate::get(e: plugin)->baseUrl |
| 986 | = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath); |
| 987 | } |
| 988 | #else |
| 989 | Q_UNUSED(basePath) |
| 990 | #endif |
| 991 | |
| 992 | const QByteArray bytes = uri.toUtf8(); |
| 993 | const char *moduleId = bytes.constData(); |
| 994 | iface->registerTypes(uri: moduleId); |
| 995 | } |
| 996 | |
| 997 | if (failures.isEmpty() && !data->registerModuleTypes(uri)) |
| 998 | return RegistrationResult::NoRegistrationFunction; |
| 999 | |
| 1000 | if (!failures.isEmpty()) { |
| 1001 | if (errors) { |
| 1002 | for (const QString &failure : std::as_const(t&: failures)) { |
| 1003 | QQmlError error; |
| 1004 | error.setDescription(failure); |
| 1005 | errors->prepend(t: error); |
| 1006 | } |
| 1007 | } |
| 1008 | return RegistrationResult::Failure; |
| 1009 | } |
| 1010 | } |
| 1011 | |
| 1012 | return RegistrationResult::Success; |
| 1013 | } |
| 1014 | |
| 1015 | /* |
| 1016 | \internal |
| 1017 | |
| 1018 | Fetches the QQmlType instance registered for \a urlString, creating a |
| 1019 | registration for it if it is not already registered, using the associated |
| 1020 | \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion |
| 1021 | details. |
| 1022 | |
| 1023 | Errors (if there are any) are placed into \a errors, if it is nonzero. |
| 1024 | Otherwise errors are printed as warnings. |
| 1025 | */ |
| 1026 | QQmlType QQmlMetaType::typeForUrl(const QString &urlString, |
| 1027 | const QHashedStringRef &qualifiedType, |
| 1028 | CompositeTypeLookupMode mode, QList<QQmlError> *errors, |
| 1029 | QTypeRevision version) |
| 1030 | { |
| 1031 | // ### unfortunate (costly) conversion |
| 1032 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl: QUrl(urlString)); |
| 1033 | |
| 1034 | QQmlMetaTypeDataPtr data; |
| 1035 | { |
| 1036 | QQmlType ret(data->urlToType.value(key: url)); |
| 1037 | if (ret.isValid() && ret.sourceUrl() == url) |
| 1038 | return ret; |
| 1039 | } |
| 1040 | |
| 1041 | const QQmlType type = createTypeForUrl( |
| 1042 | data, url, qualifiedType, mode, errors, version); |
| 1043 | data->urlToType.insert(key: url, value: type.priv()); |
| 1044 | return type; |
| 1045 | } |
| 1046 | |
| 1047 | /* |
| 1048 | Returns the latest version of \a uri installed, or an in valid QTypeRevision(). |
| 1049 | */ |
| 1050 | QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri) |
| 1051 | { |
| 1052 | QQmlMetaTypeDataPtr data; |
| 1053 | auto upper = std::upper_bound(first: data->uriToModule.begin(), last: data->uriToModule.end(), val: uri, |
| 1054 | comp: std::less<ModuleUri>()); |
| 1055 | if (upper == data->uriToModule.begin()) |
| 1056 | return QTypeRevision(); |
| 1057 | |
| 1058 | const auto module = (--upper)->get(); |
| 1059 | return (module->module() == uri) |
| 1060 | ? QTypeRevision::fromVersion(majorVersion: module->majorVersion(), minorVersion: module->maximumMinorVersion()) |
| 1061 | : QTypeRevision(); |
| 1062 | } |
| 1063 | |
| 1064 | /* |
| 1065 | Returns true if a module \a uri of this version is installed and locked; |
| 1066 | */ |
| 1067 | bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version) |
| 1068 | { |
| 1069 | QQmlMetaTypeDataPtr data; |
| 1070 | |
| 1071 | if (QQmlTypeModule* qqtm = data->findTypeModule(module: uri, version)) |
| 1072 | return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong; |
| 1073 | return false; |
| 1074 | } |
| 1075 | |
| 1076 | /* |
| 1077 | Returns the best matching registered version for the given \a module. If \a version is |
| 1078 | the does not have a major version, returns the latest registered version. Otherwise |
| 1079 | chooses the same major version and checks if the minor version is within the range |
| 1080 | of registered minor versions. If so, retuens the original version, otherwise returns |
| 1081 | an invalid version. If \a version does not have a minor version, chooses the latest one. |
| 1082 | */ |
| 1083 | QTypeRevision QQmlMetaType::matchingModuleVersion(const QString &module, QTypeRevision version) |
| 1084 | { |
| 1085 | if (!version.hasMajorVersion()) |
| 1086 | return latestModuleVersion(uri: module); |
| 1087 | |
| 1088 | QQmlMetaTypeDataPtr data; |
| 1089 | |
| 1090 | // first, check Types |
| 1091 | if (QQmlTypeModule *tm = data->findTypeModule(module, version)) { |
| 1092 | if (!version.hasMinorVersion()) |
| 1093 | return QTypeRevision::fromVersion(majorVersion: version.majorVersion(), minorVersion: tm->maximumMinorVersion()); |
| 1094 | |
| 1095 | if (tm->minimumMinorVersion() <= version.minorVersion() |
| 1096 | && tm->maximumMinorVersion() >= version.minorVersion()) { |
| 1097 | return version; |
| 1098 | } |
| 1099 | } |
| 1100 | |
| 1101 | return QTypeRevision(); |
| 1102 | } |
| 1103 | |
| 1104 | QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, QTypeRevision version) |
| 1105 | { |
| 1106 | QQmlMetaTypeDataPtr data; |
| 1107 | |
| 1108 | if (version.hasMajorVersion()) |
| 1109 | return data->findTypeModule(module: uri, version); |
| 1110 | |
| 1111 | auto range = std::equal_range(first: data->uriToModule.begin(), last: data->uriToModule.end(), |
| 1112 | val: uri, comp: std::less<ModuleUri>()); |
| 1113 | |
| 1114 | return range.first == range.second ? nullptr : (--range.second)->get(); |
| 1115 | } |
| 1116 | |
| 1117 | QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions() |
| 1118 | { |
| 1119 | QQmlMetaTypeDataPtr data; |
| 1120 | return data->parentFunctions; |
| 1121 | } |
| 1122 | |
| 1123 | QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok) |
| 1124 | { |
| 1125 | if (!v.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) { |
| 1126 | if (ok) *ok = false; |
| 1127 | return nullptr; |
| 1128 | } |
| 1129 | |
| 1130 | if (ok) *ok = true; |
| 1131 | |
| 1132 | return *(QObject *const *)v.constData(); |
| 1133 | } |
| 1134 | |
| 1135 | /* |
| 1136 | Returns the item type for a list of type \a id. |
| 1137 | */ |
| 1138 | QMetaType QQmlMetaType::listValueType(QMetaType metaType) |
| 1139 | { |
| 1140 | if (isList(type: metaType)) { |
| 1141 | const auto iface = metaType.iface(); |
| 1142 | if (iface && iface->metaObjectFn == &dynamicQmlListMarker) |
| 1143 | return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType); |
| 1144 | } else if (metaType.flags() & QMetaType::PointerToQObject) { |
| 1145 | return QMetaType(); |
| 1146 | } |
| 1147 | |
| 1148 | QQmlMetaTypeDataPtr data; |
| 1149 | Q_ASSERT(data); |
| 1150 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1151 | |
| 1152 | if (type && type->listId == metaType) |
| 1153 | return type->typeId; |
| 1154 | else |
| 1155 | return QMetaType {}; |
| 1156 | } |
| 1157 | |
| 1158 | QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, |
| 1159 | const QMetaObject *mo) |
| 1160 | { |
| 1161 | QQmlMetaTypeDataPtr data; |
| 1162 | |
| 1163 | QQmlType type(data->metaObjectToType.value(key: mo)); |
| 1164 | return type.attachedPropertiesFunction(enginePrivate: engine); |
| 1165 | } |
| 1166 | |
| 1167 | QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) |
| 1168 | { |
| 1169 | int idx = metaObject->indexOfClassInfo(name: "DefaultProperty" ); |
| 1170 | if (-1 == idx) |
| 1171 | return QMetaProperty(); |
| 1172 | |
| 1173 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
| 1174 | if (!info.value()) |
| 1175 | return QMetaProperty(); |
| 1176 | |
| 1177 | idx = metaObject->indexOfProperty(name: info.value()); |
| 1178 | if (-1 == idx) |
| 1179 | return QMetaProperty(); |
| 1180 | |
| 1181 | return metaObject->property(index: idx); |
| 1182 | } |
| 1183 | |
| 1184 | QMetaProperty QQmlMetaType::defaultProperty(QObject *obj) |
| 1185 | { |
| 1186 | if (!obj) |
| 1187 | return QMetaProperty(); |
| 1188 | |
| 1189 | const QMetaObject *metaObject = obj->metaObject(); |
| 1190 | return defaultProperty(metaObject); |
| 1191 | } |
| 1192 | |
| 1193 | QMetaMethod QQmlMetaType::defaultMethod(const QMetaObject *metaObject) |
| 1194 | { |
| 1195 | int idx = metaObject->indexOfClassInfo(name: "DefaultMethod" ); |
| 1196 | if (-1 == idx) |
| 1197 | return QMetaMethod(); |
| 1198 | |
| 1199 | QMetaClassInfo info = metaObject->classInfo(index: idx); |
| 1200 | if (!info.value()) |
| 1201 | return QMetaMethod(); |
| 1202 | |
| 1203 | idx = metaObject->indexOfMethod(method: info.value()); |
| 1204 | if (-1 == idx) |
| 1205 | return QMetaMethod(); |
| 1206 | |
| 1207 | return metaObject->method(index: idx); |
| 1208 | } |
| 1209 | |
| 1210 | QMetaMethod QQmlMetaType::defaultMethod(QObject *obj) |
| 1211 | { |
| 1212 | if (!obj) |
| 1213 | return QMetaMethod(); |
| 1214 | |
| 1215 | const QMetaObject *metaObject = obj->metaObject(); |
| 1216 | return defaultMethod(metaObject); |
| 1217 | } |
| 1218 | |
| 1219 | /*! |
| 1220 | See qmlRegisterInterface() for information about when this will return true. |
| 1221 | */ |
| 1222 | bool QQmlMetaType::isInterface(QMetaType type) |
| 1223 | { |
| 1224 | const QQmlMetaTypeDataPtr data; |
| 1225 | return data->interfaces.contains(value: type.id()); |
| 1226 | } |
| 1227 | |
| 1228 | const char *QQmlMetaType::interfaceIId(QMetaType metaType) |
| 1229 | { |
| 1230 | const QQmlMetaTypeDataPtr data; |
| 1231 | const QQmlType type(data->idToType.value(key: metaType.id())); |
| 1232 | return (type.isInterface() && type.typeId() == metaType) ? type.interfaceIId() : nullptr; |
| 1233 | } |
| 1234 | |
| 1235 | bool QQmlMetaType::isList(QMetaType type) |
| 1236 | { |
| 1237 | if (type.flags().testFlag(flag: QMetaType::IsQmlList)) |
| 1238 | return true; |
| 1239 | else |
| 1240 | return false; |
| 1241 | } |
| 1242 | |
| 1243 | /*! |
| 1244 | Returns the type (if any) of URI-qualified named \a qualifiedName and version specified |
| 1245 | by \a version_major and \a version_minor. |
| 1246 | */ |
| 1247 | QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, QTypeRevision version) |
| 1248 | { |
| 1249 | int slash = qualifiedName.indexOf(ch: QLatin1Char('/')); |
| 1250 | if (slash <= 0) |
| 1251 | return QQmlType(); |
| 1252 | |
| 1253 | QHashedStringRef module(qualifiedName.constData(), slash); |
| 1254 | QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.size() - slash - 1); |
| 1255 | |
| 1256 | return qmlType(name, module, version); |
| 1257 | } |
| 1258 | |
| 1259 | /*! |
| 1260 | \internal |
| 1261 | Returns the type (if any) of \a name in \a module and the specified \a version. |
| 1262 | |
| 1263 | If \a version has no major version, accept any version. |
| 1264 | If \a version has no minor version, accept any minor version. |
| 1265 | If \a module is empty, search in all modules and accept any version. |
| 1266 | */ |
| 1267 | QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module, |
| 1268 | QTypeRevision version) |
| 1269 | { |
| 1270 | const QQmlMetaTypeDataPtr data; |
| 1271 | |
| 1272 | const QHashedString key(QString::fromRawData(unicode: name.constData(), size: name.length()), name.hash()); |
| 1273 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(key); |
| 1274 | while (it != data->nameToType.cend() && it.key() == name) { |
| 1275 | QQmlType t(*it); |
| 1276 | if (module.isEmpty() || t.availableInVersion(module, version)) |
| 1277 | return t; |
| 1278 | ++it; |
| 1279 | } |
| 1280 | |
| 1281 | return QQmlType(); |
| 1282 | } |
| 1283 | |
| 1284 | /*! |
| 1285 | Returns the type (if any) that corresponds to the \a metaObject. Returns an invalid type if no |
| 1286 | such type is registered. |
| 1287 | */ |
| 1288 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject) |
| 1289 | { |
| 1290 | const QQmlMetaTypeDataPtr data; |
| 1291 | return QQmlType(data->metaObjectToType.value(key: metaObject)); |
| 1292 | } |
| 1293 | |
| 1294 | /*! |
| 1295 | Returns the type (if any) that corresponds to the \a metaObject in version specified |
| 1296 | by \a version_major and \a version_minor in module specified by \a uri. Returns null if no |
| 1297 | type is registered. |
| 1298 | */ |
| 1299 | QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, |
| 1300 | QTypeRevision version) |
| 1301 | { |
| 1302 | const QQmlMetaTypeDataPtr data; |
| 1303 | |
| 1304 | const auto range = data->metaObjectToType.equal_range(key: metaObject); |
| 1305 | for (auto it = range.first; it != range.second; ++it) { |
| 1306 | QQmlType t(*it); |
| 1307 | if (module.isEmpty() || t.availableInVersion(module, version)) |
| 1308 | return t; |
| 1309 | } |
| 1310 | |
| 1311 | return QQmlType(); |
| 1312 | } |
| 1313 | |
| 1314 | /*! |
| 1315 | Returns the type (if any) that corresponds to \a qmlTypeId. |
| 1316 | Returns an invalid QQmlType if no such type is registered. |
| 1317 | */ |
| 1318 | QQmlType QQmlMetaType::qmlTypeById(int qmlTypeId) |
| 1319 | { |
| 1320 | const QQmlMetaTypeDataPtr data; |
| 1321 | QQmlType type = data->types.value(i: qmlTypeId); |
| 1322 | if (type.isValid()) |
| 1323 | return type; |
| 1324 | return QQmlType(); |
| 1325 | } |
| 1326 | |
| 1327 | /*! |
| 1328 | Returns the type (if any) that corresponds to \a metaType. |
| 1329 | Returns an invalid QQmlType if no such type is registered. |
| 1330 | */ |
| 1331 | QQmlType QQmlMetaType::qmlType(QMetaType metaType) |
| 1332 | { |
| 1333 | const QQmlMetaTypeDataPtr data; |
| 1334 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1335 | return (type && type->typeId == metaType) ? QQmlType(type) : QQmlType(); |
| 1336 | } |
| 1337 | |
| 1338 | QQmlType QQmlMetaType::qmlListType(QMetaType metaType) |
| 1339 | { |
| 1340 | const QQmlMetaTypeDataPtr data; |
| 1341 | QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1342 | return (type && type->listId == metaType) ? QQmlType(type) : QQmlType(); |
| 1343 | } |
| 1344 | |
| 1345 | /*! |
| 1346 | Returns the type (if any) that corresponds to the given \a url in the set of |
| 1347 | composite types added through file imports. |
| 1348 | |
| 1349 | Returns null if no such type is registered. |
| 1350 | */ |
| 1351 | QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl) |
| 1352 | { |
| 1353 | const QUrl url = QQmlTypeLoader::normalize(unNormalizedUrl); |
| 1354 | const QQmlMetaTypeDataPtr data; |
| 1355 | |
| 1356 | QQmlType type(data->urlToType.value(key: url)); |
| 1357 | |
| 1358 | if (type.sourceUrl() == url) |
| 1359 | return type; |
| 1360 | else |
| 1361 | return QQmlType(); |
| 1362 | } |
| 1363 | |
| 1364 | QQmlType QQmlMetaType::fetchOrCreateInlineComponentTypeForUrl(const QUrl &url) |
| 1365 | { |
| 1366 | QQmlMetaTypeDataPtr data; |
| 1367 | const auto it = data->urlToType.constFind(key: url); |
| 1368 | if (it != data->urlToType.constEnd()) |
| 1369 | return QQmlType(*it); |
| 1370 | |
| 1371 | return doRegisterInlineComponentType(data, url); |
| 1372 | } |
| 1373 | |
| 1374 | /*! |
| 1375 | Returns a QQmlPropertyCache for \a obj if one is available. |
| 1376 | |
| 1377 | If \a obj is null, being deleted or contains a dynamic meta object, |
| 1378 | nullptr is returned. |
| 1379 | */ |
| 1380 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version) |
| 1381 | { |
| 1382 | if (!obj || QObjectPrivate::get(o: obj)->metaObject || QObjectPrivate::get(o: obj)->wasDeleted) |
| 1383 | return QQmlPropertyCache::ConstPtr(); |
| 1384 | return QQmlMetaType::propertyCache(metaObject: obj->metaObject(), version); |
| 1385 | } |
| 1386 | |
| 1387 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( |
| 1388 | const QMetaObject *metaObject, QTypeRevision version) |
| 1389 | { |
| 1390 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
| 1391 | return data->propertyCache(metaObject, version); |
| 1392 | } |
| 1393 | |
| 1394 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache( |
| 1395 | const QQmlType &type, QTypeRevision version) |
| 1396 | { |
| 1397 | QQmlMetaTypeDataPtr data; // not const: the cache is created on demand |
| 1398 | return data->propertyCache(type, version); |
| 1399 | } |
| 1400 | |
| 1401 | /*! |
| 1402 | * \internal |
| 1403 | * |
| 1404 | * Look up by type's baseMetaObject. |
| 1405 | */ |
| 1406 | QQmlMetaObject QQmlMetaType::rawMetaObjectForType(QMetaType metaType) |
| 1407 | { |
| 1408 | const QQmlMetaTypeDataPtr data; |
| 1409 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
| 1410 | return QQmlMetaObject(composite); |
| 1411 | |
| 1412 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1413 | return (type && type->typeId == metaType) ? type->baseMetaObject : nullptr; |
| 1414 | } |
| 1415 | |
| 1416 | /*! |
| 1417 | * \internal |
| 1418 | * |
| 1419 | * Look up by type's metaObject. |
| 1420 | */ |
| 1421 | QQmlMetaObject QQmlMetaType::metaObjectForType(QMetaType metaType) |
| 1422 | { |
| 1423 | const QQmlMetaTypeDataPtr data; |
| 1424 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
| 1425 | return QQmlMetaObject(composite); |
| 1426 | |
| 1427 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1428 | return (type && type->typeId == metaType) |
| 1429 | ? QQmlType(type).metaObject() |
| 1430 | : nullptr; |
| 1431 | } |
| 1432 | |
| 1433 | /*! |
| 1434 | * \internal |
| 1435 | * |
| 1436 | * Look up by type's metaObject and version. |
| 1437 | */ |
| 1438 | QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaType) |
| 1439 | { |
| 1440 | QQmlMetaTypeDataPtr data; |
| 1441 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
| 1442 | return composite; |
| 1443 | |
| 1444 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1445 | if (type && type->typeId == metaType) { |
| 1446 | if (const QMetaObject *mo = QQmlType(type).metaObject()) |
| 1447 | return data->propertyCache(metaObject: mo, version: type->version); |
| 1448 | } |
| 1449 | |
| 1450 | return QQmlPropertyCache::ConstPtr(); |
| 1451 | } |
| 1452 | |
| 1453 | /*! |
| 1454 | * \internal |
| 1455 | * |
| 1456 | * Look up by type's baseMetaObject and unspecified/any version. |
| 1457 | * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or |
| 1458 | * the actual type's version seems strange. The behavior has been in place for a while. |
| 1459 | */ |
| 1460 | QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType metaType) |
| 1461 | { |
| 1462 | QQmlMetaTypeDataPtr data; |
| 1463 | if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(t: metaType)) |
| 1464 | return composite; |
| 1465 | |
| 1466 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1467 | if (!type || type->typeId != metaType) |
| 1468 | return QQmlPropertyCache::ConstPtr(); |
| 1469 | |
| 1470 | const QMetaObject *metaObject = type->isValueType() |
| 1471 | ? type->metaObjectForValueType() |
| 1472 | : type->baseMetaObject; |
| 1473 | |
| 1474 | return metaObject |
| 1475 | ? data->propertyCache(metaObject, version: QTypeRevision()) |
| 1476 | : QQmlPropertyCache::ConstPtr(); |
| 1477 | } |
| 1478 | |
| 1479 | /*! |
| 1480 | * \internal |
| 1481 | * |
| 1482 | * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type |
| 1483 | * has no revisiononed attributes here. Unspecified versions are interpreted as "any". |
| 1484 | */ |
| 1485 | QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType( |
| 1486 | QMetaType metaType, QTypeRevision version) |
| 1487 | { |
| 1488 | QQmlMetaTypeDataPtr data; |
| 1489 | if (auto composite = data->findPropertyCacheInCompositeTypes(t: metaType)) |
| 1490 | return composite; |
| 1491 | |
| 1492 | const QQmlTypePrivate *typePriv = data->idToType.value(key: metaType.id()); |
| 1493 | if (!typePriv || typePriv->typeId != metaType) |
| 1494 | return QQmlPropertyCache::ConstPtr(); |
| 1495 | |
| 1496 | const QQmlType type(typePriv); |
| 1497 | if (type.containsRevisionedAttributes()) { |
| 1498 | // It can only have (revisioned) properties or methods if it has a metaobject |
| 1499 | Q_ASSERT(type.metaObject()); |
| 1500 | return data->propertyCache(type, version); |
| 1501 | } |
| 1502 | |
| 1503 | if (const QMetaObject *metaObject = type.metaObject()) |
| 1504 | return data->propertyCache(metaObject, version); |
| 1505 | |
| 1506 | return QQmlPropertyCache::ConstPtr(); |
| 1507 | } |
| 1508 | |
| 1509 | void QQmlMetaType::unregisterType(int typeIndex) |
| 1510 | { |
| 1511 | QQmlMetaTypeDataPtr data; |
| 1512 | const QQmlType type = data->types.value(i: typeIndex); |
| 1513 | if (const QQmlTypePrivate *d = type.priv()) { |
| 1514 | if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType) |
| 1515 | removeFromInlineComponents(container&: data->urlToType, reference: d); |
| 1516 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
| 1517 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
| 1518 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
| 1519 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
| 1520 | for (auto & module : data->uriToModule) |
| 1521 | module->remove(type: d); |
| 1522 | data->clearPropertyCachesForVersion(index: typeIndex); |
| 1523 | data->types[typeIndex] = QQmlType(); |
| 1524 | data->undeletableTypes.remove(value: type); |
| 1525 | } |
| 1526 | } |
| 1527 | |
| 1528 | void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, QQmlTypePrivate *type) |
| 1529 | { |
| 1530 | Q_ASSERT(type); |
| 1531 | |
| 1532 | QQmlMetaTypeDataPtr data; |
| 1533 | data->metaObjectToType.insert(key: metaobject, value: type); |
| 1534 | } |
| 1535 | |
| 1536 | static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d) |
| 1537 | { |
| 1538 | for (auto it = data->urlToType.begin(), end = data->urlToType.end(); |
| 1539 | it != end; ++it) { |
| 1540 | if (!QQmlMetaType::equalBaseUrls(aUrl: it.key(), bUrl: d->sourceUrl())) |
| 1541 | continue; |
| 1542 | |
| 1543 | const QQmlTypePrivate *icPriv = *it; |
| 1544 | if (icPriv && icPriv->count() > 1) |
| 1545 | return true; |
| 1546 | } |
| 1547 | return false; |
| 1548 | } |
| 1549 | |
| 1550 | static int doCountInternalCompositeTypeSelfReferences( |
| 1551 | QQmlMetaTypeData *data, |
| 1552 | const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) |
| 1553 | { |
| 1554 | int result = 0; |
| 1555 | auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) { |
| 1556 | if (!iface) |
| 1557 | return; |
| 1558 | |
| 1559 | const auto it = data->compositeTypes.constFind(key: iface); |
| 1560 | if (it != data->compositeTypes.constEnd() && *it == compilationUnit) |
| 1561 | ++result; |
| 1562 | }; |
| 1563 | |
| 1564 | doCheck(compilationUnit->metaType().iface()); |
| 1565 | for (auto &&inlineData: compilationUnit->inlineComponentData) |
| 1566 | doCheck(inlineData.qmlType.typeId().iface()); |
| 1567 | |
| 1568 | return result; |
| 1569 | } |
| 1570 | |
| 1571 | void QQmlMetaType::freeUnusedTypesAndCaches() |
| 1572 | { |
| 1573 | QQmlMetaTypeDataPtr data; |
| 1574 | |
| 1575 | // in case this is being called during program exit, `data` might be destructed already |
| 1576 | if (!data.isValid()) |
| 1577 | return; |
| 1578 | |
| 1579 | bool droppedAtLeastOneComposite; |
| 1580 | do { |
| 1581 | droppedAtLeastOneComposite = false; |
| 1582 | auto it = data->compositeTypes.begin(); |
| 1583 | while (it != data->compositeTypes.end()) { |
| 1584 | if ((*it)->count() <= doCountInternalCompositeTypeSelfReferences(data, compilationUnit: *it)) { |
| 1585 | it = data->compositeTypes.erase(it); |
| 1586 | droppedAtLeastOneComposite = true; |
| 1587 | } else { |
| 1588 | ++it; |
| 1589 | } |
| 1590 | } |
| 1591 | } while (droppedAtLeastOneComposite); |
| 1592 | |
| 1593 | bool deletedAtLeastOneType; |
| 1594 | do { |
| 1595 | deletedAtLeastOneType = false; |
| 1596 | QList<QQmlType>::Iterator it = data->types.begin(); |
| 1597 | while (it != data->types.end()) { |
| 1598 | const QQmlTypePrivate *d = (*it).priv(); |
| 1599 | if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) { |
| 1600 | deletedAtLeastOneType = true; |
| 1601 | |
| 1602 | if (d->regType == QQmlType::CompositeType |
| 1603 | || d->regType == QQmlType::CompositeSingletonType) { |
| 1604 | removeFromInlineComponents(container&: data->urlToType, reference: d); |
| 1605 | } |
| 1606 | removeQQmlTypePrivate(container&: data->idToType, reference: d); |
| 1607 | removeQQmlTypePrivate(container&: data->nameToType, reference: d); |
| 1608 | removeQQmlTypePrivate(container&: data->urlToType, reference: d); |
| 1609 | removeQQmlTypePrivate(container&: data->metaObjectToType, reference: d); |
| 1610 | |
| 1611 | for (auto &module : data->uriToModule) |
| 1612 | module->remove(type: d); |
| 1613 | |
| 1614 | data->clearPropertyCachesForVersion(index: d->index); |
| 1615 | *it = QQmlType(); |
| 1616 | } else { |
| 1617 | ++it; |
| 1618 | } |
| 1619 | } |
| 1620 | } while (deletedAtLeastOneType); |
| 1621 | |
| 1622 | bool deletedAtLeastOneCache; |
| 1623 | do { |
| 1624 | deletedAtLeastOneCache = false; |
| 1625 | auto it = data->propertyCaches.begin(); |
| 1626 | while (it != data->propertyCaches.end()) { |
| 1627 | if ((*it)->count() == 1) { |
| 1628 | it = data->propertyCaches.erase(it); |
| 1629 | deletedAtLeastOneCache = true; |
| 1630 | } else { |
| 1631 | ++it; |
| 1632 | } |
| 1633 | } |
| 1634 | } while (deletedAtLeastOneCache); |
| 1635 | } |
| 1636 | |
| 1637 | /*! |
| 1638 | Returns the list of registered QML type names. |
| 1639 | */ |
| 1640 | QList<QString> QQmlMetaType::qmlTypeNames() |
| 1641 | { |
| 1642 | const QQmlMetaTypeDataPtr data; |
| 1643 | |
| 1644 | QList<QString> names; |
| 1645 | names.reserve(asize: data->nameToType.size()); |
| 1646 | QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin(); |
| 1647 | while (it != data->nameToType.cend()) { |
| 1648 | QQmlType t(*it); |
| 1649 | names += t.qmlTypeName(); |
| 1650 | ++it; |
| 1651 | } |
| 1652 | |
| 1653 | return names; |
| 1654 | } |
| 1655 | |
| 1656 | /*! |
| 1657 | Returns the list of registered QML types. |
| 1658 | */ |
| 1659 | QList<QQmlType> QQmlMetaType::qmlTypes() |
| 1660 | { |
| 1661 | const QQmlMetaTypeDataPtr data; |
| 1662 | |
| 1663 | QList<QQmlType> types; |
| 1664 | for (const QQmlTypePrivate *t : data->nameToType) |
| 1665 | types.append(t: QQmlType(t)); |
| 1666 | |
| 1667 | return types; |
| 1668 | } |
| 1669 | |
| 1670 | /*! |
| 1671 | Returns the list of all registered types. |
| 1672 | */ |
| 1673 | QList<QQmlType> QQmlMetaType::qmlAllTypes() |
| 1674 | { |
| 1675 | const QQmlMetaTypeDataPtr data; |
| 1676 | return data->types; |
| 1677 | } |
| 1678 | |
| 1679 | /*! |
| 1680 | Returns the list of registered QML singleton types. |
| 1681 | */ |
| 1682 | QList<QQmlType> QQmlMetaType::qmlSingletonTypes() |
| 1683 | { |
| 1684 | const QQmlMetaTypeDataPtr data; |
| 1685 | |
| 1686 | QList<QQmlType> retn; |
| 1687 | for (const auto t : std::as_const(t: data->nameToType)) { |
| 1688 | QQmlType type(t); |
| 1689 | if (type.isSingleton()) |
| 1690 | retn.append(t: type); |
| 1691 | } |
| 1692 | return retn; |
| 1693 | } |
| 1694 | |
| 1695 | static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit) |
| 1696 | { |
| 1697 | quint32 numTypedFunctions = 0; |
| 1698 | for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions; |
| 1699 | function; ++function) { |
| 1700 | if (function->functionPtr) |
| 1701 | ++numTypedFunctions; |
| 1702 | else |
| 1703 | return false; |
| 1704 | } |
| 1705 | return numTypedFunctions == unit->qmlData->functionTableSize; |
| 1706 | } |
| 1707 | |
| 1708 | const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit( |
| 1709 | const QUrl &uri, QQmlMetaType::CacheMode mode, CachedUnitLookupError *status) |
| 1710 | { |
| 1711 | Q_ASSERT(mode != RejectAll); |
| 1712 | const QQmlMetaTypeDataPtr data; |
| 1713 | |
| 1714 | for (const auto lookup : std::as_const(t: data->lookupCachedQmlUnit)) { |
| 1715 | if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) { |
| 1716 | QString error; |
| 1717 | if (!unit->qmlData->verifyHeader(expectedSourceTimeStamp: QDateTime(), errorString: &error)) { |
| 1718 | qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error; |
| 1719 | if (status) |
| 1720 | *status = CachedUnitLookupError::VersionMismatch; |
| 1721 | return nullptr; |
| 1722 | } |
| 1723 | |
| 1724 | if (mode == RequireFullyTyped && !isFullyTyped(unit)) { |
| 1725 | qCDebug(DBG_DISK_CACHE) |
| 1726 | << "Error loading pre-compiled file " << uri |
| 1727 | << ": compilation unit contains functions not compiled to native code." ; |
| 1728 | if (status) |
| 1729 | *status = CachedUnitLookupError::NotFullyTyped; |
| 1730 | return nullptr; |
| 1731 | } |
| 1732 | |
| 1733 | if (status) |
| 1734 | *status = CachedUnitLookupError::NoError; |
| 1735 | return unit; |
| 1736 | } |
| 1737 | } |
| 1738 | |
| 1739 | if (status) |
| 1740 | *status = CachedUnitLookupError::NoUnitFound; |
| 1741 | |
| 1742 | return nullptr; |
| 1743 | } |
| 1744 | |
| 1745 | void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
| 1746 | { |
| 1747 | QQmlMetaTypeDataPtr data; |
| 1748 | data->lookupCachedQmlUnit.prepend(t: handler); |
| 1749 | } |
| 1750 | |
| 1751 | void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler) |
| 1752 | { |
| 1753 | QQmlMetaTypeDataPtr data; |
| 1754 | data->lookupCachedQmlUnit.removeAll(t: handler); |
| 1755 | } |
| 1756 | |
| 1757 | /*! |
| 1758 | Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object. |
| 1759 | */ |
| 1760 | QString QQmlMetaType::prettyTypeName(const QObject *object) |
| 1761 | { |
| 1762 | QString typeName; |
| 1763 | |
| 1764 | if (!object) |
| 1765 | return typeName; |
| 1766 | |
| 1767 | QQmlType type = QQmlMetaType::qmlType(metaObject: object->metaObject()); |
| 1768 | if (type.isValid()) { |
| 1769 | typeName = type.qmlTypeName(); |
| 1770 | const int lastSlash = typeName.lastIndexOf(c: QLatin1Char('/')); |
| 1771 | if (lastSlash != -1) |
| 1772 | typeName = typeName.mid(position: lastSlash + 1); |
| 1773 | } |
| 1774 | |
| 1775 | if (typeName.isEmpty()) { |
| 1776 | typeName = QString::fromUtf8(utf8: object->metaObject()->className()); |
| 1777 | int marker = typeName.indexOf(s: QLatin1String("_QMLTYPE_" )); |
| 1778 | if (marker != -1) |
| 1779 | typeName = typeName.left(n: marker); |
| 1780 | |
| 1781 | marker = typeName.indexOf(s: QLatin1String("_QML_" )); |
| 1782 | if (marker != -1) { |
| 1783 | typeName = QStringView{typeName}.left(n: marker) + QLatin1Char('*'); |
| 1784 | type = QQmlMetaType::qmlType(metaType: QMetaType::fromName(name: typeName.toUtf8())); |
| 1785 | if (type.isValid()) { |
| 1786 | QString qmlTypeName = type.qmlTypeName(); |
| 1787 | const int lastSlash = qmlTypeName.lastIndexOf(c: QLatin1Char('/')); |
| 1788 | if (lastSlash != -1) |
| 1789 | qmlTypeName = qmlTypeName.mid(position: lastSlash + 1); |
| 1790 | if (!qmlTypeName.isEmpty()) |
| 1791 | typeName = qmlTypeName; |
| 1792 | } |
| 1793 | } |
| 1794 | } |
| 1795 | |
| 1796 | return typeName; |
| 1797 | } |
| 1798 | |
| 1799 | QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo, |
| 1800 | const QMetaObject *baseMetaObject, |
| 1801 | QMetaObject *lastMetaObject) |
| 1802 | { |
| 1803 | QList<QQmlProxyMetaObject::ProxyData> metaObjects; |
| 1804 | mo = mo->d.superdata; |
| 1805 | |
| 1806 | if (!mo) |
| 1807 | return metaObjects; |
| 1808 | |
| 1809 | auto createProxyMetaObject = [&](QQmlTypePrivate *This, |
| 1810 | const QMetaObject *superdataBaseMetaObject, |
| 1811 | const QMetaObject *extMetaObject, |
| 1812 | QObject *(*extFunc)(QObject *)) { |
| 1813 | if (!extMetaObject) |
| 1814 | return; |
| 1815 | |
| 1816 | QMetaObjectBuilder builder; |
| 1817 | clone(builder, mo: extMetaObject, ignoreStart: superdataBaseMetaObject, ignoreEnd: baseMetaObject, |
| 1818 | policy: extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly); |
| 1819 | QMetaObject *mmo = builder.toMetaObject(); |
| 1820 | mmo->d.superdata = baseMetaObject; |
| 1821 | if (!metaObjects.isEmpty()) |
| 1822 | metaObjects.constLast().metaObject->d.superdata = mmo; |
| 1823 | else if (lastMetaObject) |
| 1824 | lastMetaObject->d.superdata = mmo; |
| 1825 | QQmlProxyMetaObject::ProxyData data = { .metaObject: mmo, .createFunc: extFunc, .propertyOffset: 0, .methodOffset: 0 }; |
| 1826 | metaObjects << data; |
| 1827 | registerMetaObjectForType(metaobject: mmo, type: This); |
| 1828 | }; |
| 1829 | |
| 1830 | for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) { |
| 1831 | // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*. |
| 1832 | // This algorithm only accounts for the most recently inserted one. That's pretty |
| 1833 | // random. However, the availability of types depends on what documents you have |
| 1834 | // loaded before. Just adding all possible extensions would also be pretty random. |
| 1835 | // The right way to do this would be to take the relations between the QML modules |
| 1836 | // into account. For this we would need proper module dependency information. |
| 1837 | if (QQmlTypePrivate *t = data->metaObjectToType.value(key: mo)) { |
| 1838 | if (t->regType == QQmlType::CppType) { |
| 1839 | createProxyMetaObject( |
| 1840 | t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject, |
| 1841 | t->extraData.cppTypeData->extFunc); |
| 1842 | } else if (t->regType == QQmlType::SingletonType) { |
| 1843 | createProxyMetaObject( |
| 1844 | t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject, |
| 1845 | t->extraData.singletonTypeData->extFunc); |
| 1846 | } |
| 1847 | } |
| 1848 | }; |
| 1849 | |
| 1850 | return metaObjects; |
| 1851 | } |
| 1852 | |
| 1853 | static bool isInternalType(int idx) |
| 1854 | { |
| 1855 | // Qt internal types |
| 1856 | switch (idx) { |
| 1857 | case QMetaType::UnknownType: |
| 1858 | case QMetaType::QStringList: |
| 1859 | case QMetaType::QObjectStar: |
| 1860 | case QMetaType::VoidStar: |
| 1861 | case QMetaType::Nullptr: |
| 1862 | case QMetaType::QVariant: |
| 1863 | case QMetaType::QLocale: |
| 1864 | case QMetaType::QImage: // scarce type, keep as QVariant |
| 1865 | case QMetaType::QPixmap: // scarce type, keep as QVariant |
| 1866 | return true; |
| 1867 | default: |
| 1868 | return false; |
| 1869 | } |
| 1870 | } |
| 1871 | |
| 1872 | bool QQmlMetaType::isValueType(QMetaType type) |
| 1873 | { |
| 1874 | if (type.flags().testFlag(flag: QMetaType::PointerToQObject)) |
| 1875 | return false; |
| 1876 | |
| 1877 | if (!type.isValid() || isInternalType(idx: type.id())) |
| 1878 | return false; |
| 1879 | |
| 1880 | return valueType(metaType: type) != nullptr; |
| 1881 | } |
| 1882 | |
| 1883 | const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType) |
| 1884 | { |
| 1885 | switch (metaType.id()) { |
| 1886 | case QMetaType::QPoint: |
| 1887 | return &QQmlPointValueType::staticMetaObject; |
| 1888 | case QMetaType::QPointF: |
| 1889 | return &QQmlPointFValueType::staticMetaObject; |
| 1890 | case QMetaType::QSize: |
| 1891 | return &QQmlSizeValueType::staticMetaObject; |
| 1892 | case QMetaType::QSizeF: |
| 1893 | return &QQmlSizeFValueType::staticMetaObject; |
| 1894 | case QMetaType::QRect: |
| 1895 | return &QQmlRectValueType::staticMetaObject; |
| 1896 | case QMetaType::QRectF: |
| 1897 | return &QQmlRectFValueType::staticMetaObject; |
| 1898 | #if QT_CONFIG(easingcurve) |
| 1899 | case QMetaType::QEasingCurve: |
| 1900 | return &QQmlEasingValueType::staticMetaObject; |
| 1901 | #endif |
| 1902 | default: |
| 1903 | break; |
| 1904 | } |
| 1905 | |
| 1906 | // It doesn't have to be a gadget for a QML type to exist, but we don't want to |
| 1907 | // call QObject pointers value types. Explicitly registered types also override |
| 1908 | // the implicit use of gadgets. |
| 1909 | if (!(metaType.flags() & QMetaType::PointerToQObject)) { |
| 1910 | const QQmlMetaTypeDataPtr data; |
| 1911 | const QQmlTypePrivate *type = data->idToType.value(key: metaType.id()); |
| 1912 | if (type && type->regType == QQmlType::CppType && type->typeId == metaType) { |
| 1913 | if (const QMetaObject *mo = type->metaObjectForValueType()) |
| 1914 | return mo; |
| 1915 | } |
| 1916 | } |
| 1917 | |
| 1918 | // If it _is_ a gadget, we can just use it. |
| 1919 | if (metaType.flags() & QMetaType::IsGadget) |
| 1920 | return metaType.metaObject(); |
| 1921 | |
| 1922 | return nullptr; |
| 1923 | } |
| 1924 | |
| 1925 | QQmlValueType *QQmlMetaType::valueType(QMetaType type) |
| 1926 | { |
| 1927 | QQmlMetaTypeDataPtr data; |
| 1928 | |
| 1929 | const auto it = data->metaTypeToValueType.constFind(key: type.id()); |
| 1930 | if (it != data->metaTypeToValueType.constEnd()) |
| 1931 | return *it; |
| 1932 | |
| 1933 | if (const QMetaObject *mo = metaObjectForValueType(metaType: type)) |
| 1934 | return *data->metaTypeToValueType.insert(key: type.id(), value: new QQmlValueType(type, mo)); |
| 1935 | return *data->metaTypeToValueType.insert(key: type.id(), value: nullptr); |
| 1936 | } |
| 1937 | |
| 1938 | QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t) |
| 1939 | { |
| 1940 | const QQmlMetaTypeDataPtr data; |
| 1941 | return data->findPropertyCacheInCompositeTypes(t); |
| 1942 | } |
| 1943 | |
| 1944 | void QQmlMetaType::registerInternalCompositeType( |
| 1945 | const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) |
| 1946 | { |
| 1947 | QQmlMetaTypeDataPtr data; |
| 1948 | |
| 1949 | auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) { |
| 1950 | Q_ASSERT(iface); |
| 1951 | Q_ASSERT(compilationUnit); |
| 1952 | |
| 1953 | // We can't assert on anything else here. We may get a completely new type as exposed |
| 1954 | // by the qmldiskcache test that changes a QML file in place during the execution |
| 1955 | // of the test. |
| 1956 | data->compositeTypes.insert(key: iface, value: compilationUnit); |
| 1957 | }; |
| 1958 | |
| 1959 | doInsert(compilationUnit->metaType().iface()); |
| 1960 | for (auto &&inlineData: compilationUnit->inlineComponentData) |
| 1961 | doInsert(inlineData.qmlType.typeId().iface()); |
| 1962 | } |
| 1963 | |
| 1964 | void QQmlMetaType::unregisterInternalCompositeType( |
| 1965 | const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) |
| 1966 | { |
| 1967 | QQmlMetaTypeDataPtr data; |
| 1968 | |
| 1969 | auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) { |
| 1970 | if (!iface) |
| 1971 | return; |
| 1972 | |
| 1973 | const auto it = data->compositeTypes.constFind(key: iface); |
| 1974 | if (it != data->compositeTypes.constEnd() && *it == compilationUnit) |
| 1975 | data->compositeTypes.erase(it); |
| 1976 | }; |
| 1977 | |
| 1978 | doRemove(compilationUnit->metaType().iface()); |
| 1979 | for (auto &&inlineData: compilationUnit->inlineComponentData) |
| 1980 | doRemove(inlineData.qmlType.typeId().iface()); |
| 1981 | } |
| 1982 | |
| 1983 | int QQmlMetaType::countInternalCompositeTypeSelfReferences( |
| 1984 | const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit) |
| 1985 | { |
| 1986 | QQmlMetaTypeDataPtr data; |
| 1987 | return doCountInternalCompositeTypeSelfReferences(data, compilationUnit); |
| 1988 | } |
| 1989 | |
| 1990 | QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit( |
| 1991 | QMetaType type) |
| 1992 | { |
| 1993 | const QQmlMetaTypeDataPtr data; |
| 1994 | return data->compositeTypes.value(key: type.iface()); |
| 1995 | } |
| 1996 | |
| 1997 | QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit( |
| 1998 | const QUrl &url) |
| 1999 | { |
| 2000 | const QUrl normalized = QQmlTypeLoader::normalize(unNormalizedUrl: url); |
| 2001 | QQmlMetaTypeDataPtr data; |
| 2002 | |
| 2003 | auto found = data->urlToType.constFind(key: normalized); |
| 2004 | if (found == data->urlToType.constEnd()) |
| 2005 | return QQmlRefPointer<QV4::CompiledData::CompilationUnit>(); |
| 2006 | |
| 2007 | const auto composite = data->compositeTypes.constFind(key: found.value()->typeId.iface()); |
| 2008 | return composite == data->compositeTypes.constEnd() |
| 2009 | ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>() |
| 2010 | : composite.value(); |
| 2011 | } |
| 2012 | |
| 2013 | QT_END_NAMESPACE |
| 2014 | |