Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlmetatype.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
6
7#include <private/qqmlextensionplugin_p.h>
8#include <private/qqmlmetatypedata_p.h>
9#include <private/qqmlpropertycachecreator_p.h>
10#include <private/qqmlscriptblob_p.h>
11#include <private/qqmlscriptdata_p.h>
12#include <private/qqmltype_p_p.h>
13#include <private/qqmltypemodule_p.h>
14#include <private/qqmlvaluetype_p.h>
15
16#include <QtCore/qcoreapplication.h>
17#include <QtCore/qmutex.h>
18#include <QtCore/qloggingcategory.h>
19
20Q_STATIC_LOGGING_CATEGORY(lcTypeRegistration, "qt.qml.typeregistration")
21
22QT_BEGIN_NAMESPACE
23
24/*!
25 \class QQmlMetaType
26 \inmodule QtQml
27 \internal
28*/
29
30struct LockedData : private QQmlMetaTypeData
31{
32 friend class QQmlMetaTypeDataPtr;
33};
34
35Q_GLOBAL_STATIC(LockedData, metaTypeData)
36Q_GLOBAL_STATIC(QRecursiveMutex, metaTypeDataLock)
37
38struct ModuleUri : public QString
39{
40 ModuleUri(const QString &string) : QString(string) {}
41 ModuleUri(const std::unique_ptr<QQmlTypeModule> &module) : QString(module->module()) {}
42};
43
45{
47public:
49 ~QQmlMetaTypeDataPtr() = default;
50
51 QQmlMetaTypeData &operator*() { return *data; }
52 QQmlMetaTypeData *operator->() { return data; }
54
55 const QQmlMetaTypeData &operator*() const { return *data; }
56 const QQmlMetaTypeData *operator->() const { return data; }
57 operator const QQmlMetaTypeData *() const { return data; }
58
59 bool isValid() const { return data != nullptr; }
60
61private:
63 LockedData *data = nullptr;
64};
65
66static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data,
67 const QQmlPrivate::RegisterInterface &type)
68{
69 auto *d = new QQmlTypePrivate(QQmlType::InterfaceType);
70 d->extraData.interfaceTypeData = type.iid;
71 d->typeId = type.typeId;
72 d->listId = type.listId;
73 d->module = QString::fromUtf8(type.uri);
74 d->version = type.version;
75 data->registerType(d);
76 return d;
77}
78
80 QQmlMetaTypeData *data, const QString &elementName,
81 const QQmlPrivate::RegisterSingletonType &type,
82 const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
83{
84 auto *d = new QQmlTypePrivate(QQmlType::SingletonType);
85 data->registerType(d);
86
87 d->setName(QString::fromUtf8(type.uri), elementName);
88 d->version = type.version;
89
90 if (type.qObjectApi) {
91 d->baseMetaObject = type.instanceMetaObject;
92 d->typeId = type.typeId;
93 d->revision = type.revision;
94 }
95
96 d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
97 d->extraData.singletonTypeData->extFunc = type.extensionObjectCreate;
98 d->extraData.singletonTypeData->extMetaObject = type.extensionMetaObject;
99
100 return d;
101}
102
103static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
104 const QQmlPrivate::RegisterType &type)
105{
106 QQmlTypePrivate *d = new QQmlTypePrivate(QQmlType::CppType);
107 data->registerType(d);
108 d->setName(QString::fromUtf8(type.uri), elementName);
109
110 d->version = type.version;
111 d->revision = type.revision;
112 d->typeId = type.typeId;
113 d->listId = type.listId;
114 d->extraData.cppTypeData->allocationSize = type.objectSize;
115 d->extraData.cppTypeData->userdata = type.userdata;
116 d->extraData.cppTypeData->newFunc = type.create;
117 d->extraData.cppTypeData->noCreationReason = type.noCreationReason;
118 d->extraData.cppTypeData->createValueTypeFunc = type.createValueType;
119 d->baseMetaObject = type.metaObject;
120 d->extraData.cppTypeData->attachedPropertiesFunc = type.attachedPropertiesFunction;
121 d->extraData.cppTypeData->attachedPropertiesType = type.attachedPropertiesMetaObject;
122 d->extraData.cppTypeData->parserStatusCast = type.parserStatusCast;
123 d->extraData.cppTypeData->propertyValueSourceCast = type.valueSourceCast;
124 d->extraData.cppTypeData->propertyValueInterceptorCast = type.valueInterceptorCast;
125 d->extraData.cppTypeData->finalizerCast = type.has(QQmlPrivate::RegisterType::FinalizerCast)
126 ? type.finalizerCast
127 : -1;
128 d->extraData.cppTypeData->extFunc = type.extensionObjectCreate;
129 d->extraData.cppTypeData->customParser = reinterpret_cast<QQmlCustomParser *>(type.customParser);
130 d->extraData.cppTypeData->registerEnumClassesUnscoped = true;
131 d->extraData.cppTypeData->registerEnumsFromRelatedTypes = true;
132 d->extraData.cppTypeData->constructValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
133 && type.creationMethod != QQmlPrivate::ValueTypeCreationMethod::None;
134 d->extraData.cppTypeData->populateValueType = type.has(QQmlPrivate::RegisterType::CreationMethod)
135 && type.creationMethod == QQmlPrivate::ValueTypeCreationMethod::Structured;
136
137 if (type.extensionMetaObject)
138 d->extraData.cppTypeData->extMetaObject = type.extensionMetaObject;
139
140 // Check if the user wants only scoped enum classes
141 if (d->baseMetaObject) {
142 auto indexOfUnscoped = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
143 if (indexOfUnscoped != -1
144 && qstrcmp(d->baseMetaObject->classInfo(indexOfUnscoped).value(), "false") == 0) {
145 d->extraData.cppTypeData->registerEnumClassesUnscoped = false;
146 }
147
148 auto indexOfRelated = d->baseMetaObject->indexOfClassInfo("RegisterEnumsFromRelatedTypes");
149 if (indexOfRelated != -1
150 && qstrcmp(d->baseMetaObject->classInfo(indexOfRelated).value(), "false") == 0) {
151 d->extraData.cppTypeData->registerEnumsFromRelatedTypes = false;
152 }
153 }
154
155 return d;
156}
157
159 QQmlMetaTypeData *data, const QUrl &url, QQmlTypePrivate *priv, const QByteArray &className)
160{
161 Q_ASSERT(!className.isEmpty());
162 QByteArray ptr = className + '*';
163 QByteArray lst = "QQmlListProperty<" + className + '>';
164
165 QQmlMetaTypeData::CompositeMetaTypes &types = data->compositeMetaTypes[url];
166 if (types.type) {
167 Q_ASSERT(types.listType);
168
169 QMetaType::unregisterMetaType(QMetaType(types.type));
170 QMetaType::unregisterMetaType(QMetaType(types.listType));
171
172 types.type->name = std::move(ptr);
173 types.type->QMetaTypeInterface::name = types.type->name.constData();
174 types.listType->name = std::move(lst);
175 types.listType->QMetaTypeInterface::name = types.listType->name.constData();
176 } else {
177 types.type = new QQmlMetaTypeInterface(std::move(ptr));
178 types.listType = new QQmlListMetaTypeInterface(std::move(lst), types.type);
179 }
180
181 QMetaType ptr_type(types.type);
182 QMetaType lst_type(types.listType);
183
184 // Retrieve the IDs once, so that the types are added to QMetaType's custom type registry.
185 ptr_type.id();
186 lst_type.id();
187
188 priv->typeId = ptr_type;
189 priv->listId = lst_type;
190}
191
192static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &elementName,
193 const QQmlPrivate::RegisterCompositeType &type)
194{
195 auto *d = new QQmlTypePrivate(QQmlType::CompositeType);
196 data->registerType(d);
197 d->setName(QString::fromUtf8(type.uri), elementName);
198 d->version = type.version;
199
200 const QUrl normalized = QQmlMetaType::normalizedUrl(type.url);
201 d->extraData.compositeTypeData = normalized;
202 addQQmlMetaTypeInterfaces(
203 data, normalized, d, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(normalized));
204 return d;
205}
206
208 QQmlMetaTypeData *data, const QString &elementName,
209 const QQmlPrivate::RegisterCompositeSingletonType &type,
210 const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
211{
212 auto *d = new QQmlTypePrivate(QQmlType::CompositeSingletonType);
213 data->registerType(d);
214 d->setName(QString::fromUtf8(type.uri), elementName);
215
216 d->version = type.version;
217
218 d->extraData.singletonTypeData->singletonInstanceInfo = siinfo;
219 const QUrl &url = siinfo->url;
220 addQQmlMetaTypeInterfaces(
221 data, url, d, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url));
222 return d;
223}
224
225void QQmlMetaType::clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
226 const QMetaObject *ignoreStart, const QMetaObject *ignoreEnd,
227 QQmlMetaType::ClonePolicy policy)
228{
229 // Set classname
230 builder.setClassName(mo->className());
231
232 // Clone Q_CLASSINFO
233 for (int ii = mo->classInfoOffset(); ii < mo->classInfoCount(); ++ii) {
234 QMetaClassInfo info = mo->classInfo(ii);
235
236 int otherIndex = ignoreEnd->indexOfClassInfo(info.name());
237 if (otherIndex >= ignoreStart->classInfoOffset() + ignoreStart->classInfoCount()) {
238 // Skip
239 } else {
240 builder.addClassInfo(info.name(), info.value());
241 }
242 }
243
244 if (policy != QQmlMetaType::CloneEnumsOnly) {
245 // Clone Q_METHODS - do this first to avoid duplicating the notify signals.
246 for (int ii = mo->methodOffset(); ii < mo->methodCount(); ++ii) {
247 QMetaMethod method = mo->method(ii);
248
249 // More complex - need to search name
250 QByteArray name = method.name();
251
252 bool found = false;
253
254 for (int ii = ignoreStart->methodOffset() + ignoreStart->methodCount();
255 !found && ii < ignoreEnd->methodOffset() + ignoreEnd->methodCount(); ++ii) {
256
257 QMetaMethod other = ignoreEnd->method(ii);
258
259 found = name == other.name();
260 }
261
262 QMetaMethodBuilder m = builder.addMethod(method);
263 if (found) // SKIP
264 m.setAccess(QMetaMethod::Private);
265 }
266
267 // Clone Q_PROPERTY
268 for (int ii = mo->propertyOffset(); ii < mo->propertyCount(); ++ii) {
269 QMetaProperty property = mo->property(ii);
270
271 int otherIndex = ignoreEnd->indexOfProperty(property.name());
272 if (otherIndex >= ignoreStart->propertyOffset() + ignoreStart->propertyCount()) {
273 builder.addProperty(QByteArray("__qml_ignore__") + property.name(),
274 QByteArray("void"));
275 // Skip
276 } else {
277 builder.addProperty(property);
278 }
279 }
280 }
281
282 // Clone enums registered with the metatype system
283 for (int ii = mo->enumeratorOffset(); ii < mo->enumeratorCount(); ++ii) {
284 QMetaEnum enumerator = mo->enumerator(ii);
285
286 int otherIndex = ignoreEnd->indexOfEnumerator(enumerator.name());
287 if (otherIndex >= ignoreStart->enumeratorOffset() + ignoreStart->enumeratorCount()) {
288 // Skip
289 } else {
290 builder.addEnumerator(enumerator);
291 }
292 }
293}
294
295void QQmlMetaType::qmlInsertModuleRegistration(const QString &uri, void (*registerFunction)())
296{
297 QQmlMetaTypeDataPtr data;
298 if (data->moduleTypeRegistrationFunctions.contains(uri))
299 qFatal("Cannot add multiple registrations for %s", qPrintable(uri));
300 else
301 data->moduleTypeRegistrationFunctions.insert(uri, registerFunction);
302}
303
304void QQmlMetaType::qmlRemoveModuleRegistration(const QString &uri)
305{
306 QQmlMetaTypeDataPtr data;
307
308 if (!data.isValid())
309 return; // shutdown/deletion race. Not a problem.
310
311 if (!data->moduleTypeRegistrationFunctions.remove(uri))
312 qFatal("Cannot remove multiple registrations for %s", qPrintable(uri));
313}
314
315bool QQmlMetaType::qmlRegisterModuleTypes(const QString &uri)
316{
317 QQmlMetaTypeDataPtr data;
318 return data->registerModuleTypes(uri);
319}
320
321void QQmlMetaType::clearTypeRegistrations()
322{
323 //Only cleans global static, assumed no running engine
324 QQmlMetaTypeDataPtr data;
325
326 data->uriToModule.clear();
327 data->types.clear();
328 data->idToType.clear();
329 data->nameToType.clear();
330 data->urlToType.clear();
331 data->speculativeInlineComponentTypes.clear();
332 data->metaObjectToType.clear();
333 data->undeletableTypes.clear();
334 data->propertyCaches.clear();
335
336 qDeleteAll(data->metaTypeToValueType);
337 data->metaTypeToValueType.clear();
338
339 data->moduleImports.clear();
340
341 data->clearCompositeTypes();
342
343 qDeleteAll(data->metaTypeToValueType);
344 data->metaTypeToValueType.clear();
345
346 data->clearCompositeMetaTypes();
347}
348
349void QQmlMetaType::registerTypeAlias(int typeIndex, const QString &name)
350{
351 QQmlMetaTypeDataPtr data;
352 const QQmlType type = data->types.value(typeIndex).type;
353 const QQmlTypePrivate *priv = type.priv();
354 data->nameToType.insert(name, priv);
355}
356
357int QQmlMetaType::registerAutoParentFunction(const QQmlPrivate::RegisterAutoParent &function)
358{
359 if (function.structVersion > 1)
360 qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
361
362 QQmlMetaTypeDataPtr data;
363
364 data->parentFunctions.append(function.function);
365
366 return data->parentFunctions.size() - 1;
367}
368
369void QQmlMetaType::unregisterAutoParentFunction(const QQmlPrivate::AutoParentFunction &function)
370{
371 QQmlMetaTypeDataPtr data;
372 data->parentFunctions.removeOne(function);
373}
374
375QQmlType QQmlMetaType::registerInterface(const QQmlPrivate::RegisterInterface &type)
376{
377 if (type.structVersion > 1)
378 qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
379
380 QQmlMetaTypeDataPtr data;
381 QQmlTypePrivate *priv = createQQmlType(data, type);
382 Q_ASSERT(priv);
383
384
385 data->idToType.insert(priv->typeId.id(), priv);
386 data->idToType.insert(priv->listId.id(), priv);
387
388 return QQmlType(priv);
389}
390
391static 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"
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(0).isLower() && (flags & QMetaType::PointerToQObject)) {
414 QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"; type names must begin with an uppercase letter"));
415 data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType), typeName));
416 return false;
417 }
418
419 if (typeName.at(0).isUpper()
420 && (flags & (QMetaType::IsGadget | QMetaType::PointerToGadget))) {
421 qCWarning(lcTypeRegistration).noquote()
422 << QCoreApplication::translate(
423 "qmlRegisterType",
424 "Invalid QML %1 name \"%2\"; "
425 "value type names should begin with a lowercase letter")
426 .arg(registrationTypeString(typeType), 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(ii).isLetterOrNumber() || typeName.at(ii) == u'_')) {
435 QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\""));
436 data->recordTypeRegFailure(failure.arg(registrationTypeString(typeType), typeName));
437 return false;
438 }
439 }
440 }
441
442 if (uri && !typeName.isEmpty()) {
443 QString nameSpace = QString::fromUtf8(uri);
444 QQmlTypeModule *qqtm = data->findTypeModule(nameSpace, version);
445 if (qqtm && qqtm->lockLevel() != QQmlTypeModule::LockLevel::Open) {
446 QString failure(QCoreApplication::translate(
447 "qmlRegisterType",
448 "Cannot install %1 '%2' into protected module '%3' version '%4'"));
449 data->recordTypeRegFailure(failure
450 .arg(registrationTypeString(typeType), typeName, nameSpace)
451 .arg(version.majorVersion()));
452 return false;
453 }
454 }
455
456 return true;
457}
458
459// NOTE: caller must hold a QMutexLocker on "data"
461 const QHashedString &uri, QTypeRevision version, QQmlMetaTypeData *data)
462{
463 if (QQmlTypeModule *module = data->findTypeModule(uri, version))
464 return module;
465 return data->addTypeModule(std::make_unique<QQmlTypeModule>(uri, version.majorVersion()));
466}
467
468// NOTE: caller must hold a QMutexLocker on "data"
469static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data)
470{
471 Q_ASSERT(type);
472
473 if (!type->elementName.isEmpty())
474 data->nameToType.insert(type->elementName, type);
475
476 if (type->baseMetaObject)
477 data->metaObjectToType.insert(type->baseMetaObject, type);
478
479 if (type->regType == QQmlType::SequentialContainerType) {
480 if (type->listId.isValid())
481 data->idToType.insert(type->listId.id(), type);
482 } else {
483 if (type->typeId.isValid())
484 data->idToType.insert(type->typeId.id(), type);
485
486 if (type->listId.flags().testFlag(QMetaType::IsQmlList))
487 data->idToType.insert(type->listId.id(), type);
488 }
489
490 if (!type->module.isEmpty()) {
491 const QHashedString &mod = type->module;
492
493 QQmlTypeModule *module = getTypeModule(mod, type->version, data);
494 Q_ASSERT(module);
495 module->add(type);
496 }
497}
498
499QQmlType QQmlMetaType::registerType(const QQmlPrivate::RegisterType &type)
500{
501 if (type.structVersion > int(QQmlPrivate::RegisterType::CurrentVersion))
502 qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
503
504 QQmlMetaTypeDataPtr data;
505
506 QString elementName = QString::fromUtf8(type.elementName);
507 if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName, type.version,
508 QMetaType(type.typeId).flags())) {
509 return QQmlType();
510 }
511
512 QQmlTypePrivate *priv = createQQmlType(data, elementName, type);
513 addTypeToData(priv, data);
514
515 return QQmlType(priv);
516}
517
518QQmlType QQmlMetaType::registerSingletonType(
519 const QQmlPrivate::RegisterSingletonType &type,
520 const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
521{
522 if (type.structVersion > 1)
523 qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
524
525 QQmlMetaTypeDataPtr data;
526
527 QString typeName = QString::fromUtf8(type.typeName);
528 if (!checkRegistration(QQmlType::SingletonType, data, type.uri, typeName, type.version,
529 QMetaType(type.typeId).flags())) {
530 return QQmlType();
531 }
532
533 QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
534
535 addTypeToData(priv, data);
536
537 return QQmlType(priv);
538}
539
540QQmlType QQmlMetaType::registerCompositeSingletonType(
541 const QQmlPrivate::RegisterCompositeSingletonType &type,
542 const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
543{
544 if (type.structVersion > 1)
545 qFatal("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(type.typeName);
551 if (!checkRegistration(
552 QQmlType::CompositeSingletonType, data, type.uri, typeName, type.version, {})) {
553 return QQmlType();
554 }
555
556 QQmlTypePrivate *priv = createQQmlType(data, typeName, type, siinfo);
557 addTypeToData(priv, data);
558
559 data->urlToType.insert(siinfo->url, priv);
560
561 return QQmlType(priv);
562}
563
564QQmlType QQmlMetaType::registerCompositeType(const QQmlPrivate::RegisterCompositeType &type)
565{
566 if (type.structVersion > 1)
567 qFatal("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(type.typeName);
573 if (!checkRegistration(QQmlType::CompositeType, data, type.uri, typeName, type.version, {}))
574 return QQmlType();
575
576 QQmlTypePrivate *priv = createQQmlType(data, typeName, type);
577 addTypeToData(priv, data);
578
579 data->urlToType.insert(QQmlMetaType::normalizedUrl(type.url), priv);
580
581 return QQmlType(priv);
582}
583
601
602
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(registrationType, data, nullptr, typeName, version, {})) {
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, QQmlPropertyCacheCreatorBase::createClassNameTypeByUrl(url));
657
658 priv->setName(QString(), 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(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(u'\n'));
692 errors->prepend(error);
693 } else {
694 qWarning("%s", failures.join(u'\n').toLatin1().constData());
695 }
696 return QQmlType();
697}
698
699QQmlType QQmlMetaType::findCompositeType(
700 const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
701 CompositeTypeLookupMode mode)
702{
703 const QUrl normalized = QQmlMetaType::normalizedUrl(url);
704 QQmlMetaTypeDataPtr data;
705
706 bool urlExists = true;
707 auto found = data->urlToType.constFind(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 [begin, end]
715 = std::as_const(data->compositeTypes).equal_range(found.value()->typeId.iface());
716 if (begin == end)
717 return QQmlType(*found);
718 for (auto it = begin; it != end; ++it) {
719 if (it.value() == compilationUnit)
720 return QQmlType(*found);
721 }
722 }
723
724 const QQmlType type = createTypeForUrl(
725 data, normalized, QHashedStringRef(), mode, nullptr, QTypeRevision());
726
727 if (!urlExists && type.isValid())
728 data->urlToType.insert(normalized, type.priv());
729
730 return type;
731}
732
733static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url)
734{
735 QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::InlineComponentType);
736 priv->setName(QString(), url.fragment());
737
738 priv->extraData.inlineComponentTypeData = url;
739 data->registerType(priv);
740
741 addQQmlMetaTypeInterfaces(
742 data, url, priv, QQmlPropertyCacheCreatorBase::createClassNameForInlineComponent(url));
743 data->urlToType.insert(url, priv);
744
745 data->idToType.insert(priv->typeId.id(), priv);
746 data->idToType.insert(priv->listId.id(), priv);
747
748 return QQmlType(priv);
749}
750
751QQmlType QQmlMetaType::findOrCreateFactualInlineComponentType(
752 const QUrl &url, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
753{
754 QQmlMetaTypeDataPtr data;
755
756 // If there is an "unclaimed" inline component type, we can "claim" it now. Otherwise
757 // we have to create a new one.
758 const auto it = data->urlToType.constFind(url);
759 if (it == data->urlToType.constEnd())
760 return doRegisterInlineComponentType(data, url);
761
762 const auto [begin, end]
763 = std::as_const(data->compositeTypes).equal_range((*it)->typeId.iface());
764 if (begin == end) {
765 data->speculativeInlineComponentTypes.remove(url);
766 return QQmlType(*it);
767 }
768
769 for (auto jt = begin; jt != end; ++jt) {
770 if (*jt != compilationUnit)
771 continue;
772
773 data->speculativeInlineComponentTypes.remove(url);
774 return QQmlType(*it);
775 }
776
777 return doRegisterInlineComponentType(data, url);
778}
779
780int QQmlMetaType::registerUnitCacheHook(
781 const QQmlPrivate::RegisterQmlUnitCacheHook &hookRegistration)
782{
783 if (hookRegistration.structVersion > 1)
784 qFatal("qmlRegisterType(): Cannot mix incompatible QML versions.");
785
786 QQmlMetaTypeDataPtr data;
787 data->lookupCachedQmlUnit << hookRegistration.lookupCachedQmlUnit;
788 return 0;
789}
790
791QQmlType QQmlMetaType::registerSequentialContainer(
792 const QQmlPrivate::RegisterSequentialContainer &container)
793{
794 if (container.structVersion > 1)
795 qFatal("qmlRegisterSequenceContainer(): Cannot mix incompatible QML versions.");
796
797 QQmlMetaTypeDataPtr data;
798
799 if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, QString(),
800 container.version, {})) {
801 return QQmlType();
802 }
803
804 QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType);
805
806 data->registerType(priv);
807 priv->setName(QString::fromUtf8(container.uri), QString());
808 priv->version = container.version;
809 priv->revision = container.revision;
810 priv->typeId = container.metaSequence.valueMetaType();
811 priv->listId = container.typeId;
812 priv->extraData.sequentialContainerTypeData = container.metaSequence;
813
814 addTypeToData(priv, data);
815
816 return QQmlType(priv);
817}
818
819void QQmlMetaType::unregisterSequentialContainer(int id)
820{
821 unregisterType(id);
822}
823
824bool QQmlMetaType::protectModule(const QString &uri, QTypeRevision version,
825 bool weakProtectAllVersions)
826{
827 QQmlMetaTypeDataPtr data;
828 if (version.hasMajorVersion()) {
829 if (QQmlTypeModule *module = data->findTypeModule(uri, version)) {
830 if (!weakProtectAllVersions) {
831 module->setLockLevel(QQmlTypeModule::LockLevel::Strong);
832 return true;
833 }
834 } else {
835 return false;
836 }
837 }
838
839 const auto range = std::equal_range(
840 data->uriToModule.begin(), data->uriToModule.end(), uri,
841 std::less<ModuleUri>());
842
843 for (auto it = range.first; it != range.second; ++it)
844 (*it)->setLockLevel(QQmlTypeModule::LockLevel::Weak);
845
846 return range.first != range.second;
847}
848
849void QQmlMetaType::registerModuleImport(const QString &uri, QTypeRevision moduleVersion,
850 const QQmlDirParser::Import &import)
851{
852 QQmlMetaTypeDataPtr data;
853
854 data->moduleImports.insert(QQmlMetaTypeData::VersionedUri(uri, moduleVersion), import);
855}
856
857void QQmlMetaType::unregisterModuleImport(const QString &uri, QTypeRevision moduleVersion,
858 const QQmlDirParser::Import &import)
859{
860 QQmlMetaTypeDataPtr data;
861 data->moduleImports.remove(QQmlMetaTypeData::VersionedUri(uri, moduleVersion), import);
862}
863
864QList<QQmlDirParser::Import> QQmlMetaType::moduleImports(
865 const QString &uri, QTypeRevision version)
866{
867 QQmlMetaTypeDataPtr data;
868 QList<QQmlDirParser::Import> result;
869
870 const auto unrevisioned = data->moduleImports.equal_range(
871 QQmlMetaTypeData::VersionedUri(uri, QTypeRevision()));
872 for (auto it = unrevisioned.second; it != unrevisioned.first;)
873 result.append(*(--it));
874
875 if (version.hasMajorVersion()) {
876 const auto revisioned = data->moduleImports.equal_range(
877 QQmlMetaTypeData::VersionedUri(uri, version));
878 for (auto it = revisioned.second; it != revisioned.first;)
879 result.append(*(--it));
880 return result;
881 }
882
883 // Use latest module available with that URI.
884 const auto begin = data->moduleImports.begin();
885 auto it = unrevisioned.first;
886 if (it == begin)
887 return result;
888
889 const QQmlMetaTypeData::VersionedUri latestVersion = (--it).key();
890 if (latestVersion.uri != uri)
891 return result;
892
893 do {
894 result += *it;
895 } while (it != begin && (--it).key() == latestVersion);
896
897 return result;
898}
899
900void QQmlMetaType::registerModule(const char *uri, QTypeRevision version)
901{
902 QQmlMetaTypeDataPtr data;
903
904 QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), version, data);
905 Q_ASSERT(module);
906
907 if (version.hasMinorVersion())
908 module->addMinorVersion(version.minorVersion());
909}
910
911int QQmlMetaType::typeId(const char *uri, QTypeRevision version, const char *qmlName)
912{
913 QQmlMetaTypeDataPtr data;
914
915 QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), version, data);
916 if (!module)
917 return -1;
918
919 QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), version);
920 if (!type.isValid())
921 return -1;
922
923 return type.index();
924}
925
926void QQmlMetaType::registerUndeletableType(const QQmlType &dtype)
927{
928 QQmlMetaTypeDataPtr data;
929 data->undeletableTypes.insert(dtype);
930}
931
932static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri,
933 QTypeRevision version)
934{
935 // Has any type previously been installed to this namespace?
936 QHashedString nameSpace(uri);
937 for (const auto &typeAndCaches : std::as_const(data->types)) {
938 const QQmlType &type = typeAndCaches.type;
939 if (type.module() == nameSpace && type.version().majorVersion() == version.majorVersion())
940 return true;
941 }
942
943 return false;
944}
945
946QQmlMetaType::RegistrationResult QQmlMetaType::registerPluginTypes(
947 QObject *instance, const QString &basePath, const QString &uri,
948 const QString &typeNamespace, QTypeRevision version, QList<QQmlError> *errors)
949{
950 if (!typeNamespace.isEmpty() && typeNamespace != uri) {
951 // This is an 'identified' module
952 // The namespace for type registrations must match the URI for locating the module
953 if (errors) {
954 QQmlError error;
955 error.setDescription(
956 QStringLiteral("Module namespace '%1' does not match import URI '%2'")
957 .arg(typeNamespace, uri));
958 errors->prepend(error);
959 }
960 return RegistrationResult::Failure;
961 }
962
963 QStringList failures;
964 QQmlMetaTypeDataPtr data;
965 {
966 QQmlMetaTypeRegistrationFailureRecorder failureRecorder(data, &failures);
967 if (!typeNamespace.isEmpty()) {
968 // This is an 'identified' module
969 if (namespaceContainsRegistrations(data, typeNamespace, version)) {
970 // Other modules have already installed to this namespace
971 if (errors) {
972 QQmlError error;
973 error.setDescription(QStringLiteral("Namespace '%1' has already been used "
974 "for type registration")
975 .arg(typeNamespace));
976 errors->prepend(error);
977 }
978 return RegistrationResult::Failure;
979 }
980 } else {
981 // This is not an identified module - provide a warning
982 qWarning().nospace() << qPrintable(
983 QStringLiteral("Module '%1' does not contain a module identifier directive - "
984 "it cannot be protected from external registrations.").arg(uri));
985 }
986
987 if (instance && !qobject_cast<QQmlEngineExtensionInterface *>(instance)) {
988 QQmlTypesExtensionInterface *iface = qobject_cast<QQmlTypesExtensionInterface *>(instance);
989 if (!iface) {
990 if (errors) {
991 QQmlError error;
992 // Also does not implement QQmlTypesExtensionInterface, but we want to discourage that.
993 error.setDescription(QStringLiteral("Module loaded for URI '%1' does not implement "
994 "QQmlEngineExtensionInterface").arg(typeNamespace));
995 errors->prepend(error);
996 }
997 return RegistrationResult::Failure;
998 }
999
1000#if QT_DEPRECATED_SINCE(6, 3)
1001 if (auto *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
1002 // basepath should point to the directory of the module, not the plugin file itself:
1003 QQmlExtensionPluginPrivate::get(plugin)->baseUrl
1004 = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
1005 }
1006#else
1007 Q_UNUSED(basePath)
1008#endif
1009
1010 const QByteArray bytes = uri.toUtf8();
1011 const char *moduleId = bytes.constData();
1012 iface->registerTypes(moduleId);
1013 }
1014
1015 if (failures.isEmpty() && !data->registerModuleTypes(uri))
1016 return RegistrationResult::NoRegistrationFunction;
1017
1018 if (!failures.isEmpty()) {
1019 if (errors) {
1020 for (const QString &failure : std::as_const(failures)) {
1021 QQmlError error;
1022 error.setDescription(failure);
1023 errors->prepend(error);
1024 }
1025 }
1026 return RegistrationResult::Failure;
1027 }
1028 }
1029
1030 return RegistrationResult::Success;
1031}
1032
1033/*
1034 \internal
1035
1036 Fetches the QQmlType instance registered for \a urlString, creating a
1037 registration for it if it is not already registered, using the associated
1038 \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion
1039 details.
1040
1041 Errors (if there are any) are placed into \a errors, if it is nonzero.
1042 Otherwise errors are printed as warnings.
1043*/
1044QQmlType QQmlMetaType::typeForUrl(
1045 const QUrl &url, const QHashedStringRef &qualifiedType, CompositeTypeLookupMode mode,
1046 QList<QQmlError> *errors, QTypeRevision version)
1047{
1048 // ### unfortunate (costly) conversion
1049 const QUrl normalized = QQmlMetaType::normalizedUrl(url);
1050
1051 QQmlMetaTypeDataPtr data;
1052 {
1053 QQmlType ret(data->urlToType.value(normalized));
1054 if (ret.isValid() && ret.sourceUrl() == normalized)
1055 return ret;
1056 }
1057
1058 const QQmlType type = createTypeForUrl(
1059 data, normalized, qualifiedType, mode, errors, version);
1060 data->urlToType.insert(normalized, type.priv());
1061 return type;
1062}
1063
1064/*
1065 Returns the latest version of \a uri installed, or an in valid QTypeRevision().
1066*/
1067QTypeRevision QQmlMetaType::latestModuleVersion(const QString &uri)
1068{
1069 QQmlMetaTypeDataPtr data;
1070 auto upper = std::upper_bound(data->uriToModule.begin(), data->uriToModule.end(), uri,
1071 std::less<ModuleUri>());
1072 if (upper == data->uriToModule.begin())
1073 return QTypeRevision();
1074
1075 const auto module = (--upper)->get();
1076 return (module->module() == uri)
1077 ? QTypeRevision::fromVersion(module->majorVersion(), module->maximumMinorVersion())
1078 : QTypeRevision();
1079}
1080
1081/*
1082 Returns true if a module \a uri of this version is installed and locked;
1083*/
1084bool QQmlMetaType::isStronglyLockedModule(const QString &uri, QTypeRevision version)
1085{
1086 QQmlMetaTypeDataPtr data;
1087
1088 if (QQmlTypeModule* qqtm = data->findTypeModule(uri, version))
1089 return qqtm->lockLevel() == QQmlTypeModule::LockLevel::Strong;
1090 return false;
1091}
1092
1093/*
1094 Returns the best matching registered version for the given \a module. If \a version is
1095 the does not have a major version, returns the latest registered version. Otherwise
1096 chooses the same major version and checks if the minor version is within the range
1097 of registered minor versions. If so, retuens the original version, otherwise returns
1098 an invalid version. If \a version does not have a minor version, chooses the latest one.
1099*/
1100QTypeRevision QQmlMetaType::matchingModuleVersion(const QString &module, QTypeRevision version)
1101{
1102 if (!version.hasMajorVersion())
1103 return latestModuleVersion(module);
1104
1105 QQmlMetaTypeDataPtr data;
1106
1107 // first, check Types
1108 if (QQmlTypeModule *tm = data->findTypeModule(module, version)) {
1109 if (!version.hasMinorVersion())
1110 return QTypeRevision::fromVersion(version.majorVersion(), tm->maximumMinorVersion());
1111
1112 if (tm->minimumMinorVersion() <= version.minorVersion()
1113 && tm->maximumMinorVersion() >= version.minorVersion()) {
1114 return version;
1115 }
1116 }
1117
1118 return QTypeRevision();
1119}
1120
1121QQmlTypeModule *QQmlMetaType::typeModule(const QString &uri, QTypeRevision version)
1122{
1123 QQmlMetaTypeDataPtr data;
1124
1125 if (version.hasMajorVersion())
1126 return data->findTypeModule(uri, version);
1127
1128 auto range = std::equal_range(data->uriToModule.begin(), data->uriToModule.end(),
1129 uri, std::less<ModuleUri>());
1130
1131 return range.first == range.second ? nullptr : (--range.second)->get();
1132}
1133
1134QList<QQmlPrivate::AutoParentFunction> QQmlMetaType::parentFunctions()
1135{
1136 QQmlMetaTypeDataPtr data;
1137 return data->parentFunctions;
1138}
1139
1140QObject *QQmlMetaType::toQObject(const QVariant &v, bool *ok)
1141{
1142 if (!v.metaType().flags().testFlag(QMetaType::PointerToQObject)) {
1143 if (ok) *ok = false;
1144 return nullptr;
1145 }
1146
1147 if (ok) *ok = true;
1148
1149 return *(QObject *const *)v.constData();
1150}
1151
1152/*
1153 Returns the item type for a list of type \a id.
1154 */
1155QMetaType QQmlMetaType::listValueType(QMetaType metaType)
1156{
1157 if (isList(metaType)) {
1158 const auto iface = metaType.iface();
1159 if (iface && iface->metaObjectFn == &dynamicQmlListMarker)
1160 return QMetaType(static_cast<const QQmlListMetaTypeInterface *>(iface)->valueType);
1161 } else if (metaType.flags() & QMetaType::PointerToQObject) {
1162 return QMetaType();
1163 }
1164
1165 QQmlMetaTypeDataPtr data;
1166 Q_ASSERT(data);
1167 QQmlTypePrivate *type = data->idToType.value(metaType.id());
1168
1169 if (type && type->listId == metaType)
1170 return type->typeId;
1171 else
1172 return QMetaType {};
1173}
1174
1175QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(
1176 QQmlTypeLoader *typeLoader, const QMetaObject *mo)
1177{
1178 QQmlMetaTypeDataPtr data;
1179
1180 QQmlType type(data->metaObjectToType.value(mo));
1181 return type.attachedPropertiesFunction(typeLoader);
1182}
1183
1184QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject)
1185{
1186 int idx = metaObject->indexOfClassInfo("DefaultProperty");
1187 if (-1 == idx)
1188 return QMetaProperty();
1189
1190 QMetaClassInfo info = metaObject->classInfo(idx);
1191 if (!info.value())
1192 return QMetaProperty();
1193
1194 idx = metaObject->indexOfProperty(info.value());
1195 if (-1 == idx)
1196 return QMetaProperty();
1197
1198 return metaObject->property(idx);
1199}
1200
1201QMetaProperty QQmlMetaType::defaultProperty(QObject *obj)
1202{
1203 if (!obj)
1204 return QMetaProperty();
1205
1206 const QMetaObject *metaObject = obj->metaObject();
1207 return defaultProperty(metaObject);
1208}
1209
1210QMetaMethod QQmlMetaType::defaultMethod(const QMetaObject *metaObject)
1211{
1212 int idx = metaObject->indexOfClassInfo("DefaultMethod");
1213 if (-1 == idx)
1214 return QMetaMethod();
1215
1216 QMetaClassInfo info = metaObject->classInfo(idx);
1217 if (!info.value())
1218 return QMetaMethod();
1219
1220 idx = metaObject->indexOfMethod(info.value());
1221 if (-1 == idx)
1222 return QMetaMethod();
1223
1224 return metaObject->method(idx);
1225}
1226
1227QMetaMethod QQmlMetaType::defaultMethod(QObject *obj)
1228{
1229 if (!obj)
1230 return QMetaMethod();
1231
1232 const QMetaObject *metaObject = obj->metaObject();
1233 return defaultMethod(metaObject);
1234}
1235
1236/*!
1237 See qmlRegisterInterface() for information about when this will return true.
1238*/
1239bool QQmlMetaType::isInterface(QMetaType metaType)
1240{
1241 const QQmlMetaTypeDataPtr data;
1242 return QQmlType(data->idToType.value(metaType.id())).isInterface();
1243}
1244
1245const char *QQmlMetaType::interfaceIId(QMetaType metaType)
1246{
1247 const QQmlMetaTypeDataPtr data;
1248 const QQmlType type(data->idToType.value(metaType.id()));
1249 return (type.isInterface() && type.typeId() == metaType) ? type.interfaceIId() : nullptr;
1250}
1251
1252bool QQmlMetaType::isList(QMetaType type)
1253{
1254 if (type.flags().testFlag(QMetaType::IsQmlList))
1255 return true;
1256 else
1257 return false;
1258}
1259
1260/*!
1261 Returns the type (if any) of URI-qualified named \a qualifiedName and version specified
1262 by \a version_major and \a version_minor.
1263*/
1264QQmlType QQmlMetaType::qmlType(const QString &qualifiedName, QTypeRevision version)
1265{
1266 int slash = qualifiedName.indexOf(QLatin1Char('/'));
1267 if (slash <= 0)
1268 return QQmlType();
1269
1270 QHashedStringRef module(qualifiedName.constData(), slash);
1271 QHashedStringRef name(qualifiedName.constData() + slash + 1, qualifiedName.size() - slash - 1);
1272
1273 return qmlType(name, module, version);
1274}
1275
1276/*!
1277 \internal
1278 Returns the type (if any) of \a name in \a module and the specified \a version.
1279
1280 If \a version has no major version, accept any version.
1281 If \a version has no minor version, accept any minor version.
1282 If \a module is empty, search in all modules and accept any version.
1283*/
1284QQmlType QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStringRef &module,
1285 QTypeRevision version)
1286{
1287 const QQmlMetaTypeDataPtr data;
1288
1289 const QHashedString key(QString::fromRawData(name.constData(), name.length()), name.hash());
1290 QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(key);
1291 while (it != data->nameToType.cend() && it.key() == name) {
1292 QQmlType t(*it);
1293 if (module.isEmpty() || t.availableInVersion(module, version))
1294 return t;
1295 ++it;
1296 }
1297
1298 return QQmlType();
1299}
1300
1301/*!
1302 Returns the type (if any) that corresponds to the \a metaObject. Returns an invalid type if no
1303 such type is registered.
1304*/
1305QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject)
1306{
1307 const QQmlMetaTypeDataPtr data;
1308 return QQmlType(data->metaObjectToType.value(metaObject));
1309}
1310
1311/*!
1312 Returns the type (if any) that corresponds to the \a metaObject in version specified
1313 by \a version_major and \a version_minor in module specified by \a uri. Returns null if no
1314 type is registered.
1315*/
1316QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStringRef &module,
1317 QTypeRevision version)
1318{
1319 const QQmlMetaTypeDataPtr data;
1320
1321 const auto range = data->metaObjectToType.equal_range(metaObject);
1322 for (auto it = range.first; it != range.second; ++it) {
1323 QQmlType t(*it);
1324 if (module.isEmpty() || t.availableInVersion(module, version))
1325 return t;
1326 }
1327
1328 return QQmlType();
1329}
1330
1331/*!
1332 Returns the type (if any) that corresponds to \a qmlTypeId.
1333 Returns an invalid QQmlType if no such type is registered.
1334*/
1335QQmlType QQmlMetaType::qmlTypeById(int qmlTypeId)
1336{
1337 const QQmlMetaTypeDataPtr data;
1338 return data->types.value(qmlTypeId).type;
1339}
1340
1341/*!
1342 \internal
1343 Returns the first QQmlType whose attachedPropertiesType equals \a attachmentMetaObject
1344 Note that a single attachment type can be provided by multiple types, though that is rare
1345 in practice.
1346 Also, in case of self-attachment, we always return the type with the self-attachment, even
1347 if another type would come first in the list. This is an optimization for the common case.
1348 Returns \c nullptr if no match is fonud
1349 */
1350QQmlType QQmlMetaType::firstQmlTypeForAttachmentMetaObject(const QMetaObject *attachmentMetaObject)
1351{
1352 const QQmlMetaTypeDataPtr data;
1353 if (auto qmlTypePriv = data->metaObjectToType.value(attachmentMetaObject)) {
1354 // quick check to handle the case where we deal with self attachment
1355 if (qmlTypePriv->regType == QQmlType::CppType &&
1356 qmlTypePriv->extraData.cppTypeData->attachedPropertiesType == attachmentMetaObject)
1357 return QQmlType(qmlTypePriv);
1358 }
1359 auto getAttachmentMetaObject = [](const QQmlTypePrivate *priv) -> const QMetaObject * {
1360 if (priv->regType != QQmlType::CppType)
1361 return nullptr;
1362 return priv->extraData.cppTypeData->attachedPropertiesType;
1363 };
1364 auto it = std::find_if(data->metaObjectToType.constBegin(),
1365 data->metaObjectToType.constEnd(),
1366 [&](const QQmlTypePrivate *type) { return attachmentMetaObject == getAttachmentMetaObject(type); }
1367 );
1368 if (it == data->metaObjectToType.constEnd())
1369 return QQmlType();
1370 return QQmlType(*it);
1371}
1372
1373/*!
1374 Returns the type (if any) that corresponds to \a metaType.
1375 Returns an invalid QQmlType if no such type is registered.
1376*/
1377QQmlType QQmlMetaType::qmlType(QMetaType metaType)
1378{
1379 const QQmlMetaTypeDataPtr data;
1380 QQmlTypePrivate *type = data->idToType.value(metaType.id());
1381 return (type && type->typeId == metaType) ? QQmlType(type) : QQmlType();
1382}
1383
1384QQmlType QQmlMetaType::qmlListType(QMetaType metaType)
1385{
1386 const QQmlMetaTypeDataPtr data;
1387 QQmlTypePrivate *type = data->idToType.value(metaType.id());
1388 return (type && type->listId == metaType) ? QQmlType(type) : QQmlType();
1389}
1390
1391/*!
1392 Returns the type (if any) that corresponds to the given \a url in the set of
1393 composite types added through file imports.
1394
1395 Returns null if no such type is registered.
1396*/
1397QQmlType QQmlMetaType::qmlType(const QUrl &unNormalizedUrl)
1398{
1399 const QUrl url = QQmlMetaType::normalizedUrl(unNormalizedUrl);
1400 const QQmlMetaTypeDataPtr data;
1401
1402 QQmlType type(data->urlToType.value(url));
1403
1404 if (type.sourceUrl() == url)
1405 return type;
1406 else
1407 return QQmlType();
1408}
1409
1410QQmlType QQmlMetaType::findOrCreateSpeculativeInlineComponentType(const QUrl &url)
1411{
1412 QQmlMetaTypeDataPtr data;
1413 const auto it = data->urlToType.constFind(url);
1414 if (it != data->urlToType.constEnd())
1415 return QQmlType(*it);
1416
1417 // Check if the base type is already registered. If so, validate that the IC exists.
1418
1419 QUrl baseUrl = url;
1420 baseUrl.setFragment(QString());
1421
1422 // Skip validation for anonymous/empty URLs. Those can only be registered with direct setData()
1423 // and are otherwise inaddressable anyway.
1424 if (baseUrl.isEmpty())
1425 return doRegisterInlineComponentType(data, url);
1426
1427 // Don't validate if the type for the base URL doesn't exist.
1428 // We'll prune invalid ICs for those when the type shows up, eventually.
1429 const auto baseIt = data->urlToType.constFind(baseUrl);
1430 if (baseIt == data->urlToType.constEnd()) {
1431 data->speculativeInlineComponentTypes.insert(url);
1432 return doRegisterInlineComponentType(data, url);
1433 }
1434
1435 // Check if the base type has a registered compilation unit.
1436 // Otherwise also defer validation.
1437 const auto cu = data->compositeTypes.value((*baseIt)->typeId.iface());
1438 if (!cu) {
1439 data->speculativeInlineComponentTypes.insert(url);
1440 return doRegisterInlineComponentType(data, url);
1441 }
1442
1443 // CU is registered. It can't have the IC since otherwise that one would should up
1444 // in data->urlToType.
1445 Q_ASSERT(std::none_of(
1446 cu->inlineComponentData.constBegin(), cu->inlineComponentData.constEnd(),
1447 [&](const QV4::CompiledData::InlineComponentData &icData) {
1448 return icData.qmlType.elementName() == url.fragment();
1449 }));
1450
1451 // IC doesn't exist in the registered CU - don't create a speculative type
1452 return QQmlType();
1453}
1454
1455/*!
1456Returns a QQmlPropertyCache for \a obj if one is available.
1457
1458If \a obj is null, being deleted or contains a dynamic meta object,
1459nullptr is returned.
1460*/
1461QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(QObject *obj, QTypeRevision version)
1462{
1463 if (!obj || QObjectPrivate::get(obj)->metaObject || QObjectPrivate::get(obj)->wasDeleted)
1464 return QQmlPropertyCache::ConstPtr();
1465 return QQmlMetaType::propertyCache(obj->metaObject(), version);
1466}
1467
1468QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(
1469 const QMetaObject *metaObject, QTypeRevision version)
1470{
1471 QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
1472 return data->propertyCache(metaObject, version);
1473}
1474
1475QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCache(
1476 const QQmlType &type, QTypeRevision version)
1477{
1478 QQmlMetaTypeDataPtr data; // not const: the cache is created on demand
1479 return data->propertyCache(type, version);
1480}
1481
1482/*!
1483 * \internal
1484 *
1485 * Look up by type's baseMetaObject.
1486 */
1487QQmlMetaObject QQmlMetaType::rawMetaObjectForType(QMetaType metaType)
1488{
1489 const QQmlMetaTypeDataPtr data;
1490 if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
1491 return QQmlMetaObject(composite);
1492
1493 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1494 return (type && type->typeId == metaType) ? type->baseMetaObject : nullptr;
1495}
1496
1497/*!
1498 * \internal
1499 *
1500 * Look up by type's metaObject.
1501 */
1502QQmlMetaObject QQmlMetaType::metaObjectForType(QMetaType metaType)
1503{
1504 const QQmlMetaTypeDataPtr data;
1505 if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
1506 return QQmlMetaObject(composite);
1507
1508 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1509 return (type && type->typeId == metaType)
1510 ? QQmlType(type).metaObject()
1511 : nullptr;
1512}
1513
1514/*!
1515 * \internal
1516 *
1517 * Look up by type's metaObject and version.
1518 */
1519QQmlPropertyCache::ConstPtr QQmlMetaType::propertyCacheForType(QMetaType metaType)
1520{
1521 QQmlMetaTypeDataPtr data;
1522 if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
1523 return composite;
1524
1525 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1526 if (type && type->typeId == metaType) {
1527 if (const QMetaObject *mo = QQmlType(type).metaObject())
1528 return data->propertyCache(mo, type->version);
1529 }
1530
1531 return QQmlPropertyCache::ConstPtr();
1532}
1533
1534/*!
1535 * \internal
1536 *
1537 * Look up by type's baseMetaObject and unspecified/any version.
1538 * TODO: Is this correct? Passing a plain QTypeRevision() rather than QTypeRevision::zero() or
1539 * the actual type's version seems strange. The behavior has been in place for a while.
1540 */
1541QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(QMetaType metaType)
1542{
1543 QQmlMetaTypeDataPtr data;
1544 if (auto composite = QQmlMetaType::findPropertyCacheInCompositeTypes(metaType))
1545 return composite;
1546
1547 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1548 if (!type || type->typeId != metaType)
1549 return QQmlPropertyCache::ConstPtr();
1550
1551 const QMetaObject *metaObject = type->isValueType()
1552 ? type->metaObjectForValueType()
1553 : type->baseMetaObject;
1554
1555 return metaObject
1556 ? data->propertyCache(metaObject, QTypeRevision())
1557 : QQmlPropertyCache::ConstPtr();
1558}
1559
1560/*!
1561 * \internal
1562 *
1563 * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type
1564 * has no revisiononed attributes here. Unspecified versions are interpreted as "any".
1565 */
1566QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(
1567 QMetaType metaType, QTypeRevision version)
1568{
1569 QQmlMetaTypeDataPtr data;
1570 if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
1571 return composite;
1572
1573 const QQmlTypePrivate *typePriv = data->idToType.value(metaType.id());
1574 if (!typePriv || typePriv->typeId != metaType)
1575 return QQmlPropertyCache::ConstPtr();
1576
1577 const QQmlType type(typePriv);
1578 if (type.containsRevisionedAttributes()) {
1579 // It can only have (revisioned) properties or methods if it has a metaobject
1580 Q_ASSERT(type.metaObject());
1581 return data->propertyCache(type, version);
1582 }
1583
1584 if (const QMetaObject *metaObject = type.metaObject())
1585 return data->propertyCache(metaObject, version);
1586
1587 return QQmlPropertyCache::ConstPtr();
1588}
1589
1590template<typename From, typename CanConvertPropCache, typename CanConvertMetaObject>
1592 const QQmlMetaTypeDataPtr &data, const From &from, QMetaType metaType,
1593 CanConvertPropCache &&canConvertPropCache, CanConvertMetaObject &&canConvertMetaObject)
1594{
1595 // There can be multiple composite types mapped to the same metatype. Since a property metatype
1596 // alone cannot specify which property cache is actually meant, the only thing we can do here
1597 // is check them all.
1598 // TODO: Ideally, the QQmlMetaTypeData should be completely dissolved and every composite
1599 // metatype should be specific to the type loader that created it. Then we wouldn't have
1600 // these problems.
1601 auto [it, end] = data->compositeTypes.equal_range(metaType.iface());
1602 if (it != end) {
1603 do {
1604 if (canConvertPropCache(
1605 from, QQmlMetaTypeData::propertyCacheForPotentialInlineComponentType(
1606 metaType, it))) {
1607 return true;
1608 }
1609 } while(++it != end);
1610
1611 // If it is a composite type and nothing matches we have a certain "no".
1612 // We don't call metaObject() on the type then because that searches compositeTypes, too.
1613 return false;
1614 }
1615
1616 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1617 if (type && type->typeId == metaType && type->baseMetaObject)
1618 return canConvertMetaObject(from, type->baseMetaObject);
1619
1620 // Types we don't know may still have metaobjects
1621 if (const QMetaObject *metaObject = metaType.metaObject())
1622 return canConvertMetaObject(from, metaObject);
1623
1624 return false;
1625}
1626
1627bool QQmlMetaType::canConvert(QObject *o, QMetaType metaType)
1628{
1629 QQmlMetaTypeDataPtr data;
1630
1631 return canConvertToPropCacheOrMetaObject(
1632 data, o, metaType, [](QObject *o, const QQmlPropertyCache::ConstPtr &propCache) {
1633 return QQmlMetaObject::canConvert(o, propCache);
1634 }, [](QObject *o, const QMetaObject *metaObject) {
1635 return QQmlMetaObject::canConvert(o, metaObject);
1636 });
1637}
1638
1639static bool inherits(
1640 const QQmlPropertyCache::ConstPtr &derived, const QQmlPropertyCache::ConstPtr &base)
1641{
1642 for (QQmlPropertyCache::ConstPtr parent = derived; parent; parent = parent->parent()) {
1643 if (parent == base)
1644 return true;
1645 }
1646
1647 return false;
1648}
1649
1650bool QQmlMetaType::canConvert(const QQmlPropertyCache::ConstPtr &from, QMetaType metaType)
1651{
1652 QQmlMetaTypeDataPtr data;
1653
1654 return canConvertToPropCacheOrMetaObject(
1655 data, from, metaType,
1656 [](const QQmlPropertyCache::ConstPtr &from, const QQmlPropertyCache::ConstPtr &to) {
1657 return inherits(from, to);
1658 }, [&](const QQmlPropertyCache::ConstPtr &from, const QMetaObject *toMeta) {
1659 if (const QMetaObject *fromMeta = from->metaObject())
1660 return QQmlMetaObject::canConvert(fromMeta, toMeta);
1661 return inherits(from, data->propertyCache(toMeta, QTypeRevision()));
1662 });
1663}
1664
1665void QQmlMetaType::unregisterType(int typeIndex)
1666{
1667 QQmlMetaTypeDataPtr data;
1668 const QQmlType type = data->types.value(typeIndex).type;
1669 if (const QQmlTypePrivate *d = type.priv()) {
1670 if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType) {
1671 removeFromInlineComponents(data->urlToType, d);
1672 removeFromInlineComponents(data->speculativeInlineComponentTypes, d);
1673 }
1674 removeQQmlTypePrivate(data->idToType, d);
1675 removeQQmlTypePrivate(data->nameToType, d);
1676 removeQQmlTypePrivate(data->urlToType, d);
1677 removeQQmlTypePrivate(data->metaObjectToType, d);
1678 data->speculativeInlineComponentTypes.remove(d->sourceUrl());
1679 for (auto & module : data->uriToModule)
1680 module->remove(d);
1681 data->types[typeIndex] = QQmlMetaTypeData::Type();
1682 data->undeletableTypes.remove(type);
1683 }
1684}
1685
1686void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, const QQmlTypePrivate *type)
1687{
1688 Q_ASSERT(type);
1689
1690 QQmlMetaTypeDataPtr data;
1691 data->metaObjectToType.insert(metaobject, type);
1692}
1693
1694static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d)
1695{
1696 for (auto it = data->urlToType.begin(), end = data->urlToType.end();
1697 it != end; ++it) {
1698 if (!QQmlMetaType::equalBaseUrls(it.key(), d->sourceUrl()))
1699 continue;
1700
1701 const QQmlTypePrivate *icPriv = *it;
1702 if (icPriv && icPriv->count() > 1)
1703 return true;
1704 }
1705 return false;
1706}
1707
1709 QQmlMetaTypeData *data,
1710 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
1711{
1712 int result = 0;
1713 auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) {
1714 if (!iface)
1715 return;
1716
1717 const auto [begin, end] = std::as_const(data->compositeTypes).equal_range(iface);
1718 for (auto it = begin; it != end; ++it) {
1719 if (*it == compilationUnit)
1720 ++result;
1721 }
1722 };
1723
1724 doCheck(compilationUnit->metaType().iface());
1725 for (auto &&inlineData: compilationUnit->inlineComponentData)
1726 doCheck(inlineData.qmlType.typeId().iface());
1727
1728 return result;
1729}
1730
1731void QQmlMetaType::freeUnusedTypesAndCaches()
1732{
1733 QQmlMetaTypeDataPtr data;
1734
1735 // in case this is being called during program exit, `data` might be destructed already
1736 if (!data.isValid())
1737 return;
1738
1739 bool droppedAtLeastOneComposite;
1740 do {
1741 droppedAtLeastOneComposite = false;
1742 auto it = data->compositeTypes.cbegin();
1743 while (it != data->compositeTypes.cend()) {
1744 const auto &cu = *it;
1745 if (cu->count() <= doCountInternalCompositeTypeSelfReferences(data, cu)) {
1746 QQmlMetaTypeData::clearCompositeType(cu);
1747 it = data->compositeTypes.erase(it);
1748 droppedAtLeastOneComposite = true;
1749 } else {
1750 ++it;
1751 }
1752 }
1753 } while (droppedAtLeastOneComposite);
1754
1755 bool deletedAtLeastOneType;
1756 do {
1757 deletedAtLeastOneType = false;
1758 auto it = data->types.begin();
1759 while (it != data->types.end()) {
1760 const QQmlTypePrivate *d = it->type.priv();
1761 if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) {
1762 deletedAtLeastOneType = true;
1763
1764 if (d->regType == QQmlType::CompositeType
1765 || d->regType == QQmlType::CompositeSingletonType) {
1766 removeFromInlineComponents(data->urlToType, d);
1767 removeFromInlineComponents(data->speculativeInlineComponentTypes, d);
1768 }
1769 removeQQmlTypePrivate(data->idToType, d);
1770 removeQQmlTypePrivate(data->nameToType, d);
1771 removeQQmlTypePrivate(data->urlToType, d);
1772 removeQQmlTypePrivate(data->metaObjectToType, d);
1773 data->speculativeInlineComponentTypes.remove(d->sourceUrl());
1774
1775 for (auto &module : data->uriToModule)
1776 module->remove(d);
1777
1778 *it = QQmlMetaTypeData::Type();
1779 } else {
1780 ++it;
1781 }
1782 }
1783 } while (deletedAtLeastOneType);
1784
1785 bool deletedAtLeastOneCache;
1786 do {
1787 deletedAtLeastOneCache = false;
1788 auto it = data->propertyCaches.begin();
1789 while (it != data->propertyCaches.end()) {
1790 if ((*it)->count() == 1) {
1791 it = data->propertyCaches.erase(it);
1792 deletedAtLeastOneCache = true;
1793 } else {
1794 ++it;
1795 }
1796 }
1797 } while (deletedAtLeastOneCache);
1798}
1799
1800/*!
1801 Returns the list of registered QML type names.
1802*/
1803QList<QString> QQmlMetaType::qmlTypeNames()
1804{
1805 const QQmlMetaTypeDataPtr data;
1806
1807 QList<QString> names;
1808 names.reserve(data->nameToType.size());
1809 QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin();
1810 while (it != data->nameToType.cend()) {
1811 QQmlType t(*it);
1812 names += t.qmlTypeName();
1813 ++it;
1814 }
1815
1816 return names;
1817}
1818
1819/*!
1820 Returns the list of registered QML types.
1821*/
1822QList<QQmlType> QQmlMetaType::qmlTypes()
1823{
1824 const QQmlMetaTypeDataPtr data;
1825
1826 QList<QQmlType> types;
1827 for (const QQmlTypePrivate *t : data->nameToType)
1828 types.append(QQmlType(t));
1829
1830 return types;
1831}
1832
1833/*!
1834 Returns the list of all registered types.
1835*/
1836QList<QQmlType> QQmlMetaType::qmlAllTypes()
1837{
1838 const QQmlMetaTypeDataPtr data;
1839 QList<QQmlType> types;
1840 types.reserve(data->types.size());
1841 std::transform(
1842 data->types.constBegin(), data->types.constEnd(),
1843 std::back_inserter(types), [](const auto &type) { return type.type; });
1844 return types;
1845}
1846
1847/*!
1848 Returns the list of registered QML singleton types.
1849*/
1850QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
1851{
1852 const QQmlMetaTypeDataPtr data;
1853
1854 QList<QQmlType> retn;
1855 for (const auto t : std::as_const(data->nameToType)) {
1856 QQmlType type(t);
1857 if (type.isSingleton())
1858 retn.append(type);
1859 }
1860 return retn;
1861}
1862
1863static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit)
1864{
1865 quint32 numTypedFunctions = 0;
1866 for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions;
1867 function; ++function) {
1868 if (function->functionPtr)
1869 ++numTypedFunctions;
1870 else
1871 return false;
1872 }
1873 return numTypedFunctions == unit->qmlData->functionTableSize;
1874}
1875
1876const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(
1877 const QUrl &uri, QQmlMetaType::CacheMode mode, CachedUnitLookupError *status)
1878{
1879 Q_ASSERT(mode != RejectAll);
1880 const QQmlMetaTypeDataPtr data;
1881
1882 for (const auto lookup : std::as_const(data->lookupCachedQmlUnit)) {
1883 if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) {
1884 QString error;
1885 if (!unit->qmlData->verifyHeader(QDateTime(), &error)) {
1886 qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error;
1887 if (status)
1888 *status = CachedUnitLookupError::VersionMismatch;
1889 return nullptr;
1890 }
1891
1892 if (mode == RequireFullyTyped && !isFullyTyped(unit)) {
1893 qCDebug(DBG_DISK_CACHE)
1894 << "Error loading pre-compiled file " << uri
1895 << ": compilation unit contains functions not compiled to native code.";
1896 if (status)
1897 *status = CachedUnitLookupError::NotFullyTyped;
1898 return nullptr;
1899 }
1900
1901 if (status)
1902 *status = CachedUnitLookupError::NoError;
1903 return unit;
1904 }
1905 }
1906
1907 qCDebug(DBG_DISK_CACHE) << "No pre-compiled unit found for" << uri;
1908
1909 if (status)
1910 *status = CachedUnitLookupError::NoUnitFound;
1911
1912 return nullptr;
1913}
1914
1915void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
1916{
1917 QQmlMetaTypeDataPtr data;
1918 data->lookupCachedQmlUnit.prepend(handler);
1919}
1920
1921void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
1922{
1923 QQmlMetaTypeDataPtr data;
1924 data->lookupCachedQmlUnit.removeAll(handler);
1925}
1926
1927/*!
1928 Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object.
1929 */
1930QString QQmlMetaType::prettyTypeName(const QObject *object)
1931{
1932 QString typeName;
1933
1934 if (!object)
1935 return typeName;
1936
1937 QQmlType type = QQmlMetaType::qmlType(object->metaObject());
1938 if (type.isValid()) {
1939 typeName = type.qmlTypeName();
1940 const int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
1941 if (lastSlash != -1)
1942 typeName = typeName.mid(lastSlash + 1);
1943 }
1944
1945 if (typeName.isEmpty()) {
1946 typeName = QString::fromUtf8(object->metaObject()->className());
1947 int marker = typeName.indexOf(QLatin1String("_QMLTYPE_"));
1948 if (marker != -1)
1949 typeName = typeName.left(marker);
1950
1951 marker = typeName.indexOf(QLatin1String("_QML_"));
1952 if (marker != -1) {
1953 typeName = QStringView{typeName}.left(marker) + QLatin1Char('*');
1954 type = QQmlMetaType::qmlType(QMetaType::fromName(typeName.toUtf8()));
1955 if (type.isValid()) {
1956 QString qmlTypeName = type.qmlTypeName();
1957 const int lastSlash = qmlTypeName.lastIndexOf(QLatin1Char('/'));
1958 if (lastSlash != -1)
1959 qmlTypeName = qmlTypeName.mid(lastSlash + 1);
1960 if (!qmlTypeName.isEmpty())
1961 typeName = qmlTypeName;
1962 }
1963 }
1964 }
1965
1966 return typeName;
1967}
1968
1969QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo,
1970 const QMetaObject *baseMetaObject,
1971 QMetaObject *lastMetaObject)
1972{
1973 QList<QQmlProxyMetaObject::ProxyData> metaObjects;
1974 mo = mo->d.superdata;
1975
1976 if (!mo)
1977 return metaObjects;
1978
1979 auto createProxyMetaObject = [&](const QQmlTypePrivate *This,
1980 const QMetaObject *superdataBaseMetaObject,
1981 const QMetaObject *extMetaObject,
1982 QObject *(*extFunc)(QObject *)) {
1983 if (!extMetaObject)
1984 return;
1985
1986 QMetaObjectBuilder builder;
1987 clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject,
1988 extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly);
1989 QMetaObject *mmo = builder.toMetaObject();
1990 mmo->d.superdata = baseMetaObject;
1991 if (!metaObjects.isEmpty())
1992 metaObjects.constLast().metaObject->d.superdata = mmo;
1993 else if (lastMetaObject)
1994 lastMetaObject->d.superdata = mmo;
1995 QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 };
1996 metaObjects << data;
1997 registerMetaObjectForType(mmo, This);
1998 };
1999
2000 for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) {
2001 // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*.
2002 // This algorithm only accounts for the most recently inserted one. That's pretty
2003 // random. However, the availability of types depends on what documents you have
2004 // loaded before. Just adding all possible extensions would also be pretty random.
2005 // The right way to do this would be to take the relations between the QML modules
2006 // into account. For this we would need proper module dependency information.
2007 if (const QQmlTypePrivate *t = data->metaObjectToType.value(mo)) {
2008 if (t->regType == QQmlType::CppType) {
2009 createProxyMetaObject(
2010 t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject,
2011 t->extraData.cppTypeData->extFunc);
2012 } else if (t->regType == QQmlType::SingletonType) {
2013 createProxyMetaObject(
2014 t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject,
2015 t->extraData.singletonTypeData->extFunc);
2016 }
2017 }
2018 };
2019
2020 return metaObjects;
2021}
2022
2023static bool isInternalType(int idx)
2024{
2025 // Qt internal types
2026 switch (idx) {
2027 case QMetaType::UnknownType:
2028 case QMetaType::QStringList:
2029 case QMetaType::QObjectStar:
2030 case QMetaType::VoidStar:
2031 case QMetaType::Nullptr:
2032 case QMetaType::QVariant:
2033 case QMetaType::QLocale:
2034 case QMetaType::QImage: // scarce type, keep as QVariant
2035 case QMetaType::QPixmap: // scarce type, keep as QVariant
2036 return true;
2037 default:
2038 return false;
2039 }
2040}
2041
2042bool QQmlMetaType::isValueType(QMetaType type)
2043{
2044 if (type.flags().testFlag(QMetaType::PointerToQObject))
2045 return false;
2046
2047 if (!type.isValid() || isInternalType(type.id()))
2048 return false;
2049
2050 return valueType(type) != nullptr;
2051}
2052
2053const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
2054{
2055 switch (metaType.id()) {
2056 case QMetaType::QPoint:
2057 return &QQmlPointValueType::staticMetaObject;
2058 case QMetaType::QPointF:
2059 return &QQmlPointFValueType::staticMetaObject;
2060 case QMetaType::QSize:
2061 return &QQmlSizeValueType::staticMetaObject;
2062 case QMetaType::QSizeF:
2063 return &QQmlSizeFValueType::staticMetaObject;
2064 case QMetaType::QRect:
2065 return &QQmlRectValueType::staticMetaObject;
2066 case QMetaType::QRectF:
2067 return &QQmlRectFValueType::staticMetaObject;
2068#if QT_CONFIG(easingcurve)
2069 case QMetaType::QEasingCurve:
2070 return &QQmlEasingValueType::staticMetaObject;
2071#endif
2072 default:
2073 break;
2074 }
2075
2076 // It doesn't have to be a gadget for a QML type to exist, but we don't want to
2077 // call QObject pointers value types. Explicitly registered types also override
2078 // the implicit use of gadgets.
2079 if (!(metaType.flags() & QMetaType::PointerToQObject)) {
2080 const QQmlMetaTypeDataPtr data;
2081 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
2082 if (type && type->regType == QQmlType::CppType && type->typeId == metaType) {
2083 if (const QMetaObject *mo = type->metaObjectForValueType())
2084 return mo;
2085 }
2086 }
2087
2088 // If it _is_ a gadget, we can just use it.
2089 if (metaType.flags() & QMetaType::IsGadget)
2090 return metaType.metaObject();
2091
2092 return nullptr;
2093}
2094
2095QQmlValueType *QQmlMetaType::valueType(QMetaType type)
2096{
2097 QQmlMetaTypeDataPtr data;
2098
2099 const auto it = data->metaTypeToValueType.constFind(type.id());
2100 if (it != data->metaTypeToValueType.constEnd())
2101 return *it;
2102
2103 if (const QMetaObject *mo = metaObjectForValueType(type))
2104 return *data->metaTypeToValueType.insert(type.id(), new QQmlValueType(type, mo));
2105 return *data->metaTypeToValueType.insert(type.id(), nullptr);
2106}
2107
2108QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t)
2109{
2110 const QQmlMetaTypeDataPtr data;
2111 return data->findPropertyCacheInCompositeTypes(t);
2112}
2113
2114void QQmlMetaType::registerInternalCompositeType(
2115 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2116{
2117 QQmlMetaTypeDataPtr data;
2118
2119 auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) {
2120 Q_ASSERT(iface);
2121 Q_ASSERT(compilationUnit);
2122
2123 // We can't assert on anything else here. We may get a completely new type as exposed
2124 // by the qmldiskcache test that changes a QML file in place during the execution
2125 // of the test.
2126 auto it = data->compositeTypes.insert(iface, compilationUnit);
2127
2128 // Erase any existing entry of the same iface/CU
2129 // TODO: In theory we should be able to avoid this case, but the current architecture
2130 // unifies the code paths for "compilation unit detected in cache" and "new
2131 // compilation unit compiled from source", and in both cases we end up here.
2132 const auto end = data->compositeTypes.end();
2133 while (++it != end && it.key() == iface) {
2134 if (*it == compilationUnit) {
2135 data->compositeTypes.erase(it);
2136 break;
2137 }
2138 }
2139 };
2140
2141 doInsert(compilationUnit->metaType().iface());
2142 for (auto &&inlineData: compilationUnit->inlineComponentData)
2143 doInsert(inlineData.qmlType.typeId().iface());
2144
2145 // Prune any speculative inline component types that were created before this CU
2146 // registered but don't actually exist. This handles the cyclic reference case where
2147 // the IC type was created speculatively when the base type wasn't registered yet.
2148 // Skip pruning for anonymous/empty URLs.
2149 const QUrl baseUrl = compilationUnit->finalUrl();
2150 if (baseUrl.isEmpty())
2151 return;
2152
2153 for (auto it = data->speculativeInlineComponentTypes.begin();
2154 it != data->speculativeInlineComponentTypes.end();) {
2155 const QUrl &url = *it;
2156 if (!equalBaseUrls(url, baseUrl)) {
2157 ++it;
2158 continue;
2159 }
2160
2161 const QString icName = url.fragment();
2162 if (compilationUnit->inlineComponentData.contains(icName)) {
2163 // IC exists - no longer speculative, just remove from tracking
2164 it = data->speculativeInlineComponentTypes.erase(it);
2165 continue;
2166 }
2167
2168 // Speculative IC type that doesn't exist - remove it from type registry
2169 const auto typeIt = data->urlToType.constFind(url);
2170 Q_ASSERT(typeIt != data->urlToType.constEnd());
2171
2172 const QQmlTypePrivate *d = *typeIt;
2173 data->urlToType.erase(typeIt);
2174 data->idToType.remove(d->typeId.id());
2175 data->idToType.remove(d->listId.id());
2176 data->types[d->index] = {};
2177 it = data->speculativeInlineComponentTypes.erase(it);
2178 }
2179}
2180
2181void QQmlMetaType::unregisterInternalCompositeType(
2182 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2183{
2184 QQmlMetaTypeDataPtr data;
2185
2186 auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) {
2187 if (!iface)
2188 return;
2189
2190 const auto [begin, end] = std::as_const(data->compositeTypes).equal_range(iface);
2191 for (auto it = begin; it != end; ++it) {
2192 if (*it == compilationUnit) {
2193 QQmlMetaTypeData::clearCompositeType(compilationUnit);
2194 data->compositeTypes.erase(it);
2195 break;
2196 }
2197 }
2198 };
2199
2200 doRemove(compilationUnit->metaType().iface());
2201 for (auto &&inlineData: compilationUnit->inlineComponentData)
2202 doRemove(inlineData.qmlType.typeId().iface());
2203
2204 const QUrl baseUrl = compilationUnit->finalUrl();
2205 if (baseUrl.isEmpty())
2206 return;
2207
2208 // Clean up any speculative IC types for this base URL
2209 for (auto it = data->speculativeInlineComponentTypes.begin();
2210 it != data->speculativeInlineComponentTypes.end();) {
2211 if (equalBaseUrls(*it, baseUrl))
2212 it = data->speculativeInlineComponentTypes.erase(it);
2213 else
2214 ++it;
2215 }
2216}
2217
2218int QQmlMetaType::countInternalCompositeTypeSelfReferences(
2219 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2220{
2221 QQmlMetaTypeDataPtr data;
2222 return doCountInternalCompositeTypeSelfReferences(data, compilationUnit);
2223}
2224
2225QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
2226 QMetaType type)
2227{
2228 const QQmlMetaTypeDataPtr data;
2229
2230 // Obtains the last inserted one
2231 return data->compositeTypes.value(type.iface());
2232}
2233
2234QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
2235 const QUrl &url)
2236{
2237 const QUrl normalized = QQmlMetaType::normalizedUrl(url);
2238 QQmlMetaTypeDataPtr data;
2239
2240 auto found = data->urlToType.constFind(normalized);
2241 if (found == data->urlToType.constEnd())
2242 return QQmlRefPointer<QV4::CompiledData::CompilationUnit>();
2243
2244 // Retrieves last inserted one
2245 const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface());
2246 return composite == data->compositeTypes.constEnd()
2247 ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>()
2248 : composite.value();
2249}
2250
2251QT_END_NAMESPACE
QQmlMetaTypeData & operator*()
QQmlMetaTypeData * operator->()
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QQmlTypePrivate * createQQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterType &type)
static void addTypeToData(QQmlTypePrivate *type, QQmlMetaTypeData *data)
static void addQQmlMetaTypeInterfaces(QQmlMetaTypeData *data, const QUrl &url, QQmlTypePrivate *priv, const QByteArray &className)
static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, QTypeRevision version)
static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit)
static QQmlTypePrivate * createQQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type, const QQmlType::SingletonInstanceInfo::ConstPtr &siinfo)
static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d)
static bool isInternalType(int idx)
static QQmlTypePrivate * createQQmlType(QQmlMetaTypeData *data, const QQmlPrivate::RegisterInterface &type)
static QQmlType createTypeForUrl(QQmlMetaTypeData *data, const QUrl &url, const QHashedStringRef &qualifiedType, QQmlMetaType::CompositeTypeLookupMode mode, QList< QQmlError > *errors, QTypeRevision version)
static bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *data, const char *uri, const QString &typeName, QTypeRevision version, QMetaType::TypeFlags flags)
static QQmlTypeModule * getTypeModule(const QHashedString &uri, QTypeRevision version, QQmlMetaTypeData *data)
static bool inherits(const QQmlPropertyCache::ConstPtr &derived, const QQmlPropertyCache::ConstPtr &base)
bool canConvertToPropCacheOrMetaObject(const QQmlMetaTypeDataPtr &data, const From &from, QMetaType metaType, CanConvertPropCache &&canConvertPropCache, CanConvertMetaObject &&canConvertMetaObject)
static QQmlType doRegisterInlineComponentType(QQmlMetaTypeData *data, const QUrl &url)
static int doCountInternalCompositeTypeSelfReferences(QQmlMetaTypeData *data, const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit)
static QString registrationTypeString(QQmlType::RegistrationType typeType)