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