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
qqml.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 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 "qqml.h"
6
7#include <private/qjsvalue_p.h>
8#include <private/qqmlbuiltinfunctions_p.h>
9#include <private/qqmlcomponent_p.h>
10#include <private/qqmlengine_p.h>
11#include <private/qqmlfinalizer_p.h>
12#include <private/qqmlimport_p.h>
13#include <private/qqmlloggingcategorybase_p.h>
14#include <private/qqmlmetaobject_p.h>
15#include <private/qqmlmetatype_p.h>
16#include <private/qqmlmetatypedata_p.h>
17#include <private/qqmltype_p_p.h>
18#include <private/qqmltypemodule_p.h>
19#include <private/qqmltypenamecache_p.h>
20#include <private/qqmltypewrapper_p.h>
21#include <private/qqmlvaluetypewrapper_p.h>
22#include <private/qv4alloca_p.h>
23#include <private/qv4dateobject_p.h>
24#include <private/qv4errorobject_p.h>
25#include <private/qv4identifiertable_p.h>
26#include <private/qv4lookup_p.h>
27#include <private/qv4qobjectwrapper_p.h>
28
29#include <QtQml/qqmlprivate.h>
30
31#include <QtCore/qmutex.h>
32#include <QtCore/qmetasequence.h>
33
35
36/*!
37 \headerfile <qqml.h>
38 \inmodule QtQml
39 \title Functions to register C++ types to QML
40
41 This header provides a collection of functions that allow the registration of
42 C++ types to QML.
43
44 \sa {Overview - QML and C++ Integration}, qqmlintegration.h, qmltyperegistrar
45*/
46
47/*!
48 \internal
49
50 This method completes the setup of all deferred properties of \a object.
51 Deferred properties are declared with
52 Q_CLASSINFO("DeferredPropertyNames", "comma,separated,property,list");
53
54 Any binding to a deferred property is not executed when the object is instantiated,
55 but only when completion is requested with qmlExecuteDeferred, or by manually
56 calling QQmlComponentPrivate::beginDeferred and completeDeferred.
57
58 \sa QV4::CompiledData::Binding::IsDeferredBinding,
59 QV4::CompiledData::Object::HasDeferredBindings,
60 QQmlData::deferData,
61 QQmlObjectCreator::setupBindings
62*/
63void qmlExecuteDeferred(QObject *object)
64{
65 QQmlData *data = QQmlData::get(object);
66
67 if (!data
68 || !data->context
69 || !data->context->engine()
70 || data->deferredData.isEmpty()
71 || data->wasDeleted(object)) {
72 return;
73 }
74
75 if (!data->propertyCache)
76 data->propertyCache = QQmlMetaType::propertyCache(object->metaObject());
77
78 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(data->context->engine());
79
80 QQmlComponentPrivate::DeferredState state;
81 QQmlComponentPrivate::beginDeferred(ep, object, &state);
82
83 // Release the reference for the deferral action (we still have one from construction)
84 data->releaseDeferredData();
85
86 QQmlComponentPrivate::completeDeferred(ep, &state);
87}
88
89QQmlContext *qmlContext(const QObject *obj)
90{
91 return QQmlEngine::contextForObject(obj);
92}
93
94QQmlEngine *qmlEngine(const QObject *obj)
95{
96 QQmlData *data = QQmlData::get(obj);
97 if (!data || !data->context)
98 return nullptr;
99 return data->context->engine();
100}
101
102static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data,
103 QObject *object, bool create)
104{
105 if (!pf)
106 return nullptr;
107
108 QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(pf) : 0;
109 if (rv || !create)
110 return rv;
111
112 rv = pf(object);
113
114 if (rv)
115 data->attachedProperties()->insert(pf, rv);
116
117 return rv;
118}
119
121 const QMetaObject *attachedMetaObject)
122{
123 QQmlEngine *engine = object ? qmlEngine(object) : nullptr;
124 return QQmlMetaType::attachedPropertiesFunc(
125 engine ? QQmlTypeLoader::get(engine) : nullptr, attachedMetaObject);
126}
127
128QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool createIfMissing)
129{
130 if (!object)
131 return nullptr;
132
133 QQmlData *data = QQmlData::get(object, createIfMissing);
134
135 // Attached properties are only on objects created by QML,
136 // unless explicitly requested (create==true)
137 if (!data)
138 return nullptr;
139
140 return resolveAttachedProperties(func, data, object, createIfMissing);
141}
142
143/*!
144 \relates <qqml.h>
145
146 This function returns the extension object that belongs to \a base, if there is any.
147 Otherwise it returns \c nullptr.
148
149 \sa QML_EXTENDED
150*/
151QObject *qmlExtendedObject(QObject *base)
152{
153 return QQmlPrivate::qmlExtendedObject(base, 0);
154}
155
156QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index)
157{
158 if (!object)
159 return nullptr;
160
161 void *result = nullptr;
162 QObjectPrivate *d = QObjectPrivate::get(object);
163 if (!d->metaObject)
164 return nullptr;
165
166 const int id = d->metaObject->metaCall(
167 object, QMetaObject::CustomCall,
168 QQmlProxyMetaObject::extensionObjectId(index), &result);
169 if (id != QQmlProxyMetaObject::extensionObjectId(index))
170 return nullptr;
171
172 return static_cast<QObject *>(result);
173}
174
175void QQmlPrivate::qmlRegistrationWarning(
176 QQmlPrivate::QmlRegistrationWarning warning, QMetaType metaType)
177{
178 switch (warning) {
179 case UnconstructibleType:
180 qWarning().nospace()
181 << metaType.name()
182 << " is neither a default constructible QObject, nor a default- "
183 << "and copy-constructible Q_GADGET, nor a QObject marked as uncreatable.\n"
184 << "You should not use it as a QML type.";
185 break;
186 case UnconstructibleSingleton:
187 qWarning()
188 << "Singleton" << metaType.name()
189 << "needs to be a concrete class with either a default constructor"
190 << "or, when adding a default constructor is infeasible, a public static"
191 << "create(QQmlEngine *, QJSEngine *) method.";
192 break;
193 case NonQObjectWithAtached:
194 qWarning()
195 << metaType.name()
196 << "is not a QObject, but has attached properties. This won't work.";
197 break;
198 }
199}
200
201QMetaType QQmlPrivate::compositeMetaType(
202 QV4::ExecutableCompilationUnit *unit, int elementNameId)
203{
204 return QQmlTypePrivate::visibleQmlTypeByName(unit, elementNameId).typeId();
205}
206
207QMetaType QQmlPrivate::compositeMetaType(
208 QV4::ExecutableCompilationUnit *unit, const QString &elementName)
209{
210 return QQmlTypePrivate::visibleQmlTypeByName(
211 unit->baseCompilationUnit(), elementName, unit->engine->typeLoader())
212 .typeId();
213}
214
215QMetaType QQmlPrivate::compositeListMetaType(
216 QV4::ExecutableCompilationUnit *unit, int elementNameId)
217{
218 return QQmlTypePrivate::visibleQmlTypeByName(unit, elementNameId).qListTypeId();
219}
220
221QMetaType QQmlPrivate::compositeListMetaType(
222 QV4::ExecutableCompilationUnit *unit, const QString &elementName)
223{
224 return QQmlTypePrivate::visibleQmlTypeByName(
225 unit->baseCompilationUnit(), elementName, unit->engine->typeLoader())
226 .qListTypeId();
227}
228
229/*!
230 \relates <qqml.h>
231 \since 5.8
232
233 This function registers the \a staticMetaObject and its extension
234 in the QML system with the name \a qmlName in the library imported
235 from \a uri having version number composed from \a versionMajor and
236 \a versionMinor.
237
238 An instance of the meta object cannot be created. An error message with
239 the given \a reason is printed if the user attempts to create it.
240
241 This function is useful for registering Q_NAMESPACE namespaces.
242
243 Returns the QML type id.
244
245 For example:
246
247 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
248 \code Q
249 namespace MyNamespace {
250 \1_NAMESPACE
251 enum MyEnum {
252 Key1,
253 Key2,
254 };
255 \1_ENUM_NS(MyEnum)
256 }
257
258 //...
259 qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "io.qt", 1, 0, "MyNamespace", "Access to enums & flags only");
260 \endcode
261
262 On the QML side, you can now use the registered enums:
263 \code
264 Component.onCompleted: console.log(MyNamespace.Key2)
265 \endcode
266
267 \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_UNCREATABLE()
268*/
269int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
270 const char *uri, int versionMajor,
271 int versionMinor, const char *qmlName,
272 const QString& reason)
273{
274 QQmlPrivate::RegisterType type = {
275 QQmlPrivate::RegisterType::CurrentVersion,
276 QMetaType(),
277 QMetaType(),
278 0,
279 nullptr,
280 nullptr,
281 reason,
282 nullptr,
283
284 uri, QTypeRevision::fromVersion(versionMajor, versionMinor), qmlName, &staticMetaObject,
285
286 QQmlAttachedPropertiesFunc(),
287 nullptr,
288
289 -1,
290 -1,
291 -1,
292
293 nullptr, nullptr,
294
295 nullptr,
296 QTypeRevision::zero(),
297 -1,
298 QQmlPrivate::ValueTypeCreationMethod::None
299 };
300
301 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
302}
303
304/*!
305 \relates <qqml.h>
306
307 Clears all stored type registrations, such as those produced with \l qmlRegisterType().
308
309 \warning
310 Do not call this function while a QQmlEngine exists or behavior will be undefined.
311 Any existing QQmlEngines must be deleted before calling this function.
312
313 This function only affects the application global cache. Delete the QQmlEngine to clear
314 all cached data relating to that engine.
315*/
316void qmlClearTypeRegistrations() // Declared in qqml.h
317{
318 QQmlMetaType::clearTypeRegistrations();
319 QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
320 qmlClearEnginePlugins();
321}
322
323/*!
324 \relates <qqml.h>
325
326 This function protects a module from further modification. This can be used
327 to prevent other plugins from injecting types into your module. It can also
328 be a performance improvement, as it allows the engine to skip checking for
329 the possibility of new types or plugins when this import is reached.
330
331 Once qmlProtectModule has been called, a QML engine will not search for a new
332 \c qmldir file to load the module anymore. It will re-use any \c qmldir files
333 it has loaded before, though. Therefore, types present at this point continue
334 to work. Mind that different QML engines may load different modules. The
335 module protection, however, is global and affects all engines. The overhead
336 of locating \c qmldir files and loading plugins may be noticeable with slow file
337 systems. Therefore, protecting a module once you are sure you won't need to
338 load it anymore can be a good optimization. Mind also that the module lock
339 not only affects plugins but also any other qmldir directives, like \c import
340 or \c prefer, as well as any composite types or scripts declared in a \c qmldir
341 file.
342
343 In addition, after this function is called, any attempt to register C++ types
344 into this uri, major version combination will lead to a runtime error.
345
346 Returns true if the module with \a uri as a \l{Identified Modules}
347 {module identifier} and \a majVersion as a major version number was found
348 and locked, otherwise returns false. The module must contain exported types
349 in order to be found.
350*/
351bool qmlProtectModule(const char *uri, int majVersion)
352{
353 return QQmlMetaType::protectModule(QString::fromUtf8(uri),
354 QTypeRevision::fromMajorVersion(majVersion));
355}
356
357/*!
358 \since 5.9
359 \relates <qqml.h>
360
361 This function registers a module in a particular \a uri with a version specified
362 in \a versionMajor and \a versionMinor.
363
364 This can be used to make a certain module version available, even if no types
365 are registered for that version. This is particularly useful for keeping the
366 versions of related modules in sync.
367*/
368
369void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
370{
371 QQmlMetaType::registerModule(uri, QTypeRevision::fromVersion(versionMajor, versionMinor));
372}
373
374static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor)
375{
376 if (importMajor == QQmlModuleImportAuto)
377 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Auto);
378 else if (importMajor == QQmlModuleImportLatest)
379 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Default);
380 else if (importMinor == QQmlModuleImportLatest)
381 return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(importMajor), QQmlDirParser::Import::Default);
382 return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(importMajor, importMinor), QQmlDirParser::Import::Default);
383}
384
385static QTypeRevision resolveModuleVersion(int moduleMajor)
386{
387 return moduleMajor == QQmlModuleImportModuleAny
388 ? QTypeRevision()
389 : QTypeRevision::fromMajorVersion(moduleMajor);
390}
391
392/*!
393 * \enum QQmlModuleImportSpecialVersions
394 * \relates <qqml.h>
395 *
396 * Defines some special values that can be passed to the version arguments of
397 * qmlRegisterModuleImport() and qmlUnregisterModuleImport().
398 *
399 * \value QQmlModuleImportModuleAny When passed as majorVersion of the base
400 * module, signifies that the import is to be
401 * applied to any version of the module.
402 * \value QQmlModuleImportLatest When passed as major or minor version of
403 * the imported module, signifies that the
404 * latest overall, or latest minor version
405 * of a specified major version shall be
406 * imported.
407 * \value QQmlModuleImportAuto When passed as major version of the imported
408 * module, signifies that the version of the
409 * base module shall be forwarded.
410 */
411
412/*!
413 * \relates <qqml.h>
414 * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
415 *
416 * This has the same effect as an \c import statement in a qmldir file: Whenever
417 * \a uri of version \a moduleMajor is imported, \a import of version
418 * \a importMajor. \a importMinor is automatically imported, too. If
419 * \a importMajor is \l QQmlModuleImportLatest the latest version
420 * available of that module is imported, and \a importMinor does not matter. If
421 * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a
422 * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the
423 * version of \a import is version of \a uri being imported, and \a importMinor
424 * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module
425 * import is applied for any major version of \a uri. For example, you may
426 * specify that whenever any version of MyModule is imported, the latest version
427 * of MyOtherModule should be imported. Then, the following call would be
428 * appropriate:
429 *
430 * \code
431 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
432 * "MyOtherModule", QQmlModuleImportLatest);
433 * \endcode
434 *
435 * Or, you may specify that whenever major version 5 of "MyModule" is imported,
436 * then version 3.14 of "MyOtherModule" should be imported:
437 *
438 * \code
439 * qmlRegisterModuleImport("MyModule", 5, "MyOtherModule", 3, 14);
440 * \endcode
441 *
442 * Finally, if you always want the same version of "MyOtherModule" to be
443 * imported whenever "MyModule" is imported, specify the following:
444 *
445 * \code
446 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
447 * "MyOtherModule", QQmlModuleImportAuto);
448 * \endcode
449 *
450 * \sa qmlUnregisterModuleImport()
451 */
452void qmlRegisterModuleImport(const char *uri, int moduleMajor,
453 const char *import, int importMajor, int importMinor)
454{
455 QQmlMetaType::registerModuleImport(
456 QString::fromUtf8(uri), resolveModuleVersion(moduleMajor),
457 resolveImport(QString::fromUtf8(import), importMajor, importMinor));
458}
459
460
461/*!
462 * \relates <qqml.h>
463 * Removes a module import previously registered with qmlRegisterModuleImport()
464 *
465 * Calling this function makes sure that \a import of version
466 * \a{importMajor}.\a{importMinor} is not automatically imported anymore when
467 * \a uri of version \a moduleMajor is. The version resolution works the same
468 * way as with \l qmlRegisterModuleImport().
469 *
470 * \sa qmlRegisterModuleImport()
471 */
472void qmlUnregisterModuleImport(const char *uri, int moduleMajor,
473 const char *import, int importMajor, int importMinor)
474{
475 QQmlMetaType::unregisterModuleImport(
476 QString::fromUtf8(uri), resolveModuleVersion(moduleMajor),
477 resolveImport(QString::fromUtf8(import), importMajor, importMinor));
478}
479
480/*!
481 \since 5.12
482 \relates <qqml.h>
483
484 Returns the QML type id of a type that was registered with the
485 name \a qmlName in a particular \a uri and a version specified in \a
486 versionMajor and \a versionMinor.
487
488 This function returns the same value as the QML type registration functions
489 such as qmlRegisterType() and qmlRegisterSingletonType().
490
491 If \a qmlName, \a uri and \a versionMajor match a registered type, but the
492 specified minor version in \a versionMinor is higher, then the id of the type
493 with the closest minor version is returned.
494
495 Returns -1 if no matching type was found or one of the given parameters
496 was invalid.
497
498 \note: qmlTypeId tries to make modules available, even if they were not accessed by any
499 engine yet. This can introduce overhead the first time a module is accessed. Trying to
500 find types from a module which does not exist always introduces this overhead.
501
502 \sa QML_ELEMENT, QML_NAMED_ELEMENT, QML_SINGLETON, qmlRegisterType(), qmlRegisterSingletonType()
503*/
504int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
505{
506 auto revision = QTypeRevision::fromVersion(versionMajor, versionMinor);
507 int id = QQmlMetaType::typeId(uri, revision, qmlName);
508 if (id != -1)
509 return id;
510 /* If the module hasn't been imported yet, we might not have the id of a
511 singleton at this point. To obtain it, we need an engine in order to
512 to do the resolution steps.
513 This is expensive, but we assume that users don't constantly query invalid
514 Types; internal code should use QQmlMetaType API.
515 */
516 QQmlEngine engine;
517 QQmlTypeLoader *typeLoader = QQmlTypeLoader::get(&engine);
518 auto loadHelper = QQml::makeRefPointer<LoadHelper>(
519 typeLoader, uri, qmlName, QQmlTypeLoader::Synchronous);
520 const QQmlType type = loadHelper->type();
521 if (type.availableInVersion(revision))
522 return type.index();
523 else
524 return -1;
525}
526
527static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
528{
529 if (!instance) {
530 QQmlError error;
531 error.setDescription(QStringLiteral("The registered singleton has already been deleted. "
532 "Ensure that it outlives the engine."));
533 QQmlEnginePrivate::get(engine)->warning(engine, error);
534 return false;
535 }
536
537 if (engine->thread() != instance->thread()) {
538 QQmlError error;
539 error.setDescription(QStringLiteral("Registered object must live in the same thread "
540 "as the engine it was registered with"));
541 QQmlEnginePrivate::get(engine)->warning(engine, error);
542 return false;
543 }
544
545 return true;
546}
547
548// From qqmlprivate.h
549#if QT_DEPRECATED_SINCE(6, 3)
550QObject *QQmlPrivate::SingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
551{
552 if (!checkSingletonInstance(qeng, m_object))
553 return nullptr;
554
555 if (alreadyCalled) {
556 QQmlError error;
557 error.setDescription(QStringLiteral("Singleton registered by registerSingletonInstance "
558 "must only be accessed from one engine"));
559 QQmlEnginePrivate::get(qeng)->warning(qeng, error);
560 return nullptr;
561 }
562
563 alreadyCalled = true;
564 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
565 return m_object;
566};
567#endif
568
569QObject *QQmlPrivate::SingletonInstanceFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
570{
571 if (!checkSingletonInstance(qeng, m_object))
572 return nullptr;
573
574 if (!m_engine) {
575 m_engine = qeng;
576 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
577 } else if (m_engine != qeng) {
578 QQmlError error;
579 error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
580 QQmlEnginePrivate::get(qeng)->warning(qeng, error);
581 return nullptr;
582 }
583
584 return m_object;
585};
586
587static QList<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
588{
589 QList<QTypeRevision> revisions;
590 if (!metaObject)
591 return revisions;
592 const int propertyOffset = metaObject->propertyOffset();
593 const int propertyCount = metaObject->propertyCount();
594 for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
595 coreIndex < propertyEnd; ++coreIndex) {
596 const QMetaProperty property = metaObject->property(coreIndex);
597 if (int revision = property.revision())
598 revisions.append(QTypeRevision::fromEncodedVersion(revision));
599 }
600 const int methodOffset = metaObject->methodOffset();
601 const int methodCount = metaObject->methodCount();
602 for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount;
603 methodIndex < methodEnd; ++methodIndex) {
604 const QMetaMethod method = metaObject->method(methodIndex);
605 if (int revision = method.revision())
606 revisions.append(QTypeRevision::fromEncodedVersion(revision));
607 }
608
609 // Need to also check parent meta objects, as their revisions are inherited.
610 if (const QMetaObject *superMeta = metaObject->superClass())
611 revisions += availableRevisions(superMeta);
612
613 return revisions;
614}
615
616template<typename Registration>
617void assignVersions(Registration *registration, QTypeRevision revision,
618 QTypeRevision defaultVersion)
619{
620 const quint8 majorVersion = revision.hasMajorVersion() ? revision.majorVersion()
621 : defaultVersion.majorVersion();
622 registration->version = revision.hasMinorVersion()
623 ? QTypeRevision::fromVersion(majorVersion, revision.minorVersion())
624 : QTypeRevision::fromMajorVersion(majorVersion);
625 registration->revision = revision;
626}
627
628static QList<QTypeRevision> prepareRevisions(const QMetaObject *metaObject, QTypeRevision added)
629{
630 auto revisions = availableRevisions(metaObject);
631 revisions.append(added);
632 return revisions;
633}
634
635static void uniqueRevisions(QList<QTypeRevision> *revisions, QTypeRevision defaultVersion,
636 QTypeRevision added)
637{
638 bool revisionsHaveMajorVersions = false;
639
640 // copy because we modify revisions in the loop
641 const QList<QTypeRevision> origRevisions = *revisions;
642 for (QTypeRevision revision : origRevisions) {
643 // allow any minor version for each explicitly specified past major one
644 if (revision.hasMajorVersion()) {
645 revisionsHaveMajorVersions = true;
646 if (revision.majorVersion() < defaultVersion.majorVersion())
647 revisions->append(QTypeRevision::fromVersion(revision.majorVersion(), 254));
648 }
649 }
650
651 if (revisionsHaveMajorVersions) {
652 if (!added.hasMajorVersion()) {
653 // If added in unspecified major version, assume default one.
654 revisions->append(QTypeRevision::fromVersion(defaultVersion.majorVersion(),
655 added.minorVersion()));
656 } else if (added.majorVersion() < defaultVersion.majorVersion()) {
657 // If added in past major version, add .0 of default version.
658 revisions->append(QTypeRevision::fromVersion(defaultVersion.majorVersion(), 0));
659 }
660 }
661
662 std::sort(revisions->begin(), revisions->end());
663 const auto it = std::unique(revisions->begin(), revisions->end());
664 revisions->erase(it, revisions->end());
665}
666
668 const QQmlPrivate::RegisterSingletonType &type)
669{
670 QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
671 siinfo->scriptCallback = type.scriptApi;
672 siinfo->qobjectCallback = type.qObjectApi;
673 siinfo->typeName = type.typeName;
674 return QQmlType::SingletonInstanceInfo::ConstPtr(
675 siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
676}
677
679 const QQmlPrivate::RegisterCompositeSingletonType &type)
680{
681 QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
682 siinfo->url = QQmlMetaType::normalizedUrl(type.url);
683 siinfo->typeName = type.typeName;
684 return QQmlType::SingletonInstanceInfo::ConstPtr(
685 siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
686}
687
688static int finalizeType(const QQmlType &dtype)
689{
690 if (!dtype.isValid())
691 return -1;
692
693 QQmlMetaType::registerUndeletableType(dtype);
694 return dtype.index();
695}
696
697using ElementNames = QVarLengthArray<const char *, 8>;
698static ElementNames classElementNames(const QMetaObject *metaObject)
699{
700 Q_ASSERT(metaObject);
701 const char *key = "QML.Element";
702
703 const int offset = metaObject->classInfoOffset();
704 const int start = metaObject->classInfoCount() + offset - 1;
705
706 ElementNames elementNames;
707
708 for (int i = start; i >= offset; --i) {
709 const QMetaClassInfo classInfo = metaObject->classInfo(i);
710 if (qstrcmp(key, classInfo.name()) == 0) {
711 const char *elementName = classInfo.value();
712
713 if (qstrcmp(elementName, "auto") == 0) {
714 const char *strippedClassName = metaObject->className();
715 for (const char *c = strippedClassName; *c != '\0'; c++) {
716 if (*c == ':')
717 strippedClassName = c + 1;
718 }
719 elementName = strippedClassName;
720 } else if (qstrcmp(elementName, "anonymous") == 0) {
721 if (elementNames.isEmpty())
722 elementNames.push_back(nullptr);
723 else if (elementNames[0] != nullptr)
724 qWarning() << metaObject->className() << "is both anonymous and named";
725 continue;
726 }
727
728 if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
729 qWarning() << metaObject->className() << "is both anonymous and named";
730 elementNames[0] = elementName;
731 } else {
732 elementNames.push_back(elementName);
733 }
734 }
735 }
736
737 return elementNames;
738}
739
741{
742 AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
743
744 void registerAliases(int typeId)
745 {
746 if (elementNames) {
747 for (int i = 1, end = elementNames->length(); i < end; ++i)
748 otherNames.append(QString::fromUtf8(elementNames->at(i)));
749 elementNames = nullptr;
750 }
751
752 for (const QString &otherName : std::as_const(otherNames))
753 QQmlMetaType::registerTypeAlias(typeId, otherName);
754 }
755
756private:
757 const ElementNames *elementNames;
758 QVarLengthArray<QString, 8> otherNames;
759};
760
761
763 const QQmlPrivate::RegisterTypeAndRevisions &type,
764 const ElementNames &elementNames)
765{
766 using namespace QQmlPrivate;
767
768 const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
769 const bool creatable = (elementNames[0] != nullptr || isValueType)
770 && boolClassInfo(type.classInfoMetaObject, "QML.Creatable", true);
771
772 QString noCreateReason;
773 ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
774
775 if (!creatable) {
776 noCreateReason = QString::fromUtf8(
777 classInfo(type.classInfoMetaObject, "QML.UncreatableReason"));
778 if (noCreateReason.isEmpty())
779 noCreateReason = QLatin1String("Type cannot be created in QML.");
780 } else if (isValueType) {
781 const char *method = classInfo(type.classInfoMetaObject, "QML.CreationMethod");
782 if (qstrcmp(method, "structured") == 0)
783 creationMethod = ValueTypeCreationMethod::Structured;
784 else if (qstrcmp(method, "construct") == 0)
785 creationMethod = ValueTypeCreationMethod::Construct;
786 }
787
788 RegisterType typeRevision = {
789 QQmlPrivate::RegisterType::CurrentVersion,
790 type.typeId,
791 type.listId,
792 creatable ? type.objectSize : 0,
793 nullptr,
794 nullptr,
795 noCreateReason,
796 type.createValueType,
797 type.uri,
798 type.version,
799 nullptr,
800 type.metaObject,
801 type.attachedPropertiesFunction,
802 type.attachedPropertiesMetaObject,
803 type.parserStatusCast,
804 type.valueSourceCast,
805 type.valueInterceptorCast,
806 type.extensionObjectCreate,
807 type.extensionMetaObject,
808 nullptr,
809 QTypeRevision(),
810 type.structVersion > 0 ? type.finalizerCast : -1,
811 creationMethod
812 };
813
814 QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
815 0,
816 type.uri,
817 type.version,
818 nullptr,
819 type.listId,
820 type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
821 QTypeRevision(),
822 };
823
824 const QTypeRevision added = revisionClassInfo(
825 type.classInfoMetaObject, "QML.AddedInVersion",
826 QTypeRevision::fromVersion(type.version.majorVersion(), 0));
827 const QTypeRevision removed = revisionClassInfo(
828 type.classInfoMetaObject, "QML.RemovedInVersion");
829 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
830 "QML.ExtraVersion");
831
832 auto revisions = prepareRevisions(type.metaObject, added) + furtherRevisions;
833 if (type.attachedPropertiesMetaObject)
834 revisions += availableRevisions(type.attachedPropertiesMetaObject);
835 uniqueRevisions(&revisions, type.version, added);
836
837 AliasRegistrar aliasRegistrar(&elementNames);
838 for (QTypeRevision revision : std::as_const(revisions)) {
839 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
840 break;
841
842 assignVersions(&typeRevision, revision, type.version);
843
844 // When removed or before added, we still add revisions, but anonymous ones
845 if (typeRevision.version < added
846 || (removed.isValid() && !(typeRevision.version < removed))) {
847 typeRevision.elementName = nullptr;
848 typeRevision.create = nullptr;
849 typeRevision.userdata = nullptr;
850 } else {
851 typeRevision.elementName = elementNames[0];
852 typeRevision.create = creatable ? type.create : nullptr;
853 typeRevision.userdata = type.userdata;
854 }
855
856 typeRevision.customParser = type.customParserFactory();
857 const int id = qmlregister(TypeRegistration, &typeRevision);
858 if (type.qmlTypeIds)
859 type.qmlTypeIds->append(id);
860
861 if (typeRevision.elementName)
862 aliasRegistrar.registerAliases(id);
863
864 if (sequenceRevision.metaSequence != QMetaSequence()) {
865 sequenceRevision.version = typeRevision.version;
866 sequenceRevision.revision = typeRevision.revision;
867 const int id = QQmlPrivate::qmlregister(
868 QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
869 if (type.qmlTypeIds)
870 type.qmlTypeIds->append(id);
871 }
872 }
873}
874
876 const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
877 const ElementNames &elementNames)
878{
879 using namespace QQmlPrivate;
880
881 RegisterSingletonType revisionRegistration = {
882 0,
883 type.uri,
884 type.version,
885 elementNames[0],
886 nullptr,
887 type.qObjectApi,
888 type.instanceMetaObject,
889 type.typeId,
890 type.extensionObjectCreate,
891 type.extensionMetaObject,
892 QTypeRevision()
893 };
894 const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
895 = singletonInstanceInfo(revisionRegistration);
896
897 const QTypeRevision added = revisionClassInfo(
898 type.classInfoMetaObject, "QML.AddedInVersion",
899 QTypeRevision::fromVersion(type.version.majorVersion(), 0));
900 const QTypeRevision removed = revisionClassInfo(
901 type.classInfoMetaObject, "QML.RemovedInVersion");
902 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(type.classInfoMetaObject,
903 "QML.ExtraVersion");
904
905 auto revisions = prepareRevisions(type.instanceMetaObject, added) + furtherRevisions;
906 uniqueRevisions(&revisions, type.version, added);
907
908 AliasRegistrar aliasRegistrar(&elementNames);
909 for (QTypeRevision revision : std::as_const(revisions)) {
910 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
911 break;
912
913 assignVersions(&revisionRegistration, revision, type.version);
914
915 // When removed or before added, we still add revisions, but anonymous ones
916 if (revisionRegistration.version < added
917 || (removed.isValid() && !(revisionRegistration.version < removed))) {
918 revisionRegistration.typeName = nullptr;
919 revisionRegistration.qObjectApi = nullptr;
920 } else {
921 revisionRegistration.typeName = elementNames[0];
922 revisionRegistration.qObjectApi = type.qObjectApi;
923 }
924
925 const int id = finalizeType(
926 QQmlMetaType::registerSingletonType(revisionRegistration, siinfo));
927 if (type.qmlTypeIds)
928 type.qmlTypeIds->append(id);
929
930 if (revisionRegistration.typeName)
931 aliasRegistrar.registerAliases(id);
932 }
933}
934
935/*
936This method is "over generalized" to allow us to (potentially) register more types of things in
937the future without adding exported symbols.
938*/
939int QQmlPrivate::qmlregister(RegistrationType type, void *data)
940{
941 switch (type) {
942 case AutoParentRegistration:
943 return QQmlMetaType::registerAutoParentFunction(
944 *reinterpret_cast<RegisterAutoParent *>(data));
945 case QmlUnitCacheHookRegistration:
946 return QQmlMetaType::registerUnitCacheHook(
947 *reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
948 case TypeAndRevisionsRegistration: {
949 const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
950 if (type.structVersion > 1 && type.forceAnonymous) {
951 doRegisterTypeAndRevisions(type, {nullptr});
952 } else {
953 const ElementNames names = classElementNames(type.classInfoMetaObject);
954 if (names.isEmpty()) {
955 qWarning().nospace() << "Missing QML.Element class info for "
956 << type.classInfoMetaObject->className();
957 } else {
958 doRegisterTypeAndRevisions(type, names);
959 }
960
961 }
962 break;
963 }
964 case SingletonAndRevisionsRegistration: {
965 const RegisterSingletonTypeAndRevisions &type
966 = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
967 const ElementNames names = classElementNames(type.classInfoMetaObject);
968 if (names.isEmpty()) {
969 qWarning().nospace() << "Missing QML.Element class info for "
970 << type.classInfoMetaObject->className();
971 } else {
972 doRegisterSingletonAndRevisions(type, names);
973 }
974 break;
975 }
976 case SequentialContainerAndRevisionsRegistration: {
977 const RegisterSequentialContainerAndRevisions &type
978 = *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
979 RegisterSequentialContainer revisionRegistration = {
980 0,
981 type.uri,
982 type.version,
983 nullptr,
984 type.typeId,
985 type.metaSequence,
986 QTypeRevision()
987 };
988
989 const QTypeRevision added = revisionClassInfo(
990 type.classInfoMetaObject, "QML.AddedInVersion",
991 QTypeRevision::fromMinorVersion(0));
992 QList<QTypeRevision> revisions = revisionClassInfos(
993 type.classInfoMetaObject, "QML.ExtraVersion");
994 revisions.append(added);
995 uniqueRevisions(&revisions, type.version, added);
996
997 for (QTypeRevision revision : std::as_const(revisions)) {
998 if (revision < added)
999 continue;
1000 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
1001 break;
1002
1003 assignVersions(&revisionRegistration, revision, type.version);
1004 const int id = qmlregister(SequentialContainerRegistration, &revisionRegistration);
1005 if (type.qmlTypeIds)
1006 type.qmlTypeIds->append(id);
1007 }
1008 break;
1009 }
1010 case TypeRegistration:
1011 return finalizeType(
1012 QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data)));
1013 case InterfaceRegistration:
1014 return finalizeType(
1015 QQmlMetaType::registerInterface(*reinterpret_cast<RegisterInterface *>(data)));
1016 case SingletonRegistration:
1017 return finalizeType(QQmlMetaType::registerSingletonType(
1018 *reinterpret_cast<RegisterSingletonType *>(data),
1019 singletonInstanceInfo(*reinterpret_cast<RegisterSingletonType *>(data))));
1020 case CompositeRegistration:
1021 return finalizeType(QQmlMetaType::registerCompositeType(
1022 *reinterpret_cast<RegisterCompositeType *>(data)));
1023 case CompositeSingletonRegistration:
1024 return finalizeType(QQmlMetaType::registerCompositeSingletonType(
1025 *reinterpret_cast<RegisterCompositeSingletonType *>(data),
1026 singletonInstanceInfo(*reinterpret_cast<RegisterCompositeSingletonType *>(data))));
1027 case SequentialContainerRegistration:
1028 return finalizeType(QQmlMetaType::registerSequentialContainer(
1029 *reinterpret_cast<RegisterSequentialContainer *>(data)));
1030 default:
1031 return -1;
1032 }
1033
1034 return -1;
1035}
1036
1037void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
1038{
1039 switch (type) {
1040 case AutoParentRegistration:
1041 QQmlMetaType::unregisterAutoParentFunction(reinterpret_cast<AutoParentFunction>(data));
1042 break;
1043 case QmlUnitCacheHookRegistration:
1044 QQmlMetaType::removeCachedUnitLookupFunction(
1045 reinterpret_cast<QmlUnitCacheLookupFunction>(data));
1046 break;
1047 case SequentialContainerRegistration:
1048 QQmlMetaType::unregisterSequentialContainer(data);
1049 break;
1050 case TypeRegistration:
1051 case InterfaceRegistration:
1052 case SingletonRegistration:
1053 case CompositeRegistration:
1054 case CompositeSingletonRegistration:
1055 QQmlMetaType::unregisterType(data);
1056 break;
1057 case TypeAndRevisionsRegistration:
1058 case SingletonAndRevisionsRegistration:
1059 case SequentialContainerAndRevisionsRegistration:
1060 // Currently unnecessary. We'd need a special data structure to hold
1061 // URI + majorVersion and then we'd iterate the minor versions, look up the
1062 // associated QQmlType objects by uri/elementName/major/minor and qmlunregister
1063 // each of them.
1064 Q_UNREACHABLE();
1065 break;
1066 }
1067}
1068
1069QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObject,
1070 const char *key)
1071{
1072 QList<QTypeRevision> revisions;
1073 for (int index = indexOfOwnClassInfo(metaObject, key); index != -1;
1074 index = indexOfOwnClassInfo(metaObject, key, index - 1)) {
1075 revisions.push_back(QTypeRevision::fromEncodedVersion(
1076 QLatin1StringView(metaObject->classInfo(index).value()).toInt()));
1077 }
1078 return revisions;
1079}
1080
1081/*!
1082 \relates <qqml.h>
1083
1084 This function registers a type in the QML system with the name \a qmlName, in the type namespace imported from \a uri having the
1085 version number composed from \a versionMajor and \a versionMinor, but any attempt to instantiate the type
1086 will produce the given error \a message.
1087
1088 Normally, the types exported by a plugin should be fixed. However, if a C++ type is not available, you should
1089 at least "reserve" the QML type name, and give the user of the unavailable type a meaningful error message.
1090
1091 Returns the QML type id.
1092
1093 Example:
1094
1095 \code
1096 #ifdef NO_GAMES_ALLOWED
1097 qmlRegisterTypeNotAvailable("MinehuntCore", 0, 1, "Game", "Get back to work, slacker!");
1098 #else
1099 qmlRegisterType<MinehuntGame>("MinehuntCore", 0, 1, "Game");
1100 #endif
1101 \endcode
1102
1103 This will cause any QML which imports the "MinehuntCore" type namespace and attempts to use the type to produce an error message:
1104 \code
1105 fun.qml: Get back to work, slacker!
1106 Game {
1107 ^
1108 \endcode
1109
1110 Without this, a generic "Game is not a type" message would be given.
1111
1112 \sa QML_UNAVAILABLE, qmlRegisterUncreatableType(),
1113 {Choosing the Correct Integration Method Between C++ and QML}
1114*/
1115int qmlRegisterTypeNotAvailable(
1116 const char *uri, int versionMajor, int versionMinor,
1117 const char *qmlName, const QString &message)
1118{
1119 return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
1120 uri, versionMajor, versionMinor, qmlName, message);
1121}
1122
1123namespace QQmlPrivate {
1124template<>
1126 const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
1127 QList<int> *qmlTypeIds, const QMetaObject *extension, bool)
1128{
1129 using T = QQmlTypeNotAvailable;
1130
1131 RegisterTypeAndRevisions type = {
1132 3,
1133 QmlMetaType<T>::self(),
1134 QmlMetaType<T>::list(),
1135 0,
1136 nullptr,
1137 nullptr,
1138 nullptr,
1139
1140 uri,
1141 QTypeRevision::fromMajorVersion(versionMajor),
1142
1143 &QQmlTypeNotAvailable::staticMetaObject,
1144 classInfoMetaObject,
1145
1146 attachedPropertiesFunc<T>(),
1147 attachedPropertiesMetaObject<T>(),
1148
1149 StaticCastSelector<T, QQmlParserStatus>::cast(),
1150 StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
1151 StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
1152
1153 nullptr,
1154 extension,
1155 qmlCreateCustomParser<T>,
1156 qmlTypeIds,
1157 QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
1158 false,
1159 QmlMetaType<T>::sequence(),
1160 };
1161
1162 qmlregister(TypeAndRevisionsRegistration, &type);
1163}
1164
1166
1168{
1169 return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
1170 ->thisObject();
1171}
1172
1174{
1175 return engine->handle()->qmlEngine();
1176}
1177
1178static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext)
1179{
1180 QQmlEngine *engine = aotContext->qmlEngine();
1181 return engine ? QQmlEnginePrivate::get(aotContext->qmlEngine())->propertyCapture : nullptr;
1182}
1183
1189
1195
1200
1202{
1203 if (auto *frame = engine->handle()->currentStackFrame) {
1206 }
1207}
1208
1213
1214static bool markPointer(const QVariant &element, QV4::MarkStack *markStack)
1215{
1216 if (!element.metaType().flags().testFlag(QMetaType::PointerToQObject))
1217 return false;
1218
1219 QV4::QObjectWrapper::markWrapper(
1220 *static_cast<QObject *const *>(element.constData()), markStack);
1221 return true;
1222}
1223
1224static void iterateVariant(const QVariant &element, std::vector<QVariant> *elements)
1225{
1226#define ADD_CASE(Type, id, T)
1227 case QMetaType::Type:
1228
1229 const QMetaType elementMetaType = element.metaType();
1230 switch (elementMetaType.id()) {
1231 case QMetaType::QVariantMap:
1232 for (const QVariant &variant : *static_cast<const QVariantMap *>(element.constData()))
1233 elements->push_back(variant);
1234 return;
1235 case QMetaType::QVariantHash:
1236 for (const QVariant &variant : *static_cast<const QVariantHash *>(element.constData()))
1237 elements->push_back(variant);
1238 return;
1239 case QMetaType::QVariantList:
1240 for (const QVariant &variant : *static_cast<const QVariantList *>(element.constData()))
1241 elements->push_back(variant);
1242 return;
1243 QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(ADD_CASE)
1244 QT_FOR_EACH_STATIC_CORE_CLASS(ADD_CASE)
1245 QT_FOR_EACH_STATIC_GUI_CLASS(ADD_CASE)
1246 case QMetaType::QStringList:
1247 case QMetaType::QByteArrayList:
1248 return;
1249 default:
1250 break;
1251 }
1252
1253 if (elementMetaType == QMetaType::fromType<QJSValue>()
1254 || elementMetaType == QMetaType::fromType<QJSManagedValue>()
1255 || elementMetaType == QMetaType::fromType<QJSPrimitiveValue>()) {
1256 // QJSValue and QJSManagedValue effectively hold persistent values anyway.
1257 // QJSPrimitiveValue can only hold primitives or QString.
1258 return;
1259 }
1260
1261 QMetaSequence::Iterable iterable;
1262 if (!QMetaType::convert(
1263 element.metaType(), element.constData(),
1264 QMetaType::fromType<QMetaSequence::Iterable>(), &iterable)) {
1265 return;
1266 }
1267
1268 switch (iterable.metaContainer().valueMetaType().id()) {
1269 QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(ADD_CASE)
1270 QT_FOR_EACH_STATIC_CORE_CLASS(ADD_CASE)
1271 QT_FOR_EACH_STATIC_GUI_CLASS(ADD_CASE)
1272 case QMetaType::QStringList:
1273 case QMetaType::QByteArrayList:
1274 return;
1275 default:
1276 break;
1277 }
1278
1279 for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it)
1280 elements->push_back(*it);
1281
1282#undef ADD_CASE
1283}
1284
1286{
1288 return;
1289
1292
1293 while (!stack.empty()) {
1294 const QVariant element = std::as_const(stack).back();
1295 stack.pop_back();
1298 }
1299}
1300
1311
1322
1323static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCache *ancestor)
1324{
1325 for (const QQmlPropertyCache *cache = descendent; cache; cache = cache->parent().data()) {
1326 if (cache == ancestor)
1327 return true;
1328 }
1329 return false;
1330}
1331
1333
1339
1340template<bool StrictType>
1358
1359template<bool StrictType = false>
1376
1377template<bool StrictType = false>
1387
1394
1396{
1397 // We've just initialized the lookup. So everything must be fine here.
1398
1400
1403
1404 const QMetaObject *metaObject
1405 = reinterpret_cast<const QMetaObject *>(lookup->qobjectFallbackLookup.metaObject - 1);
1407
1408 return {qmlData, metaObject, PropertyResult::OK};
1409}
1410
1430
1443
1455
1466
1479
1491
1492template<bool StrictType, typename Op>
1505
1506template<bool StrictType = false>
1509{
1511 if (property->isResettable()) {
1513 } else {
1514 v4->throwError(
1515 QLatin1String("Cannot assign [undefined] to ") +
1517 }
1518 });
1519}
1520
1521template<bool StrictType = false>
1528
1529template<typename Op>
1543
1545{
1547 lookup, object, [&](const QMetaObject *metaObject, int coreIndex) {
1548 void *args[] = { value, nullptr };
1550 });
1551}
1552
1555{
1557 lookup, object, [&](const QMetaObject *metaObject, int coreIndex) {
1559 void *args[] = { nullptr };
1561 } else {
1562 const QMetaType propType(reinterpret_cast<const QtPrivate::QMetaTypeInterface *>(
1564 v4->throwError(
1565 QLatin1String("Cannot assign [undefined] to ") +
1567 }
1568 });
1569}
1570
1571static bool isEnumUnderlyingType(QMetaType enumType, QMetaType numberType)
1572{
1573 // You can pass the underlying type of an enum.
1574 // We don't want to check for the actual underlying type because
1575 // moc and qmltyperegistrar are not very precise about it. Especially
1576 // the long and longlong types can be ambiguous.
1577
1578 const bool isUnsigned = enumType.flags() & QMetaType::IsUnsignedEnumeration;
1579 switch (enumType.sizeOf()) {
1580 case 1:
1581 return isUnsigned
1582 ? numberType == QMetaType::fromType<quint8>()
1583 : numberType == QMetaType::fromType<qint8>();
1584 case 2:
1585 return isUnsigned
1586 ? numberType == QMetaType::fromType<ushort>()
1587 : numberType == QMetaType::fromType<short>();
1588 case 4:
1589 // The default type, if moc doesn't know the actual enum type, is int.
1590 // However, the compiler can still decide to encode the enum in uint.
1591 // Therefore, we also accept int for uint enums.
1592 // TODO: This is technically UB.
1593 return isUnsigned
1594 ? (numberType == QMetaType::fromType<int>()
1595 || numberType == QMetaType::fromType<uint>())
1596 : numberType == QMetaType::fromType<int>();
1597 case 8:
1598 return isUnsigned
1599 ? numberType == QMetaType::fromType<qulonglong>()
1600 : numberType == QMetaType::fromType<qlonglong>();
1601 }
1602
1603 return false;
1604}
1605
1606static bool canHoldVoid(QMetaType type)
1607{
1608 // We cannot directly store void, but we can put it into QVariant or QJSPrimitiveValue
1609 return !type.isValid()
1610 || type == QMetaType::fromType<QVariant>()
1611 || type == QMetaType::fromType<QJSPrimitiveValue>();
1612}
1613
1614static bool isTypeCompatible(QMetaType source, QMetaType target)
1615{
1616 if (source == target)
1617 return true;
1618
1619 if ((source.flags() & QMetaType::IsQmlList)
1620 && (target.flags() & QMetaType::IsQmlList)) {
1621 // We want to check the value types here, but we cannot easily do it.
1622 // Internally those are all QObject* lists, though.
1623 return true;
1624 }
1625
1626 if (target.flags() & QMetaType::PointerToQObject) {
1627 // We accept any derived class, too
1628
1629 const QMetaObject *targetMetaObject = target.metaObject();
1630 const QMetaObject *sourceMetaObject = source.metaObject();
1631 if (!sourceMetaObject)
1632 sourceMetaObject = QQmlMetaType::metaObjectForType(source).metaObject();
1633
1634 while (sourceMetaObject && sourceMetaObject != targetMetaObject)
1635 sourceMetaObject = sourceMetaObject->superClass();
1636
1637 return sourceMetaObject != nullptr;
1638 }
1639
1640 if (target.flags() & QMetaType::IsEnumeration)
1641 return isEnumUnderlyingType(target, source);
1642
1643 if (source.flags() & QMetaType::IsEnumeration)
1644 return isEnumUnderlyingType(source, target);
1645
1646 if (!source.isValid())
1647 return canHoldVoid(target);
1648
1649 return false;
1650}
1651
1683
1711
1717
1718template<QV4::Lookup::Call FallbackCall>
1720 const AOTCompiledContext *aotContext, QV4::Lookup *lookup, QObject *object)
1721{
1722 QV4::Scope scope(aotContext->engine->handle());
1723 QV4::PropertyKey id = scope.engine->identifierTable->asPropertyKey(
1724 aotContext->compilationUnit->runtimeStrings[lookup->nameIndex]);
1725
1726 Q_ASSERT(id.isString());
1727
1728 QV4::ScopedString name(scope, id.asStringOrSymbol());
1729
1730 Q_ASSERT(!name->equals(scope.engine->id_toString()));
1731 Q_ASSERT(!name->equals(scope.engine->id_destroy()));
1732
1733 QQmlData *ddata = QQmlData::get(object, true);
1734 Q_ASSERT(ddata);
1735 if (ddata->isQueuedForDeletion)
1737
1738 const QQmlPropertyData *property;
1739 if (!ddata->propertyCache) {
1740 property = QQmlPropertyCache::property(object, name, aotContext->qmlContext, nullptr);
1741 } else {
1742 property = ddata->propertyCache->property(
1743 name.getPointer(), object, aotContext->qmlContext);
1744 }
1745
1746 if (!property) {
1747 const QMetaObject *metaObject = object->metaObject();
1748 if (!metaObject)
1750
1751 const int coreIndex = metaObject->indexOfProperty(
1752 name->toQStringNoThrow().toUtf8().constData());
1753 if (coreIndex < 0)
1755
1756 const QMetaProperty property = metaObject->property(coreIndex);
1757
1758 lookup->releasePropertyCache();
1759 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
1760 lookup->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
1761 lookup->qobjectFallbackLookup.metaType = quintptr(property.metaType().iface()) + 1;
1762 lookup->qobjectFallbackLookup.coreIndex = coreIndex;
1763 lookup->qobjectFallbackLookup.notifyIndex =
1764 QMetaObjectPrivate::signalIndex(property.notifySignal());
1765
1766 if constexpr (FallbackCall == QV4::Lookup::Call::ContextGetterScopeObjectPropertyFallback
1767 || FallbackCall == QV4::Lookup::Call::GetterQObjectPropertyFallback) {
1768 lookup->qobjectFallbackLookup.isConstantOrResettable = property.isConstant() ? 1 : 0;
1769 } else if constexpr (FallbackCall == QV4::Lookup::Call::SetterQObjectPropertyFallback) {
1770 lookup->qobjectFallbackLookup.isConstantOrResettable = property.isResettable() ? 1 : 0;
1771 }
1773 }
1774
1775 Q_ASSERT(ddata->propertyCache);
1776
1777 QV4::setupQObjectLookup(lookup, ddata, property);
1778
1780}
1781
1783template<QV4::Lookup::Call ObjectCall, QV4::Lookup::Call FallbackCall, LookupType Type>
1784void initObjectLookup(const AOTCompiledContext *aotContext, uint index, QObject *object)
1785{
1786 QV4::ExecutionEngine *v4 = aotContext->engine->handle();
1787 if (v4->hasException) {
1788 v4->amendException();
1789 return;
1790 }
1791
1792 QV4::Lookup *lookup = aotContext->compilationUnit->runtimeLookups + index;
1793 switch (initObjectLookup<FallbackCall>(aotContext, lookup, object)) {
1795 lookup->call = ObjectCall;
1796 lookup->asVariant = (Type == LookupType::Variant);
1797 break;
1799 lookup->call = FallbackCall;
1800 lookup->asVariant = (Type == LookupType::Variant);
1801 break;
1803 v4->throwTypeError();
1804 break;
1805 }
1806
1807 return;
1808}
1809
1810
1812 QV4::Lookup *lookup, QV4::ExecutableCompilationUnit *compilationUnit,
1813 const QMetaObject *metaObject)
1814{
1815 Q_ASSERT(metaObject);
1816 const QByteArray name = compilationUnit->runtimeStrings[lookup->nameIndex]->toQString().toUtf8();
1817 int coreIndex = metaObject->indexOfProperty(name.constData());
1818 QMetaType lookupType = metaObject->property(coreIndex).metaType();
1819
1820 QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(
1821 QMetaObject::ReadProperty, &metaObject, &coreIndex);
1822
1823 lookup->qgadgetLookup.metaObject = quintptr(metaObject) + 1;
1824 lookup->qgadgetLookup.coreIndex = coreIndex;
1825 lookup->qgadgetLookup.metaType = lookupType.iface();
1826}
1827
1856
1880
1886
1888{
1889#if QT_CONFIG(translation)
1891#else
1892 return QString();
1893#endif
1894}
1895
1897{
1899 switch (lookup->call) {
1912 return QMetaType::fromType<QObject *>();
1915 // We can do this because the fallback lookup gets invalidated for every call.
1916 return QMetaType(reinterpret_cast<const QtPrivate::QMetaTypeInterface *>(
1918 }
1924 return QMetaType::fromType<void>();
1926 return QMetaType::fromType<QString>();
1927 default:
1929 }
1930 default:
1931 break;
1932 }
1933
1935}
1936
1937static bool isUndefined(const void *value, QMetaType type)
1938{
1939 if (type == QMetaType::fromType<QVariant>())
1940 return !static_cast<const QVariant *>(value)->isValid();
1941 if (type == QMetaType::fromType<QJSValue>())
1942 return static_cast<const QJSValue *>(value)->isUndefined();
1943 if (type == QMetaType::fromType<QJSPrimitiveValue>()) {
1944 return static_cast<const QJSPrimitiveValue *>(value)->type()
1945 == QJSPrimitiveValue::Undefined;
1946 }
1947 return false;
1948}
1949
1951{
1952 // We don't really use any part of the lookup machinery here.
1953 // The QV4::Lookup is created on the stack to conveniently get the property cache, and through
1954 // the property cache we store a value into the property.
1955
1956 const auto unwrapVariant = [&]() {
1957 if (type == QMetaType::fromType<QVariant>()) {
1958 QVariant *variant = static_cast<QVariant *>(value);
1959 type = variant->metaType();
1960 value = variant->data();
1961 }
1962 };
1963
1964 QV4::Lookup lookup;
1965 memset(&lookup, 0, sizeof(QV4::Lookup));
1967 lookup.forCall = false;
1970
1972 this, &lookup, qmlScopeObject)) {
1973 case ObjectLookupResult::Object: {
1977 } else if (isUndefined(value, type)) {
1978
1979 // NB: In order to have a meaningful reset() here, the type needs to be a wrapper type
1980 // that can hold undefined. For example QVariant. The caller must not unwrap it.
1981
1983 } else {
1984
1985 // Unwrap any QVariant so that we get a meaningful conversion below.
1986 unwrapVariant();
1987
1990 QV4::Scope scope(v4);
1995 }
1996 }
1997
1999 break;
2000 }
2002 propType = QMetaType(reinterpret_cast<const QtPrivate::QMetaTypeInterface *>(
2006 } else if (isUndefined(value, type)) {
2007
2008 // NB: In order to have a meaningful reset() here, the type needs to be a wrapper type
2009 // that can hold undefined. For example QVariant. The caller must not unwrap it.
2011
2012 } else {
2013
2014 // Unwrap any QVariant so that we get a meaningful conversion below.
2015 unwrapVariant();
2016
2019 QV4::Scope scope(v4);
2024 }
2025 }
2026 break;
2027 }
2030 return;
2031 }
2032
2033 switch (storeResult) {
2034 case PropertyResult::NeedsInit: {
2035 const QString error = QLatin1String("Cannot assign ") +
2037 QLatin1String(" to ") +
2040 break;
2041 }
2042 case PropertyResult::Deleted:
2044 QStringLiteral("Value is null and could not be converted to an object"));
2045 break;
2046 case PropertyResult::OK:
2047 break;
2048 }
2049}
2050
2058
2060{
2061 if (wrapper) {
2062 // We have to check this here because you may pass a plain QObject that only
2063 // turns out to be a QQmlLoggingCategoryBase at run time.
2067 *ok = true;
2068 if (!loggingCategory) {
2070 QStringLiteral("A QmlLoggingCatgory was provided without a valid name"));
2071 }
2072 return loggingCategory;
2073 }
2074 }
2075
2076 *ok = false;
2077 return qmlEngine() ? &lcQml() : &lcJs();
2078}
2079
2082{
2084
2086 Q_ASSERT(frame);
2087
2088 const QByteArray source(frame->source().toUtf8());
2092
2093 switch (type) {
2094 case QtDebugMsg:
2096 break;
2097 case QtInfoMsg:
2099 break;
2100 case QtWarningMsg:
2102 break;
2103 case QtCriticalMsg:
2105 break;
2106 default:
2107 break;
2108 }
2109}
2110
2118
2123
2128
2130 double year, double month, double day, double hours,
2131 double minutes, double seconds, double msecs) const
2132{
2135}
2136
2138 const QQmlType &type, const QV4::CompiledData::ParameterType &parameter)
2139{
2140 return parameter.isList() ? type.qListTypeId() : type.typeId();
2141}
2142
2153
2156 QObject *thisObject, void **args, int argc)
2157{
2158 Q_ALLOCA_INIT();
2159 // We need to re-fetch the method on every call because it can be shadowed.
2160
2164
2165 // The getter mustn't reset the isVariant flag
2167
2168 // Since we have an asVariant lookup, the function may have been overridden in the mean time.
2169 if (!function)
2170 return false;
2171
2172 Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
2174
2176 return !scope.hasException();
2177}
2178
2181 QObject *thisObject, void **args, int argc)
2182{
2184
2185 Q_ALLOCA_INIT();
2186 Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
2191 for (int i = 0; i < argc; ++i)
2193
2195 }
2196
2200 switch (method->methodIndex()) {
2202 return method->method_destroy(
2203 engine, thisObject, argc > 0 ? *static_cast<int *>(args[1]) : 0);
2207 thisObject);
2208 if (void *returnValue = args[0])
2209 *static_cast<QString *>(returnValue) = std::move(result);
2210 return true;
2211 }
2212 default:
2213 break;
2214 }
2215 return false;
2216}
2217
2220 QObject *thisObject, void **args, int argc)
2221{
2222 Q_ALLOCA_INIT();
2226
2227 switch (v4Function->kind) {
2228 case QV4::Function::AotCompiled: {
2231 return !engine->hasException;
2232 }
2233 case QV4::Function::JsTyped: {
2237
2238 Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
2241 for (qsizetype i = 0; i != argc; ++i) {
2244 }
2245
2247 return !engine->hasException;
2248 }
2249 case QV4::Function::JsUntyped: {
2250 // We can call untyped functions if we're not expecting a specific return value and don't
2251 // have to pass any arguments. The compiler verifies this.
2252 Q_ASSERT(argc == 0);
2255 return !engine->hasException;
2256 }
2257 case QV4::Function::Eval:
2258 break;
2259 }
2260
2261 Q_UNREACHABLE_RETURN(false);
2262}
2263
2265 const AOTCompiledContext *aotContext, QV4::Lookup *lookup, const QString &object)
2266{
2267 aotContext->engine->handle()->throwTypeError(
2268 QStringLiteral("Property '%1' of object %2 is not a function").arg(
2269 aotContext->compilationUnit->runtimeStrings[lookup->nameIndex]->toQString(),
2270 object));
2271};
2272
2295
2297 QV4::QObjectMethod *method, QV4::Lookup *lookup, int relativeMethodIndex)
2298{
2299 Q_ASSERT(lookup->qobjectMethodLookup.method.get() == method->d());
2300
2301 const auto *d = method->d();
2302 const int methodCount = d->methodCount;
2303
2304 if (methodCount == 1) {
2305 // No need to resolve the overload if there's only one method.
2306 // This can be:
2307 // 1. A QML-declared signal where the relativeMethodIndex is -1.
2308 // 2. A QML-declared function in an object compiled via qmltc where the
2309 // relativeMethodIndex isn't actually the metaobject index.
2310 // NB: QML-declared functions can't be overloaded, so there is no
2311 // ambiguity here.
2312 // 3. A C++-declared invokable. In that case this is an optimization.
2313 lookup->qobjectMethodLookup.propertyData = d->methods;
2314 return;
2315 }
2316
2317 for (int i = 0, end = d->methodCount; i != end; ++i) {
2318 const QMetaMethod metaMethod = d->methods[i].metaMethod();
2319 if (metaMethod.relativeMethodIndex() != relativeMethodIndex)
2320 continue;
2321
2322 lookup->qobjectMethodLookup.propertyData = d->methods + i;
2323 return;
2324 }
2325
2326 Q_UNREACHABLE();
2327}
2328
2330{
2331 QV4::Heap::QObjectMethod *d = method->d();
2332 switch (method->methodIndex()) {
2335 return false;
2336 default:
2337 break;
2338 }
2339
2341 return true;
2342}
2343
2373
2375{
2377 int objectId = -1;
2378 QQmlContextData *context = nullptr;
2380
2381 switch (lookup->call) {
2385 break;
2391 if (objectId != -1 && objectId < context->numIdValues())
2392 break;
2393 }
2394 break;
2395 }
2396 default:
2397 return false;
2398 }
2399
2400 Q_ASSERT(objectId >= 0);
2401 Q_ASSERT(context != nullptr);
2405 *static_cast<QObject **>(target) = context->idValue(objectId);
2406 return true;
2407}
2408
2433
2435 uint index, QObject *object, void **args, int argc) const
2436{
2438
2439 if (!object) {
2440 engine->handle()->throwTypeError(QStringLiteral("Cannot call method '%1' of null")
2442 return false;
2443 }
2444
2445 switch (lookup->call) {
2448 return lookup->asVariant
2453 if (lookup->asVariant) {
2454 // If the method can be shadowed, the overridden method can be taken away, too.
2455 // In that case we might end up with a QObjectMethod or random other values instead.
2456 // callQObjectMethodAsVariant is flexible enough to handle that.
2458 }
2459
2460 // Here we always retrieve a fresh ArrowFunction via the getter.
2464
2465 // The getter mustn't touch the asVariant bit
2467
2468 if (function)
2470
2472 }
2473 default:
2474 break;
2475 }
2476
2477 return false;
2478}
2479
2481{
2482 if (engine->hasError()) {
2484 return;
2485 }
2486
2489
2490 const auto *ddata = QQmlData::get(object, false);
2492 // We cannot lookup functions on an object with VME metaobject but no QObjectWrapper
2493 throwIsNotAFunctionError(this, lookup, QStringLiteral("[object Object]"));
2494 return;
2495 }
2496
2499 if (auto *method = function->as<QV4::QObjectMethod>()) {
2501 lookup->asVariant = true;
2502 return;
2503 }
2504
2505 if (function->as<QV4::ArrowFunction>()) {
2506 // Can't have overloads of JavaScript functions.
2507 lookup->asVariant = true;
2508 return;
2509 }
2510
2512}
2513
2516{
2517 if (engine->hasError()) {
2519 return;
2520 }
2521
2524
2525 const auto *ddata = QQmlData::get(object, false);
2527 // We cannot lookup functions on an object with VME metaobject but no QObjectWrapper
2528 throwIsNotAFunctionError(this, lookup, QStringLiteral("[object Object]"));
2529 return;
2530 }
2531
2534 if (auto *method = function->as<QV4::QObjectMethod>()) {
2537 return;
2538 }
2539
2540 if (function->as<QV4::ArrowFunction>()) {
2541 // Can't have overloads of JavaScript functions.
2542 return;
2543 }
2544
2546}
2547
2549{
2552 return false;
2556 val,
2559 return false;
2560 }
2561 return true;
2562}
2563
2574
2576{
2578
2579 if (!qmlScopeObject) {
2582 return false;
2583 }
2584
2586 switch (lookup->call) {
2589 break;
2593 break;
2594 default:
2595 return false;
2596 }
2597
2598 switch (result) {
2600 return false;
2601 case PropertyResult::Deleted:
2603 QStringLiteral("Cannot read property '%1' of null")
2605 return false;
2606 case PropertyResult::OK:
2607 return true;
2608 }
2609
2610 Q_UNREACHABLE_RETURN(false);
2611}
2612
2614{
2616
2618 switch (lookup->call) {
2621 break;
2625 break;
2626 default:
2627 return false;
2628 }
2629
2630 switch (result) {
2632 return false;
2633 case PropertyResult::Deleted: // Silently omit the write back. Same as interpreter
2634 case PropertyResult::OK:
2635 return true;
2636 }
2637
2638 Q_UNREACHABLE_RETURN(false);
2639}
2640
2641
2642
2650
2652{
2655
2659
2660 // We don't handle non-QObject singletons (as those can't be declared in qmltypes anyway)
2662 *static_cast<QObject **>(target) = wrapper->object();
2663 return true;
2664 }
2665
2666 return false;
2667}
2668
2671
2672template<QV4::Lookup::Call call>
2673static void initTypeWrapperLookup(
2674 const AOTCompiledContext *context, QV4::Lookup *lookup, uint importNamespace)
2675{
2676 Q_ASSERT(!context->engine->hasError());
2677 if (importNamespace != AOTCompiledContext::InvalidStringId) {
2678 QV4::Scope scope(context->engine->handle());
2679 QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
2680
2681 QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
2682 Q_ASSERT(typeLoader);
2683 if (const QQmlImportRef *importRef
2684 = context->qmlContext->imports()->query(import, typeLoader).importNamespace) {
2685
2686 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
2687 scope, QV4::QQmlTypeWrapper::create(
2688 scope.engine, nullptr, context->qmlContext->imports(), importRef));
2689
2690 // This is not a contextGetter since we actually load from the namespace.
2691 wrapper = lookup->getter(context->engine->handle(), wrapper);
2692
2693 // In theory, the getter may have populated the lookup's property cache.
2694 lookup->releasePropertyCache();
2695
2696 lookup->call = call;
2697 switch (call) {
2698 case QV4::Lookup::Call::ContextGetterSingleton:
2699 lookup->qmlContextSingletonLookup.singletonObject.set(scope.engine, wrapper->heapObject());
2700 break;
2701 case QV4::Lookup::Call::ContextGetterType:
2702 lookup->qmlTypeLookup.qmlTypeWrapper.set(scope.engine, wrapper->heapObject());
2703 break;
2704 default:
2705 break;
2706 }
2707 return;
2708 }
2709 scope.engine->throwTypeError();
2710 } else {
2711 QV4::ExecutionEngine *v4 = context->engine->handle();
2712 lookup->contextGetter(v4, nullptr);
2713 if (lookup->call != call) {
2714 const QString error
2715 = QLatin1String(call == QV4::Lookup::Call::ContextGetterSingleton
2716 ? "%1 was a singleton at compile time, "
2717 "but is not a singleton anymore."
2718 : "%1 was not a singleton at compile time, "
2719 "but is a singleton now.")
2720 .arg(context->compilationUnit->runtimeStrings[lookup->nameIndex]->toQString());
2721 v4->throwTypeError(error);
2722 }
2723 }
2724}
2730}
2744}
2777}
2780{
2783 return false;
2784
2785 const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
2787
2789 *static_cast<const QMetaObject **>(target)
2791 return true;
2792}
2798}
2801{
2803 const auto doThrow = [&]() {
2805 QStringLiteral("Cannot read property '%1' of null")
2807 return false;
2808 };
2809
2810 if (!object)
2811 return doThrow();
2812
2814 switch (lookup->call) {
2819 break;
2825 break;
2826 default:
2827 return false;
2828 }
2829
2830 switch (result) {
2831 case PropertyResult::Deleted:
2832 return doThrow();
2834 return false;
2835 case PropertyResult::OK:
2836 return true;
2837 }
2838
2839 Q_UNREACHABLE_RETURN(false);
2840}
2843{
2845 if (!object)
2846 return true;
2847
2849 switch (lookup->call) {
2854 break;
2860 break;
2861 default:
2862 return false;
2863 }
2864
2865 switch (result) {
2867 return false;
2868 case PropertyResult::Deleted: // Silently omit the write back
2869 case PropertyResult::OK:
2870 return true;
2871 }
2872
2873 Q_UNREACHABLE_RETURN(false);
2874}
2882}
2890}
2892bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) const
2893{
2894 Q_ASSERT(value);
2895
2898 return false;
2899
2900 const QMetaObject *metaObject
2901 = reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1);
2903
2904 void *args[] = { target, nullptr };
2906 reinterpret_cast<QObject*>(value), QMetaObject::ReadProperty,
2908 return true;
2909}
2912{
2913 Q_ASSERT(value);
2914
2917 return false;
2918
2919 const QMetaObject *metaObject
2920 = reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1);
2922
2923 void *args[] = { source, nullptr };
2925 reinterpret_cast<QObject*>(value), QMetaObject::WriteProperty,
2927 return true;
2928}
2936}
2939{
2942 return false;
2943 const bool isUnsigned
2947 case 1:
2948 if (isUnsigned)
2949 *static_cast<quint8 *>(target) = encoded;
2950 else
2951 *static_cast<qint8 *>(target) = encoded;
2952 return true;
2953 case 2:
2954 if (isUnsigned)
2955 *static_cast<quint16 *>(target) = encoded;
2956 else
2957 *static_cast<qint16 *>(target) = encoded;
2958 return true;
2959 case 4:
2960 if (isUnsigned)
2961 *static_cast<quint32 *>(target) = encoded;
2962 else
2963 *static_cast<qint32 *>(target) = encoded;
2964 return true;
2965 case 8:
2966 if (isUnsigned)
2967 *static_cast<quint64 *>(target) = encoded;
2968 else
2969 *static_cast<qint64 *>(target) = encoded;
2970 return true;
2971 default:
2972 break;
2973 }
2974
2975 return false;
2976}
2995}
2998{
2999 const auto doThrow = [&]() {
3001 QStringLiteral("Value is null and could not be converted to an object"));
3002 return false;
3003 };
3004
3005 if (!object)
3006 return doThrow();
3007
3010 switch (lookup->call) {
3015 break;
3021 break;
3022 default:
3023 return false;
3024 }
3025
3026 switch (result) {
3027 case PropertyResult::Deleted:
3028 return doThrow();
3030 return false;
3031 case PropertyResult::OK:
3032 return true;
3033 }
3034
3035 Q_UNREACHABLE_RETURN(false);
3036}
3044}
3052}
3054static PropertyResult storeValueProperty(
3055 QV4::Lookup *lookup, const QMetaObject *metaObject, void *target, void *value)
3056{
3057 void *args[] = { value, nullptr };
3058 metaObject->d.static_metacall(
3059 reinterpret_cast<QObject *>(target), QMetaObject::WriteProperty,
3060 lookup->qgadgetLookup.coreIndex, args);
3061 return PropertyResult::OK;
3062}
3064static PropertyResult resetValueProperty(
3065 QV4::Lookup *lookup, const QMetaObject *metaObject, void *target, QV4::ExecutionEngine *v4)
3066{
3067 const QMetaProperty property = metaObject->property(lookup->qgadgetLookup.coreIndex);
3068 if (property.isResettable()) {
3069 void *args[] = { nullptr };
3070 metaObject->d.static_metacall(
3071 reinterpret_cast<QObject *>(target), QMetaObject::ResetProperty,
3072 lookup->qgadgetLookup.coreIndex, args);
3073 } else {
3074 v4->throwError(
3075 QLatin1String("Cannot assign [undefined] to ") +
3076 QLatin1String(property.metaType().name()));
3077 }
3078
3079 return PropertyResult::OK;
3080}
3082static PropertyResult storeValueAsVariant(
3083 QV4::ExecutionEngine *v4, QV4::Lookup *lookup, const QMetaObject *metaObject,
3084 void *target, void *value)
3085{
3086 QVariant *variant = static_cast<QVariant *>(value);
3087 const QMetaType propType = metaObject->property(lookup->qgadgetLookup.coreIndex).metaType();
3088 if (propType == QMetaType::fromType<QVariant>())
3089 return storeValueProperty(lookup, metaObject, target, variant);
3090
3091 if (!variant->isValid())
3092 return resetValueProperty(lookup, metaObject, target, v4);
3093
3094 if (isTypeCompatible(variant->metaType(), propType))
3095 return storeValueProperty(lookup, metaObject, target, variant->data());
3096
3097 QVariant converted(propType);
3098 QV4::Scope scope(v4);
3099 QV4::ScopedValue val(scope, v4->fromVariant(*variant));
3100 if (v4->metaTypeFromJS(val, propType, converted.data())
3101 || QMetaType::convert(
3102 variant->metaType(), variant->constData(), propType, converted.data())) {
3103 return storeValueProperty(lookup, metaObject, target, converted.data());
3104 }
3105
3106 v4->throwError(
3107 QLatin1String("Cannot assign ") + QLatin1String(variant->metaType().name())
3108 + QLatin1String(" to ") + QLatin1String(propType.name()));
3109
3111}
3114 uint index, void *target, void *value) const
3115{
3118 return false;
3119
3120 const QMetaObject *metaObject
3121 = reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1);
3122
3126
3127 switch (result) {
3128 case PropertyResult::OK:
3129 return true;
3131 return false;
3132 case PropertyResult::Deleted:
3133 Q_UNREACHABLE();
3134 }
3135
3136 return false;
3137}
3138
3139template<LookupType Type>
3140void initValueLookup(
3141 const AOTCompiledContext *aotContext, uint index, const QMetaObject *metaObject)
3142{
3143 QV4::ExecutionEngine *v4 = aotContext->engine->handle();
3144 if (v4->hasException) {
3145 v4->amendException();
3146 return;
3147 }
3148
3149 QV4::Lookup *lookup = aotContext->compilationUnit->runtimeLookups + index;
3150 initValueLookup(lookup, aotContext->compilationUnit, metaObject);
3151 lookup->call = QV4::Lookup::Call::SetterValueTypeProperty;
3152 lookup->asVariant = (Type == LookupType::Variant);
3153}
3158}
3164}
3166bool AOTCompiledContext::callValueLookup(uint index, void *target, void **args, int argc) const
3167{
3168 Q_UNUSED(argc);
3169
3172 return false;
3173
3175
3176 const QMetaObject *metaObject
3177 = reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1);
3178
3180 reinterpret_cast<QObject *>(target), QMetaObject::InvokeMetaMethod,
3182
3183 return true;
3184}
3201}
3208}
3209
3210namespace AOTLookupValidation {
3212class Validator
3213{
3214public:
3215 Validator(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu);
3217 bool validateLookup(const Lookup &lookup, const Signature &signature);
3218
3219private:
3220 bool validatePropertyLookup(const QMetaObject *mo, const QString &member,
3221 const PropertySignature &expected) const;
3222 bool validateEnumKeyLookup(const QMetaObject *mo, const QString &member,
3223 const QString &enumName, const EnumKeySignature &expected) const;
3224 bool validateMethodLookup(const QMetaObject *mo, const QString &member,
3225 const MethodSignature &expected) const;
3226 bool methodMatchesSignature(const QMetaMethod &method, const QString &member,
3227 const MethodSignature &expected) const;
3228
3229 bool typesMatch(const QMetaType &actual, const Type &expected) const;
3230 const QMetaObject *metaObjectForType(const Type &type) const;
3231 QQmlType compositeType(const Type &type) const;
3232 const QQmlImportInstance *findImport(const QString &module, QTypeRevision version) const;
3233 const QMetaObject *baseMOAtDepth(const QMetaObject *mo, int depth) const;
3234
3235 QQmlEngine *m_engine = nullptr;
3236 QV4::CompiledData::CompilationUnit *m_compilationUnit = nullptr;
3237 QQmlTypeLoader *m_loader = nullptr;
3238};
3240Validator::Validator(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu)
3241 : m_engine(engine), m_compilationUnit(cu), m_loader(QQmlTypeLoader::get(engine))
3242{
3243 Q_ASSERT(m_engine);
3244 Q_ASSERT(m_compilationUnit);
3245 Q_ASSERT(m_loader);
3246}
3248bool Validator::validateLookup(const Lookup &lookup, const Signature &signature)
3249{
3250 if (!m_engine || !m_compilationUnit || !m_loader)
3251 return false;
3252
3253 const auto *mo = metaObjectForType(lookup.base);
3254 if (!mo)
3255 return false;
3256
3257 if (const auto *sig = std::get_if<PropertySignature>(&signature)) {
3258 return validatePropertyLookup(mo, lookup.member, *sig);
3259 } else if (const auto *sig = std::get_if<EnumKeySignature>(&signature)) {
3260 return validateEnumKeyLookup(mo, lookup.enumName, lookup.member, *sig);
3261 } else if (const auto *sig = std::get_if<MethodSignature>(&signature)) {
3262 return validateMethodLookup(mo, lookup.member, *sig);
3263 }
3264
3265 Q_UNREACHABLE_RETURN(false);
3266}
3267
3268bool Validator::validatePropertyLookup(const QMetaObject *mo, const QString &member,
3269 const PropertySignature &expected) const
3270{
3271 const int index = mo->indexOfProperty(member.toUtf8());
3272 if (index == -1)
3273 return false;
3274
3275 const auto prop = mo->property(index);
3276 if (!prop.isValid())
3277 return false;
3278
3279 if (prop.relativePropertyIndex() != expected.relativeIndex)
3280 return false;
3281
3282 return typesMatch(prop.metaType(), expected.type);
3283}
3284
3285bool Validator::validateEnumKeyLookup(const QMetaObject *mo, const QString &enumName,
3286 const QString &member, const EnumKeySignature &expected) const
3287{
3288 const int index = mo->indexOfEnumerator(enumName.toUtf8());
3289 if (index == -1)
3290 return false;
3291
3292 const auto enumerator = mo->enumerator(index);
3293 if (!enumerator.isValid())
3294 return false;
3295
3296 if (enumerator.isFlag() != (expected.isFlag == IsFlag::Yes))
3297 return false;
3298
3299 const auto actualValue = enumerator.keyToValue64(member.toUtf8());
3300 return actualValue.has_value() && expected.value == *actualValue;
3301}
3302
3303bool Validator::methodMatchesSignature(const QMetaMethod &method, const QString &member,
3304 const MethodSignature &expected) const
3305{
3306 if (method.nameView() != member.toUtf8())
3307 return false;
3308
3309 if (method.relativeMethodIndex() != expected.relativeIndex)
3310 return false;
3311
3312 if ((method.methodType() == QMetaMethod::Signal) != (expected.isSignal == IsSignal::Yes))
3313 return false;
3314
3315 if (!typesMatch(method.returnMetaType(), expected.types[0]))
3316 return false;
3317
3318 const int paramCount = method.parameterCount();
3319 const auto &expectedParamNames = expected.paramNames;
3320 if (paramCount != int(expectedParamNames.size()))
3321 return false;
3322
3323 const auto actualParameterNames = method.parameterNames();
3324 for (int i = 0; i < paramCount; ++i) {
3325 if (actualParameterNames[i] != expectedParamNames[i].toUtf8())
3326 return false;
3327 }
3328
3329 const auto &expectedParamTypes = expected.types;
3330 for (int i = 0; i < paramCount; ++i) {
3331 if (!typesMatch(method.parameterMetaType(i), expectedParamTypes[i + 1]))
3332 return false;
3333 }
3334 return true;
3335}
3336
3337bool Validator::validateMethodLookup(const QMetaObject *mo, const QString &member,
3338 const MethodSignature &expected) const
3339{
3340 // Iterate in reverse to match method resolution
3341 for (int i = mo->methodCount() - 1; i >= 0; --i) {
3342 if (methodMatchesSignature(mo->method(i), member, expected))
3343 return true;
3344 }
3345
3346 return false;
3347}
3349static QByteArrayView trimConstPointer(QByteArrayView typeName)
3350{
3351 QByteArrayView noConst = typeName.trimmed();
3352 const QByteArrayView c = "const";
3353 if (noConst.startsWith(c))
3354 noConst.slice(c.length());
3355 QByteArrayView res = noConst.trimmed();
3356 for (auto it = typeName.crbegin(); it != typeName.crend(); ++it) {
3357 if (*it == ' ' || *it == '*')
3358 res = res.chopped(1);
3359 else
3360 break;
3361 }
3362 return res;
3363}
3364
3365bool Validator::typesMatch(const QMetaType &actual, const Type &expected) const
3366{
3367 if (expected.isComposite == IsComposite::No) {
3368 // TODO this just checks the T, not the indirection level (QTBUG-147142)
3369 const QByteArray actualStar = trimConstPointer(actual.name()) + '*';
3370 const QByteArray expectedStar = trimConstPointer(expected.name.toLatin1()) + '*';
3371 return QMetaType::fromName(actualStar) == QMetaType::fromName(expectedStar);
3372 }
3373
3374 const auto *actualMo = actual.metaObject();
3375 if (!actualMo)
3376 return false;
3377
3378 const auto *expectedMO = metaObjectForType(expected);
3379 if (!expectedMO)
3380 return false;
3381
3382 return actualMo == expectedMO;
3383}
3384
3385const QMetaObject *Validator::metaObjectForType(const Type &type) const
3386{
3387 if (type.isComposite == IsComposite::No) {
3388 const QString &extensionTypeName = type.icNameOrExtensionTypeName;
3389 const QString &typeName = extensionTypeName.isEmpty() ? type.name : extensionTypeName;
3390 const QString typeNameStar = typeName + u'*';
3391 const QMetaType mt = QMetaType::fromName(typeNameStar.toUtf8());
3392 return mt.metaObject();
3393 }
3394
3395 return compositeType(type).typeId().metaObject();
3396}
3397
3398QQmlType Validator::compositeType(const Type &type) const
3399{
3400 const QString icName =
3401 type.isInlineComponent == IsIC::Yes ? type.icNameOrExtensionTypeName : QString();
3402
3403 if (type.module == s_thisCuModule && type.name == s_thisCuType)
3404 return m_compilationUnit->qmlTypeForComponent(icName);
3405
3406 Q_ASSERT(type.module != s_thisCuModule);
3407 Q_ASSERT(type.name != s_thisCuType);
3408
3409 if (type.module.isEmpty() || type.name.isEmpty())
3410 return QQmlType();
3411
3412 const QQmlImportInstance *import = findImport(type.module, type.version);
3413 if (!import)
3414 return QQmlType();
3415
3416 // If the module also has a C++ type of the same name, that one takes precedence. We cannot
3417 // reach the composite type through the module then.
3418 QQmlType qmlType;
3419 if (!import->resolveType(m_loader, type.name, nullptr, &qmlType) || !qmlType.isComposite())
3420 return QQmlType();
3421
3422 // Types only used in script code are resolved, but not loaded yet. Load them like
3423 // QQmlTypeWrapper does. If the type is loaded already, this is just a cache lookup.
3424 const auto cu =
3425 m_loader->getType(qmlType.sourceUrl(), QQmlTypeLoader::Synchronous)->compilationUnit();
3426 return cu ? cu->qmlTypeForComponent(icName) : QQmlType();
3427}
3428
3429const QQmlImportInstance *Validator::findImport(const QString &module, QTypeRevision version) const
3430{
3431 // We need the exact import the signature was recorded for. Different versions of a module can
3432 // provide different types under the same name. The compilation unit doesn't have to have
3433 // that import itself, though. It may get hold of the type via a property of a type defined
3434 // in a different document. Then the other document has the import. The compilation unit and
3435 // the compilation units of the types it uses have already resolved all their imports. We
3436 // don't have to look further. If the import isn't among them, the type has moved to a
3437 // different module or the imports have changed since the signature was recorded.
3438 QList<const QV4::CompiledData::CompilationUnit *> pending{ m_compilationUnit };
3439 QSet<const QV4::CompiledData::CompilationUnit *> seen{ m_compilationUnit };
3440 while (!pending.isEmpty()) {
3441 const QV4::CompiledData::CompilationUnit *cu = pending.takeFirst();
3442 Q_ASSERT(cu->typeNameCache);
3443 if (const QQmlImportInstance *import =
3444 cu->typeNameCache->findImportByModuleUri(module, version)) {
3445 return import;
3446 }
3447
3448 for (const QV4::ResolvedTypeReference *ref : cu->resolvedTypes) {
3449 const QV4::CompiledData::CompilationUnit *dependency = ref->compilationUnit().data();
3450 if (dependency && !seen.contains(dependency)) {
3451 seen.insert(dependency);
3452 pending.append(dependency);
3453 }
3454 }
3455 }
3456
3457 return nullptr;
3458}
3460bool validateLookupSignature(QQmlEngine *engine, QV4::CompiledData::CompilationUnit *cu,
3461 const Lookup &lookup, const Signature &signature)
3462{
3463 Validator validator(engine, cu);
3464 return validator.validateLookup(lookup, signature);
3465}
3466
3467} // namespace AOTLookupValidation
3468
3469} // namespace QQmlPrivate
3470
3471/*!
3472 \macro QML_DECLARE_TYPE()
3473 \relates <qqml.h>
3474
3475 Equivalent to \c Q_DECLARE_METATYPE(TYPE *) and \c Q_DECLARE_METATYPE(QQmlListProperty<TYPE>)
3476*/
3477
3478/*!
3479 \macro QML_DECLARE_TYPEINFO(Type,Flags)
3480 \relates <qqml.h>
3481
3482 Declares additional properties of the given \a Type as described by the
3483 specified \a Flags.
3484
3485 Current the only supported type info is \c QML_HAS_ATTACHED_PROPERTIES which
3486 declares that the \a Type supports \l {Attached Properties and Attached Signal Handlers}
3487 {attached properties}. QML_DECLARE_TYPEINFO() is not necessary if \a Type contains the
3488 QML_ATTACHED macro.
3489*/
3490
3491/*!
3492 \fn template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
3493 \relates <qqml.h>
3494
3495 This template function registers the C++ type in the QML system with
3496 the name \a qmlName, in the library imported from \a uri having the
3497 version number composed from \a versionMajor and \a versionMinor.
3498
3499 Returns the QML type id.
3500
3501 There are two forms of this template function:
3502
3503 \code
3504 template<typename T>
3505 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
3506
3507 template<typename T, int metaObjectRevision>
3508 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
3509 \endcode
3510
3511 The former is the standard form which registers the type \e T as a new type.
3512 The latter allows a particular revision of a class to be registered in
3513 a specified version (see \l {Type Revisions and Versions}).
3514
3515
3516 For example, this registers a C++ class \c MySliderItem as a QML type
3517 named \c Slider for version 1.0 of a type namespace called
3518 "com.mycompany.qmlcomponents":
3519
3520 \code
3521 qmlRegisterType<MySliderItem>("com.mycompany.qmlcomponents", 1, 0, "Slider");
3522 \endcode
3523
3524 Once this is registered, the type can be used in QML by importing the
3525 specified type namespace and version number:
3526
3527 \qml
3528 import com.mycompany.qmlcomponents 1.0
3529
3530 Slider {
3531 // ...
3532 }
3533 \endqml
3534
3535 Note that it's perfectly reasonable for a library to register types to older versions
3536 than the actual version of the library. Indeed, it is normal for the new library to allow
3537 QML written to previous versions to continue to work, even if more advanced versions of
3538 some of its types are available.
3539
3540 \sa QML_ELEMENT, QML_NAMED_ELEMENT(),
3541 {Choosing the Correct Integration Method Between C++ and QML}
3542*/
3543
3544/*!
3545 \fn template<typename T, int metaObjectRevision> int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
3546 \relates <qqml.h>
3547
3548 This template function registers the C++ type \a T at the specified revision
3549 \a metaObjectRevision in the QML system with the library imported from \a uri
3550 having the version number composed from \a versionMajor and \a versionMinor.
3551
3552 Returns the QML type id.
3553
3554 \code
3555 template<typename T, int metaObjectRevision>
3556 int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor);
3557 \endcode
3558
3559 This function is typically used to register the revision of a base class to
3560 use for the specified version of the type (see \l {Type Revisions and Versions}).
3561*/
3562
3563/*!
3564 \fn template <typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
3565 \relates <qqml.h>
3566
3567 This template function registers the C++ type in the QML system with
3568 the name \a qmlName, in the library imported from \a uri having the
3569 version number composed from \a versionMajor and \a versionMinor.
3570
3571 While the type has a name and a type, it cannot be created, and the
3572 given error \a message will result if creation is attempted.
3573
3574 This is useful where the type is only intended for providing attached properties or enum values.
3575
3576 Returns the QML type id.
3577
3578 \sa QML_UNCREATABLE(), qmlRegisterTypeNotAvailable(),
3579 {Choosing the Correct Integration Method Between C++ and QML}
3580*/
3581
3582/*!
3583 \fn template <typename T, typename E> int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
3584 \relates <qqml.h>
3585
3586 This template function registers the C++ type and its extension object in the
3587 QML system with the name \a qmlName in the library imported from \a uri having
3588 version number composed from \a versionMajor and \a versionMinor. Properties
3589 not available in the main type will be searched for in the extension object.
3590
3591 Returns the QML type id.
3592
3593 \sa QML_EXTENDED(), qmlRegisterType(), {Registering Extension Objects}
3594*/
3595
3596/*!
3597 \fn template <typename T, typename E> int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
3598 \relates <qqml.h>
3599
3600 This template function registers the C++ type and its extension
3601 in the QML system with the name \a qmlName in the library imported
3602 from \a uri having version number composed from \a versionMajor and
3603 \a versionMinor.
3604
3605 While the type has a name and a type, it cannot be created. An error
3606 message with the given \a reason is printed if the user attempts to
3607 create an instance of this type.
3608
3609 This is useful where the type is only intended for providing attached
3610 properties, enum values or an abstract base class with its extension.
3611
3612 Returns the QML type id.
3613
3614 \sa QML_EXTENDED(), QML_UNCREATABLE(), qmlRegisterUncreatableType()
3615*/
3616
3617/*!
3618 \fn int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, QQmlCustomParser *parser)
3619 \relates <qqml.h>
3620 \internal
3621
3622 This template function registers the C++ type and its extension
3623 in the QML system with the name \a qmlName in the library imported
3624 from \a uri having version number composed from \a versionMajor and
3625 \a versionMinor. Properties from the C++ type or its extension that
3626 cannot be resolved directly by the QML system will be resolved using
3627 the \a parser provided.
3628
3629 Returns the QML type id.
3630
3631 \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED()
3632*/
3633
3634/*!
3635 \fn template <typename T> int qmlRegisterAnonymousType(const char *uri, int versionMajor)
3636 \relates <qqml.h>
3637
3638 This template function registers the C++ type in the QML system as an anonymous type. The
3639 resulting QML type does not have a name. Therefore, instances of this type cannot be created from
3640 the QML system. You can, however, access instances of the type when they are exposed as properties
3641 of other types.
3642
3643 Use this function when the type will not be referenced by name, specifically for C++ types that
3644 are used on the left-hand side of a property binding. To indicate to which module the type belongs
3645 use \a uri and \a versionMajor.
3646
3647 For example, consider the following two classes:
3648
3649 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
3650 \code Q
3651 class Bar : public QObject
3652 {
3653 \1_OBJECT
3654 Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged)
3655
3656 public:
3657 Bar() {}
3658
3659 QString baz() const { return mBaz; }
3660
3661 void setBaz(const QString &baz)
3662 {
3663 if (baz == mBaz)
3664 return;
3665
3666 mBaz = baz;
3667 emit bazChanged();
3668 }
3669
3670 signals:
3671 void bazChanged();
3672
3673 private:
3674 QString mBaz;
3675 };
3676
3677 class Foo : public QObject
3678 {
3679 \1_OBJECT
3680 Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL)
3681
3682 public:
3683 Foo() {}
3684
3685 Bar *bar() { return &mBar; }
3686
3687 private:
3688 Bar mBar;
3689 };
3690 \endcode
3691
3692 In QML, we assign a string to the \c baz property of \c bar:
3693
3694 \code
3695 Foo {
3696 bar.baz: "abc"
3697 Component.onCompleted: print(bar.baz)
3698 }
3699 \endcode
3700
3701 For the QML engine to know that the \c Bar type has a \c baz property,
3702 we have to make \c Bar known:
3703
3704 \code
3705 qmlRegisterType<Foo>("App", 1, 0, "Foo");
3706 qmlRegisterAnonymousType<Bar>("App", 1);
3707 \endcode
3708
3709 As the \c Foo type is instantiated in QML, it must be registered
3710 with the version of \l qmlRegisterType() that takes an element name.
3711
3712 Returns the QML type id.
3713
3714 \since 5.14
3715 \sa QML_ANONYMOUS, {Choosing the Correct Integration Method Between C++ and QML}
3716*/
3717
3718/*!
3719 \fn int qmlRegisterInterface(const char *typeName)
3720 \relates <qqml.h>
3721
3722 This template function registers the C++ type in the QML system
3723 under the name \a typeName.
3724
3725 Types registered as an interface with the engine should also
3726 declare themselves as an interface with the
3727 \l {The Meta-Object System}{meta object system}. For example:
3728
3729 \code
3730 struct FooInterface
3731 {
3732 public:
3733 virtual ~FooInterface();
3734 virtual void doSomething() = 0;
3735 };
3736
3737 Q_DECLARE_INTERFACE(FooInterface, "org.foo.FooInterface")
3738 \endcode
3739
3740 When registered with the QML engine in this way, they can be used as
3741 property types:
3742
3743 Q_PROPERTY(FooInterface *foo READ foo WRITE setFoo)
3744
3745 When you assign a \l QObject sub-class to this property, the QML engine does
3746 the interface cast to \c FooInterface* automatically.
3747
3748 Returns the QML type id.
3749
3750 \sa QML_INTERFACE
3751*/
3752
3753/*!
3754 \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QJSValue(QQmlEngine *, QJSEngine *)> callback)
3755 \relates <qqml.h>
3756
3757 This function may be used to register a singleton type provider \a callback in a particular \a uri
3758 and \a typeName with a version specified in \a versionMajor and \a versionMinor.
3759
3760 Installing a singleton type allows developers to provide arbitrary functionality
3761 (methods and properties) to a client without requiring individual instances of the type to
3762 be instantiated by the client.
3763
3764 A singleton type may be either a QObject or a QJSValue.
3765 This function should be used to register a singleton type provider function which returns a QJSValue as a singleton type.
3766
3767 \b{NOTE:} QJSValue singleton type properties will \b{not} trigger binding re-evaluation if changed.
3768
3769 Usage:
3770 \code
3771 // First, define the singleton type provider function (callback).
3772 static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3773 {
3774 Q_UNUSED(engine)
3775
3776 static int seedValue = 5;
3777 QJSValue example = scriptEngine->newObject();
3778 example.setProperty("someProperty", seedValue++);
3779 return example;
3780 }
3781
3782 // Second, register the singleton type provider with QML by calling this function in an initialization function.
3783 qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);
3784 \endcode
3785
3786 Alternatively, you can use a C++11 lambda:
3787
3788 \code
3789 qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue {
3790 Q_UNUSED(engine)
3791
3792 static int seedValue = 5;
3793 QJSValue example = scriptEngine->newObject();
3794 example.setProperty("someProperty", seedValue++);
3795 return example;
3796 });
3797 \endcode
3798
3799 In order to use the registered singleton type in QML, you must import the singleton type.
3800 \qml
3801 import QtQuick 2.0
3802 import Qt.example.qjsvalueApi 1.0 as ExampleApi
3803 Item {
3804 id: root
3805 property int someValue: ExampleApi.MyApi.someProperty
3806 }
3807 \endqml
3808
3809 \sa QML_SINGLETON, {Choosing the Correct Integration Method Between C++ and QML}
3810*/
3811
3812/*!
3813 \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool createIfMissing)
3814 \relates <qqml.h>
3815
3816 The form of this template function is:
3817
3818 \code
3819 template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool createIfMissing = true)
3820 \endcode
3821
3822 This returns the attached object instance that has been attached to the specified
3823 \a attachee by the attaching type \e T.
3824
3825 If \a attachee is \nullptr, returns \nullptr.
3826
3827 If an existing attached object instance of type \e T exists, it will return
3828 it. Otherwise, it will return a newly created instance if
3829 \a createIfMissing is \c true and \e T is a valid attaching type, or
3830 \nullptr if it's not.
3831
3832 \sa QML_ATTACHED(), {Providing Attached Properties}
3833*/
3834
3835/*!
3836 \fn template <typename T> int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
3837 \relates <qqml.h>
3838
3839 This function may be used to register a singleton type provider \a callback in a particular \a uri
3840 and \a typeName with a version specified in \a versionMajor and \a versionMinor.
3841
3842 Installing a singleton type into a uri allows developers to provide arbitrary functionality
3843 (methods and properties) to clients without requiring individual instances ot the type to be
3844 instantiated by the client.
3845
3846 A singleton type may be either a QObject or a QJSValue.
3847 This function should be used to register a singleton type provider function which returns a QObject
3848 of the given type T as a singleton type.
3849
3850 A QObject singleton type may be referenced via the type name with which it was registered, and this
3851 typename may be used as the target in a \l Connections type or otherwise used as any other type id would.
3852 One exception to this is that a QObject singleton type property may not be aliased.
3853
3854 \b{NOTE:} A QObject singleton type instance returned from a singleton type provider is owned by
3855 the QML engine unless the object has explicit QQmlEngine::CppOwnership flag set.
3856
3857 Usage:
3858 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
3859 \code Q
3860 // First, define your QObject which provides the functionality.
3861 class SingletonTypeExample : public QObject
3862 {
3863 \1_OBJECT
3864 Q_PROPERTY (int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
3865
3866 public:
3867 SingletonTypeExample(QObject *parent = nullptr)
3868 : QObject(parent), m_someProperty(0)
3869 {
3870 }
3871
3872 ~SingletonTypeExample() {}
3873
3874 Q_INVOKABLE int doSomething() { setSomeProperty(5); return m_someProperty; }
3875
3876 int someProperty() const { return m_someProperty; }
3877 void setSomeProperty(int val) { m_someProperty = val; emit somePropertyChanged(val); }
3878
3879 signals:
3880 void somePropertyChanged(int newValue);
3881
3882 private:
3883 int m_someProperty;
3884 };
3885
3886 // Second, define the singleton type provider function (callback).
3887 static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3888 {
3889 Q_UNUSED(engine)
3890 Q_UNUSED(scriptEngine)
3891
3892 SingletonTypeExample *example = new SingletonTypeExample();
3893 return example;
3894 }
3895
3896 // Third, register the singleton type provider with QML by calling this function in an initialization function.
3897 qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);
3898 \endcode
3899
3900 Alternatively, you can use a C++11 lambda:
3901
3902 \code
3903 qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
3904 Q_UNUSED(engine)
3905 Q_UNUSED(scriptEngine)
3906
3907 SingletonTypeExample *example = new SingletonTypeExample();
3908 return example;
3909 });
3910 \endcode
3911
3912 In order to use the registered singleton type in QML, you must import the singleton type.
3913 \qml
3914 import QtQuick 2.0
3915 import Qt.example.qobjectSingleton 1.0
3916 Item {
3917 id: root
3918 property int someValue: MyApi.someProperty
3919
3920 Component.onCompleted: {
3921 someValue = MyApi.doSomething()
3922 }
3923 }
3924 \endqml
3925
3926 \sa QML_SINGLETON, {Choosing the Correct Integration Method Between C++ and QML}
3927*/
3928
3929/*!
3930 \fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
3931 \relates <qqml.h>
3932
3933 This function may be used to register a singleton type with the name \a qmlName, in the library imported from \a uri having
3934 the version number composed from \a versionMajor and \a versionMinor. The type is defined by the QML file located at \a url.
3935 The url must be an absolute URL, i.e. url.isRelative() == false.
3936
3937 In addition the type's QML file must have pragma Singleton statement among its import statements.
3938
3939 A singleton type may be referenced via the type name with which it was registered, and this typename may be used as the
3940 target in a \l Connections type or otherwise used as any other type id would. One exception to this is that a singleton
3941 type property may not be aliased (because the singleton type name does not identify an object within the same component
3942 as any other item).
3943
3944 Usage:
3945 \qml
3946 // First, define your QML singleton type which provides the functionality.
3947 pragma Singleton
3948 import QtQuick 2.0
3949 Item {
3950 property int testProp1: 125
3951 }
3952 \endqml
3953
3954 \code
3955 // Second, register the QML singleton type by calling this function in an initialization function.
3956 qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton");
3957 \endcode
3958
3959 In order to use the registered singleton type in QML, you must import the singleton type.
3960 \qml
3961 import QtQuick 2.0
3962 import Qt.example.qobjectSingleton 1.0
3963 Item {
3964 id: root
3965 property int someValue: RegisteredSingleton.testProp1
3966 }
3967 \endqml
3968
3969 It is also possible to have QML singleton types registered without using the qmlRegisterSingletonType function.
3970 That can be done by adding a pragma Singleton statement among the imports of the type's QML file. In addition
3971 the type must be defined in a qmldir file with a singleton keyword and the qmldir must be imported by the QML
3972 files using the singleton.
3973
3974 \sa QML_SINGLETON
3975*/
3976
3977/*!
3978 \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *cppObject)
3979 \relates <qqml.h>
3980 \since 5.14
3981
3982 This function is used to register a singleton object \a cppObject, with a
3983 particular \a uri and \a typeName. Its version is a combination of \a
3984 versionMajor and \a versionMinor.
3985
3986 Installing a singleton type into a URI allows you to provide arbitrary
3987 functionality (methods and properties) to QML code without requiring
3988 individual instances of the type to be instantiated by the client.
3989
3990 Use this function to register an object of the given type T as a singleton
3991 type.
3992
3993 A QObject singleton type may be referenced via the type name with which it
3994 was registered; in turn this type name may be used as the target in a \l
3995 Connections type, or like any other type ID. However, there's one
3996 exception: a QObject singleton type property can't be aliased because the
3997 singleton type name does not identify an object within the same component
3998 as any other item.
3999
4000 \note \a cppObject must outlive the QML engine in which it is used.
4001 Moreover, \a cppObject must have the same thread affinity as the engine. If
4002 you want separate singleton instances for multiple engines, you need to use
4003 \l {qmlRegisterSingletonType}. See \l{Threads and QObjects} for more
4004 information about thread safety.
4005
4006 \b{NOTE:} qmlRegisterSingleton can only be used when all types of that module are registered procedurally.
4007
4008 Usage:
4009 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
4010 \code Q
4011 // First, define your QObject which provides the functionality.
4012 class SingletonTypeExample : public QObject
4013 {
4014 \1_OBJECT
4015 Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
4016
4017 public:
4018 explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
4019
4020 Q_INVOKABLE int doSomething()
4021 {
4022 setSomeProperty(5);
4023 return m_someProperty;
4024 }
4025
4026 int someProperty() const { return m_someProperty; }
4027 void setSomeProperty(int val) {
4028 if (m_someProperty != val) {
4029 m_someProperty = val;
4030 emit somePropertyChanged(val);
4031 }
4032 }
4033
4034 signals:
4035 void somePropertyChanged(int newValue);
4036
4037 private:
4038 int m_someProperty = 0;
4039 };
4040 \endcode
4041
4042 \code
4043 // Second, create an instance of the object
4044
4045 // allocate example before the engine to ensure that it outlives it
4046 QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample);
4047 QQmlEngine engine;
4048
4049 // Third, register the singleton type provider with QML by calling this
4050 // function in an initialization function.
4051 qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
4052 \endcode
4053
4054
4055 In order to use the registered singleton type in QML, you must import the
4056 URI with the corresponding version.
4057 \qml
4058 import QtQuick 2.0
4059 import Qt.example.qobjectSingleton 1.0
4060 Item {
4061 id: root
4062 property int someValue: MyApi.someProperty
4063
4064 Component.onCompleted: {
4065 console.log(MyApi.doSomething())
4066 }
4067 }
4068 \endqml
4069
4070 \sa QML_SINGLETON, qmlRegisterSingletonType
4071 */
4072
4073/*!
4074 \fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName);
4075 \relates <qqml.h>
4076
4077 This function registers a type in the QML system with the name \a qmlName, in the library imported from \a uri having the
4078 version number composed from \a versionMajor and \a versionMinor. The type is defined by the QML file located at \a url. The
4079 url must be an absolute URL, i.e. url.isRelative() == false.
4080
4081 Normally QML files can be loaded as types directly from other QML files, or using a qmldir file. This function allows
4082 registration of files to types from C++ code, such as when the type mapping needs to be procedurally determined at startup.
4083
4084 Returns -1 if the registration was not successful.
4085*/
4086
4087QT_END_NAMESPACE
PropertyResult loadFallbackAsVariant(QV4::Lookup *lookup, QObject *object, void *target, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1467
static ObjectLookupResult initObjectLookup(const AOTCompiledContext *aotContext, QV4::Lookup *lookup, QObject *object)
Definition qqml.cpp:1719
void qmlRegisterTypeAndRevisions< QQmlTypeNotAvailable, void >(const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject, QList< int > *qmlTypeIds, const QMetaObject *extension, bool)
Definition qqml.cpp:1125
PropertyResult writeBackObjectAsVariant(QV4::Lookup *lookup, QObject *object, void *source)
Definition qqml.cpp:1456
static PropertyResult writeBackFallbackProperty(QV4::Lookup *lookup, QObject *object, void *source)
Definition qqml.cpp:1431
static PropertyResult changeFallbackProperty(QV4::Lookup *lookup, QObject *object, Op op)
Definition qqml.cpp:1530
static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *lookup, QObject *object)
Definition qqml.cpp:1395
static void throwIsNotAFunctionError(const AOTCompiledContext *aotContext, QV4::Lookup *lookup, const QString &object)
Definition qqml.cpp:2264
static bool markPointer(const QVariant &element, QV4::MarkStack *markStack)
Definition qqml.cpp:1214
PropertyResult loadObjectProperty(QV4::Lookup *lookup, QObject *object, void *target, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1360
static bool isEnumUnderlyingType(QMetaType enumType, QMetaType numberType)
Definition qqml.cpp:1571
static PropertyResult storeFallbackAsVariant(QV4::ExecutionEngine *v4, QV4::Lookup *lookup, QObject *object, void *value)
Definition qqml.cpp:1684
static void iterateVariant(const QVariant &element, std::vector< QVariant > *elements)
Definition qqml.cpp:1224
static PropertyResult changeObjectProperty(QV4::Lookup *lookup, QObject *object, Op op)
Definition qqml.cpp:1493
static PropertyResult storeObjectProperty(QV4::Lookup *lookup, QObject *object, void *value)
Definition qqml.cpp:1522
PropertyResult loadObjectAsVariant(QV4::Lookup *lookup, QObject *object, void *target, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1444
static bool callQObjectMethod(QV4::ExecutionEngine *engine, QV4::Lookup *lookup, QObject *thisObject, void **args, int argc)
Definition qqml.cpp:2179
ObjectPropertyQmlData findObjectPropertyQmlData(QV4::Lookup *lookup, QObject *object)
Definition qqml.cpp:1341
static void captureFallbackProperty(QObject *object, int coreIndex, int notifyIndex, bool isConstant, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1301
static void initValueLookup(QV4::Lookup *lookup, QV4::ExecutableCompilationUnit *compilationUnit, const QMetaObject *metaObject)
Definition qqml.cpp:1811
static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCache *ancestor)
Definition qqml.cpp:1323
static bool canHoldVoid(QMetaType type)
Definition qqml.cpp:1606
static bool tryEnsureMethodsCache(QV4::QObjectMethod *method, QObject *object)
Definition qqml.cpp:2329
static void captureObjectProperty(QObject *object, const QQmlPropertyCache *propertyCache, const QQmlPropertyData *property, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1312
PropertyResult writeBackFallbackAsVariant(QV4::Lookup *lookup, QObject *object, void *source)
Definition qqml.cpp:1480
static PropertyResult storeFallbackProperty(QV4::Lookup *lookup, QObject *object, void *value)
Definition qqml.cpp:1544
static bool isUndefined(const void *value, QMetaType type)
Definition qqml.cpp:1937
static bool callQObjectMethodAsVariant(QV4::ExecutionEngine *engine, QV4::Lookup *lookup, QObject *thisObject, void **args, int argc)
Definition qqml.cpp:2154
static QMetaType jsTypedFunctionArgument(const QQmlType &type, const QV4::CompiledData::ParameterType &parameter)
Definition qqml.cpp:2137
static PropertyResult resetFallbackProperty(QV4::Lookup *lookup, QObject *object, QV4::ExecutionEngine *v4)
Definition qqml.cpp:1553
static void resolveQObjectMethodOverload(QV4::QObjectMethod *method, QV4::Lookup *lookup, int relativeMethodIndex)
Definition qqml.cpp:2296
static PropertyResult loadFallbackProperty(QV4::Lookup *lookup, QObject *object, void *target, const AOTCompiledContext *aotContext)
Definition qqml.cpp:1411
static bool callArrowFunction(QV4::ExecutionEngine *engine, QV4::ArrowFunction *function, QObject *thisObject, void **args, int argc)
Definition qqml.cpp:2218
static PropertyResult resetObjectProperty(QV4::Lookup *lookup, QObject *object, QV4::ExecutionEngine *v4)
Definition qqml.cpp:1507
static PropertyResult storeObjectAsVariant(QV4::ExecutionEngine *v4, QV4::Lookup *lookup, QObject *object, void *value)
Definition qqml.cpp:1652
static bool callQObjectMethodWithTypes(QV4::ExecutionEngine *engine, QV4::Lookup *lookup, QObject *thisObject, void **args, QMetaType *types, int argc)
Definition qqml.cpp:2143
static bool isTypeCompatible(QMetaType source, QMetaType target)
Definition qqml.cpp:1614
PropertyResult writeBackObjectProperty(QV4::Lookup *lookup, QObject *object, void *source)
Definition qqml.cpp:1378
void initObjectLookup(const AOTCompiledContext *aotContext, uint index, QObject *object)
Definition qqml.cpp:1784
static QQmlPropertyCapture * propertyCapture(const AOTCompiledContext *aotContext)
Definition qqml.cpp:1178
Combined button and popup list for selecting options.
QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object, const QMetaObject *attachedMetaObject)
Definition qqml.cpp:120
static QList< QTypeRevision > availableRevisions(const QMetaObject *metaObject)
Definition qqml.cpp:587
static void doRegisterSingletonAndRevisions(const QQmlPrivate::RegisterSingletonTypeAndRevisions &type, const ElementNames &elementNames)
Definition qqml.cpp:875
static ElementNames classElementNames(const QMetaObject *metaObject)
Definition qqml.cpp:698
QObject * qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool createIfMissing)
Definition qqml.cpp:128
static void uniqueRevisions(QList< QTypeRevision > *revisions, QTypeRevision defaultVersion, QTypeRevision added)
Definition qqml.cpp:635
void assignVersions(Registration *registration, QTypeRevision revision, QTypeRevision defaultVersion)
Definition qqml.cpp:617
static int finalizeType(const QQmlType &dtype)
Definition qqml.cpp:688
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:94
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:89
static QObject * resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data, QObject *object, bool create)
Definition qqml.cpp:102
QT_BEGIN_NAMESPACE void qmlExecuteDeferred(QObject *object)
\inmodule QtQml \title Functions to register C++ types to QML
Definition qqml.cpp:63
static QList< QTypeRevision > prepareRevisions(const QMetaObject *metaObject, QTypeRevision added)
Definition qqml.cpp:628
static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor)
Definition qqml.cpp:374
static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
Definition qqml.cpp:527
static void doRegisterTypeAndRevisions(const QQmlPrivate::RegisterTypeAndRevisions &type, const ElementNames &elementNames)
Definition qqml.cpp:762
static QTypeRevision resolveModuleVersion(int moduleMajor)
Definition qqml.cpp:385
static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(const QQmlPrivate::RegisterSingletonType &type)
Definition qqml.cpp:667
Q_QML_EXPORT void qmlRegisterModuleImport(const char *uri, int moduleMajor, const char *import, int importMajor=QQmlModuleImportLatest, int importMinor=QQmlModuleImportLatest)
Q_QML_EXPORT bool qmlProtectModule(const char *uri, int majVersion)
Q_QML_EXPORT void qmlUnregisterModuleImport(const char *uri, int moduleMajor, const char *import, int importMajor=QQmlModuleImportLatest, int importMinor=QQmlModuleImportLatest)
Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
AliasRegistrar(const ElementNames *elementNames)
Definition qqml.cpp:742
void registerAliases(int typeId)
Definition qqml.cpp:744
const QMetaObject * metaObject
Definition qqml.cpp:1391