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
4#include "qqml.h"
5
6#include <QtQml/qqmlprivate.h>
7
8#include <private/qjsvalue_p.h>
9#include <private/qqmlbuiltinfunctions_p.h>
10#include <private/qqmlcomponent_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/qv4dateobject_p.h>
21#include <private/qv4errorobject_p.h>
22#include <private/qv4identifiertable_p.h>
23#include <private/qv4lookup_p.h>
24#include <private/qv4qobjectwrapper_p.h>
25
26#include <QtCore/qmutex.h>
27
28QT_BEGIN_NAMESPACE
29
30Q_DECLARE_LOGGING_CATEGORY(lcQml);
31Q_DECLARE_LOGGING_CATEGORY(lcJs);
32
33/*!
34 \headerfile <qqml.h>
35 \inmodule QtQml
36 \title Functions to register C++ types to QML
37
38 This header provides a collection of functions that allow the registration of
39 C++ types to QML.
40
41 \sa {Overview - QML and C++ Integration}, qqmlintegration.h, qmltyperegistrar
42*/
43
44/*!
45 \internal
46
47 This method completes the setup of all deferred properties of \a object.
48 Deferred properties are declared with
49 Q_CLASSINFO("DeferredPropertyNames", "comma,separated,property,list");
50
51 Any binding to a deferred property is not executed when the object is instantiated,
52 but only when completion is requested with qmlExecuteDeferred, or by manually
53 calling QQmlComponentPrivate::beginDeferred and completeDeferred.
54
55 \sa QV4::CompiledData::Binding::IsDeferredBinding,
56 QV4::CompiledData::Object::HasDeferredBindings,
57 QQmlData::deferData,
58 QQmlObjectCreator::setupBindings
59*/
60void qmlExecuteDeferred(QObject *object)
61{
62 QQmlData *data = QQmlData::get(object);
63
64 if (!data
65 || !data->context
66 || !data->context->engine()
67 || data->deferredData.isEmpty()
68 || data->wasDeleted(object)) {
69 return;
70 }
71
72 if (!data->propertyCache)
73 data->propertyCache = QQmlMetaType::propertyCache(metaObject: object->metaObject());
74
75 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
76
77 QQmlComponentPrivate::DeferredState state;
78 QQmlComponentPrivate::beginDeferred(enginePriv: ep, object, deferredState: &state);
79
80 // Release the reference for the deferral action (we still have one from construction)
81 data->releaseDeferredData();
82
83 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState: &state);
84}
85
86QQmlContext *qmlContext(const QObject *obj)
87{
88 return QQmlEngine::contextForObject(obj);
89}
90
91QQmlEngine *qmlEngine(const QObject *obj)
92{
93 QQmlData *data = QQmlData::get(object: obj);
94 if (!data || !data->context)
95 return nullptr;
96 return data->context->engine();
97}
98
99static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data,
100 QObject *object, bool create)
101{
102 if (!pf)
103 return nullptr;
104
105 QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(key: pf) : 0;
106 if (rv || !create)
107 return rv;
108
109 rv = pf(object);
110
111 if (rv)
112 data->attachedProperties()->insert(key: pf, value: rv);
113
114 return rv;
115}
116
117QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object,
118 const QMetaObject *attachedMetaObject)
119{
120 QQmlEngine *engine = object ? qmlEngine(obj: object) : nullptr;
121 return QQmlMetaType::attachedPropertiesFunc(engine ? QQmlEnginePrivate::get(e: engine) : nullptr,
122 attachedMetaObject);
123}
124
125QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool create)
126{
127 if (!object)
128 return nullptr;
129
130 QQmlData *data = QQmlData::get(object, create);
131
132 // Attached properties are only on objects created by QML,
133 // unless explicitly requested (create==true)
134 if (!data)
135 return nullptr;
136
137 return resolveAttachedProperties(pf: func, data, object, create);
138}
139
140/*!
141 \relates qqml.h
142
143 This function returns the extension object that belongs to \a base, if there is any.
144 Otherwise it returns \c nullptr.
145
146 \sa QML_EXTENDED
147*/
148QObject *qmlExtendedObject(QObject *base)
149{
150 return QQmlPrivate::qmlExtendedObject(base, 0);
151}
152
153QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index)
154{
155 if (!object)
156 return nullptr;
157
158 void *result = nullptr;
159 QObjectPrivate *d = QObjectPrivate::get(o: object);
160 if (!d->metaObject)
161 return nullptr;
162
163 const int id = d->metaObject->metaCall(
164 object, QMetaObject::CustomCall,
165 id: QQmlProxyMetaObject::extensionObjectId(id: index), &result);
166 if (id != QQmlProxyMetaObject::extensionObjectId(id: index))
167 return nullptr;
168
169 return static_cast<QObject *>(result);
170}
171
172void QQmlPrivate::qmlRegistrationWarning(
173 QQmlPrivate::QmlRegistrationWarning warning, QMetaType metaType)
174{
175 switch (warning) {
176 case UnconstructibleType:
177 qWarning().nospace()
178 << metaType.name()
179 << " is neither a default constructible QObject, nor a default- "
180 << "and copy-constructible Q_GADGET, nor marked as uncreatable.\n"
181 << "You should not use it as a QML type.";
182 break;
183 case UnconstructibleSingleton:
184 qWarning()
185 << "Singleton" << metaType.name()
186 << "needs to be a concrete class with either a default constructor"
187 << "or, when adding a default constructor is infeasible, a public static"
188 << "create(QQmlEngine *, QJSEngine *) method.";
189 break;
190 case NonQObjectWithAtached:
191 qWarning()
192 << metaType.name()
193 << "is not a QObject, but has attached properties. This won't work.";
194 break;
195 }
196}
197
198QMetaType QQmlPrivate::compositeMetaType(
199 QV4::ExecutableCompilationUnit *unit, int elementNameId)
200{
201 return QQmlTypePrivate::visibleQmlTypeByName(unit, elementNameId).typeId();
202}
203
204QMetaType QQmlPrivate::compositeMetaType(
205 QV4::ExecutableCompilationUnit *unit, const QString &elementName)
206{
207 return QQmlTypePrivate::visibleQmlTypeByName(
208 unit: unit->baseCompilationUnit(), elementName, typeLoader: unit->engine->typeLoader())
209 .typeId();
210}
211
212QMetaType QQmlPrivate::compositeListMetaType(
213 QV4::ExecutableCompilationUnit *unit, int elementNameId)
214{
215 return QQmlTypePrivate::visibleQmlTypeByName(unit, elementNameId).qListTypeId();
216}
217
218QMetaType QQmlPrivate::compositeListMetaType(
219 QV4::ExecutableCompilationUnit *unit, const QString &elementName)
220{
221 return QQmlTypePrivate::visibleQmlTypeByName(
222 unit: unit->baseCompilationUnit(), elementName, typeLoader: unit->engine->typeLoader())
223 .qListTypeId();
224}
225
226/*!
227 \relates qqml.h
228 \since 5.8
229
230 This function registers the \a staticMetaObject and its extension
231 in the QML system with the name \a qmlName in the library imported
232 from \a uri having version number composed from \a versionMajor and
233 \a versionMinor.
234
235 An instance of the meta object cannot be created. An error message with
236 the given \a reason is printed if the user attempts to create it.
237
238 This function is useful for registering Q_NAMESPACE namespaces.
239
240 Returns the QML type id.
241
242 For example:
243
244 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
245 \code Q
246 namespace MyNamespace {
247 \1_NAMESPACE
248 enum MyEnum {
249 Key1,
250 Key2,
251 };
252 \1_ENUM_NS(MyEnum)
253 }
254
255 //...
256 qmlRegisterUncreatableMetaObject(MyNamespace::staticMetaObject, "io.qt", 1, 0, "MyNamespace", "Access to enums & flags only");
257 \endcode
258
259 On the QML side, you can now use the registered enums:
260 \code
261 Component.onCompleted: console.log(MyNamespace.Key2)
262 \endcode
263
264 \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_UNCREATABLE()
265*/
266int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
267 const char *uri, int versionMajor,
268 int versionMinor, const char *qmlName,
269 const QString& reason)
270{
271 QQmlPrivate::RegisterType type = {
272 .structVersion: QQmlPrivate::RegisterType::CurrentVersion,
273 .typeId: QMetaType(),
274 .listId: QMetaType(),
275 .objectSize: 0,
276 .create: nullptr,
277 .userdata: nullptr,
278 .noCreationReason: reason,
279 .createValueType: nullptr,
280
281 .uri: uri, .version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor), .elementName: qmlName, .metaObject: &staticMetaObject,
282
283 .attachedPropertiesFunction: QQmlAttachedPropertiesFunc(),
284 .attachedPropertiesMetaObject: nullptr,
285
286 .parserStatusCast: -1,
287 .valueSourceCast: -1,
288 .valueInterceptorCast: -1,
289
290 .extensionObjectCreate: nullptr, .extensionMetaObject: nullptr,
291
292 .customParser: nullptr,
293 .revision: QTypeRevision::zero(),
294 .finalizerCast: -1,
295 .creationMethod: QQmlPrivate::ValueTypeCreationMethod::None
296 };
297
298 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
299}
300
301/*!
302 \relates qqml.h
303
304 Clears all stored type registrations, such as those produced with \l qmlRegisterType().
305
306 Do not call this function while a QQmlEngine exists or behavior will be undefined.
307 Any existing QQmlEngines must be deleted before calling this function. This function
308 only affects the application global cache. Delete the QQmlEngine to clear all cached
309 data relating to that engine.
310*/
311void qmlClearTypeRegistrations() // Declared in qqml.h
312{
313 QQmlMetaType::clearTypeRegistrations();
314 QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
315 qmlClearEnginePlugins();
316}
317
318/*!
319 \relates qqml.h
320
321 This function protects a module from further modification. This can be used
322 to prevent other plugins from injecting types into your module. It can also
323 be a performance improvement, as it allows the engine to skip checking for
324 the possibility of new types or plugins when this import is reached.
325
326 Once qmlProtectModule has been called, a QML engine will not search for a new
327 \c qmldir file to load the module anymore. It will re-use any \c qmldir files
328 it has loaded before, though. Therefore, types present at this point continue
329 to work. Mind that different QML engines may load different modules. The
330 module protection, however, is global and affects all engines. The overhead
331 of locating \c qmldir files and loading plugins may be noticeable with slow file
332 systems. Therefore, protecting a module once you are sure you won't need to
333 load it anymore can be a good optimization. Mind also that the module lock
334 not only affects plugins but also any other qmldir directives, like \c import
335 or \c prefer, as well as any composite types or scripts declared in a \c qmldir
336 file.
337
338 In addition, after this function is called, any attempt to register C++ types
339 into this uri, major version combination will lead to a runtime error.
340
341 Returns true if the module with \a uri as a \l{Identified Modules}
342 {module identifier} and \a majVersion as a major version number was found
343 and locked, otherwise returns false. The module must contain exported types
344 in order to be found.
345*/
346bool qmlProtectModule(const char *uri, int majVersion)
347{
348 return QQmlMetaType::protectModule(uri: QString::fromUtf8(utf8: uri),
349 version: QTypeRevision::fromMajorVersion(majorVersion: majVersion));
350}
351
352/*!
353 \since 5.9
354 \relates qqml.h
355
356 This function registers a module in a particular \a uri with a version specified
357 in \a versionMajor and \a versionMinor.
358
359 This can be used to make a certain module version available, even if no types
360 are registered for that version. This is particularly useful for keeping the
361 versions of related modules in sync.
362*/
363
364void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
365{
366 QQmlMetaType::registerModule(uri, version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor));
367}
368
369static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor)
370{
371 if (importMajor == QQmlModuleImportAuto)
372 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Auto);
373 else if (importMajor == QQmlModuleImportLatest)
374 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Default);
375 else if (importMinor == QQmlModuleImportLatest)
376 return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(majorVersion: importMajor), QQmlDirParser::Import::Default);
377 return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(majorVersion: importMajor, minorVersion: importMinor), QQmlDirParser::Import::Default);
378}
379
380static QTypeRevision resolveModuleVersion(int moduleMajor)
381{
382 return moduleMajor == QQmlModuleImportModuleAny
383 ? QTypeRevision()
384 : QTypeRevision::fromMajorVersion(majorVersion: moduleMajor);
385}
386
387/*!
388 * \enum QQmlModuleImportSpecialVersions
389 * \relates qqml.h
390 *
391 * Defines some special values that can be passed to the version arguments of
392 * qmlRegisterModuleImport() and qmlUnregisterModuleImport().
393 *
394 * \value QQmlModuleImportModuleAny When passed as majorVersion of the base
395 * module, signifies that the import is to be
396 * applied to any version of the module.
397 * \value QQmlModuleImportLatest When passed as major or minor version of
398 * the imported module, signifies that the
399 * latest overall, or latest minor version
400 * of a specified major version shall be
401 * imported.
402 * \value QQmlModuleImportAuto When passed as major version of the imported
403 * module, signifies that the version of the
404 * base module shall be forwarded.
405 */
406
407/*!
408 * \relates qqml.h
409 * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
410 *
411 * This has the same effect as an \c import statement in a qmldir file: Whenever
412 * \a uri of version \a moduleMajor is imported, \a import of version
413 * \a importMajor. \a importMinor is automatically imported, too. If
414 * \a importMajor is \l QQmlModuleImportLatest the latest version
415 * available of that module is imported, and \a importMinor does not matter. If
416 * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a
417 * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the
418 * version of \a import is version of \a uri being imported, and \a importMinor
419 * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module
420 * import is applied for any major version of \a uri. For example, you may
421 * specify that whenever any version of MyModule is imported, the latest version
422 * of MyOtherModule should be imported. Then, the following call would be
423 * appropriate:
424 *
425 * \code
426 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
427 * "MyOtherModule", QQmlModuleImportLatest);
428 * \endcode
429 *
430 * Or, you may specify that whenever major version 5 of "MyModule" is imported,
431 * then version 3.14 of "MyOtherModule" should be imported:
432 *
433 * \code
434 * qmlRegisterModuleImport("MyModule", 5, "MyOtherModule", 3, 14);
435 * \endcode
436 *
437 * Finally, if you always want the same version of "MyOtherModule" to be
438 * imported whenever "MyModule" is imported, specify the following:
439 *
440 * \code
441 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
442 * "MyOtherModule", QQmlModuleImportAuto);
443 * \endcode
444 *
445 * \sa qmlUnregisterModuleImport()
446 */
447void qmlRegisterModuleImport(const char *uri, int moduleMajor,
448 const char *import, int importMajor, int importMinor)
449{
450 QQmlMetaType::registerModuleImport(
451 uri: QString::fromUtf8(utf8: uri), version: resolveModuleVersion(moduleMajor),
452 import: resolveImport(uri: QString::fromUtf8(utf8: import), importMajor, importMinor));
453}
454
455
456/*!
457 * \relates qqml.h
458 * Removes a module import previously registered with qmlRegisterModuleImport()
459 *
460 * Calling this function makes sure that \a import of version
461 * \a{importMajor}.\a{importMinor} is not automatically imported anymore when
462 * \a uri of version \a moduleMajor is. The version resolution works the same
463 * way as with \l qmlRegisterModuleImport().
464 *
465 * \sa qmlRegisterModuleImport()
466 */
467void qmlUnregisterModuleImport(const char *uri, int moduleMajor,
468 const char *import, int importMajor, int importMinor)
469{
470 QQmlMetaType::unregisterModuleImport(
471 uri: QString::fromUtf8(utf8: uri), version: resolveModuleVersion(moduleMajor),
472 import: resolveImport(uri: QString::fromUtf8(utf8: import), importMajor, importMinor));
473}
474
475/*!
476 \since 5.12
477 \relates qqml.h
478
479 Returns the QML type id of a type that was registered with the
480 name \a qmlName in a particular \a uri and a version specified in \a
481 versionMajor and \a versionMinor.
482
483 This function returns the same value as the QML type registration functions
484 such as qmlRegisterType() and qmlRegisterSingletonType().
485
486 If \a qmlName, \a uri and \a versionMajor match a registered type, but the
487 specified minor version in \a versionMinor is higher, then the id of the type
488 with the closest minor version is returned.
489
490 Returns -1 if no matching type was found or one of the given parameters
491 was invalid.
492
493 \note: qmlTypeId tries to make modules available, even if they were not accessed by any
494 engine yet. This can introduce overhead the first time a module is accessed. Trying to
495 find types from a module which does not exist always introduces this overhead.
496
497 \sa QML_ELEMENT, QML_NAMED_ELEMENT, QML_SINGLETON, qmlRegisterType(), qmlRegisterSingletonType()
498*/
499int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
500{
501 auto revision = QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor);
502 int id = QQmlMetaType::typeId(uri, version: revision, qmlName);
503 if (id != -1)
504 return id;
505 /* If the module hasn't been imported yet, we might not have the id of a
506 singleton at this point. To obtain it, we need an engine in order to
507 to do the resolution steps.
508 This is expensive, but we assume that users don't constantly query invalid
509 Types; internal code should use QQmlMetaType API.
510 */
511 QQmlEngine engine;
512 auto *enginePriv = QQmlEnginePrivate::get(e: &engine);
513 auto loadHelper = QQml::makeRefPointer<LoadHelper>(args: &enginePriv->typeLoader, args&: uri);
514 auto type = loadHelper->resolveType(typeName: qmlName).type;
515 if (type.availableInVersion(version: revision))
516 return type.index();
517 else
518 return -1;
519}
520
521static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
522{
523 if (!instance) {
524 QQmlError error;
525 error.setDescription(QStringLiteral("The registered singleton has already been deleted. "
526 "Ensure that it outlives the engine."));
527 QQmlEnginePrivate::get(e: engine)->warning(engine, error);
528 return false;
529 }
530
531 if (engine->thread() != instance->thread()) {
532 QQmlError error;
533 error.setDescription(QStringLiteral("Registered object must live in the same thread "
534 "as the engine it was registered with"));
535 QQmlEnginePrivate::get(e: engine)->warning(engine, error);
536 return false;
537 }
538
539 return true;
540}
541
542// From qqmlprivate.h
543#if QT_DEPRECATED_SINCE(6, 3)
544QObject *QQmlPrivate::SingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
545{
546 if (!checkSingletonInstance(engine: qeng, instance: m_object))
547 return nullptr;
548
549 if (alreadyCalled) {
550 QQmlError error;
551 error.setDescription(QStringLiteral("Singleton registered by registerSingletonInstance "
552 "must only be accessed from one engine"));
553 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
554 return nullptr;
555 }
556
557 alreadyCalled = true;
558 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
559 return m_object;
560};
561#endif
562
563QObject *QQmlPrivate::SingletonInstanceFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
564{
565 if (!checkSingletonInstance(engine: qeng, instance: m_object))
566 return nullptr;
567
568 if (!m_engine) {
569 m_engine = qeng;
570 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
571 } else if (m_engine != qeng) {
572 QQmlError error;
573 error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
574 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
575 return nullptr;
576 }
577
578 return m_object;
579};
580
581static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
582{
583 QVector<QTypeRevision> revisions;
584 if (!metaObject)
585 return revisions;
586 const int propertyOffset = metaObject->propertyOffset();
587 const int propertyCount = metaObject->propertyCount();
588 for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
589 coreIndex < propertyEnd; ++coreIndex) {
590 const QMetaProperty property = metaObject->property(index: coreIndex);
591 if (int revision = property.revision())
592 revisions.append(t: QTypeRevision::fromEncodedVersion(value: revision));
593 }
594 const int methodOffset = metaObject->methodOffset();
595 const int methodCount = metaObject->methodCount();
596 for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount;
597 methodIndex < methodEnd; ++methodIndex) {
598 const QMetaMethod method = metaObject->method(index: methodIndex);
599 if (int revision = method.revision())
600 revisions.append(t: QTypeRevision::fromEncodedVersion(value: revision));
601 }
602
603 // Need to also check parent meta objects, as their revisions are inherited.
604 if (const QMetaObject *superMeta = metaObject->superClass())
605 revisions += availableRevisions(metaObject: superMeta);
606
607 return revisions;
608}
609
610template<typename Registration>
611void assignVersions(Registration *registration, QTypeRevision revision,
612 QTypeRevision defaultVersion)
613{
614 const quint8 majorVersion = revision.hasMajorVersion() ? revision.majorVersion()
615 : defaultVersion.majorVersion();
616 registration->version = revision.hasMinorVersion()
617 ? QTypeRevision::fromVersion(majorVersion, minorVersion: revision.minorVersion())
618 : QTypeRevision::fromMajorVersion(majorVersion);
619 registration->revision = revision;
620}
621
622static QVector<QTypeRevision> prepareRevisions(const QMetaObject *metaObject, QTypeRevision added)
623{
624 auto revisions = availableRevisions(metaObject);
625 revisions.append(t: added);
626 return revisions;
627}
628
629static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision defaultVersion,
630 QTypeRevision added)
631{
632 bool revisionsHaveMajorVersions = false;
633 for (QTypeRevision revision : QVector<QTypeRevision>(*revisions)) { // yes, copy
634 // allow any minor version for each explicitly specified past major one
635 if (revision.hasMajorVersion()) {
636 revisionsHaveMajorVersions = true;
637 if (revision.majorVersion() < defaultVersion.majorVersion())
638 revisions->append(t: QTypeRevision::fromVersion(majorVersion: revision.majorVersion(), minorVersion: 254));
639 }
640 }
641
642 if (revisionsHaveMajorVersions) {
643 if (!added.hasMajorVersion()) {
644 // If added in unspecified major version, assume default one.
645 revisions->append(t: QTypeRevision::fromVersion(majorVersion: defaultVersion.majorVersion(),
646 minorVersion: added.minorVersion()));
647 } else if (added.majorVersion() < defaultVersion.majorVersion()) {
648 // If added in past major version, add .0 of default version.
649 revisions->append(t: QTypeRevision::fromVersion(majorVersion: defaultVersion.majorVersion(), minorVersion: 0));
650 }
651 }
652
653 std::sort(first: revisions->begin(), last: revisions->end());
654 const auto it = std::unique(first: revisions->begin(), last: revisions->end());
655 revisions->erase(abegin: it, aend: revisions->end());
656}
657
658static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
659 const QQmlPrivate::RegisterSingletonType &type)
660{
661 QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
662 siinfo->scriptCallback = type.scriptApi;
663 siinfo->qobjectCallback = type.qObjectApi;
664 siinfo->typeName = type.typeName;
665 return QQmlType::SingletonInstanceInfo::ConstPtr(
666 siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
667}
668
669static QQmlType::SingletonInstanceInfo::ConstPtr singletonInstanceInfo(
670 const QQmlPrivate::RegisterCompositeSingletonType &type)
671{
672 QQmlType::SingletonInstanceInfo::Ptr siinfo = QQmlType::SingletonInstanceInfo::create();
673 siinfo->url = QQmlTypeLoader::normalize(unNormalizedUrl: type.url);
674 siinfo->typeName = type.typeName;
675 return QQmlType::SingletonInstanceInfo::ConstPtr(
676 siinfo.take(), QQmlType::SingletonInstanceInfo::ConstPtr::Adopt);
677}
678
679static int finalizeType(const QQmlType &dtype)
680{
681 if (!dtype.isValid())
682 return -1;
683
684 QQmlMetaType::registerUndeletableType(dtype);
685 return dtype.index();
686}
687
688using ElementNames = QVarLengthArray<const char *, 8>;
689static ElementNames classElementNames(const QMetaObject *metaObject)
690{
691 Q_ASSERT(metaObject);
692 const char *key = "QML.Element";
693
694 const int offset = metaObject->classInfoOffset();
695 const int start = metaObject->classInfoCount() + offset - 1;
696
697 ElementNames elementNames;
698
699 for (int i = start; i >= offset; --i) {
700 const QMetaClassInfo classInfo = metaObject->classInfo(index: i);
701 if (qstrcmp(str1: key, str2: classInfo.name()) == 0) {
702 const char *elementName = classInfo.value();
703
704 if (qstrcmp(str1: elementName, str2: "auto") == 0) {
705 const char *strippedClassName = metaObject->className();
706 for (const char *c = strippedClassName; *c != '\0'; c++) {
707 if (*c == ':')
708 strippedClassName = c + 1;
709 }
710 elementName = strippedClassName;
711 } else if (qstrcmp(str1: elementName, str2: "anonymous") == 0) {
712 if (elementNames.isEmpty())
713 elementNames.push_back(t: nullptr);
714 else if (elementNames[0] != nullptr)
715 qWarning() << metaObject->className() << "is both anonymous and named";
716 continue;
717 }
718
719 if (!elementNames.isEmpty() && elementNames[0] == nullptr) {
720 qWarning() << metaObject->className() << "is both anonymous and named";
721 elementNames[0] = elementName;
722 } else {
723 elementNames.push_back(t: elementName);
724 }
725 }
726 }
727
728 return elementNames;
729}
730
731struct AliasRegistrar
732{
733 AliasRegistrar(const ElementNames *elementNames) : elementNames(elementNames) {}
734
735 void registerAliases(int typeId)
736 {
737 if (elementNames) {
738 for (int i = 1, end = elementNames->length(); i < end; ++i)
739 otherNames.append(t: QString::fromUtf8(utf8: elementNames->at(idx: i)));
740 elementNames = nullptr;
741 }
742
743 for (const QString &otherName : std::as_const(t&: otherNames))
744 QQmlMetaType::registerTypeAlias(typeId, name: otherName);
745 }
746
747private:
748 const ElementNames *elementNames;
749 QVarLengthArray<QString, 8> otherNames;
750};
751
752
753static void doRegisterTypeAndRevisions(
754 const QQmlPrivate::RegisterTypeAndRevisions &type,
755 const ElementNames &elementNames)
756{
757 using namespace QQmlPrivate;
758
759 const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
760 const bool creatable = (elementNames[0] != nullptr || isValueType)
761 && boolClassInfo(metaObject: type.classInfoMetaObject, key: "QML.Creatable", defaultValue: true);
762
763 QString noCreateReason;
764 ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
765
766 if (!creatable) {
767 noCreateReason = QString::fromUtf8(
768 utf8: classInfo(metaObject: type.classInfoMetaObject, key: "QML.UncreatableReason"));
769 if (noCreateReason.isEmpty())
770 noCreateReason = QLatin1String("Type cannot be created in QML.");
771 } else if (isValueType) {
772 const char *method = classInfo(metaObject: type.classInfoMetaObject, key: "QML.CreationMethod");
773 if (qstrcmp(str1: method, str2: "structured") == 0)
774 creationMethod = ValueTypeCreationMethod::Structured;
775 else if (qstrcmp(str1: method, str2: "construct") == 0)
776 creationMethod = ValueTypeCreationMethod::Construct;
777 }
778
779 RegisterType typeRevision = {
780 .structVersion: QQmlPrivate::RegisterType::CurrentVersion,
781 .typeId: type.typeId,
782 .listId: type.listId,
783 .objectSize: creatable ? type.objectSize : 0,
784 .create: nullptr,
785 .userdata: nullptr,
786 .noCreationReason: noCreateReason,
787 .createValueType: type.createValueType,
788 .uri: type.uri,
789 .version: type.version,
790 .elementName: nullptr,
791 .metaObject: type.metaObject,
792 .attachedPropertiesFunction: type.attachedPropertiesFunction,
793 .attachedPropertiesMetaObject: type.attachedPropertiesMetaObject,
794 .parserStatusCast: type.parserStatusCast,
795 .valueSourceCast: type.valueSourceCast,
796 .valueInterceptorCast: type.valueInterceptorCast,
797 .extensionObjectCreate: type.extensionObjectCreate,
798 .extensionMetaObject: type.extensionMetaObject,
799 .customParser: nullptr,
800 .revision: QTypeRevision(),
801 .finalizerCast: type.structVersion > 0 ? type.finalizerCast : -1,
802 .creationMethod: creationMethod
803 };
804
805 QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
806 .structVersion: 0,
807 .uri: type.uri,
808 .version: type.version,
809 .typeName: nullptr,
810 .typeId: type.listId,
811 .metaSequence: type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
812 .revision: QTypeRevision(),
813 };
814
815 const QTypeRevision added = revisionClassInfo(
816 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
817 defaultValue: QTypeRevision::fromVersion(majorVersion: type.version.majorVersion(), minorVersion: 0));
818 const QTypeRevision removed = revisionClassInfo(
819 metaObject: type.classInfoMetaObject, key: "QML.RemovedInVersion");
820 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(metaObject: type.classInfoMetaObject,
821 key: "QML.ExtraVersion");
822
823 auto revisions = prepareRevisions(metaObject: type.metaObject, added) + furtherRevisions;
824 if (type.attachedPropertiesMetaObject)
825 revisions += availableRevisions(metaObject: type.attachedPropertiesMetaObject);
826 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
827
828 AliasRegistrar aliasRegistrar(&elementNames);
829 for (QTypeRevision revision : revisions) {
830 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
831 break;
832
833 assignVersions(registration: &typeRevision, revision, defaultVersion: type.version);
834
835 // When removed or before added, we still add revisions, but anonymous ones
836 if (typeRevision.version < added
837 || (removed.isValid() && !(typeRevision.version < removed))) {
838 typeRevision.elementName = nullptr;
839 typeRevision.create = nullptr;
840 typeRevision.userdata = nullptr;
841 } else {
842 typeRevision.elementName = elementNames[0];
843 typeRevision.create = creatable ? type.create : nullptr;
844 typeRevision.userdata = type.userdata;
845 }
846
847 typeRevision.customParser = type.customParserFactory();
848 const int id = qmlregister(TypeRegistration, &typeRevision);
849 if (type.qmlTypeIds)
850 type.qmlTypeIds->append(t: id);
851
852 if (typeRevision.elementName)
853 aliasRegistrar.registerAliases(typeId: id);
854
855 if (sequenceRevision.metaSequence != QMetaSequence()) {
856 sequenceRevision.version = typeRevision.version;
857 sequenceRevision.revision = typeRevision.revision;
858 const int id = QQmlPrivate::qmlregister(
859 QQmlPrivate::SequentialContainerRegistration, &sequenceRevision);
860 if (type.qmlTypeIds)
861 type.qmlTypeIds->append(t: id);
862 }
863 }
864}
865
866static void doRegisterSingletonAndRevisions(
867 const QQmlPrivate::RegisterSingletonTypeAndRevisions &type,
868 const ElementNames &elementNames)
869{
870 using namespace QQmlPrivate;
871
872 RegisterSingletonType revisionRegistration = {
873 .structVersion: 0,
874 .uri: type.uri,
875 .version: type.version,
876 .typeName: elementNames[0],
877 .scriptApi: nullptr,
878 .qObjectApi: type.qObjectApi,
879 .instanceMetaObject: type.instanceMetaObject,
880 .typeId: type.typeId,
881 .extensionObjectCreate: type.extensionObjectCreate,
882 .extensionMetaObject: type.extensionMetaObject,
883 .revision: QTypeRevision()
884 };
885 const QQmlType::SingletonInstanceInfo::ConstPtr siinfo
886 = singletonInstanceInfo(type: revisionRegistration);
887
888 const QTypeRevision added = revisionClassInfo(
889 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
890 defaultValue: QTypeRevision::fromVersion(majorVersion: type.version.majorVersion(), minorVersion: 0));
891 const QTypeRevision removed = revisionClassInfo(
892 metaObject: type.classInfoMetaObject, key: "QML.RemovedInVersion");
893 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(metaObject: type.classInfoMetaObject,
894 key: "QML.ExtraVersion");
895
896 auto revisions = prepareRevisions(metaObject: type.instanceMetaObject, added) + furtherRevisions;
897 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
898
899 AliasRegistrar aliasRegistrar(&elementNames);
900 for (QTypeRevision revision : std::as_const(t&: revisions)) {
901 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
902 break;
903
904 assignVersions(registration: &revisionRegistration, revision, defaultVersion: type.version);
905
906 // When removed or before added, we still add revisions, but anonymous ones
907 if (revisionRegistration.version < added
908 || (removed.isValid() && !(revisionRegistration.version < removed))) {
909 revisionRegistration.typeName = nullptr;
910 revisionRegistration.qObjectApi = nullptr;
911 } else {
912 revisionRegistration.typeName = elementNames[0];
913 revisionRegistration.qObjectApi = type.qObjectApi;
914 }
915
916 const int id = finalizeType(
917 dtype: QQmlMetaType::registerSingletonType(type: revisionRegistration, siinfo));
918 if (type.qmlTypeIds)
919 type.qmlTypeIds->append(t: id);
920
921 if (revisionRegistration.typeName)
922 aliasRegistrar.registerAliases(typeId: id);
923 }
924}
925
926/*
927This method is "over generalized" to allow us to (potentially) register more types of things in
928the future without adding exported symbols.
929*/
930int QQmlPrivate::qmlregister(RegistrationType type, void *data)
931{
932 switch (type) {
933 case AutoParentRegistration:
934 return QQmlMetaType::registerAutoParentFunction(
935 autoparent: *reinterpret_cast<RegisterAutoParent *>(data));
936 case QmlUnitCacheHookRegistration:
937 return QQmlMetaType::registerUnitCacheHook(
938 hookRegistration: *reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
939 case TypeAndRevisionsRegistration: {
940 const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
941 if (type.structVersion > 1 && type.forceAnonymous) {
942 doRegisterTypeAndRevisions(type, elementNames: {nullptr});
943 } else {
944 const ElementNames names = classElementNames(metaObject: type.classInfoMetaObject);
945 if (names.isEmpty()) {
946 qWarning().nospace() << "Missing QML.Element class info for "
947 << type.classInfoMetaObject->className();
948 } else {
949 doRegisterTypeAndRevisions(type, elementNames: names);
950 }
951
952 }
953 break;
954 }
955 case SingletonAndRevisionsRegistration: {
956 const RegisterSingletonTypeAndRevisions &type
957 = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
958 const ElementNames names = classElementNames(metaObject: type.classInfoMetaObject);
959 if (names.isEmpty()) {
960 qWarning().nospace() << "Missing QML.Element class info for "
961 << type.classInfoMetaObject->className();
962 } else {
963 doRegisterSingletonAndRevisions(type, elementNames: names);
964 }
965 break;
966 }
967 case SequentialContainerAndRevisionsRegistration: {
968 const RegisterSequentialContainerAndRevisions &type
969 = *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
970 RegisterSequentialContainer revisionRegistration = {
971 .structVersion: 0,
972 .uri: type.uri,
973 .version: type.version,
974 .typeName: nullptr,
975 .typeId: type.typeId,
976 .metaSequence: type.metaSequence,
977 .revision: QTypeRevision()
978 };
979
980 const QTypeRevision added = revisionClassInfo(
981 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
982 defaultValue: QTypeRevision::fromMinorVersion(minorVersion: 0));
983 QList<QTypeRevision> revisions = revisionClassInfos(
984 metaObject: type.classInfoMetaObject, key: "QML.ExtraVersion");
985 revisions.append(t: added);
986 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
987
988 for (QTypeRevision revision : std::as_const(t&: revisions)) {
989 if (revision < added)
990 continue;
991 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
992 break;
993
994 assignVersions(registration: &revisionRegistration, revision, defaultVersion: type.version);
995 const int id = qmlregister(type: SequentialContainerRegistration, data: &revisionRegistration);
996 if (type.qmlTypeIds)
997 type.qmlTypeIds->append(t: id);
998 }
999 break;
1000 }
1001 case TypeRegistration:
1002 return finalizeType(
1003 dtype: QQmlMetaType::registerType(type: *reinterpret_cast<RegisterType *>(data)));
1004 case InterfaceRegistration:
1005 return finalizeType(
1006 dtype: QQmlMetaType::registerInterface(type: *reinterpret_cast<RegisterInterface *>(data)));
1007 case SingletonRegistration:
1008 return finalizeType(dtype: QQmlMetaType::registerSingletonType(
1009 type: *reinterpret_cast<RegisterSingletonType *>(data),
1010 siinfo: singletonInstanceInfo(type: *reinterpret_cast<RegisterSingletonType *>(data))));
1011 case CompositeRegistration:
1012 return finalizeType(dtype: QQmlMetaType::registerCompositeType(
1013 type: *reinterpret_cast<RegisterCompositeType *>(data)));
1014 case CompositeSingletonRegistration:
1015 return finalizeType(dtype: QQmlMetaType::registerCompositeSingletonType(
1016 type: *reinterpret_cast<RegisterCompositeSingletonType *>(data),
1017 siinfo: singletonInstanceInfo(type: *reinterpret_cast<RegisterCompositeSingletonType *>(data))));
1018 case SequentialContainerRegistration:
1019 return finalizeType(dtype: QQmlMetaType::registerSequentialContainer(
1020 sequenceRegistration: *reinterpret_cast<RegisterSequentialContainer *>(data)));
1021 default:
1022 return -1;
1023 }
1024
1025 return -1;
1026}
1027
1028void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
1029{
1030 switch (type) {
1031 case AutoParentRegistration:
1032 QQmlMetaType::unregisterAutoParentFunction(function: reinterpret_cast<AutoParentFunction>(data));
1033 break;
1034 case QmlUnitCacheHookRegistration:
1035 QQmlMetaType::removeCachedUnitLookupFunction(
1036 handler: reinterpret_cast<QmlUnitCacheLookupFunction>(data));
1037 break;
1038 case SequentialContainerRegistration:
1039 QQmlMetaType::unregisterSequentialContainer(id: data);
1040 break;
1041 case TypeRegistration:
1042 case InterfaceRegistration:
1043 case SingletonRegistration:
1044 case CompositeRegistration:
1045 case CompositeSingletonRegistration:
1046 QQmlMetaType::unregisterType(type: data);
1047 break;
1048 case TypeAndRevisionsRegistration:
1049 case SingletonAndRevisionsRegistration:
1050 case SequentialContainerAndRevisionsRegistration:
1051 // Currently unnecessary. We'd need a special data structure to hold
1052 // URI + majorVersion and then we'd iterate the minor versions, look up the
1053 // associated QQmlType objects by uri/elementName/major/minor and qmlunregister
1054 // each of them.
1055 Q_UNREACHABLE();
1056 break;
1057 }
1058}
1059
1060QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObject,
1061 const char *key)
1062{
1063 QList<QTypeRevision> revisions;
1064 for (int index = indexOfOwnClassInfo(metaObject, key); index != -1;
1065 index = indexOfOwnClassInfo(metaObject, key, startOffset: index - 1)) {
1066 revisions.push_back(t: QTypeRevision::fromEncodedVersion(
1067 value: QLatin1StringView(metaObject->classInfo(index).value()).toInt()));
1068 }
1069 return revisions;
1070}
1071
1072/*!
1073 \relates qqml.h
1074
1075 This function registers a type in the QML system with the name \a qmlName, in the type namespace imported from \a uri having the
1076 version number composed from \a versionMajor and \a versionMinor, but any attempt to instantiate the type
1077 will produce the given error \a message.
1078
1079 Normally, the types exported by a plugin should be fixed. However, if a C++ type is not available, you should
1080 at least "reserve" the QML type name, and give the user of the unavailable type a meaningful error message.
1081
1082 Returns the QML type id.
1083
1084 Example:
1085
1086 \code
1087 #ifdef NO_GAMES_ALLOWED
1088 qmlRegisterTypeNotAvailable("MinehuntCore", 0, 1, "Game", "Get back to work, slacker!");
1089 #else
1090 qmlRegisterType<MinehuntGame>("MinehuntCore", 0, 1, "Game");
1091 #endif
1092 \endcode
1093
1094 This will cause any QML which imports the "MinehuntCore" type namespace and attempts to use the type to produce an error message:
1095 \code
1096 fun.qml: Get back to work, slacker!
1097 Game {
1098 ^
1099 \endcode
1100
1101 Without this, a generic "Game is not a type" message would be given.
1102
1103 \sa QML_UNAVAILABLE, qmlRegisterUncreatableType(),
1104 {Choosing the Correct Integration Method Between C++ and QML}
1105*/
1106int qmlRegisterTypeNotAvailable(
1107 const char *uri, int versionMajor, int versionMinor,
1108 const char *qmlName, const QString &message)
1109{
1110 return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
1111 uri, versionMajor, versionMinor, qmlName, reason: message);
1112}
1113
1114namespace QQmlPrivate {
1115template<>
1116void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
1117 const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
1118 QVector<int> *qmlTypeIds, const QMetaObject *extension, bool)
1119{
1120 using T = QQmlTypeNotAvailable;
1121
1122 RegisterTypeAndRevisions type = {
1123 .structVersion: 3,
1124 .typeId: QmlMetaType<T>::self(),
1125 .listId: QmlMetaType<T>::list(),
1126 .objectSize: 0,
1127 .create: nullptr,
1128 .userdata: nullptr,
1129 .createValueType: nullptr,
1130
1131 .uri: uri,
1132 .version: QTypeRevision::fromMajorVersion(majorVersion: versionMajor),
1133
1134 .metaObject: &QQmlTypeNotAvailable::staticMetaObject,
1135 .classInfoMetaObject: classInfoMetaObject,
1136
1137 .attachedPropertiesFunction: attachedPropertiesFunc<T>(),
1138 .attachedPropertiesMetaObject: attachedPropertiesMetaObject<T>(),
1139
1140 .parserStatusCast: StaticCastSelector<T, QQmlParserStatus>::cast(),
1141 .valueSourceCast: StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
1142 .valueInterceptorCast: StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
1143
1144 .extensionObjectCreate: nullptr,
1145 .extensionMetaObject: extension,
1146 .customParserFactory: qmlCreateCustomParser<T>,
1147 .qmlTypeIds: qmlTypeIds,
1148 .finalizerCast: QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
1149 .forceAnonymous: false,
1150 .listMetaSequence: QmlMetaType<T>::sequence(),
1151 };
1152
1153 qmlregister(type: TypeAndRevisionsRegistration, data: &type);
1154}
1155
1156QObject *AOTCompiledContext::thisObject() const
1157{
1158 return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
1159 ->thisObject();
1160}
1161
1162QQmlEngine *AOTCompiledContext::qmlEngine() const
1163{
1164 return engine->handle()->qmlEngine();
1165}
1166
1167static QQmlPropertyCapture *propertyCapture(const AOTCompiledContext *aotContext)
1168{
1169 QQmlEngine *engine = aotContext->qmlEngine();
1170 return engine ? QQmlEnginePrivate::get(e: aotContext->qmlEngine())->propertyCapture : nullptr;
1171}
1172
1173QJSValue AOTCompiledContext::jsMetaType(int index) const
1174{
1175 return QJSValuePrivate::fromReturnedValue(
1176 d: compilationUnit->runtimeClasses[index]->asReturnedValue());
1177}
1178
1179void AOTCompiledContext::setInstructionPointer(int offset) const
1180{
1181 if (auto *frame = engine->handle()->currentStackFrame)
1182 frame->instructionPointer = offset;
1183}
1184
1185void AOTCompiledContext::setReturnValueUndefined() const
1186{
1187 if (auto *frame = engine->handle()->currentStackFrame) {
1188 Q_ASSERT(frame->isMetaTypesFrame());
1189 static_cast<QV4::MetaTypesStackFrame *>(frame)->setReturnValueUndefined();
1190 }
1191}
1192
1193static void captureFallbackProperty(
1194 QObject *object, int coreIndex, int notifyIndex, bool isConstant,
1195 const AOTCompiledContext *aotContext)
1196{
1197 if (isConstant)
1198 return;
1199
1200 if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
1201 capture->captureProperty(object, coreIndex, notifyIndex);
1202}
1203
1204static void captureObjectProperty(
1205 QObject *object, const QQmlPropertyCache *propertyCache,
1206 const QQmlPropertyData *property, const AOTCompiledContext *aotContext)
1207{
1208 if (property->isConstant())
1209 return;
1210
1211 if (QQmlPropertyCapture *capture = propertyCapture(aotContext))
1212 capture->captureProperty(object, propertyCache, property);
1213}
1214
1215static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCache *ancestor)
1216{
1217 for (const QQmlPropertyCache *cache = descendent; cache; cache = cache->parent().data()) {
1218 if (cache == ancestor)
1219 return true;
1220 }
1221 return false;
1222}
1223
1224enum class PropertyResult { OK, NeedsInit, Deleted };
1225
1226struct ObjectPropertyQmlData
1227{
1228 QQmlData *qmlData;
1229 PropertyResult result;
1230};
1231
1232template<bool StrictType>
1233ObjectPropertyQmlData findObjectPropertyQmlData(QV4::Lookup *l, QObject *object)
1234{
1235 QQmlData *qmlData = QQmlData::get(object);
1236 if (!qmlData)
1237 return {.qmlData: qmlData, .result: PropertyResult::NeedsInit};
1238 if (qmlData->isQueuedForDeletion)
1239 return {.qmlData: qmlData, .result: PropertyResult::Deleted};
1240 Q_ASSERT(!QQmlData::wasDeleted(object));
1241 const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
1242 if (StrictType) {
1243 if (qmlData->propertyCache.data() != propertyCache)
1244 return {.qmlData: qmlData, .result: PropertyResult::NeedsInit};
1245 } else if (!inherits(descendent: qmlData->propertyCache.data(), ancestor: propertyCache)) {
1246 return {.qmlData: qmlData, .result: PropertyResult::NeedsInit};
1247 }
1248 return {.qmlData: qmlData, .result: PropertyResult::OK};
1249}
1250
1251template<bool StrictType = false>
1252PropertyResult loadObjectProperty(
1253 QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
1254{
1255 const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
1256 if (data.result != PropertyResult::OK)
1257 return data.result;
1258
1259 const QQmlPropertyData *propertyData = l->qobjectLookup.propertyData;
1260 const int coreIndex = propertyData->coreIndex();
1261 if (data.qmlData->hasPendingBindingBit(coreIndex))
1262 data.qmlData->flushPendingBinding(coreIndex);
1263
1264 captureObjectProperty(object, propertyCache: l->qobjectLookup.propertyCache, property: propertyData, aotContext);
1265 propertyData->readProperty(target: object, property: target);
1266 return PropertyResult::OK;
1267}
1268
1269template<bool StrictType = false>
1270PropertyResult writeBackObjectProperty(QV4::Lookup *l, QObject *object, void *source)
1271{
1272 const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
1273 if (data.result != PropertyResult::OK)
1274 return data.result;
1275
1276 l->qobjectLookup.propertyData->writeProperty(target: object, value: source, flags: {});
1277 return PropertyResult::OK;
1278}
1279
1280struct FallbackPropertyQmlData
1281{
1282 QQmlData *qmlData;
1283 const QMetaObject *metaObject;
1284 PropertyResult result;
1285};
1286
1287static FallbackPropertyQmlData findFallbackPropertyQmlData(QV4::Lookup *l, QObject *object)
1288{
1289 QQmlData *qmlData = QQmlData::get(object);
1290 if (qmlData && qmlData->isQueuedForDeletion)
1291 return {.qmlData: qmlData, .metaObject: nullptr, .result: PropertyResult::Deleted};
1292
1293 Q_ASSERT(!QQmlData::wasDeleted(object));
1294
1295 const QMetaObject *metaObject
1296 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1297 if (!metaObject || metaObject != object->metaObject())
1298 return {.qmlData: qmlData, .metaObject: nullptr, .result: PropertyResult::NeedsInit};
1299
1300 return {.qmlData: qmlData, .metaObject: metaObject, .result: PropertyResult::OK};
1301}
1302
1303static PropertyResult loadFallbackProperty(
1304 QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
1305{
1306 const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
1307 if (data.result != PropertyResult::OK)
1308 return data.result;
1309
1310 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1311 if (data.qmlData && data.qmlData->hasPendingBindingBit(coreIndex))
1312 data.qmlData->flushPendingBinding(coreIndex);
1313
1314 captureFallbackProperty(object, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
1315 isConstant: l->qobjectFallbackLookup.isConstant, aotContext);
1316
1317 void *a[] = { target, nullptr };
1318 data.metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
1319
1320 return PropertyResult::OK;
1321}
1322
1323static PropertyResult writeBackFallbackProperty(QV4::Lookup *l, QObject *object, void *source)
1324{
1325 const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
1326 if (data.result != PropertyResult::OK)
1327 return data.result;
1328
1329 void *a[] = { source, nullptr };
1330 data.metaObject->metacall(
1331 object, QMetaObject::WriteProperty, l->qobjectFallbackLookup.coreIndex, a);
1332
1333 return PropertyResult::OK;
1334}
1335
1336PropertyResult loadObjectAsVariant(
1337 QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
1338{
1339 QVariant *variant = static_cast<QVariant *>(target);
1340 const QMetaType propType = l->qobjectLookup.propertyData->propType();
1341 if (propType == QMetaType::fromType<QVariant>())
1342 return loadObjectProperty<true>(l, object, target: variant, aotContext);
1343
1344 *variant = QVariant(propType);
1345 return loadObjectProperty<true>(l, object, target: variant->data(), aotContext);
1346}
1347
1348PropertyResult writeBackObjectAsVariant(QV4::Lookup *l, QObject *object, void *source)
1349{
1350 QVariant *variant = static_cast<QVariant *>(source);
1351 const QMetaType propType = l->qobjectLookup.propertyData->propType();
1352 if (propType == QMetaType::fromType<QVariant>())
1353 return writeBackObjectProperty<true>(l, object, source: variant);
1354
1355 Q_ASSERT(variant->metaType() == propType);
1356 return writeBackObjectProperty<true>(l, object, source: variant->data());
1357}
1358
1359PropertyResult loadFallbackAsVariant(
1360 QV4::Lookup *l, QObject *object, void *target, const AOTCompiledContext *aotContext)
1361{
1362 const QMetaObject *metaObject
1363 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1364 Q_ASSERT(metaObject);
1365
1366 QVariant *variant = static_cast<QVariant *>(target);
1367 const QMetaType propType = metaObject->property(index: l->qobjectFallbackLookup.coreIndex).metaType();
1368 if (propType == QMetaType::fromType<QVariant>())
1369 return loadFallbackProperty(l, object, target: variant, aotContext);
1370
1371 *variant = QVariant(propType);
1372 return loadFallbackProperty(l, object, target: variant->data(), aotContext);
1373}
1374
1375PropertyResult writeBackFallbackAsVariant(QV4::Lookup *l, QObject *object, void *source)
1376{
1377 const QMetaObject *metaObject
1378 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1379 Q_ASSERT(metaObject);
1380
1381 QVariant *variant = static_cast<QVariant *>(source);
1382 const QMetaType propType = metaObject->property(index: l->qobjectFallbackLookup.coreIndex).metaType();
1383 if (propType == QMetaType::fromType<QVariant>())
1384 return writeBackFallbackProperty(l, object, source: variant);
1385
1386 Q_ASSERT(variant->metaType() == propType);
1387 return writeBackFallbackProperty(l, object, source: variant->data());
1388}
1389
1390template<bool StrictType, typename Op>
1391static PropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
1392{
1393 const ObjectPropertyQmlData data = findObjectPropertyQmlData<StrictType>(l, object);
1394 if (data.result != PropertyResult::OK)
1395 return data.result;
1396
1397 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
1398 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(property->coreIndex()));
1399 op(property);
1400 return PropertyResult::OK;
1401}
1402
1403template<bool StrictType = false>
1404static PropertyResult resetObjectProperty(
1405 QV4::Lookup *l, QObject *object, QV4::ExecutionEngine *v4)
1406{
1407 return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
1408 if (property->isResettable()) {
1409 property->resetProperty(target: object, flags: {});
1410 } else {
1411 v4->throwError(
1412 message: QLatin1String("Cannot assign [undefined] to ") +
1413 QLatin1String(property->propType().name()));
1414 }
1415 });
1416}
1417
1418template<bool StrictType = false>
1419static PropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
1420{
1421 return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
1422 property->writeProperty(target: object, value, flags: {});
1423 });
1424}
1425
1426template<typename Op>
1427static PropertyResult changeFallbackProperty(QV4::Lookup *l, QObject *object, Op op)
1428{
1429 const FallbackPropertyQmlData data = findFallbackPropertyQmlData(l, object);
1430 if (data.result != PropertyResult::OK)
1431 return data.result;
1432
1433 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1434 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(coreIndex));
1435
1436 op(data.metaObject, coreIndex);
1437 return PropertyResult::OK;
1438}
1439
1440static PropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
1441{
1442 return changeFallbackProperty(l, object, op: [&](const QMetaObject *metaObject, int coreIndex) {
1443 void *args[] = { value, nullptr };
1444 metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
1445 });
1446}
1447
1448static PropertyResult resetFallbackProperty(
1449 QV4::Lookup *l, QObject *object, const QMetaProperty *property, QV4::ExecutionEngine *v4)
1450{
1451 return changeFallbackProperty(l, object, op: [&](const QMetaObject *metaObject, int coreIndex) {
1452 if (property->isResettable()) {
1453 void *args[] = { nullptr };
1454 metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
1455 } else {
1456 v4->throwError(
1457 message: QLatin1String("Cannot assign [undefined] to ") +
1458 QLatin1String(property->typeName()));
1459 }
1460 });
1461}
1462
1463static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
1464{
1465 if (!lookupType.isValid()) {
1466 // If type is invalid, then the calling code depends on the lookup
1467 // to be set up in order to query the type, via lookupResultMetaType.
1468 // We cannot verify the type in this case.
1469 } else if ((lookupType.flags() & QMetaType::IsQmlList)
1470 && (propertyType.flags() & QMetaType::IsQmlList)) {
1471 // We want to check the value types here, but we cannot easily do it.
1472 // Internally those are all QObject* lists, though.
1473 } else if (lookupType.flags() & QMetaType::PointerToQObject) {
1474 // We accept any base class as type, too
1475
1476 const QMetaObject *typeMetaObject = lookupType.metaObject();
1477 const QMetaObject *foundMetaObject = propertyType.metaObject();
1478 if (!foundMetaObject)
1479 foundMetaObject = QQmlMetaType::metaObjectForType(metaType: propertyType).metaObject();
1480
1481 while (foundMetaObject && foundMetaObject != typeMetaObject)
1482 foundMetaObject = foundMetaObject->superClass();
1483
1484 if (!foundMetaObject)
1485 return false;
1486 } else if (propertyType.flags() & QMetaType::IsEnumeration) {
1487 if (propertyType == lookupType)
1488 return true;
1489
1490 // You can pass the underlying type of an enum.
1491 // We don't want to check for the actual underlying type because
1492 // moc and qmltyperegistrar are not very precise about it. Especially
1493 // the long and longlong types can be ambiguous.
1494
1495 const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
1496 switch (propertyType.sizeOf()) {
1497 case 1:
1498 return isUnsigned
1499 ? lookupType == QMetaType::fromType<quint8>()
1500 : lookupType == QMetaType::fromType<qint8>();
1501 case 2:
1502 return isUnsigned
1503 ? lookupType == QMetaType::fromType<ushort>()
1504 : lookupType == QMetaType::fromType<short>();
1505 case 4:
1506 // The default type, if moc doesn't know the actual enum type, is int.
1507 // However, the compiler can still decide to encode the enum in uint.
1508 // Therefore, we also accept int for uint enums.
1509 // TODO: This is technically UB.
1510 return isUnsigned
1511 ? (lookupType == QMetaType::fromType<int>()
1512 || lookupType == QMetaType::fromType<uint>())
1513 : lookupType == QMetaType::fromType<int>();
1514 case 8:
1515 return isUnsigned
1516 ? lookupType == QMetaType::fromType<qulonglong>()
1517 : lookupType == QMetaType::fromType<qlonglong>();
1518 }
1519
1520 return false;
1521 } else if (propertyType != lookupType) {
1522 return false;
1523 }
1524 return true;
1525}
1526
1527static PropertyResult storeObjectAsVariant(
1528 QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
1529{
1530 QVariant *variant = static_cast<QVariant *>(value);
1531 const QMetaType propType = l->qobjectLookup.propertyData->propType();
1532 if (propType == QMetaType::fromType<QVariant>())
1533 return storeObjectProperty<true>(l, object, value: variant);
1534
1535 if (!variant->isValid())
1536 return resetObjectProperty<true>(l, object, v4);
1537
1538 const QMetaType variantMetaType = variant->metaType();
1539
1540 if (isTypeCompatible(lookupType: variantMetaType, propertyType: propType))
1541 return storeObjectProperty<true>(l, object, value: variant->data());
1542
1543 QVariant converted(propType);
1544 QV4::Scope scope(v4);
1545 QV4::ScopedValue val(scope, v4->fromVariant(*variant));
1546 if (v4->metaTypeFromJS(value: val, type: propType, data: converted.data())
1547 || QMetaType::convert(
1548 fromType: variantMetaType, from: variant->constData(), toType: propType, to: converted.data())) {
1549 return storeObjectProperty<true>(l, object, value: converted.data());
1550 }
1551
1552 v4->throwError(
1553 message: QLatin1String("Cannot assign ") + QLatin1String(variantMetaType.name())
1554 + QLatin1String(" to ") + QLatin1String(propType.name()));
1555
1556 return PropertyResult::NeedsInit;
1557}
1558
1559static PropertyResult storeFallbackAsVariant(
1560 QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
1561{
1562 QVariant *variant = static_cast<QVariant *>(value);
1563
1564 const QMetaObject *metaObject
1565 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1566 Q_ASSERT(metaObject);
1567
1568 const QMetaProperty property = metaObject->property(index: l->qobjectFallbackLookup.coreIndex);
1569 const QMetaType propType = property.metaType();
1570 if (propType == QMetaType::fromType<QVariant>())
1571 return storeFallbackProperty(l, object, value: variant);
1572
1573 if (!variant->isValid())
1574 return resetFallbackProperty(l, object, property: &property, v4);
1575
1576 if (isTypeCompatible(lookupType: variant->metaType(), propertyType: propType))
1577 return storeFallbackProperty(l, object, value: variant->data());
1578
1579 QVariant converted(propType);
1580 QV4::Scope scope(v4);
1581 QV4::ScopedValue val(scope, v4->fromVariant(*variant));
1582 if (v4->metaTypeFromJS(value: val, type: propType, data: converted.data())
1583 || QMetaType::convert(
1584 fromType: variant->metaType(), from: variant->constData(), toType: propType, to: converted.data())) {
1585 return storeFallbackProperty(l, object, value: converted.data());
1586 }
1587
1588 return PropertyResult::NeedsInit;
1589}
1590
1591enum class ObjectLookupResult {
1592 Failure,
1593 Object,
1594 Fallback,
1595 ObjectAsVariant,
1596 FallbackAsVariant,
1597};
1598
1599static ObjectLookupResult initObjectLookup(
1600 const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
1601{
1602 QV4::Scope scope(aotContext->engine->handle());
1603 QV4::PropertyKey id = scope.engine->identifierTable->asPropertyKey(
1604 str: aotContext->compilationUnit->runtimeStrings[l->nameIndex]);
1605
1606 Q_ASSERT(id.isString());
1607
1608 QV4::ScopedString name(scope, id.asStringOrSymbol());
1609
1610 Q_ASSERT(!name->equals(scope.engine->id_toString()));
1611 Q_ASSERT(!name->equals(scope.engine->id_destroy()));
1612
1613 QQmlData *ddata = QQmlData::get(object, create: true);
1614 Q_ASSERT(ddata);
1615 if (ddata->isQueuedForDeletion)
1616 return ObjectLookupResult::Failure;
1617
1618 const QQmlPropertyData *property;
1619 if (!ddata->propertyCache) {
1620 property = QQmlPropertyCache::property(object, name, aotContext->qmlContext, nullptr);
1621 } else {
1622 property = ddata->propertyCache->property(
1623 key: name.getPointer(), object, context: aotContext->qmlContext);
1624 }
1625
1626 const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
1627 if (!property) {
1628 const QMetaObject *metaObject = object->metaObject();
1629 if (!metaObject)
1630 return ObjectLookupResult::Failure;
1631
1632 const int coreIndex = metaObject->indexOfProperty(
1633 name: name->toQStringNoThrow().toUtf8().constData());
1634 if (coreIndex < 0)
1635 return ObjectLookupResult::Failure;
1636
1637 const QMetaProperty property = metaObject->property(index: coreIndex);
1638
1639 l->releasePropertyCache();
1640 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
1641 l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
1642 l->qobjectFallbackLookup.coreIndex = coreIndex;
1643 l->qobjectFallbackLookup.notifyIndex =
1644 QMetaObjectPrivate::signalIndex(m: property.notifySignal());
1645 l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
1646 return doVariantLookup
1647 ? ObjectLookupResult::FallbackAsVariant
1648 : ObjectLookupResult::Fallback;
1649 }
1650
1651 Q_ASSERT(ddata->propertyCache);
1652
1653 QV4::setupQObjectLookup(lookup: l, ddata, propertyData: property);
1654
1655 return doVariantLookup
1656 ? ObjectLookupResult::ObjectAsVariant
1657 : ObjectLookupResult::Object;
1658}
1659
1660static void initValueLookup(
1661 QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
1662 const QMetaObject *metaObject)
1663{
1664 Q_ASSERT(metaObject);
1665 const QByteArray name = compilationUnit->runtimeStrings[l->nameIndex]->toQString().toUtf8();
1666 const int coreIndex = metaObject->indexOfProperty(name: name.constData());
1667 QMetaType lookupType = metaObject->property(index: coreIndex).metaType();
1668 l->qgadgetLookup.metaObject = quintptr(metaObject) + 1;
1669 l->qgadgetLookup.coreIndex = coreIndex;
1670 l->qgadgetLookup.metaType = lookupType.iface();
1671}
1672
1673static void amendException(QV4::ExecutionEngine *engine)
1674{
1675 const int missingLineNumber = engine->currentStackFrame->missingLineNumber();
1676 const int lineNumber = engine->currentStackFrame->lineNumber();
1677 Q_ASSERT(missingLineNumber != lineNumber);
1678
1679 auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
1680 for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
1681 if (it->line == missingLineNumber) {
1682 it->line = lineNumber;
1683 break;
1684 }
1685 }
1686 };
1687
1688 amendStackTrace(&engine->exceptionStackTrace);
1689
1690 QV4::Scope scope(engine);
1691 QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
1692 if (error) // else some other value was thrown
1693 amendStackTrace(error->d()->stackTrace);
1694}
1695
1696
1697bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
1698{
1699 if (!object)
1700 return false;
1701
1702 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1703 if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
1704 || l->getter == QV4::Lookup::getterQObject
1705 || l->getter == QV4::Lookup::getterQObjectAsVariant) {
1706 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
1707 QQmlData::flushPendingBinding(object, coreIndex: property->coreIndex());
1708 captureObjectProperty(object, propertyCache: l->qobjectLookup.propertyCache, property, aotContext: this);
1709 return true;
1710 }
1711
1712 if (l->getter == QV4::Lookup::getterFallback
1713 || l->getter == QV4::Lookup::getterFallbackAsVariant) {
1714 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1715 QQmlData::flushPendingBinding(object, coreIndex);
1716 captureFallbackProperty(
1717 object, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
1718 isConstant: l->qobjectFallbackLookup.isConstant, aotContext: this);
1719 return true;
1720 }
1721
1722 return false;
1723}
1724
1725bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
1726{
1727 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1728 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty) {
1729 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
1730 QQmlData::flushPendingBinding(object: qmlScopeObject, coreIndex: property->coreIndex());
1731 captureObjectProperty(object: qmlScopeObject, propertyCache: l->qobjectLookup.propertyCache, property, aotContext: this);
1732 return true;
1733 }
1734
1735 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
1736 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1737 QQmlData::flushPendingBinding(object: qmlScopeObject, coreIndex);
1738 captureFallbackProperty(object: qmlScopeObject, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
1739 isConstant: l->qobjectFallbackLookup.isConstant, aotContext: this);
1740 return true;
1741 }
1742
1743 return false;
1744}
1745
1746void AOTCompiledContext::captureTranslation() const
1747{
1748 if (QQmlPropertyCapture *capture = propertyCapture(aotContext: this))
1749 capture->captureTranslation();
1750}
1751
1752QString AOTCompiledContext::translationContext() const
1753{
1754#if QT_CONFIG(translation)
1755 return QV4::GlobalExtensions::currentTranslationContext(engine: engine->handle());
1756#else
1757 return QString();
1758#endif
1759}
1760
1761QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
1762{
1763 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1764 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
1765 || l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
1766 || l->getter == QV4::Lookup::getterQObject
1767 || l->setter == QV4::Lookup::setterQObject
1768 || l->getter == QV4::Lookup::getterQObjectAsVariant
1769 || l->setter == QV4::Lookup::setterQObjectAsVariant) {
1770 return l->qobjectLookup.propertyData->propType();
1771 } else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
1772 return QMetaType(l->qgadgetLookup.metaType);
1773 } else if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue) {
1774 return QMetaType::fromType<int>();
1775 } else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
1776 || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
1777 || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
1778 || l->getter == QV4::QObjectWrapper::lookupAttached) {
1779 return QMetaType::fromType<QObject *>();
1780 } else if (l->getter == QV4::Lookup::getterFallback
1781 || l->setter == QV4::Lookup::setterFallback
1782 || l->getter == QV4::Lookup::getterFallbackAsVariant
1783 || l->setter == QV4::Lookup::setterFallbackAsVariant
1784 || l->qmlContextPropertyGetter
1785 == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
1786 const QMetaObject *metaObject
1787 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1788 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1789 return metaObject->property(index: coreIndex).metaType();
1790 }
1791 return QMetaType();
1792}
1793
1794static bool isUndefined(const void *value, QMetaType type)
1795{
1796 if (type == QMetaType::fromType<QVariant>())
1797 return !static_cast<const QVariant *>(value)->isValid();
1798 if (type == QMetaType::fromType<QJSValue>())
1799 return static_cast<const QJSValue *>(value)->isUndefined();
1800 if (type == QMetaType::fromType<QJSPrimitiveValue>()) {
1801 return static_cast<const QJSPrimitiveValue *>(value)->type()
1802 == QJSPrimitiveValue::Undefined;
1803 }
1804 return false;
1805}
1806
1807void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType type) const
1808{
1809 // We don't really use any part of the lookup machinery here.
1810 // The QV4::Lookup is created on the stack to conveniently get the property cache, and through
1811 // the property cache we store a value into the property.
1812
1813 const auto unwrapVariant = [&]() {
1814 if (type == QMetaType::fromType<QVariant>()) {
1815 QVariant *variant = static_cast<QVariant *>(value);
1816 type = variant->metaType();
1817 value = variant->data();
1818 }
1819 };
1820
1821 QV4::Lookup l;
1822 memset(s: &l, c: 0, n: sizeof(QV4::Lookup));
1823 l.nameIndex = nameIndex;
1824 l.forCall = false;
1825 PropertyResult storeResult = PropertyResult::NeedsInit;
1826 QMetaType propType;
1827
1828 switch (initObjectLookup(aotContext: this, l: &l, object: qmlScopeObject, type: QMetaType())) {
1829 case ObjectLookupResult::ObjectAsVariant:
1830 case ObjectLookupResult::Object: {
1831 propType = l.qobjectLookup.propertyData->propType();
1832 if (isTypeCompatible(lookupType: type, propertyType: propType)) {
1833 storeResult = storeObjectProperty(l: &l, object: qmlScopeObject, value);
1834 } else if (isUndefined(value, type)) {
1835
1836 // NB: In order to have a meaningful reset() here, the type needs to be a wrapper type
1837 // that can hold undefined. For example QVariant. The caller must not unwrap it.
1838
1839 storeResult = resetObjectProperty(l: &l, object: qmlScopeObject, v4: engine->handle());
1840 } else {
1841
1842 // Unwrap any QVariant so that we get a meaningful conversion below.
1843 unwrapVariant();
1844
1845 QVariant var(propType);
1846 QV4::ExecutionEngine *v4 = engine->handle();
1847 QV4::Scope scope(v4);
1848 QV4::ScopedValue val(scope, v4->metaTypeToJS(type, data: value));
1849 if (v4->metaTypeFromJS(value: val, type: propType, data: var.data())
1850 || QMetaType::convert(fromType: type, from: value, toType: propType, to: var.data())) {
1851 storeResult = storeObjectProperty(l: &l, object: qmlScopeObject, value: var.data());
1852 }
1853 }
1854
1855 l.qobjectLookup.propertyCache->release();
1856 break;
1857 }
1858 case ObjectLookupResult::FallbackAsVariant:
1859 case ObjectLookupResult::Fallback: {
1860 const QMetaObject *metaObject
1861 = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
1862 const QMetaProperty property = metaObject->property(index: l.qobjectFallbackLookup.coreIndex);
1863 propType = property.metaType();
1864 if (isTypeCompatible(lookupType: type, propertyType: propType)) {
1865 storeResult = storeFallbackProperty(l: &l, object: qmlScopeObject, value);
1866 } else if (isUndefined(value, type)) {
1867
1868 // NB: In order to have a meaningful reset() here, the type needs to be a wrapper type
1869 // that can hold undefined. For example QVariant. The caller must not unwrap it.
1870
1871 storeResult = resetFallbackProperty(l: &l, object: qmlScopeObject, property: &property, v4: engine->handle());
1872 } else {
1873
1874 // Unwrap any QVariant so that we get a meaningful conversion below.
1875 unwrapVariant();
1876
1877 QVariant var(propType);
1878 QV4::ExecutionEngine *v4 = engine->handle();
1879 QV4::Scope scope(v4);
1880 QV4::ScopedValue val(scope, v4->metaTypeToJS(type, data: value));
1881 if (v4->metaTypeFromJS(value: val, type: propType, data: var.data())
1882 || QMetaType::convert(fromType: type, from: value, toType: propType, to: var.data())) {
1883 storeResult = storeFallbackProperty(l: &l, object: qmlScopeObject, value: var.data());
1884 }
1885 }
1886 break;
1887 }
1888 case ObjectLookupResult::Failure:
1889 engine->handle()->throwTypeError();
1890 return;
1891 }
1892
1893 switch (storeResult) {
1894 case PropertyResult::NeedsInit: {
1895 const QString error = QLatin1String("Cannot assign ") +
1896 QLatin1String(type.name()) +
1897 QLatin1String(" to ") +
1898 QLatin1String(propType.name());
1899 engine->throwError(message: error);
1900 break;
1901 }
1902 case PropertyResult::Deleted:
1903 engine->handle()->throwTypeError(
1904 QStringLiteral("Value is null and could not be converted to an object"));
1905 break;
1906 case PropertyResult::OK:
1907 break;
1908 }
1909}
1910
1911QJSValue AOTCompiledContext::javaScriptGlobalProperty(uint nameIndex) const
1912{
1913 QV4::Scope scope(engine->handle());
1914 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[nameIndex]);
1915 QV4::ScopedObject global(scope, scope.engine->globalObject);
1916 return QJSValuePrivate::fromReturnedValue(d: global->get(id: name->toPropertyKey()));
1917}
1918
1919const QLoggingCategory *AOTCompiledContext::resolveLoggingCategory(QObject *wrapper, bool *ok) const
1920{
1921 if (wrapper) {
1922 // We have to check this here because you may pass a plain QObject that only
1923 // turns out to be a QQmlLoggingCategoryBase at run time.
1924 if (QQmlLoggingCategoryBase *qQmlLoggingCategory
1925 = qobject_cast<QQmlLoggingCategoryBase *>(object: wrapper)) {
1926 const QLoggingCategory *loggingCategory = qQmlLoggingCategory->category();
1927 *ok = true;
1928 if (!loggingCategory) {
1929 engine->handle()->throwError(
1930 QStringLiteral("A QmlLoggingCatgory was provided without a valid name"));
1931 }
1932 return loggingCategory;
1933 }
1934 }
1935
1936 *ok = false;
1937 return qmlEngine() ? &lcQml() : &lcJs();
1938}
1939
1940void AOTCompiledContext::writeToConsole(
1941 QtMsgType type, const QString &message, const QLoggingCategory *loggingCategory) const
1942{
1943 Q_ASSERT(loggingCategory->isEnabled(type));
1944
1945 const QV4::CppStackFrame *frame = engine->handle()->currentStackFrame;
1946 Q_ASSERT(frame);
1947
1948 const QByteArray source(frame->source().toUtf8());
1949 const QByteArray function(frame->function().toUtf8());
1950 QMessageLogger logger(source.constData(), frame->lineNumber(),
1951 function.constData(), loggingCategory->categoryName());
1952
1953 switch (type) {
1954 case QtDebugMsg:
1955 logger.debug(msg: "%s", qUtf8Printable(message));
1956 break;
1957 case QtInfoMsg:
1958 logger.info(msg: "%s", qUtf8Printable(message));
1959 break;
1960 case QtWarningMsg:
1961 logger.warning(msg: "%s", qUtf8Printable(message));
1962 break;
1963 case QtCriticalMsg:
1964 logger.critical(msg: "%s", qUtf8Printable(message));
1965 break;
1966 default:
1967 break;
1968 }
1969}
1970
1971QVariant AOTCompiledContext::constructValueType(
1972 QMetaType resultMetaType, const QMetaObject *resultMetaObject,
1973 int ctorIndex, void *ctorArg) const
1974{
1975 return QQmlValueTypeProvider::constructValueType(
1976 targetMetaType: resultMetaType, targetMetaObject: resultMetaObject, ctorIndex, ctorArg);
1977}
1978
1979QDateTime AOTCompiledContext::constructDateTime(double timestamp) const
1980{
1981 return QV4::DateObject::timestampToDateTime(timestamp);
1982}
1983
1984QDateTime AOTCompiledContext::constructDateTime(const QString &string) const
1985{
1986 return QV4::DateObject::stringToDateTime(string, engine: engine->handle());
1987}
1988
1989QDateTime AOTCompiledContext::constructDateTime(
1990 double year, double month, double day, double hours,
1991 double minutes, double seconds, double msecs) const
1992{
1993 return constructDateTime(timestamp: QV4::DateObject::componentsToTimestamp(
1994 year, month, day, hours, mins: minutes, secs: seconds, ms: msecs, v4: engine->handle()));
1995}
1996
1997bool AOTCompiledContext::callQmlContextPropertyLookup(
1998 uint index, void **args, const QMetaType *types, int argc) const
1999{
2000 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2001 QV4::Scope scope(engine->handle());
2002 QV4::ScopedValue thisObject(scope);
2003 QV4::ScopedFunctionObject function(
2004 scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
2005 if (!function) {
2006 scope.engine->throwTypeError(
2007 QStringLiteral("Property '%1' of object [null] is not a function").arg(
2008 a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
2009 return false;
2010 }
2011
2012 function->call(thisObject: qmlScopeObject, argv: args, types, argc);
2013 return !scope.hasException();
2014}
2015
2016void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
2017{
2018 Q_UNUSED(index);
2019 Q_ASSERT(engine->hasError());
2020 amendException(engine: engine->handle());
2021}
2022
2023bool AOTCompiledContext::loadContextIdLookup(uint index, void *target) const
2024{
2025 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2026 int objectId = -1;
2027 QQmlContextData *context = nullptr;
2028 Q_ASSERT(qmlContext);
2029
2030 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject) {
2031 objectId = l->qmlContextIdObjectLookup.objectId;
2032 context = qmlContext;
2033 } else if (l->qmlContextPropertyGetter
2034 == QV4::QQmlContextWrapper::lookupIdObjectInParentContext) {
2035 QV4::Scope scope(engine->handle());
2036 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
2037 for (context = qmlContext; context; context = context->parent().data()) {
2038 objectId = context->propertyIndex(name);
2039 if (objectId != -1 && objectId < context->numIdValues())
2040 break;
2041 }
2042 } else {
2043 return false;
2044 }
2045
2046 Q_ASSERT(objectId >= 0);
2047 Q_ASSERT(context != nullptr);
2048 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(e: qmlEngine());
2049 if (QQmlPropertyCapture *capture = engine->propertyCapture)
2050 capture->captureProperty(context->idValueBindings(index: objectId));
2051 *static_cast<QObject **>(target) = context->idValue(index: objectId);
2052 return true;
2053}
2054
2055void AOTCompiledContext::initLoadContextIdLookup(uint index) const
2056{
2057 Q_ASSERT(!engine->hasError());
2058 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2059 QV4::Scope scope(engine->handle());
2060 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
2061 const QQmlRefPointer<QQmlContextData> ownContext = qmlContext;
2062 for (auto context = ownContext; context; context = context->parent()) {
2063 const int propertyIdx = context->propertyIndex(name);
2064 if (propertyIdx == -1 || propertyIdx >= context->numIdValues())
2065 continue;
2066
2067 if (context.data() == ownContext.data()) {
2068 l->qmlContextIdObjectLookup.objectId = propertyIdx;
2069 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObject;
2070 } else {
2071 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObjectInParentContext;
2072 }
2073
2074 return;
2075 }
2076
2077 Q_UNREACHABLE();
2078}
2079
2080bool AOTCompiledContext::callObjectPropertyLookup(
2081 uint index, QObject *object, void **args, const QMetaType *types, int argc) const
2082{
2083 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2084 QV4::Scope scope(engine->handle());
2085 QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object));
2086 QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
2087 if (!function) {
2088 scope.engine->throwTypeError(
2089 QStringLiteral("Property '%1' of object [object Object] is not a function")
2090 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
2091 return false;
2092 }
2093
2094 function->call(thisObject: object, argv: args, types, argc);
2095 return !scope.hasException();
2096}
2097
2098void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
2099{
2100 Q_UNUSED(index);
2101 Q_ASSERT(engine->hasError());
2102 amendException(engine: engine->handle());
2103}
2104
2105bool AOTCompiledContext::callGlobalLookup(
2106 uint index, void **args, const QMetaType *types, int argc) const
2107{
2108 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2109 QV4::Scope scope(engine->handle());
2110 QV4::ScopedFunctionObject function(scope, l->globalGetter(l, scope.engine));
2111 if (!function) {
2112 scope.engine->throwTypeError(
2113 QStringLiteral("Property '%1' of object [null] is not a function")
2114 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
2115 return false;
2116 }
2117
2118 function->call(thisObject: nullptr, argv: args, types, argc);
2119 return true;
2120}
2121
2122void AOTCompiledContext::initCallGlobalLookup(uint index) const
2123{
2124 Q_UNUSED(index);
2125 Q_ASSERT(engine->hasError());
2126 amendException(engine: engine->handle());
2127}
2128
2129bool AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const
2130{
2131 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2132 QV4::Scope scope(engine->handle());
2133 QV4::ScopedValue val(scope, l->globalGetter(l, engine->handle()));
2134 if (!QV4::ExecutionEngine::metaTypeFromJS(value: val, type, data: target)) {
2135 engine->handle()->throwTypeError();
2136 return false;
2137 }
2138 return true;
2139}
2140
2141void AOTCompiledContext::initLoadGlobalLookup(uint index) const
2142{
2143 Q_UNUSED(index);
2144 Q_ASSERT(engine->hasError());
2145 amendException(engine: engine->handle());
2146}
2147
2148bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
2149{
2150 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2151
2152 if (!qmlScopeObject) {
2153 engine->handle()->throwReferenceError(
2154 name: compilationUnit->runtimeStrings[l->nameIndex]->toQString());
2155 return false;
2156 }
2157
2158 PropertyResult result = PropertyResult::NeedsInit;
2159 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
2160 result = loadObjectProperty(l, object: qmlScopeObject, target, aotContext: this);
2161 else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
2162 result = loadFallbackProperty(l, object: qmlScopeObject, target, aotContext: this);
2163 else
2164 return false;
2165
2166 switch (result) {
2167 case PropertyResult::NeedsInit:
2168 return false;
2169 case PropertyResult::Deleted:
2170 engine->handle()->throwTypeError(
2171 QStringLiteral("Cannot read property '%1' of null")
2172 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
2173 return false;
2174 case PropertyResult::OK:
2175 return true;
2176 }
2177
2178 Q_UNREACHABLE_RETURN(false);
2179}
2180
2181bool AOTCompiledContext::writeBackScopeObjectPropertyLookup(uint index, void *source) const
2182{
2183 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2184
2185 PropertyResult result = PropertyResult::NeedsInit;
2186 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
2187 result = writeBackObjectProperty(l, object: qmlScopeObject, source);
2188 else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
2189 result = writeBackFallbackProperty(l, object: qmlScopeObject, source);
2190 else
2191 return false;
2192
2193 switch (result) {
2194 case PropertyResult::NeedsInit:
2195 return false;
2196 case PropertyResult::Deleted: // Silently omit the write back. Same as interpreter
2197 case PropertyResult::OK:
2198 return true;
2199 }
2200
2201 Q_UNREACHABLE_RETURN(false);
2202}
2203
2204void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const
2205{
2206 // TODO: The only thing we need the type for is checking whether it's QVariant.
2207 // Replace it with an enum and simplify code generation.
2208
2209 QV4::ExecutionEngine *v4 = engine->handle();
2210 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2211
2212 if (v4->hasException) {
2213 amendException(engine: v4);
2214 return;
2215 }
2216
2217 switch (initObjectLookup(aotContext: this, l, object: qmlScopeObject, type)) {
2218 case ObjectLookupResult::ObjectAsVariant:
2219 case ObjectLookupResult::Object:
2220 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
2221 break;
2222 case ObjectLookupResult::FallbackAsVariant:
2223 case ObjectLookupResult::Fallback:
2224 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
2225 break;
2226 case ObjectLookupResult::Failure:
2227 v4->throwTypeError();
2228 break;
2229 }
2230}
2231
2232bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const
2233{
2234 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2235 QV4::Scope scope(engine->handle());
2236
2237 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton) {
2238 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
2239 scope, l->qmlContextSingletonLookup.singletonObject);
2240
2241 // We don't handle non-QObject singletons (as those can't be declared in qmltypes anyway)
2242 Q_ASSERT(wrapper);
2243 *static_cast<QObject **>(target) = wrapper->object();
2244 return true;
2245 }
2246
2247 return false;
2248}
2249
2250using QmlContextPropertyGetter
2251 = QV4::ReturnedValue (*)(QV4::Lookup *l, QV4::ExecutionEngine *engine, QV4::Value *thisObject);
2252
2253template<QmlContextPropertyGetter qmlContextPropertyGetter>
2254static void initTypeWrapperLookup(
2255 const AOTCompiledContext *context, QV4::Lookup *l, uint importNamespace)
2256{
2257 Q_ASSERT(!context->engine->hasError());
2258 if (importNamespace != AOTCompiledContext::InvalidStringId) {
2259 QV4::Scope scope(context->engine->handle());
2260 QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
2261
2262 QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
2263 Q_ASSERT(typeLoader);
2264 if (const QQmlImportRef *importRef
2265 = context->qmlContext->imports()->query(key: import, typeLoader).importNamespace) {
2266
2267 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
2268 scope, QV4::QQmlTypeWrapper::create(
2269 scope.engine, nullptr, context->qmlContext->imports(), importRef));
2270
2271 // This is not a contextGetter since we actually load from the namespace.
2272 wrapper = l->getter(l, context->engine->handle(), wrapper);
2273
2274 // In theory, the getter may have populated the lookup's property cache.
2275 l->releasePropertyCache();
2276
2277 l->qmlContextPropertyGetter = qmlContextPropertyGetter;
2278 if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
2279 l->qmlContextSingletonLookup.singletonObject.set(engine: scope.engine, heapObject: wrapper->heapObject());
2280 else if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType)
2281 l->qmlTypeLookup.qmlTypeWrapper.set(engine: scope.engine, heapObject: wrapper->heapObject());
2282 return;
2283 }
2284 scope.engine->throwTypeError();
2285 } else {
2286 QV4::ExecutionEngine *v4 = context->engine->handle();
2287 l->qmlContextPropertyGetter(l, v4, nullptr);
2288 if (l->qmlContextPropertyGetter != qmlContextPropertyGetter) {
2289 const QString error
2290 = QLatin1String(qmlContextPropertyGetter
2291 == QV4::QQmlContextWrapper::lookupSingleton
2292 ? "%1 was a singleton at compile time, "
2293 "but is not a singleton anymore."
2294 : "%1 was not a singleton at compile time, "
2295 "but is a singleton now.")
2296 .arg(args: context->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
2297 v4->throwTypeError(message: error);
2298 }
2299 }
2300}
2301
2302void AOTCompiledContext::initLoadSingletonLookup(uint index, uint importNamespace) const
2303{
2304 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2305 initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupSingleton>(context: this, l, importNamespace);
2306}
2307
2308bool AOTCompiledContext::loadAttachedLookup(uint index, QObject *object, void *target) const
2309{
2310 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2311 if (l->getter != QV4::QObjectWrapper::lookupAttached)
2312 return false;
2313
2314 QV4::Scope scope(engine->handle());
2315 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(scope, l->qmlTypeLookup.qmlTypeWrapper);
2316 Q_ASSERT(wrapper);
2317 *static_cast<QObject **>(target) = qmlAttachedPropertiesObject(
2318 object, func: wrapper->d()->type().attachedPropertiesFunction(
2319 engine: QQmlEnginePrivate::get(e: qmlEngine())));
2320 return true;
2321}
2322
2323void AOTCompiledContext::initLoadAttachedLookup(
2324 uint index, uint importNamespace, QObject *object) const
2325{
2326 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2327 QV4::Scope scope(engine->handle());
2328 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
2329
2330 QQmlType type;
2331 QQmlTypeLoader *typeLoader = scope.engine->typeLoader();
2332 Q_ASSERT(typeLoader);
2333 if (importNamespace != InvalidStringId) {
2334 QV4::ScopedString import(scope, compilationUnit->runtimeStrings[importNamespace]);
2335 if (const QQmlImportRef *importRef
2336 = qmlContext->imports()->query(key: import, typeLoader).importNamespace) {
2337 type = qmlContext->imports()->query(key: name, importNamespace: importRef, typeLoader).type;
2338 }
2339 } else {
2340 type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(key: name, typeLoader).type;
2341 }
2342
2343 if (!type.isValid()) {
2344 scope.engine->throwTypeError();
2345 return;
2346 }
2347
2348 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
2349 scope, QV4::QQmlTypeWrapper::create(scope.engine, object, type,
2350 QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
2351
2352 l->qmlTypeLookup.qmlTypeWrapper.set(engine: scope.engine, heapObject: wrapper->d());
2353 l->getter = QV4::QObjectWrapper::lookupAttached;
2354}
2355
2356bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
2357{
2358 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2359 if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupType)
2360 return false;
2361
2362 const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
2363 l->qmlTypeLookup.qmlTypeWrapper.get());
2364
2365 QMetaType metaType = typeWrapper->type().typeId();
2366 *static_cast<const QMetaObject **>(target)
2367 = QQmlMetaType::metaObjectForType(metaType).metaObject();
2368 return true;
2369}
2370
2371void AOTCompiledContext::initLoadTypeLookup(uint index, uint importNamespace) const
2372{
2373 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2374 initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupType>(context: this, l, importNamespace);
2375}
2376
2377bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
2378{
2379 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2380 const auto doThrow = [&]() {
2381 engine->handle()->throwTypeError(
2382 QStringLiteral("Cannot read property '%1' of null")
2383 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
2384 return false;
2385 };
2386
2387 if (!object)
2388 return doThrow();
2389
2390 PropertyResult result = PropertyResult::NeedsInit;
2391 if (l->getter == QV4::Lookup::getterQObject)
2392 result = loadObjectProperty(l, object, target, aotContext: this);
2393 else if (l->getter == QV4::Lookup::getterFallback)
2394 result = loadFallbackProperty(l, object, target, aotContext: this);
2395 else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
2396 result = loadObjectAsVariant(l, object, target, aotContext: this);
2397 else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
2398 result = loadFallbackAsVariant(l, object, target, aotContext: this);
2399 else
2400 return false;
2401
2402 switch (result) {
2403 case PropertyResult::Deleted:
2404 return doThrow();
2405 case PropertyResult::NeedsInit:
2406 return false;
2407 case PropertyResult::OK:
2408 return true;
2409 }
2410
2411 Q_UNREACHABLE_RETURN(false);
2412}
2413
2414bool AOTCompiledContext::writeBackObjectLookup(uint index, QObject *object, void *source) const
2415{
2416 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2417 if (!object)
2418 return true;
2419
2420 PropertyResult result = PropertyResult::NeedsInit;
2421 if (l->getter == QV4::Lookup::getterQObject)
2422 result = writeBackObjectProperty(l, object, source);
2423 else if (l->getter == QV4::Lookup::getterFallback)
2424 result = writeBackFallbackProperty(l, object, source);
2425 else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
2426 result = writeBackObjectAsVariant(l, object, source);
2427 else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
2428 result = writeBackFallbackAsVariant(l, object, source);
2429 else
2430 return false;
2431
2432 switch (result) {
2433 case PropertyResult::NeedsInit:
2434 return false;
2435 case PropertyResult::Deleted: // Silently omit the write back
2436 case PropertyResult::OK:
2437 return true;
2438 }
2439
2440 Q_UNREACHABLE_RETURN(false);
2441}
2442
2443void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaType type) const
2444{
2445 // TODO: The only thing we need the type for is checking whether it's QVariant.
2446 // Replace it with an enum and simplify code generation.
2447
2448 QV4::ExecutionEngine *v4 = engine->handle();
2449 if (v4->hasException) {
2450 amendException(engine: v4);
2451 } else {
2452 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2453 switch (initObjectLookup(aotContext: this, l, object, type)) {
2454 case ObjectLookupResult::Object:
2455 l->getter = QV4::Lookup::getterQObject;
2456 break;
2457 case ObjectLookupResult::ObjectAsVariant:
2458 l->getter = QV4::Lookup::getterQObjectAsVariant;
2459 break;
2460 case ObjectLookupResult::Fallback:
2461 l->getter = QV4::Lookup::getterFallback;
2462 break;
2463 case ObjectLookupResult::FallbackAsVariant:
2464 l->getter = QV4::Lookup::getterFallbackAsVariant;
2465 break;
2466 case ObjectLookupResult::Failure:
2467 engine->handle()->throwTypeError();
2468 break;
2469 }
2470 }
2471}
2472
2473bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) const
2474{
2475 Q_ASSERT(value);
2476
2477 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2478 if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
2479 return false;
2480
2481 const QMetaObject *metaObject
2482 = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
2483 Q_ASSERT(metaObject);
2484
2485 void *args[] = { target, nullptr };
2486 metaObject->d.static_metacall(
2487 reinterpret_cast<QObject*>(value), QMetaObject::ReadProperty,
2488 l->qgadgetLookup.coreIndex, args);
2489 return true;
2490}
2491
2492bool AOTCompiledContext::writeBackValueLookup(uint index, void *value, void *source) const
2493{
2494 Q_ASSERT(value);
2495
2496 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2497 if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
2498 return false;
2499
2500 const QMetaObject *metaObject
2501 = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
2502 Q_ASSERT(metaObject);
2503
2504 void *args[] = { source, nullptr };
2505 metaObject->d.static_metacall(
2506 reinterpret_cast<QObject*>(value), QMetaObject::WriteProperty,
2507 l->qgadgetLookup.coreIndex, args);
2508 return true;
2509}
2510
2511void AOTCompiledContext::initGetValueLookup(
2512 uint index, const QMetaObject *metaObject, QMetaType type) const
2513{
2514 Q_UNUSED(type); // TODO: Remove the type argument and simplify code generation
2515 Q_ASSERT(!engine->hasError());
2516 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2517 initValueLookup(l, compilationUnit: compilationUnit, metaObject);
2518 l->getter = QV4::QQmlValueTypeWrapper::lookupGetter;
2519}
2520
2521bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
2522{
2523 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2524 if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
2525 return false;
2526 const bool isUnsigned
2527 = l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
2528 const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
2529 switch (l->qmlEnumValueLookup.metaType->size) {
2530 case 1:
2531 if (isUnsigned)
2532 *static_cast<quint8 *>(target) = encoded;
2533 else
2534 *static_cast<qint8 *>(target) = encoded;
2535 return true;
2536 case 2:
2537 if (isUnsigned)
2538 *static_cast<quint16 *>(target) = encoded;
2539 else
2540 *static_cast<qint16 *>(target) = encoded;
2541 return true;
2542 case 4:
2543 if (isUnsigned)
2544 *static_cast<quint32 *>(target) = encoded;
2545 else
2546 *static_cast<qint32 *>(target) = encoded;
2547 return true;
2548 case 8:
2549 if (isUnsigned)
2550 *static_cast<quint64 *>(target) = encoded;
2551 else
2552 *static_cast<qint64 *>(target) = encoded;
2553 return true;
2554 default:
2555 break;
2556 }
2557
2558 return false;
2559}
2560
2561void AOTCompiledContext::initGetEnumLookup(
2562 uint index, const QMetaObject *metaObject,
2563 const char *enumerator, const char *enumValue) const
2564{
2565 Q_ASSERT(!engine->hasError());
2566 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2567 if (!metaObject) {
2568 engine->handle()->throwTypeError(
2569 QStringLiteral("Cannot read property '%1' of undefined")
2570 .arg(a: QString::fromUtf8(utf8: enumValue)));
2571 return;
2572 }
2573 const int enumIndex = metaObject->indexOfEnumerator(name: enumerator);
2574 const QMetaEnum metaEnum = metaObject->enumerator(index: enumIndex);
2575 l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(key: enumValue);
2576 l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
2577 l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
2578}
2579
2580bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *value) const
2581{
2582 const auto doThrow = [&]() {
2583 engine->handle()->throwTypeError(
2584 QStringLiteral("Value is null and could not be converted to an object"));
2585 return false;
2586 };
2587
2588 if (!object)
2589 return doThrow();
2590
2591 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2592 PropertyResult result = PropertyResult::NeedsInit;
2593 if (l->setter == QV4::Lookup::setterQObject)
2594 result = storeObjectProperty(l, object, value);
2595 else if (l->setter == QV4::Lookup::setterFallback)
2596 result = storeFallbackProperty(l, object, value);
2597 else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
2598 result = storeObjectAsVariant(v4: engine->handle(), l, object, value);
2599 else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
2600 result = storeFallbackAsVariant(v4: engine->handle(), l, object, value);
2601 else
2602 return false;
2603
2604 switch (result) {
2605 case PropertyResult::Deleted:
2606 return doThrow();
2607 case PropertyResult::NeedsInit:
2608 return false;
2609 case PropertyResult::OK:
2610 return true;
2611 }
2612
2613 Q_UNREACHABLE_RETURN(false);
2614}
2615
2616void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaType type) const
2617{
2618 // TODO: The only thing we need the type for is checking whether it's QVariant.
2619 // Replace it with an enum and simplify code generation.
2620
2621 QV4::ExecutionEngine *v4 = engine->handle();
2622 if (v4->hasException) {
2623 amendException(engine: v4);
2624 } else {
2625 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2626 switch (initObjectLookup(aotContext: this, l, object, type)) {
2627 case ObjectLookupResult::Object:
2628 l->setter = QV4::Lookup::setterQObject;
2629 break;
2630 case ObjectLookupResult::ObjectAsVariant:
2631 l->setter = QV4::Lookup::setterQObjectAsVariant;
2632 break;
2633 case ObjectLookupResult::Fallback:
2634 l->setter = QV4::Lookup::setterFallback;
2635 break;
2636 case ObjectLookupResult::FallbackAsVariant:
2637 l->setter = QV4::Lookup::setterFallbackAsVariant;
2638 break;
2639 case ObjectLookupResult::Failure:
2640 engine->handle()->throwTypeError();
2641 break;
2642 }
2643 }
2644}
2645
2646static PropertyResult storeValueProperty(
2647 QV4::Lookup *lookup, const QMetaObject *metaObject, void *target, void *value)
2648{
2649 void *args[] = { value, nullptr };
2650 metaObject->d.static_metacall(
2651 reinterpret_cast<QObject *>(target), QMetaObject::WriteProperty,
2652 lookup->qgadgetLookup.coreIndex, args);
2653 return PropertyResult::OK;
2654}
2655
2656static PropertyResult resetValueProperty(
2657 QV4::Lookup *lookup, const QMetaObject *metaObject, void *target, QV4::ExecutionEngine *v4)
2658{
2659 const QMetaProperty property = metaObject->property(index: lookup->qgadgetLookup.coreIndex);
2660 if (property.isResettable()) {
2661 void *args[] = { nullptr };
2662 metaObject->d.static_metacall(
2663 reinterpret_cast<QObject *>(target), QMetaObject::ResetProperty,
2664 lookup->qgadgetLookup.coreIndex, args);
2665 } else {
2666 v4->throwError(
2667 message: QLatin1String("Cannot assign [undefined] to ") +
2668 QLatin1String(property.metaType().name()));
2669 }
2670
2671 return PropertyResult::OK;
2672}
2673
2674static PropertyResult storeValueAsVariant(
2675 QV4::ExecutionEngine *v4, QV4::Lookup *lookup, const QMetaObject *metaObject,
2676 void *target, void *value)
2677{
2678 QVariant *variant = static_cast<QVariant *>(value);
2679 const QMetaType propType = metaObject->property(index: lookup->qgadgetLookup.coreIndex).metaType();
2680 if (propType == QMetaType::fromType<QVariant>())
2681 return storeValueProperty(lookup, metaObject, target, value: variant);
2682
2683 if (!variant->isValid())
2684 return resetValueProperty(lookup, metaObject, target, v4);
2685
2686 if (isTypeCompatible(lookupType: variant->metaType(), propertyType: propType))
2687 return storeValueProperty(lookup, metaObject, target, value: variant->data());
2688
2689 QVariant converted(propType);
2690 QV4::Scope scope(v4);
2691 QV4::ScopedValue val(scope, v4->fromVariant(*variant));
2692 if (v4->metaTypeFromJS(value: val, type: propType, data: converted.data())
2693 || QMetaType::convert(
2694 fromType: variant->metaType(), from: variant->constData(), toType: propType, to: converted.data())) {
2695 return storeValueProperty(lookup, metaObject, target, value: converted.data());
2696 }
2697
2698 v4->throwError(
2699 message: QLatin1String("Cannot assign ") + QLatin1String(variant->metaType().name())
2700 + QLatin1String(" to ") + QLatin1String(propType.name()));
2701
2702 return PropertyResult::NeedsInit;
2703}
2704
2705bool AOTCompiledContext::setValueLookup(
2706 uint index, void *target, void *value) const
2707{
2708 QV4::Lookup *lookup = compilationUnit->runtimeLookups + index;
2709 const QMetaObject *metaObject
2710 = reinterpret_cast<const QMetaObject *>(lookup->qgadgetLookup.metaObject - 1);
2711
2712 PropertyResult result = PropertyResult::NeedsInit;
2713 if (lookup->setter == QV4::QQmlValueTypeWrapper::lookupSetter)
2714 result = storeValueProperty(lookup, metaObject, target, value);
2715 else if (lookup->setter == QV4::QQmlValueTypeWrapper::lookupSetterAsVariant)
2716 result = storeValueAsVariant(v4: engine->handle(), lookup, metaObject, target, value);
2717
2718 switch (result) {
2719 case PropertyResult::OK:
2720 return true;
2721 case PropertyResult::NeedsInit:
2722 return false;
2723 case PropertyResult::Deleted:
2724 Q_UNREACHABLE();
2725 }
2726
2727 return false;
2728}
2729
2730void AOTCompiledContext::initSetValueLookup(
2731 uint index, const QMetaObject *metaObject, QMetaType type) const
2732{
2733 // TODO: The only thing we need the type for is checking whether it's QVariant.
2734 // Replace it with an enum and simplify code generation.
2735
2736 QV4::ExecutionEngine *v4 = engine->handle();
2737 if (v4->hasException) {
2738 amendException(engine: v4);
2739 return;
2740 }
2741
2742 Q_ASSERT(!engine->hasError());
2743 QV4::Lookup *lookup = compilationUnit->runtimeLookups + index;
2744 initValueLookup(l: lookup, compilationUnit: compilationUnit, metaObject);
2745 lookup->setter = (type == QMetaType::fromType<QVariant>())
2746 ? QV4::QQmlValueTypeWrapper::lookupSetterAsVariant
2747 : QV4::QQmlValueTypeWrapper::lookupSetter;
2748}
2749
2750} // namespace QQmlPrivate
2751
2752/*!
2753 \macro QML_DECLARE_TYPE()
2754 \relates qqml.h
2755
2756 Equivalent to \c Q_DECLARE_METATYPE(TYPE *) and \c Q_DECLARE_METATYPE(QQmlListProperty<TYPE>)
2757*/
2758
2759/*!
2760 \macro QML_DECLARE_TYPEINFO(Type,Flags)
2761 \relates qqml.h
2762
2763 Declares additional properties of the given \a Type as described by the
2764 specified \a Flags.
2765
2766 Current the only supported type info is \c QML_HAS_ATTACHED_PROPERTIES which
2767 declares that the \a Type supports \l {Attached Properties and Attached Signal Handlers}
2768 {attached properties}. QML_DECLARE_TYPEINFO() is not necessary if \a Type contains the
2769 QML_ATTACHED macro.
2770*/
2771
2772/*!
2773 \fn template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
2774 \relates qqml.h
2775
2776 This template function registers the C++ type in the QML system with
2777 the name \a qmlName, in the library imported from \a uri having the
2778 version number composed from \a versionMajor and \a versionMinor.
2779
2780 Returns the QML type id.
2781
2782 There are two forms of this template function:
2783
2784 \code
2785 template<typename T>
2786 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
2787
2788 template<typename T, int metaObjectRevision>
2789 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
2790 \endcode
2791
2792 The former is the standard form which registers the type \e T as a new type.
2793 The latter allows a particular revision of a class to be registered in
2794 a specified version (see \l {Type Revisions and Versions}).
2795
2796
2797 For example, this registers a C++ class \c MySliderItem as a QML type
2798 named \c Slider for version 1.0 of a type namespace called
2799 "com.mycompany.qmlcomponents":
2800
2801 \code
2802 qmlRegisterType<MySliderItem>("com.mycompany.qmlcomponents", 1, 0, "Slider");
2803 \endcode
2804
2805 Once this is registered, the type can be used in QML by importing the
2806 specified type namespace and version number:
2807
2808 \qml
2809 import com.mycompany.qmlcomponents 1.0
2810
2811 Slider {
2812 // ...
2813 }
2814 \endqml
2815
2816 Note that it's perfectly reasonable for a library to register types to older versions
2817 than the actual version of the library. Indeed, it is normal for the new library to allow
2818 QML written to previous versions to continue to work, even if more advanced versions of
2819 some of its types are available.
2820
2821 \sa QML_ELEMENT, QML_NAMED_ELEMENT(),
2822 {Choosing the Correct Integration Method Between C++ and QML}
2823*/
2824
2825/*!
2826 \fn template<typename T, int metaObjectRevision> int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor)
2827 \relates qqml.h
2828
2829 This template function registers the specified revision of a C++ type in the QML system with
2830 the library imported from \a uri having the version number composed
2831 from \a versionMajor and \a versionMinor.
2832
2833 Returns the QML type id.
2834
2835 \code
2836 template<typename T, int metaObjectRevision>
2837 int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor);
2838 \endcode
2839
2840 This function is typically used to register the revision of a base class to
2841 use for the specified version of the type (see \l {Type Revisions and Versions}).
2842*/
2843
2844/*!
2845 \fn template <typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message)
2846 \relates qqml.h
2847
2848 This template function registers the C++ type in the QML system with
2849 the name \a qmlName, in the library imported from \a uri having the
2850 version number composed from \a versionMajor and \a versionMinor.
2851
2852 While the type has a name and a type, it cannot be created, and the
2853 given error \a message will result if creation is attempted.
2854
2855 This is useful where the type is only intended for providing attached properties or enum values.
2856
2857 Returns the QML type id.
2858
2859 \sa QML_UNCREATABLE(), qmlRegisterTypeNotAvailable(),
2860 {Choosing the Correct Integration Method Between C++ and QML}
2861*/
2862
2863/*!
2864 \fn template <typename T, typename E> int qmlRegisterExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
2865 \relates qqml.h
2866
2867 This template function registers the C++ type and its extension object in the
2868 QML system with the name \a qmlName in the library imported from \a uri having
2869 version number composed from \a versionMajor and \a versionMinor. Properties
2870 not available in the main type will be searched for in the extension object.
2871
2872 Returns the QML type id.
2873
2874 \sa QML_EXTENDED(), qmlRegisterType(), {Registering Extension Objects}
2875*/
2876
2877/*!
2878 \fn template <typename T, typename E> int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason)
2879 \relates qqml.h
2880
2881 This template function registers the C++ type and its extension
2882 in the QML system with the name \a qmlName in the library imported
2883 from \a uri having version number composed from \a versionMajor and
2884 \a versionMinor.
2885
2886 While the type has a name and a type, it cannot be created. An error
2887 message with the given \a reason is printed if the user attempts to
2888 create an instance of this type.
2889
2890 This is useful where the type is only intended for providing attached
2891 properties, enum values or an abstract base class with its extension.
2892
2893 Returns the QML type id.
2894
2895 \sa QML_EXTENDED(), QML_UNCREATABLE(), qmlRegisterUncreatableType()
2896*/
2897
2898/*!
2899 \fn int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, QQmlCustomParser *parser)
2900 \relates qqml.h
2901 \internal
2902
2903 This template function registers the C++ type and its extension
2904 in the QML system with the name \a qmlName in the library imported
2905 from \a uri having version number composed from \a versionMajor and
2906 \a versionMinor. Properties from the C++ type or its extension that
2907 cannot be resolved directly by the QML system will be resolved using
2908 the \a parser provided.
2909
2910 Returns the QML type id.
2911
2912 \sa QML_ELEMENT, QML_NAMED_ELEMENT(), QML_EXTENDED()
2913*/
2914
2915/*!
2916 \fn template <typename T> int qmlRegisterAnonymousType(const char *uri, int versionMajor)
2917 \relates qqml.h
2918
2919 This template function registers the C++ type in the QML system as an anonymous type. The
2920 resulting QML type does not have a name. Therefore, instances of this type cannot be created from
2921 the QML system. You can, however, access instances of the type when they are exposed as properties
2922 of other types.
2923
2924 Use this function when the type will not be referenced by name, specifically for C++ types that
2925 are used on the left-hand side of a property binding. To indicate to which module the type belongs
2926 use \a uri and \a versionMajor.
2927
2928 For example, consider the following two classes:
2929
2930 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
2931 \code Q
2932 class Bar : public QObject
2933 {
2934 \1_OBJECT
2935 Q_PROPERTY(QString baz READ baz WRITE setBaz NOTIFY bazChanged)
2936
2937 public:
2938 Bar() {}
2939
2940 QString baz() const { return mBaz; }
2941
2942 void setBaz(const QString &baz)
2943 {
2944 if (baz == mBaz)
2945 return;
2946
2947 mBaz = baz;
2948 emit bazChanged();
2949 }
2950
2951 signals:
2952 void bazChanged();
2953
2954 private:
2955 QString mBaz;
2956 };
2957
2958 class Foo : public QObject
2959 {
2960 \1_OBJECT
2961 Q_PROPERTY(Bar *bar READ bar CONSTANT FINAL)
2962
2963 public:
2964 Foo() {}
2965
2966 Bar *bar() { return &mBar; }
2967
2968 private:
2969 Bar mBar;
2970 };
2971 \endcode
2972
2973 In QML, we assign a string to the \c baz property of \c bar:
2974
2975 \code
2976 Foo {
2977 bar.baz: "abc"
2978 Component.onCompleted: print(bar.baz)
2979 }
2980 \endcode
2981
2982 For the QML engine to know that the \c Bar type has a \c baz property,
2983 we have to make \c Bar known:
2984
2985 \code
2986 qmlRegisterType<Foo>("App", 1, 0, "Foo");
2987 qmlRegisterAnonymousType<Bar>("App", 1);
2988 \endcode
2989
2990 As the \c Foo type is instantiated in QML, it must be registered
2991 with the version of \l qmlRegisterType() that takes an element name.
2992
2993 Returns the QML type id.
2994
2995 \since 5.14
2996 \sa QML_ANONYMOUS, {Choosing the Correct Integration Method Between C++ and QML}
2997*/
2998
2999/*!
3000 \fn int qmlRegisterInterface(const char *typeName)
3001 \relates qqml.h
3002
3003 This template function registers the C++ type in the QML system
3004 under the name \a typeName.
3005
3006 Types registered as an interface with the engine should also
3007 declare themselves as an interface with the
3008 \l {The Meta-Object System}{meta object system}. For example:
3009
3010 \code
3011 struct FooInterface
3012 {
3013 public:
3014 virtual ~FooInterface();
3015 virtual void doSomething() = 0;
3016 };
3017
3018 Q_DECLARE_INTERFACE(FooInterface, "org.foo.FooInterface")
3019 \endcode
3020
3021 When registered with the QML engine in this way, they can be used as
3022 property types:
3023
3024 Q_PROPERTY(FooInterface *foo READ foo WRITE setFoo)
3025
3026 When you assign a \l QObject sub-class to this property, the QML engine does
3027 the interface cast to \c FooInterface* automatically.
3028
3029 Returns the QML type id.
3030
3031 \sa QML_INTERFACE
3032*/
3033
3034/*!
3035 \fn int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QJSValue(QQmlEngine *, QJSEngine *)> callback)
3036 \relates qqml.h
3037
3038 This function may be used to register a singleton type provider \a callback in a particular \a uri
3039 and \a typeName with a version specified in \a versionMajor and \a versionMinor.
3040
3041 Installing a singleton type allows developers to provide arbitrary functionality
3042 (methods and properties) to a client without requiring individual instances of the type to
3043 be instantiated by the client.
3044
3045 A singleton type may be either a QObject or a QJSValue.
3046 This function should be used to register a singleton type provider function which returns a QJSValue as a singleton type.
3047
3048 \b{NOTE:} QJSValue singleton type properties will \b{not} trigger binding re-evaluation if changed.
3049
3050 Usage:
3051 \code
3052 // First, define the singleton type provider function (callback).
3053 static QJSValue example_qjsvalue_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3054 {
3055 Q_UNUSED(engine)
3056
3057 static int seedValue = 5;
3058 QJSValue example = scriptEngine->newObject();
3059 example.setProperty("someProperty", seedValue++);
3060 return example;
3061 }
3062
3063 // Second, register the singleton type provider with QML by calling this function in an initialization function.
3064 qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", example_qjsvalue_singletontype_provider);
3065 \endcode
3066
3067 Alternatively, you can use a C++11 lambda:
3068
3069 \code
3070 qmlRegisterSingletonType("Qt.example.qjsvalueApi", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QJSValue {
3071 Q_UNUSED(engine)
3072
3073 static int seedValue = 5;
3074 QJSValue example = scriptEngine->newObject();
3075 example.setProperty("someProperty", seedValue++);
3076 return example;
3077 });
3078 \endcode
3079
3080 In order to use the registered singleton type in QML, you must import the singleton type.
3081 \qml
3082 import QtQuick 2.0
3083 import Qt.example.qjsvalueApi 1.0 as ExampleApi
3084 Item {
3085 id: root
3086 property int someValue: ExampleApi.MyApi.someProperty
3087 }
3088 \endqml
3089
3090 \sa QML_SINGLETON, {Choosing the Correct Integration Method Between C++ and QML}
3091*/
3092
3093/*!
3094 \fn template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create)
3095 \relates qqml.h
3096
3097 The form of this template function is:
3098
3099 \code
3100 template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *attachee, bool create = true)
3101 \endcode
3102
3103 This returns the attached object instance that has been attached to the specified
3104 \a attachee by the attaching type \e T.
3105
3106 If \a create is true and type \e T is a valid attaching type, this creates and returns a new
3107 attached object instance.
3108
3109 Returns \nullptr if type \e T is not a valid attaching type, or if \a create is false and no
3110 attachment object instance has previously been created for \a attachee.
3111
3112 \sa QML_ATTACHED(), {Providing Attached Properties}
3113*/
3114
3115/*!
3116 \fn template <typename T> int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, std::function<QObject*(QQmlEngine *, QJSEngine *)> callback)
3117 \relates qqml.h
3118
3119 This function may be used to register a singleton type provider \a callback in a particular \a uri
3120 and \a typeName with a version specified in \a versionMajor and \a versionMinor.
3121
3122 Installing a singleton type into a uri allows developers to provide arbitrary functionality
3123 (methods and properties) to clients without requiring individual instances ot the type to be
3124 instantiated by the client.
3125
3126 A singleton type may be either a QObject or a QJSValue.
3127 This function should be used to register a singleton type provider function which returns a QObject
3128 of the given type T as a singleton type.
3129
3130 A QObject singleton type may be referenced via the type name with which it was registered, and this
3131 typename may be used as the target in a \l Connections type or otherwise used as any other type id would.
3132 One exception to this is that a QObject singleton type property may not be aliased.
3133
3134 \b{NOTE:} A QObject singleton type instance returned from a singleton type provider is owned by
3135 the QML engine unless the object has explicit QQmlEngine::CppOwnership flag set.
3136
3137 Usage:
3138 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
3139 \code Q
3140 // First, define your QObject which provides the functionality.
3141 class SingletonTypeExample : public QObject
3142 {
3143 \1_OBJECT
3144 Q_PROPERTY (int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
3145
3146 public:
3147 SingletonTypeExample(QObject *parent = nullptr)
3148 : QObject(parent), m_someProperty(0)
3149 {
3150 }
3151
3152 ~SingletonTypeExample() {}
3153
3154 Q_INVOKABLE int doSomething() { setSomeProperty(5); return m_someProperty; }
3155
3156 int someProperty() const { return m_someProperty; }
3157 void setSomeProperty(int val) { m_someProperty = val; emit somePropertyChanged(val); }
3158
3159 signals:
3160 void somePropertyChanged(int newValue);
3161
3162 private:
3163 int m_someProperty;
3164 };
3165
3166 // Second, define the singleton type provider function (callback).
3167 static QObject *example_qobject_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3168 {
3169 Q_UNUSED(engine)
3170 Q_UNUSED(scriptEngine)
3171
3172 SingletonTypeExample *example = new SingletonTypeExample();
3173 return example;
3174 }
3175
3176 // Third, register the singleton type provider with QML by calling this function in an initialization function.
3177 qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", example_qobject_singletontype_provider);
3178 \endcode
3179
3180 Alternatively, you can use a C++11 lambda:
3181
3182 \code
3183 qmlRegisterSingletonType<SingletonTypeExample>("Qt.example.qobjectSingleton", 1, 0, "MyApi", [](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * {
3184 Q_UNUSED(engine)
3185 Q_UNUSED(scriptEngine)
3186
3187 SingletonTypeExample *example = new SingletonTypeExample();
3188 return example;
3189 });
3190 \endcode
3191
3192 In order to use the registered singleton type in QML, you must import the singleton type.
3193 \qml
3194 import QtQuick 2.0
3195 import Qt.example.qobjectSingleton 1.0
3196 Item {
3197 id: root
3198 property int someValue: MyApi.someProperty
3199
3200 Component.onCompleted: {
3201 someValue = MyApi.doSomething()
3202 }
3203 }
3204 \endqml
3205
3206 \sa QML_SINGLETON, {Choosing the Correct Integration Method Between C++ and QML}
3207*/
3208
3209/*!
3210 \fn int qmlRegisterSingletonType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
3211 \relates qqml.h
3212
3213 This function may be used to register a singleton type with the name \a qmlName, in the library imported from \a uri having
3214 the version number composed from \a versionMajor and \a versionMinor. The type is defined by the QML file located at \a url.
3215 The url must be an absolute URL, i.e. url.isRelative() == false.
3216
3217 In addition the type's QML file must have pragma Singleton statement among its import statements.
3218
3219 A singleton type may be referenced via the type name with which it was registered, and this typename may be used as the
3220 target in a \l Connections type or otherwise used as any other type id would. One exception to this is that a singleton
3221 type property may not be aliased (because the singleton type name does not identify an object within the same component
3222 as any other item).
3223
3224 Usage:
3225 \qml
3226 // First, define your QML singleton type which provides the functionality.
3227 pragma Singleton
3228 import QtQuick 2.0
3229 Item {
3230 property int testProp1: 125
3231 }
3232 \endqml
3233
3234 \code
3235 // Second, register the QML singleton type by calling this function in an initialization function.
3236 qmlRegisterSingletonType(QUrl("file:///absolute/path/SingletonType.qml"), "Qt.example.qobjectSingleton", 1, 0, "RegisteredSingleton");
3237 \endcode
3238
3239 In order to use the registered singleton type in QML, you must import the singleton type.
3240 \qml
3241 import QtQuick 2.0
3242 import Qt.example.qobjectSingleton 1.0
3243 Item {
3244 id: root
3245 property int someValue: RegisteredSingleton.testProp1
3246 }
3247 \endqml
3248
3249 It is also possible to have QML singleton types registered without using the qmlRegisterSingletonType function.
3250 That can be done by adding a pragma Singleton statement among the imports of the type's QML file. In addition
3251 the type must be defined in a qmldir file with a singleton keyword and the qmldir must be imported by the QML
3252 files using the singleton.
3253
3254 \sa QML_SINGLETON
3255*/
3256
3257/*!
3258 \fn int qmlRegisterSingletonInstance(const char *uri, int versionMajor, int versionMinor, const char *typeName, QObject *cppObject)
3259 \relates qqml.h
3260 \since 5.14
3261
3262 This function is used to register a singleton object \a cppObject, with a
3263 particular \a uri and \a typeName. Its version is a combination of \a
3264 versionMajor and \a versionMinor.
3265
3266 Installing a singleton type into a URI allows you to provide arbitrary
3267 functionality (methods and properties) to QML code without requiring
3268 individual instances of the type to be instantiated by the client.
3269
3270 Use this function to register an object of the given type T as a singleton
3271 type.
3272
3273 A QObject singleton type may be referenced via the type name with which it
3274 was registered; in turn this type name may be used as the target in a \l
3275 Connections type, or like any other type ID. However, there's one
3276 exception: a QObject singleton type property can't be aliased because the
3277 singleton type name does not identify an object within the same component
3278 as any other item.
3279
3280 \note \a cppObject must outlive the QML engine in which it is used.
3281 Moreover, \cppObject must have the same thread affinity as the engine. If
3282 you want separate singleton instances for multiple engines, you need to use
3283 \l {qmlRegisterSingletonType}. See \l{Threads and QObjects} for more
3284 information about thread safety.
3285
3286 \b{NOTE:} qmlRegisterSingleton can only be used when all types of that module are registered procedurally.
3287
3288 Usage:
3289 //! Workaround for MOC not respecting comments and triggering an error on certain Qt macros.
3290 \code Q
3291 // First, define your QObject which provides the functionality.
3292 class SingletonTypeExample : public QObject
3293 {
3294 \1_OBJECT
3295 Q_PROPERTY(int someProperty READ someProperty WRITE setSomeProperty NOTIFY somePropertyChanged)
3296
3297 public:
3298 explicit SingletonTypeExample(QObject* parent = nullptr) : QObject(parent) {}
3299
3300 Q_INVOKABLE int doSomething()
3301 {
3302 setSomeProperty(5);
3303 return m_someProperty;
3304 }
3305
3306 int someProperty() const { return m_someProperty; }
3307 void setSomeProperty(int val) {
3308 if (m_someProperty != val) {
3309 m_someProperty = val;
3310 emit somePropertyChanged(val);
3311 }
3312 }
3313
3314 signals:
3315 void somePropertyChanged(int newValue);
3316
3317 private:
3318 int m_someProperty = 0;
3319 };
3320 \endcode
3321
3322 \code
3323 // Second, create an instance of the object
3324
3325 // allocate example before the engine to ensure that it outlives it
3326 QScopedPointer<SingletonTypeExample> example(new SingletonTypeExample);
3327 QQmlEngine engine;
3328
3329 // Third, register the singleton type provider with QML by calling this
3330 // function in an initialization function.
3331 qmlRegisterSingletonInstance("Qt.example.qobjectSingleton", 1, 0, "MyApi", example.get());
3332 \endcode
3333
3334
3335 In order to use the registered singleton type in QML, you must import the
3336 URI with the corresponding version.
3337 \qml
3338 import QtQuick 2.0
3339 import Qt.example.qobjectSingleton 1.0
3340 Item {
3341 id: root
3342 property int someValue: MyApi.someProperty
3343
3344 Component.onCompleted: {
3345 console.log(MyApi.doSomething())
3346 }
3347 }
3348 \endqml
3349
3350 \sa QML_SINGLETON, qmlRegisterSingletonType
3351 */
3352
3353/*!
3354 \fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName);
3355 \relates qqml.h
3356
3357 This function registers a type in the QML system with the name \a qmlName, in the library imported from \a uri having the
3358 version number composed from \a versionMajor and \a versionMinor. The type is defined by the QML file located at \a url. The
3359 url must be an absolute URL, i.e. url.isRelative() == false.
3360
3361 Normally QML files can be loaded as types directly from other QML files, or using a qmldir file. This function allows
3362 registration of files to types from C++ code, such as when the type mapping needs to be procedurally determined at startup.
3363
3364 Returns -1 if the registration was not successful.
3365*/
3366
3367QT_END_NAMESPACE
3368

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qml/qml/qqml.cpp