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