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 * Returns all candidate property caches for a composite
1565 * metatype instead of only the last inserted one.
1566 * compare rawPropertyCacheForType (which handles however also non-composites)
1567 */
1568QVarLengthArray<QQmlPropertyCache::ConstPtr, 4>
1569QQmlMetaType::rawCompositePropertyCachesForType(QMetaType metaType)
1570{
1571 const QQmlMetaTypeDataPtr data;
1572 return data->findPropertyCachesInCompositeTypes(metaType);
1573}
1574
1575/*!
1576 * \internal
1577 *
1578 * Look up by QQmlType and version. We only fall back to lookup by metaobject if the type
1579 * has no revisiononed attributes here. Unspecified versions are interpreted as "any".
1580 */
1581QQmlPropertyCache::ConstPtr QQmlMetaType::rawPropertyCacheForType(
1582 QMetaType metaType, QTypeRevision version)
1583{
1584 QQmlMetaTypeDataPtr data;
1585 if (auto composite = data->findPropertyCacheInCompositeTypes(metaType))
1586 return composite;
1587
1588 const QQmlTypePrivate *typePriv = data->idToType.value(metaType.id());
1589 if (!typePriv || typePriv->typeId != metaType)
1590 return QQmlPropertyCache::ConstPtr();
1591
1592 const QQmlType type(typePriv);
1593 if (type.containsRevisionedAttributes()) {
1594 // It can only have (revisioned) properties or methods if it has a metaobject
1595 Q_ASSERT(type.metaObject());
1596 return data->propertyCache(type, version);
1597 }
1598
1599 if (const QMetaObject *metaObject = type.metaObject())
1600 return data->propertyCache(metaObject, version);
1601
1602 return QQmlPropertyCache::ConstPtr();
1603}
1604
1605template<typename From, typename CanConvertPropCache, typename CanConvertMetaObject>
1607 const QQmlMetaTypeDataPtr &data, const From &from, QMetaType metaType,
1608 CanConvertPropCache &&canConvertPropCache, CanConvertMetaObject &&canConvertMetaObject)
1609{
1610 // There can be multiple composite types mapped to the same metatype. Since a property metatype
1611 // alone cannot specify which property cache is actually meant, the only thing we can do here
1612 // is check them all.
1613 // TODO: Ideally, the QQmlMetaTypeData should be completely dissolved and every composite
1614 // metatype should be specific to the type loader that created it. Then we wouldn't have
1615 // these problems.
1616 auto [it, end] = data->compositeTypes.equal_range(metaType.iface());
1617 if (it != end) {
1618 do {
1619 if (canConvertPropCache(
1620 from, QQmlMetaTypeData::propertyCacheForPotentialInlineComponentType(
1621 metaType, it))) {
1622 return true;
1623 }
1624 } while(++it != end);
1625
1626 // If it is a composite type and nothing matches we have a certain "no".
1627 // We don't call metaObject() on the type then because that searches compositeTypes, too.
1628 return false;
1629 }
1630
1631 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
1632 if (type && type->typeId == metaType && type->baseMetaObject)
1633 return canConvertMetaObject(from, type->baseMetaObject);
1634
1635 // Types we don't know may still have metaobjects
1636 if (const QMetaObject *metaObject = metaType.metaObject())
1637 return canConvertMetaObject(from, metaObject);
1638
1639 return false;
1640}
1641
1642bool QQmlMetaType::canConvert(QObject *o, QMetaType metaType)
1643{
1644 QQmlMetaTypeDataPtr data;
1645
1646 return canConvertToPropCacheOrMetaObject(
1647 data, o, metaType, [](QObject *o, const QQmlPropertyCache::ConstPtr &propCache) {
1648 return QQmlMetaObject::canConvert(o, propCache);
1649 }, [](QObject *o, const QMetaObject *metaObject) {
1650 return QQmlMetaObject::canConvert(o, metaObject);
1651 });
1652}
1653
1654static bool inherits(
1655 const QQmlPropertyCache::ConstPtr &derived, const QQmlPropertyCache::ConstPtr &base)
1656{
1657 for (QQmlPropertyCache::ConstPtr parent = derived; parent; parent = parent->parent()) {
1658 if (parent == base)
1659 return true;
1660 }
1661
1662 return false;
1663}
1664
1665bool QQmlMetaType::canConvert(const QQmlPropertyCache::ConstPtr &from, QMetaType metaType)
1666{
1667 QQmlMetaTypeDataPtr data;
1668
1669 return canConvertToPropCacheOrMetaObject(
1670 data, from, metaType,
1671 [](const QQmlPropertyCache::ConstPtr &from, const QQmlPropertyCache::ConstPtr &to) {
1672 return inherits(from, to);
1673 }, [&](const QQmlPropertyCache::ConstPtr &from, const QMetaObject *toMeta) {
1674 if (const QMetaObject *fromMeta = from->metaObject())
1675 return QQmlMetaObject::canConvert(fromMeta, toMeta);
1676 return inherits(from, data->propertyCache(toMeta, QTypeRevision()));
1677 });
1678}
1679
1680void QQmlMetaType::unregisterType(int typeIndex)
1681{
1682 QQmlMetaTypeDataPtr data;
1683 const QQmlType type = data->types.value(typeIndex).type;
1684 if (const QQmlTypePrivate *d = type.priv()) {
1685 if (d->regType == QQmlType::CompositeType || d->regType == QQmlType::CompositeSingletonType) {
1686 removeFromInlineComponents(data->urlToType, d);
1687 removeFromInlineComponents(data->speculativeInlineComponentTypes, d);
1688 }
1689 removeQQmlTypePrivate(data->idToType, d);
1690 removeQQmlTypePrivate(data->nameToType, d);
1691 removeQQmlTypePrivate(data->urlToType, d);
1692 removeQQmlTypePrivate(data->metaObjectToType, d);
1693 data->speculativeInlineComponentTypes.remove(d->sourceUrl());
1694 for (auto & module : data->uriToModule)
1695 module->remove(d);
1696 data->types[typeIndex] = QQmlMetaTypeData::Type();
1697 data->undeletableTypes.remove(type);
1698 }
1699}
1700
1701void QQmlMetaType::registerMetaObjectForType(const QMetaObject *metaobject, const QQmlTypePrivate *type)
1702{
1703 Q_ASSERT(type);
1704
1705 QQmlMetaTypeDataPtr data;
1706 data->metaObjectToType.insert(metaobject, type);
1707}
1708
1709static bool hasActiveInlineComponents(const QQmlMetaTypeData *data, const QQmlTypePrivate *d)
1710{
1711 for (auto it = data->urlToType.begin(), end = data->urlToType.end();
1712 it != end; ++it) {
1713 if (!QQmlMetaType::equalBaseUrls(it.key(), d->sourceUrl()))
1714 continue;
1715
1716 const QQmlTypePrivate *icPriv = *it;
1717 if (icPriv && icPriv->count() > 1)
1718 return true;
1719 }
1720 return false;
1721}
1722
1724 QQmlMetaTypeData *data,
1725 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
1726{
1727 int result = 0;
1728 auto doCheck = [&](const QtPrivate::QMetaTypeInterface *iface) {
1729 if (!iface)
1730 return;
1731
1732 const auto [begin, end] = std::as_const(data->compositeTypes).equal_range(iface);
1733 for (auto it = begin; it != end; ++it) {
1734 if (*it == compilationUnit)
1735 ++result;
1736 }
1737 };
1738
1739 doCheck(compilationUnit->metaType().iface());
1740 for (const auto &inlineData : std::as_const(compilationUnit->inlineComponentData))
1741 doCheck(inlineData.qmlType.typeId().iface());
1742
1743 return result;
1744}
1745
1746void QQmlMetaType::freeUnusedTypesAndCaches()
1747{
1748 QQmlMetaTypeDataPtr data;
1749
1750 // in case this is being called during program exit, `data` might be destructed already
1751 if (!data.isValid())
1752 return;
1753
1754 bool droppedAtLeastOneComposite;
1755 do {
1756 droppedAtLeastOneComposite = false;
1757 auto it = data->compositeTypes.cbegin();
1758 while (it != data->compositeTypes.cend()) {
1759 const auto &cu = *it;
1760 if (cu->count() <= doCountInternalCompositeTypeSelfReferences(data, cu)) {
1761 QQmlMetaTypeData::clearCompositeType(cu);
1762 it = data->compositeTypes.erase(it);
1763 droppedAtLeastOneComposite = true;
1764 } else {
1765 ++it;
1766 }
1767 }
1768 } while (droppedAtLeastOneComposite);
1769
1770 bool deletedAtLeastOneType;
1771 do {
1772 deletedAtLeastOneType = false;
1773 auto it = data->types.begin();
1774 while (it != data->types.end()) {
1775 const QQmlTypePrivate *d = it->type.priv();
1776 if (d && d->count() == 1 && !hasActiveInlineComponents(data, d)) {
1777 deletedAtLeastOneType = true;
1778
1779 if (d->regType == QQmlType::CompositeType
1780 || d->regType == QQmlType::CompositeSingletonType) {
1781 removeFromInlineComponents(data->urlToType, d);
1782 removeFromInlineComponents(data->speculativeInlineComponentTypes, d);
1783 }
1784 removeQQmlTypePrivate(data->idToType, d);
1785 removeQQmlTypePrivate(data->nameToType, d);
1786 removeQQmlTypePrivate(data->urlToType, d);
1787 removeQQmlTypePrivate(data->metaObjectToType, d);
1788 data->speculativeInlineComponentTypes.remove(d->sourceUrl());
1789
1790 for (auto &module : data->uriToModule)
1791 module->remove(d);
1792
1793 *it = QQmlMetaTypeData::Type();
1794 } else {
1795 ++it;
1796 }
1797 }
1798 } while (deletedAtLeastOneType);
1799
1800 bool deletedAtLeastOneCache;
1801 do {
1802 deletedAtLeastOneCache = false;
1803 auto it = data->propertyCaches.begin();
1804 while (it != data->propertyCaches.end()) {
1805 if ((*it)->count() == 1) {
1806 it = data->propertyCaches.erase(it);
1807 deletedAtLeastOneCache = true;
1808 } else {
1809 ++it;
1810 }
1811 }
1812 } while (deletedAtLeastOneCache);
1813}
1814
1815/*!
1816 Returns the list of registered QML type names.
1817*/
1818QList<QString> QQmlMetaType::qmlTypeNames()
1819{
1820 const QQmlMetaTypeDataPtr data;
1821
1822 QList<QString> names;
1823 names.reserve(data->nameToType.size());
1824 QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.cbegin();
1825 while (it != data->nameToType.cend()) {
1826 QQmlType t(*it);
1827 names += t.qmlTypeName();
1828 ++it;
1829 }
1830
1831 return names;
1832}
1833
1834/*!
1835 Returns the list of registered QML types.
1836*/
1837QList<QQmlType> QQmlMetaType::qmlTypes()
1838{
1839 const QQmlMetaTypeDataPtr data;
1840
1841 QList<QQmlType> types;
1842 for (const QQmlTypePrivate *t : data->nameToType)
1843 types.append(QQmlType(t));
1844
1845 return types;
1846}
1847
1848/*!
1849 Returns the list of all registered types.
1850*/
1851QList<QQmlType> QQmlMetaType::qmlAllTypes()
1852{
1853 const QQmlMetaTypeDataPtr data;
1854 QList<QQmlType> types;
1855 types.reserve(data->types.size());
1856 std::transform(
1857 data->types.constBegin(), data->types.constEnd(),
1858 std::back_inserter(types), [](const auto &type) { return type.type; });
1859 return types;
1860}
1861
1862/*!
1863 Returns the list of registered QML singleton types.
1864*/
1865QList<QQmlType> QQmlMetaType::qmlSingletonTypes()
1866{
1867 const QQmlMetaTypeDataPtr data;
1868
1869 QList<QQmlType> retn;
1870 for (const auto t : std::as_const(data->nameToType)) {
1871 QQmlType type(t);
1872 if (type.isSingleton())
1873 retn.append(type);
1874 }
1875 return retn;
1876}
1877
1878static bool isFullyTyped(const QQmlPrivate::CachedQmlUnit *unit)
1879{
1880 quint32 numTypedFunctions = 0;
1881 for (const QQmlPrivate::AOTCompiledFunction *function = unit->aotCompiledFunctions;
1882 function; ++function) {
1883 if (function->functionPtr)
1884 ++numTypedFunctions;
1885 else
1886 return false;
1887 }
1888 return numTypedFunctions == unit->qmlData->functionTableSize;
1889}
1890
1891const QQmlPrivate::CachedQmlUnit *QQmlMetaType::findCachedCompilationUnit(
1892 const QUrl &uri, QQmlMetaType::CacheMode mode, CachedUnitLookupError *status)
1893{
1894 Q_ASSERT(mode != RejectAll);
1895 const QQmlMetaTypeDataPtr data;
1896
1897 for (const auto lookup : std::as_const(data->lookupCachedQmlUnit)) {
1898 if (const QQmlPrivate::CachedQmlUnit *unit = lookup(uri)) {
1899 QString error;
1900 if (!unit->qmlData->verifyHeader(QDateTime(), &error)) {
1901 qCDebug(DBG_DISK_CACHE) << "Error loading pre-compiled file " << uri << ":" << error;
1902 if (status)
1903 *status = CachedUnitLookupError::VersionMismatch;
1904 return nullptr;
1905 }
1906
1907 if (mode == RequireFullyTyped && !isFullyTyped(unit)) {
1908 qCDebug(DBG_DISK_CACHE)
1909 << "Error loading pre-compiled file " << uri
1910 << ": compilation unit contains functions not compiled to native code.";
1911 if (status)
1912 *status = CachedUnitLookupError::NotFullyTyped;
1913 return nullptr;
1914 }
1915
1916 if (status)
1917 *status = CachedUnitLookupError::NoError;
1918 return unit;
1919 }
1920 }
1921
1922 qCDebug(DBG_DISK_CACHE) << "No pre-compiled unit found for" << uri;
1923
1924 if (status)
1925 *status = CachedUnitLookupError::NoUnitFound;
1926
1927 return nullptr;
1928}
1929
1930void QQmlMetaType::prependCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
1931{
1932 QQmlMetaTypeDataPtr data;
1933 data->lookupCachedQmlUnit.prepend(handler);
1934}
1935
1936void QQmlMetaType::removeCachedUnitLookupFunction(QQmlPrivate::QmlUnitCacheLookupFunction handler)
1937{
1938 QQmlMetaTypeDataPtr data;
1939 data->lookupCachedQmlUnit.removeAll(handler);
1940}
1941
1942/*!
1943 Returns the pretty QML type name (e.g. 'Item' instead of 'QtQuickItem') for the given object.
1944 */
1945QString QQmlMetaType::prettyTypeName(const QObject *object)
1946{
1947 QString typeName;
1948
1949 if (!object)
1950 return typeName;
1951
1952 QQmlType type = QQmlMetaType::qmlType(object->metaObject());
1953 if (type.isValid()) {
1954 typeName = type.qmlTypeName();
1955 const int lastSlash = typeName.lastIndexOf(QLatin1Char('/'));
1956 if (lastSlash != -1)
1957 typeName = typeName.mid(lastSlash + 1);
1958 }
1959
1960 if (typeName.isEmpty()) {
1961 typeName = QString::fromUtf8(object->metaObject()->className());
1962 int marker = typeName.indexOf(QLatin1String("_QMLTYPE_"));
1963 if (marker != -1)
1964 typeName = typeName.left(marker);
1965
1966 marker = typeName.indexOf(QLatin1String("_QML_"));
1967 if (marker != -1) {
1968 typeName = QStringView{typeName}.left(marker) + QLatin1Char('*');
1969 type = QQmlMetaType::qmlType(QMetaType::fromName(typeName.toUtf8()));
1970 if (type.isValid()) {
1971 QString qmlTypeName = type.qmlTypeName();
1972 const int lastSlash = qmlTypeName.lastIndexOf(QLatin1Char('/'));
1973 if (lastSlash != -1)
1974 qmlTypeName = qmlTypeName.mid(lastSlash + 1);
1975 if (!qmlTypeName.isEmpty())
1976 typeName = qmlTypeName;
1977 }
1978 }
1979 }
1980
1981 return typeName;
1982}
1983
1984QList<QQmlProxyMetaObject::ProxyData> QQmlMetaType::proxyData(const QMetaObject *mo,
1985 const QMetaObject *baseMetaObject,
1986 QMetaObject *lastMetaObject)
1987{
1988 QList<QQmlProxyMetaObject::ProxyData> metaObjects;
1989 mo = mo->d.superdata;
1990
1991 if (!mo)
1992 return metaObjects;
1993
1994 auto createProxyMetaObject = [&](const QQmlTypePrivate *This,
1995 const QMetaObject *superdataBaseMetaObject,
1996 const QMetaObject *extMetaObject,
1997 QObject *(*extFunc)(QObject *)) {
1998 if (!extMetaObject)
1999 return;
2000
2001 QMetaObjectBuilder builder;
2002 clone(builder, extMetaObject, superdataBaseMetaObject, baseMetaObject,
2003 extFunc ? QQmlMetaType::CloneAll : QQmlMetaType::CloneEnumsOnly);
2004 QMetaObject *mmo = builder.toMetaObject();
2005 mmo->d.superdata = baseMetaObject;
2006 if (!metaObjects.isEmpty())
2007 metaObjects.constLast().metaObject->d.superdata = mmo;
2008 else if (lastMetaObject)
2009 lastMetaObject->d.superdata = mmo;
2010 QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 };
2011 metaObjects << data;
2012 registerMetaObjectForType(mmo, This);
2013 };
2014
2015 for (const QQmlMetaTypeDataPtr data; mo; mo = mo->d.superdata) {
2016 // TODO: There can in fact be multiple QQmlTypePrivate* for a single QMetaObject*.
2017 // This algorithm only accounts for the most recently inserted one. That's pretty
2018 // random. However, the availability of types depends on what documents you have
2019 // loaded before. Just adding all possible extensions would also be pretty random.
2020 // The right way to do this would be to take the relations between the QML modules
2021 // into account. For this we would need proper module dependency information.
2022 if (const QQmlTypePrivate *t = data->metaObjectToType.value(mo)) {
2023 if (t->regType == QQmlType::CppType) {
2024 createProxyMetaObject(
2025 t, t->baseMetaObject, t->extraData.cppTypeData->extMetaObject,
2026 t->extraData.cppTypeData->extFunc);
2027 } else if (t->regType == QQmlType::SingletonType) {
2028 createProxyMetaObject(
2029 t, t->baseMetaObject, t->extraData.singletonTypeData->extMetaObject,
2030 t->extraData.singletonTypeData->extFunc);
2031 }
2032 }
2033 };
2034
2035 return metaObjects;
2036}
2037
2038static bool isInternalType(int idx)
2039{
2040 // Qt internal types
2041 switch (idx) {
2042 case QMetaType::UnknownType:
2043 case QMetaType::QStringList:
2044 case QMetaType::QObjectStar:
2045 case QMetaType::VoidStar:
2046 case QMetaType::Nullptr:
2047 case QMetaType::QVariant:
2048 case QMetaType::QLocale:
2049 case QMetaType::QImage: // scarce type, keep as QVariant
2050 case QMetaType::QPixmap: // scarce type, keep as QVariant
2051 return true;
2052 default:
2053 return false;
2054 }
2055}
2056
2057bool QQmlMetaType::isValueType(QMetaType type)
2058{
2059 if (type.flags().testFlag(QMetaType::PointerToQObject))
2060 return false;
2061
2062 if (!type.isValid() || isInternalType(type.id()))
2063 return false;
2064
2065 return valueType(type) != nullptr;
2066}
2067
2068const QMetaObject *QQmlMetaType::metaObjectForValueType(QMetaType metaType)
2069{
2070 switch (metaType.id()) {
2071 case QMetaType::QPoint:
2072 return &QQmlPointValueType::staticMetaObject;
2073 case QMetaType::QPointF:
2074 return &QQmlPointFValueType::staticMetaObject;
2075 case QMetaType::QSize:
2076 return &QQmlSizeValueType::staticMetaObject;
2077 case QMetaType::QSizeF:
2078 return &QQmlSizeFValueType::staticMetaObject;
2079 case QMetaType::QRect:
2080 return &QQmlRectValueType::staticMetaObject;
2081 case QMetaType::QRectF:
2082 return &QQmlRectFValueType::staticMetaObject;
2083#if QT_CONFIG(easingcurve)
2084 case QMetaType::QEasingCurve:
2085 return &QQmlEasingValueType::staticMetaObject;
2086#endif
2087 default:
2088 break;
2089 }
2090
2091 // It doesn't have to be a gadget for a QML type to exist, but we don't want to
2092 // call QObject pointers value types. Explicitly registered types also override
2093 // the implicit use of gadgets.
2094 if (!(metaType.flags() & QMetaType::PointerToQObject)) {
2095 const QQmlMetaTypeDataPtr data;
2096 const QQmlTypePrivate *type = data->idToType.value(metaType.id());
2097 if (type && type->regType == QQmlType::CppType && type->typeId == metaType) {
2098 if (const QMetaObject *mo = type->metaObjectForValueType())
2099 return mo;
2100 }
2101 }
2102
2103 // If it _is_ a gadget, we can just use it.
2104 if (metaType.flags() & QMetaType::IsGadget)
2105 return metaType.metaObject();
2106
2107 return nullptr;
2108}
2109
2110QQmlValueType *QQmlMetaType::valueType(QMetaType type)
2111{
2112 QQmlMetaTypeDataPtr data;
2113
2114 const auto it = data->metaTypeToValueType.constFind(type.id());
2115 if (it != data->metaTypeToValueType.constEnd())
2116 return *it;
2117
2118 if (const QMetaObject *mo = metaObjectForValueType(type))
2119 return *data->metaTypeToValueType.insert(type.id(), new QQmlValueType(type, mo));
2120 return *data->metaTypeToValueType.insert(type.id(), nullptr);
2121}
2122
2123QQmlPropertyCache::ConstPtr QQmlMetaType::findPropertyCacheInCompositeTypes(QMetaType t)
2124{
2125 const QQmlMetaTypeDataPtr data;
2126 return data->findPropertyCacheInCompositeTypes(t);
2127}
2128
2129void QQmlMetaType::registerInternalCompositeType(
2130 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2131{
2132 QQmlMetaTypeDataPtr data;
2133
2134 auto doInsert = [&data, &compilationUnit](const QtPrivate::QMetaTypeInterface *iface) {
2135 Q_ASSERT(iface);
2136 Q_ASSERT(compilationUnit);
2137
2138 // We can't assert on anything else here. We may get a completely new type as exposed
2139 // by the qmldiskcache test that changes a QML file in place during the execution
2140 // of the test.
2141 auto it = data->compositeTypes.insert(iface, compilationUnit);
2142
2143 // Erase any existing entry of the same iface/CU
2144 // TODO: In theory we should be able to avoid this case, but the current architecture
2145 // unifies the code paths for "compilation unit detected in cache" and "new
2146 // compilation unit compiled from source", and in both cases we end up here.
2147 const auto end = data->compositeTypes.end();
2148 while (++it != end && it.key() == iface) {
2149 if (*it == compilationUnit) {
2150 data->compositeTypes.erase(it);
2151 break;
2152 }
2153 }
2154 };
2155
2156 doInsert(compilationUnit->metaType().iface());
2157 for (const auto &inlineData : std::as_const(compilationUnit->inlineComponentData))
2158 doInsert(inlineData.qmlType.typeId().iface());
2159
2160 // Prune any speculative inline component types that were created before this CU
2161 // registered but don't actually exist. This handles the cyclic reference case where
2162 // the IC type was created speculatively when the base type wasn't registered yet.
2163 // Skip pruning for anonymous/empty URLs.
2164 const QUrl baseUrl = compilationUnit->finalUrl();
2165 if (baseUrl.isEmpty())
2166 return;
2167
2168 for (auto it = data->speculativeInlineComponentTypes.begin();
2169 it != data->speculativeInlineComponentTypes.end();) {
2170 const QUrl &url = *it;
2171 if (!equalBaseUrls(url, baseUrl)) {
2172 ++it;
2173 continue;
2174 }
2175
2176 const QString icName = url.fragment();
2177 if (compilationUnit->inlineComponentData.contains(icName)) {
2178 // IC exists - no longer speculative, just remove from tracking
2179 it = data->speculativeInlineComponentTypes.erase(it);
2180 continue;
2181 }
2182
2183 // Speculative IC type that doesn't exist - remove it from type registry
2184 const auto typeIt = data->urlToType.constFind(url);
2185 Q_ASSERT(typeIt != data->urlToType.constEnd());
2186
2187 const QQmlTypePrivate *d = *typeIt;
2188 data->urlToType.erase(typeIt);
2189 data->idToType.remove(d->typeId.id());
2190 data->idToType.remove(d->listId.id());
2191 data->types[d->index] = {};
2192 it = data->speculativeInlineComponentTypes.erase(it);
2193 }
2194}
2195
2196void QQmlMetaType::unregisterInternalCompositeType(
2197 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2198{
2199 QQmlMetaTypeDataPtr data;
2200
2201 auto doRemove = [&](const QtPrivate::QMetaTypeInterface *iface) {
2202 if (!iface)
2203 return;
2204
2205 const auto [begin, end] = std::as_const(data->compositeTypes).equal_range(iface);
2206 for (auto it = begin; it != end; ++it) {
2207 if (*it == compilationUnit) {
2208 QQmlMetaTypeData::clearCompositeType(compilationUnit);
2209 data->compositeTypes.erase(it);
2210 break;
2211 }
2212 }
2213 };
2214
2215 doRemove(compilationUnit->metaType().iface());
2216 for (const auto &inlineData : std::as_const(compilationUnit->inlineComponentData))
2217 doRemove(inlineData.qmlType.typeId().iface());
2218
2219 const QUrl baseUrl = compilationUnit->finalUrl();
2220 if (baseUrl.isEmpty())
2221 return;
2222
2223 // Clean up any speculative IC types for this base URL
2224 for (auto it = data->speculativeInlineComponentTypes.begin();
2225 it != data->speculativeInlineComponentTypes.end();) {
2226 if (equalBaseUrls(*it, baseUrl))
2227 it = data->speculativeInlineComponentTypes.erase(it);
2228 else
2229 ++it;
2230 }
2231}
2232
2233void QQmlMetaType::deepClearCompositeType(
2234 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &oldUnit)
2235{
2236 unregisterInternalCompositeType(oldUnit);
2237 unregisterType(oldUnit->qmlType.index());
2238 for (const auto &icData : std::as_const(oldUnit->inlineComponentData))
2239 unregisterType(icData.qmlType.index());
2240}
2241
2242int QQmlMetaType::countInternalCompositeTypeSelfReferences(
2243 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
2244{
2245 QQmlMetaTypeDataPtr data;
2246 return doCountInternalCompositeTypeSelfReferences(data, compilationUnit);
2247}
2248
2249QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
2250 QMetaType type)
2251{
2252 const QQmlMetaTypeDataPtr data;
2253
2254 // Obtains the last inserted one
2255 return data->compositeTypes.value(type.iface());
2256}
2257
2258QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlMetaType::obtainCompilationUnit(
2259 const QUrl &url)
2260{
2261 const QUrl normalized = QQmlMetaType::normalizedUrl(url);
2262 QQmlMetaTypeDataPtr data;
2263
2264 auto found = data->urlToType.constFind(normalized);
2265 if (found == data->urlToType.constEnd())
2266 return QQmlRefPointer<QV4::CompiledData::CompilationUnit>();
2267
2268 // Retrieves last inserted one
2269 const auto composite = data->compositeTypes.constFind(found.value()->typeId.iface());
2270 return composite == data->compositeTypes.constEnd()
2271 ? QQmlRefPointer<QV4::CompiledData::CompilationUnit>()
2272 : composite.value();
2273}
2274
2275QT_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)