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