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