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