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/qqmlengine_p.h>
10#include <private/qqmlmetatype_p.h>
11#include <private/qqmlmetatypedata_p.h>
12#include <private/qqmltype_p_p.h>
13#include <private/qqmltypemodule_p.h>
14#include <private/qqmlcomponent_p.h>
15#include <private/qqmltypewrapper_p.h>
16#include <private/qqmlvaluetypewrapper_p.h>
17#include <private/qv4lookup_p.h>
18#include <private/qv4qobjectwrapper_p.h>
19#include <private/qv4identifiertable_p.h>
20#include <private/qv4errorobject_p.h>
21#include <private/qqmlbuiltinfunctions_p.h>
22#include <private/qqmlfinalizer_p.h>
23#include <private/qqmlloggingcategory_p.h>
24
25#include <QtCore/qmutex.h>
26
27QT_BEGIN_NAMESPACE
28
29Q_DECLARE_LOGGING_CATEGORY(lcQml);
30Q_DECLARE_LOGGING_CATEGORY(lcJs);
31
32/*!
33 \internal
34
35 This method completes the setup of all deferred properties of \a object.
36 Deferred properties are declared with
37 Q_CLASSINFO("DeferredPropertyNames", "comma,separated,property,list");
38
39 Any binding to a deferred property is not executed when the object is instantiated,
40 but only when completion is requested with qmlExecuteDeferred, or by manually
41 calling QQmlComponentPrivate::beginDeferred and completeDeferred.
42
43 \sa QV4::CompiledData::Binding::IsDeferredBinding,
44 QV4::CompiledData::Object::HasDeferredBindings,
45 QQmlData::deferData,
46 QQmlObjectCreator::setupBindings
47*/
48void qmlExecuteDeferred(QObject *object)
49{
50 QQmlData *data = QQmlData::get(object);
51
52 if (!data
53 || !data->context
54 || !data->context->engine()
55 || data->deferredData.isEmpty()
56 || data->wasDeleted(object)) {
57 return;
58 }
59
60 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: data->context->engine());
61
62 QQmlComponentPrivate::DeferredState state;
63 QQmlComponentPrivate::beginDeferred(enginePriv: ep, object, deferredState: &state);
64
65 // Release the reference for the deferral action (we still have one from construction)
66 data->releaseDeferredData();
67
68 QQmlComponentPrivate::completeDeferred(enginePriv: ep, deferredState: &state);
69}
70
71QQmlContext *qmlContext(const QObject *obj)
72{
73 return QQmlEngine::contextForObject(obj);
74}
75
76QQmlEngine *qmlEngine(const QObject *obj)
77{
78 QQmlData *data = QQmlData::get(object: obj);
79 if (!data || !data->context)
80 return nullptr;
81 return data->context->engine();
82}
83
84static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data,
85 QObject *object, bool create)
86{
87 if (!pf)
88 return nullptr;
89
90 QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(key: pf) : 0;
91 if (rv || !create)
92 return rv;
93
94 rv = pf(object);
95
96 if (rv)
97 data->attachedProperties()->insert(key: pf, value: rv);
98
99 return rv;
100}
101
102QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object,
103 const QMetaObject *attachedMetaObject)
104{
105 QQmlEngine *engine = object ? qmlEngine(obj: object) : nullptr;
106 return QQmlMetaType::attachedPropertiesFunc(engine ? QQmlEnginePrivate::get(e: engine) : nullptr,
107 attachedMetaObject);
108}
109
110QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool create)
111{
112 if (!object)
113 return nullptr;
114
115 QQmlData *data = QQmlData::get(object, create);
116
117 // Attached properties are only on objects created by QML,
118 // unless explicitly requested (create==true)
119 if (!data)
120 return nullptr;
121
122 return resolveAttachedProperties(pf: func, data, object, create);
123}
124
125QObject *qmlExtendedObject(QObject *object)
126{
127 return QQmlPrivate::qmlExtendedObject(object, 0);
128}
129
130QObject *QQmlPrivate::qmlExtendedObject(QObject *object, int index)
131{
132 if (!object)
133 return nullptr;
134
135 void *result = nullptr;
136 QObjectPrivate *d = QObjectPrivate::get(o: object);
137 if (!d->metaObject)
138 return nullptr;
139
140 const int id = d->metaObject->metaCall(
141 object, QMetaObject::CustomCall,
142 id: QQmlProxyMetaObject::extensionObjectId(id: index), &result);
143 if (id != QQmlProxyMetaObject::extensionObjectId(id: index))
144 return nullptr;
145
146 return static_cast<QObject *>(result);
147}
148
149void QQmlPrivate::qmlRegistrationWarning(
150 QQmlPrivate::QmlRegistrationWarning warning, QMetaType metaType)
151{
152 switch (warning) {
153 case UnconstructibleType:
154 qWarning()
155 << metaType.name()
156 << "is neither a QObject, nor default- and copy-constructible, nor uncreatable."
157 << "You should not use it as a QML type.";
158 break;
159 case UnconstructibleSingleton:
160 qWarning()
161 << "Singleton" << metaType.name()
162 << "needs either a default constructor or, when adding a default"
163 << "constructor is infeasible, a public static"
164 << "create(QQmlEngine *, QJSEngine *) method.";
165 break;
166 case NonQObjectWithAtached:
167 qWarning()
168 << metaType.name()
169 << "is not a QObject, but has attached properties. This won't work.";
170 break;
171 }
172}
173
174int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject,
175 const char *uri, int versionMajor,
176 int versionMinor, const char *qmlName,
177 const QString& reason)
178{
179 QQmlPrivate::RegisterType type = {
180 .structVersion: QQmlPrivate::RegisterType::CurrentVersion,
181 .typeId: QMetaType(),
182 .listId: QMetaType(),
183 .objectSize: 0,
184 .create: nullptr,
185 .userdata: nullptr,
186 .noCreationReason: reason,
187 .createValueType: nullptr,
188
189 .uri: uri, .version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor), .elementName: qmlName, .metaObject: &staticMetaObject,
190
191 .attachedPropertiesFunction: QQmlAttachedPropertiesFunc(),
192 .attachedPropertiesMetaObject: nullptr,
193
194 .parserStatusCast: -1,
195 .valueSourceCast: -1,
196 .valueInterceptorCast: -1,
197
198 .extensionObjectCreate: nullptr, .extensionMetaObject: nullptr,
199
200 .customParser: nullptr,
201 .revision: QTypeRevision::zero(),
202 .finalizerCast: -1,
203 .creationMethod: QQmlPrivate::ValueTypeCreationMethod::None
204 };
205
206 return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
207}
208
209void qmlClearTypeRegistrations() // Declared in qqml.h
210{
211 QQmlMetaType::clearTypeRegistrations();
212 QQmlEnginePrivate::baseModulesUninitialized = true; //So the engine re-registers its types
213 qmlClearEnginePlugins();
214}
215
216//From qqml.h
217bool qmlProtectModule(const char *uri, int majVersion)
218{
219 return QQmlMetaType::protectModule(uri: QString::fromUtf8(utf8: uri),
220 version: QTypeRevision::fromMajorVersion(majorVersion: majVersion));
221}
222
223//From qqml.h
224void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
225{
226 QQmlMetaType::registerModule(uri, version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor));
227}
228
229static QQmlDirParser::Import resolveImport(const QString &uri, int importMajor, int importMinor)
230{
231 if (importMajor == QQmlModuleImportAuto)
232 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Auto);
233 else if (importMajor == QQmlModuleImportLatest)
234 return QQmlDirParser::Import(uri, QTypeRevision(), QQmlDirParser::Import::Default);
235 else if (importMinor == QQmlModuleImportLatest)
236 return QQmlDirParser::Import(uri, QTypeRevision::fromMajorVersion(majorVersion: importMajor), QQmlDirParser::Import::Default);
237 return QQmlDirParser::Import(uri, QTypeRevision::fromVersion(majorVersion: importMajor, minorVersion: importMinor), QQmlDirParser::Import::Default);
238}
239
240static QTypeRevision resolveModuleVersion(int moduleMajor)
241{
242 return moduleMajor == QQmlModuleImportModuleAny
243 ? QTypeRevision()
244 : QTypeRevision::fromMajorVersion(majorVersion: moduleMajor);
245}
246
247/*!
248 * \enum QQmlModuleImportSpecialVersions
249 * \relates QQmlEngine
250 *
251 * Defines some special values that can be passed to the version arguments of
252 * qmlRegisterModuleImport() and qmlUnregisterModuleImport().
253 *
254 * \value QQmlModuleImportModuleAny When passed as majorVersion of the base
255 * module, signifies that the import is to be
256 * applied to any version of the module.
257 * \value QQmlModuleImportLatest When passed as major or minor version of
258 * the imported module, signifies that the
259 * latest overall, or latest minor version
260 * of a specified major version shall be
261 * imported.
262 * \value QQmlModuleImportAuto When passed as major version of the imported
263 * module, signifies that the version of the
264 * base module shall be forwarded.
265 */
266
267/*!
268 * \relates QQmlEngine
269 * Registers a qmldir-import for module \a uri of major version \a moduleMajor.
270 *
271 * This has the same effect as an \c import statement in a qmldir file: Whenever
272 * \a uri of version \a moduleMajor is imported, \a import of version
273 * \a importMajor. \a importMinor is automatically imported, too. If
274 * \a importMajor is \l QQmlModuleImportLatest the latest version
275 * available of that module is imported, and \a importMinor does not matter. If
276 * \a importMinor is \l QQmlModuleImportLatest the latest minor version of a
277 * \a importMajor is chosen. If \a importMajor is \l QQmlModuleImportAuto the
278 * version of \a import is version of \a uri being imported, and \a importMinor
279 * does not matter. If \a moduleMajor is \l QQmlModuleImportModuleAny the module
280 * import is applied for any major version of \a uri. For example, you may
281 * specify that whenever any version of MyModule is imported, the latest version
282 * of MyOtherModule should be imported. Then, the following call would be
283 * appropriate:
284 *
285 * \code
286 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
287 * "MyOtherModule", QQmlModuleImportLatest);
288 * \endcode
289 *
290 * Or, you may specify that whenever major version 5 of "MyModule" is imported,
291 * then version 3.14 of "MyOtherModule" should be imported:
292 *
293 * \code
294 * qmlRegisterModuleImport("MyModule", 5, "MyOtherModule", 3, 14);
295 * \endcode
296 *
297 * Finally, if you always want the same version of "MyOtherModule" to be
298 * imported whenever "MyModule" is imported, specify the following:
299 *
300 * \code
301 * qmlRegisterModuleImport("MyModule", QQmlModuleImportModuleAny,
302 * "MyOtherModule", QQmlModuleImportAuto);
303 * \endcode
304 *
305 * \sa qmlUnregisterModuleImport()
306 */
307void qmlRegisterModuleImport(const char *uri, int moduleMajor,
308 const char *import, int importMajor, int importMinor)
309{
310 QQmlMetaType::registerModuleImport(
311 uri: QString::fromUtf8(utf8: uri), version: resolveModuleVersion(moduleMajor),
312 import: resolveImport(uri: QString::fromUtf8(utf8: import), importMajor, importMinor));
313}
314
315
316/*!
317 * \relates QQmlEngine
318 * Removes a module import previously registered with qmlRegisterModuleImport()
319 *
320 * Calling this function makes sure that \a import of version
321 * \a{importMajor}.\a{importMinor} is not automatically imported anymore when
322 * \a uri of version \a moduleMajor is. The version resolution works the same
323 * way as with \l qmlRegisterModuleImport().
324 *
325 * \sa qmlRegisterModuleImport()
326 */
327void qmlUnregisterModuleImport(const char *uri, int moduleMajor,
328 const char *import, int importMajor, int importMinor)
329{
330 QQmlMetaType::unregisterModuleImport(
331 uri: QString::fromUtf8(utf8: uri), version: resolveModuleVersion(moduleMajor),
332 import: resolveImport(uri: QString::fromUtf8(utf8: import), importMajor, importMinor));
333}
334
335//From qqml.h
336int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
337{
338 return QQmlMetaType::typeId(uri, version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor), qmlName);
339}
340
341static bool checkSingletonInstance(QQmlEngine *engine, QObject *instance)
342{
343 if (!instance) {
344 QQmlError error;
345 error.setDescription(QStringLiteral("The registered singleton has already been deleted. "
346 "Ensure that it outlives the engine."));
347 QQmlEnginePrivate::get(e: engine)->warning(engine, error);
348 return false;
349 }
350
351 if (engine->thread() != instance->thread()) {
352 QQmlError error;
353 error.setDescription(QStringLiteral("Registered object must live in the same thread "
354 "as the engine it was registered with"));
355 QQmlEnginePrivate::get(e: engine)->warning(engine, error);
356 return false;
357 }
358
359 return true;
360}
361
362// From qqmlprivate.h
363#if QT_DEPRECATED_SINCE(6, 3)
364QObject *QQmlPrivate::SingletonFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
365{
366 if (!checkSingletonInstance(engine: qeng, instance: m_object))
367 return nullptr;
368
369 if (alreadyCalled) {
370 QQmlError error;
371 error.setDescription(QStringLiteral("Singleton registered by registerSingletonInstance "
372 "must only be accessed from one engine"));
373 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
374 return nullptr;
375 }
376
377 alreadyCalled = true;
378 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
379 return m_object;
380};
381#endif
382
383QObject *QQmlPrivate::SingletonInstanceFunctor::operator()(QQmlEngine *qeng, QJSEngine *)
384{
385 if (!checkSingletonInstance(engine: qeng, instance: m_object))
386 return nullptr;
387
388 if (!m_engine) {
389 m_engine = qeng;
390 QJSEngine::setObjectOwnership(m_object, QQmlEngine::CppOwnership);
391 } else if (m_engine != qeng) {
392 QQmlError error;
393 error.setDescription(QLatin1String("Singleton registered by registerSingletonInstance must only be accessed from one engine"));
394 QQmlEnginePrivate::get(e: qeng)->warning(qeng, error);
395 return nullptr;
396 }
397
398 return m_object;
399};
400
401static QVector<QTypeRevision> availableRevisions(const QMetaObject *metaObject)
402{
403 QVector<QTypeRevision> revisions;
404 if (!metaObject)
405 return revisions;
406 const int propertyOffset = metaObject->propertyOffset();
407 const int propertyCount = metaObject->propertyCount();
408 for (int coreIndex = propertyOffset, propertyEnd = propertyOffset + propertyCount;
409 coreIndex < propertyEnd; ++coreIndex) {
410 const QMetaProperty property = metaObject->property(index: coreIndex);
411 if (int revision = property.revision())
412 revisions.append(t: QTypeRevision::fromEncodedVersion(value: revision));
413 }
414 const int methodOffset = metaObject->methodOffset();
415 const int methodCount = metaObject->methodCount();
416 for (int methodIndex = methodOffset, methodEnd = methodOffset + methodCount;
417 methodIndex < methodEnd; ++methodIndex) {
418 const QMetaMethod method = metaObject->method(index: methodIndex);
419 if (int revision = method.revision())
420 revisions.append(t: QTypeRevision::fromEncodedVersion(value: revision));
421 }
422
423 // Need to also check parent meta objects, as their revisions are inherited.
424 if (const QMetaObject *superMeta = metaObject->superClass())
425 revisions += availableRevisions(metaObject: superMeta);
426
427 return revisions;
428}
429
430template<typename Registration>
431void assignVersions(Registration *registration, QTypeRevision revision,
432 QTypeRevision defaultVersion)
433{
434 const quint8 majorVersion = revision.hasMajorVersion() ? revision.majorVersion()
435 : defaultVersion.majorVersion();
436 registration->version = revision.hasMinorVersion()
437 ? QTypeRevision::fromVersion(majorVersion, minorVersion: revision.minorVersion())
438 : QTypeRevision::fromMajorVersion(majorVersion);
439 registration->revision = revision;
440}
441
442static QVector<QTypeRevision> prepareRevisions(const QMetaObject *metaObject, QTypeRevision added)
443{
444 auto revisions = availableRevisions(metaObject);
445 revisions.append(t: added);
446 return revisions;
447}
448
449static void uniqueRevisions(QVector<QTypeRevision> *revisions, QTypeRevision defaultVersion,
450 QTypeRevision added)
451{
452 bool revisionsHaveMajorVersions = false;
453 for (QTypeRevision revision : QVector<QTypeRevision>(*revisions)) { // yes, copy
454 // allow any minor version for each explicitly specified past major one
455 if (revision.hasMajorVersion()) {
456 revisionsHaveMajorVersions = true;
457 if (revision.majorVersion() < defaultVersion.majorVersion())
458 revisions->append(t: QTypeRevision::fromVersion(majorVersion: revision.majorVersion(), minorVersion: 254));
459 }
460 }
461
462 if (revisionsHaveMajorVersions) {
463 if (!added.hasMajorVersion()) {
464 // If added in unspecified major version, assume default one.
465 revisions->append(t: QTypeRevision::fromVersion(majorVersion: defaultVersion.majorVersion(),
466 minorVersion: added.minorVersion()));
467 } else if (added.majorVersion() < defaultVersion.majorVersion()) {
468 // If added in past major version, add .0 of default version.
469 revisions->append(t: QTypeRevision::fromVersion(majorVersion: defaultVersion.majorVersion(), minorVersion: 0));
470 }
471 }
472
473 std::sort(first: revisions->begin(), last: revisions->end());
474 const auto it = std::unique(first: revisions->begin(), last: revisions->end());
475 revisions->erase(abegin: it, aend: revisions->end());
476}
477
478/*
479This method is "over generalized" to allow us to (potentially) register more types of things in
480the future without adding exported symbols.
481*/
482int QQmlPrivate::qmlregister(RegistrationType type, void *data)
483{
484 QQmlType dtype;
485 switch (type) {
486 case AutoParentRegistration:
487 return QQmlMetaType::registerAutoParentFunction(
488 autoparent: *reinterpret_cast<RegisterAutoParent *>(data));
489 case QmlUnitCacheHookRegistration:
490 return QQmlMetaType::registerUnitCacheHook(
491 hookRegistration: *reinterpret_cast<RegisterQmlUnitCacheHook *>(data));
492 case TypeAndRevisionsRegistration: {
493 const RegisterTypeAndRevisions &type = *reinterpret_cast<RegisterTypeAndRevisions *>(data);
494 const char *elementName = (type.structVersion > 1 && type.forceAnonymous)
495 ? nullptr
496 : classElementName(metaObject: type.classInfoMetaObject);
497 const bool isValueType = !(type.typeId.flags() & QMetaType::PointerToQObject);
498 const bool creatable = (elementName != nullptr || isValueType)
499 && boolClassInfo(metaObject: type.classInfoMetaObject, key: "QML.Creatable", defaultValue: true);
500
501 QString noCreateReason;
502 ValueTypeCreationMethod creationMethod = ValueTypeCreationMethod::None;
503
504 if (!creatable) {
505 noCreateReason = QString::fromUtf8(
506 utf8: classInfo(metaObject: type.classInfoMetaObject, key: "QML.UncreatableReason"));
507 if (noCreateReason.isEmpty())
508 noCreateReason = QLatin1String("Type cannot be created in QML.");
509 } else if (isValueType) {
510 const char *method = classInfo(metaObject: type.classInfoMetaObject, key: "QML.CreationMethod");
511 if (qstrcmp(str1: method, str2: "structured") == 0)
512 creationMethod = ValueTypeCreationMethod::Structured;
513 else if (qstrcmp(str1: method, str2: "construct") == 0)
514 creationMethod = ValueTypeCreationMethod::Construct;
515 }
516
517 RegisterType typeRevision = {
518 .structVersion: QQmlPrivate::RegisterType::CurrentVersion,
519 .typeId: type.typeId,
520 .listId: type.listId,
521 .objectSize: creatable ? type.objectSize : 0,
522 .create: nullptr,
523 .userdata: nullptr,
524 .noCreationReason: noCreateReason,
525 .createValueType: type.createValueType,
526 .uri: type.uri,
527 .version: type.version,
528 .elementName: nullptr,
529 .metaObject: type.metaObject,
530 .attachedPropertiesFunction: type.attachedPropertiesFunction,
531 .attachedPropertiesMetaObject: type.attachedPropertiesMetaObject,
532 .parserStatusCast: type.parserStatusCast,
533 .valueSourceCast: type.valueSourceCast,
534 .valueInterceptorCast: type.valueInterceptorCast,
535 .extensionObjectCreate: type.extensionObjectCreate,
536 .extensionMetaObject: type.extensionMetaObject,
537 .customParser: nullptr,
538 .revision: QTypeRevision(),
539 .finalizerCast: type.structVersion > 0 ? type.finalizerCast : -1,
540 .creationMethod: creationMethod
541 };
542
543 QQmlPrivate::RegisterSequentialContainer sequenceRevision = {
544 .structVersion: 0,
545 .uri: type.uri,
546 .version: type.version,
547 .typeName: nullptr,
548 .typeId: type.listId,
549 .metaSequence: type.structVersion > 1 ? type.listMetaSequence : QMetaSequence(),
550 .revision: QTypeRevision(),
551 };
552
553 const QTypeRevision added = revisionClassInfo(
554 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
555 defaultValue: QTypeRevision::fromVersion(majorVersion: type.version.majorVersion(), minorVersion: 0));
556 const QTypeRevision removed = revisionClassInfo(
557 metaObject: type.classInfoMetaObject, key: "QML.RemovedInVersion");
558 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(metaObject: type.classInfoMetaObject,
559 key: "QML.ExtraVersion");
560
561 auto revisions = prepareRevisions(metaObject: type.metaObject, added) + furtherRevisions;
562 if (type.attachedPropertiesMetaObject)
563 revisions += availableRevisions(metaObject: type.attachedPropertiesMetaObject);
564 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
565
566 for (QTypeRevision revision : revisions) {
567 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
568 break;
569
570 assignVersions(registration: &typeRevision, revision, defaultVersion: type.version);
571
572 // When removed or before added, we still add revisions, but anonymous ones
573 if (typeRevision.version < added
574 || (removed.isValid() && !(typeRevision.version < removed))) {
575 typeRevision.elementName = nullptr;
576 typeRevision.create = nullptr;
577 typeRevision.userdata = nullptr;
578 } else {
579 typeRevision.elementName = elementName;
580 typeRevision.create = creatable ? type.create : nullptr;
581 typeRevision.userdata = type.userdata;
582 }
583
584 typeRevision.customParser = type.customParserFactory();
585 const int id = qmlregister(type: TypeRegistration, data: &typeRevision);
586 if (type.qmlTypeIds)
587 type.qmlTypeIds->append(t: id);
588
589 if (sequenceRevision.metaSequence != QMetaSequence()) {
590 sequenceRevision.version = typeRevision.version;
591 sequenceRevision.revision = typeRevision.revision;
592 const int id = QQmlPrivate::qmlregister(
593 type: QQmlPrivate::SequentialContainerRegistration, data: &sequenceRevision);
594 if (type.qmlTypeIds)
595 type.qmlTypeIds->append(t: id);
596 }
597 }
598 break;
599 }
600 case SingletonAndRevisionsRegistration: {
601 const RegisterSingletonTypeAndRevisions &type
602 = *reinterpret_cast<RegisterSingletonTypeAndRevisions *>(data);
603 const char *elementName = classElementName(metaObject: type.classInfoMetaObject);
604 RegisterSingletonType revisionRegistration = {
605 .structVersion: 0,
606 .uri: type.uri,
607 .version: type.version,
608 .typeName: elementName,
609 .scriptApi: nullptr,
610 .qObjectApi: type.qObjectApi,
611 .instanceMetaObject: type.instanceMetaObject,
612 .typeId: type.typeId,
613 .extensionObjectCreate: type.extensionObjectCreate,
614 .extensionMetaObject: type.extensionMetaObject,
615 .revision: QTypeRevision()
616 };
617
618 const QTypeRevision added = revisionClassInfo(
619 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
620 defaultValue: QTypeRevision::fromMinorVersion(minorVersion: 0));
621 const QTypeRevision removed = revisionClassInfo(
622 metaObject: type.classInfoMetaObject, key: "QML.RemovedInVersion");
623 const QList<QTypeRevision> furtherRevisions = revisionClassInfos(metaObject: type.classInfoMetaObject,
624 key: "QML.ExtraVersion");
625
626 auto revisions = prepareRevisions(metaObject: type.instanceMetaObject, added) + furtherRevisions;
627 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
628
629 for (QTypeRevision revision : std::as_const(t&: revisions)) {
630 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
631 break;
632
633 assignVersions(registration: &revisionRegistration, revision, defaultVersion: type.version);
634
635 // When removed or before added, we still add revisions, but anonymous ones
636 if (revisionRegistration.version < added
637 || (removed.isValid() && !(revisionRegistration.version < removed))) {
638 revisionRegistration.typeName = nullptr;
639 revisionRegistration.qObjectApi = nullptr;
640 } else {
641 revisionRegistration.typeName = elementName;
642 revisionRegistration.qObjectApi = type.qObjectApi;
643 }
644
645 const int id = qmlregister(type: SingletonRegistration, data: &revisionRegistration);
646 if (type.qmlTypeIds)
647 type.qmlTypeIds->append(t: id);
648 }
649 break;
650 }
651 case SequentialContainerAndRevisionsRegistration: {
652 const RegisterSequentialContainerAndRevisions &type
653 = *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
654 RegisterSequentialContainer revisionRegistration = {
655 .structVersion: 0,
656 .uri: type.uri,
657 .version: type.version,
658 .typeName: nullptr,
659 .typeId: type.typeId,
660 .metaSequence: type.metaSequence,
661 .revision: QTypeRevision()
662 };
663
664 const QTypeRevision added = revisionClassInfo(
665 metaObject: type.classInfoMetaObject, key: "QML.AddedInVersion",
666 defaultValue: QTypeRevision::fromMinorVersion(minorVersion: 0));
667 QList<QTypeRevision> revisions = revisionClassInfos(
668 metaObject: type.classInfoMetaObject, key: "QML.ExtraVersion");
669 revisions.append(t: added);
670 uniqueRevisions(revisions: &revisions, defaultVersion: type.version, added);
671
672 for (QTypeRevision revision : std::as_const(t&: revisions)) {
673 if (revision < added)
674 continue;
675 if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
676 break;
677
678 assignVersions(registration: &revisionRegistration, revision, defaultVersion: type.version);
679 const int id = qmlregister(type: SequentialContainerRegistration, data: &revisionRegistration);
680 if (type.qmlTypeIds)
681 type.qmlTypeIds->append(t: id);
682 }
683 break;
684 }
685 case TypeRegistration:
686 dtype = QQmlMetaType::registerType(type: *reinterpret_cast<RegisterType *>(data));
687 break;
688 case InterfaceRegistration:
689 dtype = QQmlMetaType::registerInterface(type: *reinterpret_cast<RegisterInterface *>(data));
690 break;
691 case SingletonRegistration:
692 dtype = QQmlMetaType::registerSingletonType(type: *reinterpret_cast<RegisterSingletonType *>(data));
693 break;
694 case CompositeRegistration:
695 dtype = QQmlMetaType::registerCompositeType(type: *reinterpret_cast<RegisterCompositeType *>(data));
696 break;
697 case CompositeSingletonRegistration:
698 dtype = QQmlMetaType::registerCompositeSingletonType(type: *reinterpret_cast<RegisterCompositeSingletonType *>(data));
699 break;
700 case SequentialContainerRegistration:
701 dtype = QQmlMetaType::registerSequentialContainer(sequenceRegistration: *reinterpret_cast<RegisterSequentialContainer *>(data));
702 break;
703 default:
704 return -1;
705 }
706
707 if (!dtype.isValid())
708 return -1;
709
710 QQmlMetaType::registerUndeletableType(dtype);
711 return dtype.index();
712}
713
714void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
715{
716 switch (type) {
717 case AutoParentRegistration:
718 QQmlMetaType::unregisterAutoParentFunction(function: reinterpret_cast<AutoParentFunction>(data));
719 break;
720 case QmlUnitCacheHookRegistration:
721 QQmlMetaType::removeCachedUnitLookupFunction(
722 handler: reinterpret_cast<QmlUnitCacheLookupFunction>(data));
723 break;
724 case SequentialContainerRegistration:
725 QQmlMetaType::unregisterSequentialContainer(id: data);
726 break;
727 case TypeRegistration:
728 case InterfaceRegistration:
729 case SingletonRegistration:
730 case CompositeRegistration:
731 case CompositeSingletonRegistration:
732 QQmlMetaType::unregisterType(type: data);
733 break;
734 case TypeAndRevisionsRegistration:
735 case SingletonAndRevisionsRegistration:
736 case SequentialContainerAndRevisionsRegistration:
737 // Currently unnecessary. We'd need a special data structure to hold
738 // URI + majorVersion and then we'd iterate the minor versions, look up the
739 // associated QQmlType objects by uri/elementName/major/minor and qmlunregister
740 // each of them.
741 Q_UNREACHABLE();
742 break;
743 }
744}
745
746QList<QTypeRevision> QQmlPrivate::revisionClassInfos(const QMetaObject *metaObject,
747 const char *key)
748{
749 QList<QTypeRevision> revisions;
750 for (int index = indexOfOwnClassInfo(metaObject, key); index != -1;
751 index = indexOfOwnClassInfo(metaObject, key, startOffset: index - 1)) {
752 revisions.push_back(t: QTypeRevision::fromEncodedVersion(
753 value: QLatin1StringView(metaObject->classInfo(index).value()).toInt()));
754 }
755 return revisions;
756}
757
758int qmlRegisterTypeNotAvailable(
759 const char *uri, int versionMajor, int versionMinor,
760 const char *qmlName, const QString &message)
761{
762 return qmlRegisterUncreatableType<QQmlTypeNotAvailable>(
763 uri, versionMajor, versionMinor, qmlName, reason: message);
764}
765
766namespace QQmlPrivate {
767template<>
768void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
769 const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
770 QVector<int> *qmlTypeIds, const QMetaObject *extension, bool)
771{
772 using T = QQmlTypeNotAvailable;
773
774 RegisterTypeAndRevisions type = {
775 .structVersion: 3,
776 .typeId: QmlMetaType<T>::self(),
777 .listId: QmlMetaType<T>::list(),
778 .objectSize: 0,
779 .create: nullptr,
780 .userdata: nullptr,
781 .createValueType: nullptr,
782
783 .uri: uri,
784 .version: QTypeRevision::fromMajorVersion(majorVersion: versionMajor),
785
786 .metaObject: &QQmlTypeNotAvailable::staticMetaObject,
787 .classInfoMetaObject: classInfoMetaObject,
788
789 .attachedPropertiesFunction: attachedPropertiesFunc<T>(),
790 .attachedPropertiesMetaObject: attachedPropertiesMetaObject<T>(),
791
792 .parserStatusCast: StaticCastSelector<T, QQmlParserStatus>::cast(),
793 .valueSourceCast: StaticCastSelector<T, QQmlPropertyValueSource>::cast(),
794 .valueInterceptorCast: StaticCastSelector<T, QQmlPropertyValueInterceptor>::cast(),
795
796 .extensionObjectCreate: nullptr,
797 .extensionMetaObject: extension,
798 .customParserFactory: qmlCreateCustomParser<T>,
799 .qmlTypeIds: qmlTypeIds,
800 .finalizerCast: QQmlPrivate::StaticCastSelector<T, QQmlFinalizerHook>::cast(),
801 .forceAnonymous: false,
802 .listMetaSequence: QmlMetaType<T>::sequence(),
803 };
804
805 qmlregister(type: TypeAndRevisionsRegistration, data: &type);
806}
807
808QObject *AOTCompiledContext::thisObject() const
809{
810 return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
811 ->thisObject();
812}
813
814QQmlEngine *AOTCompiledContext::qmlEngine() const
815{
816 return qmlContext ? qmlContext->engine() : nullptr;
817}
818
819QJSValue AOTCompiledContext::jsMetaType(int index) const
820{
821 return QJSValuePrivate::fromReturnedValue(
822 d: compilationUnit->runtimeClasses[index]->asReturnedValue());
823}
824
825void AOTCompiledContext::setInstructionPointer(int offset) const
826{
827 if (auto *frame = engine->handle()->currentStackFrame)
828 frame->instructionPointer = offset;
829}
830
831void AOTCompiledContext::setReturnValueUndefined() const
832{
833 if (auto *frame = engine->handle()->currentStackFrame) {
834 Q_ASSERT(frame->isMetaTypesFrame());
835 static_cast<QV4::MetaTypesStackFrame *>(frame)->setReturnValueUndefined();
836 }
837}
838
839static QQmlPropertyCapture *propertyCapture(const QQmlContextData *qmlContext)
840{
841 if (!qmlContext)
842 return nullptr;
843
844 QQmlEngine *engine = qmlContext->engine();
845 Q_ASSERT(engine);
846 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: engine);
847 Q_ASSERT(ep);
848 return ep->propertyCapture;
849}
850
851static void captureFallbackProperty(
852 QObject *object, int coreIndex, int notifyIndex, bool isConstant,
853 const QQmlContextData *qmlContext)
854{
855 if (isConstant)
856 return;
857
858 if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
859 capture->captureProperty(object, coreIndex, notifyIndex);
860}
861
862static void captureObjectProperty(
863 QObject *object, const QQmlPropertyCache *propertyCache,
864 const QQmlPropertyData *property, QQmlContextData *qmlContext)
865{
866 if (property->isConstant())
867 return;
868
869 if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
870 capture->captureProperty(object, propertyCache, property);
871}
872
873static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCache *ancestor)
874{
875 for (const QQmlPropertyCache *cache = descendent; cache; cache = cache->parent().data()) {
876 if (cache == ancestor)
877 return true;
878 }
879 return false;
880}
881
882enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
883
884template<bool StrictType = false>
885ObjectPropertyResult loadObjectProperty(
886 QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
887{
888 QQmlData *qmlData = QQmlData::get(object);
889 if (!qmlData)
890 return ObjectPropertyResult::NeedsInit;
891 if (qmlData->isQueuedForDeletion)
892 return ObjectPropertyResult::Deleted;
893 Q_ASSERT(!QQmlData::wasDeleted(object));
894 const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
895 if (StrictType) {
896 if (qmlData->propertyCache.data() != propertyCache)
897 return ObjectPropertyResult::NeedsInit;
898 } else if (!inherits(descendent: qmlData->propertyCache.data(), ancestor: propertyCache)) {
899 return ObjectPropertyResult::NeedsInit;
900 }
901 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
902
903 const int coreIndex = property->coreIndex();
904 if (qmlData->hasPendingBindingBit(coreIndex))
905 qmlData->flushPendingBinding(coreIndex);
906
907 captureObjectProperty(object, propertyCache, property, qmlContext);
908 property->readProperty(target: object, property: target);
909 return ObjectPropertyResult::OK;
910}
911
912static ObjectPropertyResult loadFallbackProperty(
913 QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
914{
915 QQmlData *qmlData = QQmlData::get(object);
916 if (qmlData && qmlData->isQueuedForDeletion)
917 return ObjectPropertyResult::Deleted;
918
919 Q_ASSERT(!QQmlData::wasDeleted(object));
920
921 const QMetaObject *metaObject
922 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
923 if (!metaObject || metaObject != object->metaObject())
924 return ObjectPropertyResult::NeedsInit;
925
926 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
927 if (qmlData && qmlData->hasPendingBindingBit(coreIndex))
928 qmlData->flushPendingBinding(coreIndex);
929
930 captureFallbackProperty(object, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
931 isConstant: l->qobjectFallbackLookup.isConstant, qmlContext);
932
933 void *a[] = { target, nullptr };
934 metaObject->metacall(object, QMetaObject::ReadProperty, coreIndex, a);
935
936 return ObjectPropertyResult::OK;
937}
938
939ObjectPropertyResult loadObjectAsVariant(
940 QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
941{
942 QVariant *variant = static_cast<QVariant *>(target);
943 const QMetaType propType = l->qobjectLookup.propertyData->propType();
944 if (propType == QMetaType::fromType<QVariant>())
945 return loadObjectProperty<true>(l, object, target: variant, qmlContext);
946
947 *variant = QVariant(propType);
948 return loadObjectProperty<true>(l, object, target: variant->data(), qmlContext);
949}
950
951ObjectPropertyResult loadFallbackAsVariant(
952 QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
953{
954 const QMetaObject *metaObject
955 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
956 Q_ASSERT(metaObject);
957
958 QVariant *variant = static_cast<QVariant *>(target);
959 const QMetaType propType = metaObject->property(index: l->qobjectFallbackLookup.coreIndex).metaType();
960 if (propType == QMetaType::fromType<QVariant>())
961 return loadFallbackProperty(l, object, target: variant, qmlContext);
962
963 *variant = QVariant(propType);
964 return loadFallbackProperty(l, object, target: variant->data(), qmlContext);
965}
966
967template<bool StrictType, typename Op>
968static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
969{
970 const QQmlData *qmlData = QQmlData::get(object);
971 if (!qmlData)
972 return ObjectPropertyResult::NeedsInit;
973 if (qmlData->isQueuedForDeletion)
974 return ObjectPropertyResult::Deleted;
975 Q_ASSERT(!QQmlData::wasDeleted(object));
976 if (StrictType) {
977 if (qmlData->propertyCache.data() != l->qobjectLookup.propertyCache)
978 return ObjectPropertyResult::NeedsInit;
979 } else if (!inherits(descendent: qmlData->propertyCache.data(), ancestor: l->qobjectLookup.propertyCache)) {
980 return ObjectPropertyResult::NeedsInit;
981 }
982 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
983 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(property->coreIndex()));
984 op(property);
985 return ObjectPropertyResult::OK;
986}
987
988template<bool StrictType = false>
989static ObjectPropertyResult resetObjectProperty(QV4::Lookup *l, QObject *object)
990{
991 return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
992 property->resetProperty(target: object, flags: {});
993 });
994}
995
996template<bool StrictType = false>
997static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
998{
999 return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
1000 property->writeProperty(target: object, value, flags: {});
1001 });
1002}
1003
1004template<typename Op>
1005static ObjectPropertyResult changeFallbackProperty(QV4::Lookup *l, QObject *object, Op op)
1006{
1007 const QQmlData *qmlData = QQmlData::get(object);
1008 if (qmlData && qmlData->isQueuedForDeletion)
1009 return ObjectPropertyResult::Deleted;
1010 Q_ASSERT(!QQmlData::wasDeleted(object));
1011
1012 const QMetaObject *metaObject
1013 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1014 if (!metaObject || metaObject != object->metaObject())
1015 return ObjectPropertyResult::NeedsInit;
1016
1017 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1018 QQmlPropertyPrivate::removeBinding(o: object, index: QQmlPropertyIndex(coreIndex));
1019
1020 op(metaObject, coreIndex);
1021 return ObjectPropertyResult::OK;
1022}
1023
1024static ObjectPropertyResult storeFallbackProperty(QV4::Lookup *l, QObject *object, void *value)
1025{
1026 return changeFallbackProperty(l, object, op: [&](const QMetaObject *metaObject, int coreIndex) {
1027 void *args[] = { value, nullptr };
1028 metaObject->metacall(object, QMetaObject::WriteProperty, coreIndex, args);
1029 });
1030}
1031
1032static ObjectPropertyResult resetFallbackProperty(QV4::Lookup *l, QObject *object)
1033{
1034 return changeFallbackProperty(l, object, op: [&](const QMetaObject *metaObject, int coreIndex) {
1035 void *args[] = { nullptr };
1036 metaObject->metacall(object, QMetaObject::ResetProperty, coreIndex, args);
1037 });
1038}
1039
1040static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
1041{
1042 if (!lookupType.isValid()) {
1043 // If type is invalid, then the calling code depends on the lookup
1044 // to be set up in order to query the type, via lookupResultMetaType.
1045 // We cannot verify the type in this case.
1046 } else if ((lookupType.flags() & QMetaType::IsQmlList)
1047 && (propertyType.flags() & QMetaType::IsQmlList)) {
1048 // We want to check the value types here, but we cannot easily do it.
1049 // Internally those are all QObject* lists, though.
1050 } else if (lookupType.flags() & QMetaType::PointerToQObject) {
1051 // We accept any base class as type, too
1052
1053 const QMetaObject *typeMetaObject = lookupType.metaObject();
1054 const QMetaObject *foundMetaObject = propertyType.metaObject();
1055 if (!foundMetaObject)
1056 foundMetaObject = QQmlMetaType::metaObjectForType(metaType: propertyType).metaObject();
1057
1058 while (foundMetaObject && foundMetaObject != typeMetaObject)
1059 foundMetaObject = foundMetaObject->superClass();
1060
1061 if (!foundMetaObject)
1062 return false;
1063 } else if (propertyType.flags() & QMetaType::IsEnumeration) {
1064 if (propertyType == lookupType)
1065 return true;
1066
1067 // You can pass the underlying type of an enum.
1068 // We don't want to check for the actual underlying type because
1069 // moc and qmltyperegistrar are not very precise about it. Especially
1070 // the long and longlong types can be ambiguous.
1071
1072 const bool isUnsigned = propertyType.flags() & QMetaType::IsUnsignedEnumeration;
1073 switch (propertyType.sizeOf()) {
1074 case 1:
1075 return isUnsigned
1076 ? lookupType == QMetaType::fromType<quint8>()
1077 : lookupType == QMetaType::fromType<qint8>();
1078 case 2:
1079 return isUnsigned
1080 ? lookupType == QMetaType::fromType<ushort>()
1081 : lookupType == QMetaType::fromType<short>();
1082 case 4:
1083 // The default type, if moc doesn't know the actual enum type, is int.
1084 // However, the compiler can still decide to encode the enum in uint.
1085 // Therefore, we also accept int for uint enums.
1086 // TODO: This is technically UB.
1087 return isUnsigned
1088 ? (lookupType == QMetaType::fromType<int>()
1089 || lookupType == QMetaType::fromType<uint>())
1090 : lookupType == QMetaType::fromType<int>();
1091 case 8:
1092 return isUnsigned
1093 ? lookupType == QMetaType::fromType<qulonglong>()
1094 : lookupType == QMetaType::fromType<qlonglong>();
1095 }
1096
1097 return false;
1098 } else if (propertyType != lookupType) {
1099 return false;
1100 }
1101 return true;
1102}
1103
1104static ObjectPropertyResult storeObjectAsVariant(
1105 QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
1106{
1107 QVariant *variant = static_cast<QVariant *>(value);
1108 const QMetaType propType = l->qobjectLookup.propertyData->propType();
1109 if (propType == QMetaType::fromType<QVariant>())
1110 return storeObjectProperty<true>(l, object, value: variant);
1111
1112 if (!variant->isValid())
1113 return resetObjectProperty<true>(l, object);
1114
1115 if (isTypeCompatible(lookupType: variant->metaType(), propertyType: propType))
1116 return storeObjectProperty<true>(l, object, value: variant->data());
1117
1118 QVariant converted(propType);
1119 v4->metaTypeFromJS(value: v4->fromVariant(*variant), type: propType, data: converted.data());
1120 return storeObjectProperty<true>(l, object, value: converted.data());
1121}
1122
1123static ObjectPropertyResult storeFallbackAsVariant(
1124 QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
1125{
1126 QVariant *variant = static_cast<QVariant *>(value);
1127
1128 const QMetaObject *metaObject
1129 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1130 Q_ASSERT(metaObject);
1131
1132 const QMetaType propType = metaObject->property(index: l->qobjectFallbackLookup.coreIndex).metaType();
1133 if (propType == QMetaType::fromType<QVariant>())
1134 return storeFallbackProperty(l, object, value: variant);
1135
1136 if (!propType.isValid())
1137 return resetFallbackProperty(l, object);
1138
1139 if (isTypeCompatible(lookupType: variant->metaType(), propertyType: propType))
1140 return storeFallbackProperty(l, object, value: variant->data());
1141
1142 QVariant converted(propType);
1143 v4->metaTypeFromJS(value: v4->fromVariant(*variant), type: propType, data: converted.data());
1144 return storeFallbackProperty(l, object, value: converted.data());
1145}
1146
1147enum class ObjectLookupResult {
1148 Failure,
1149 Object,
1150 Fallback,
1151 ObjectAsVariant,
1152 FallbackAsVariant,
1153};
1154
1155static ObjectLookupResult initObjectLookup(
1156 const AOTCompiledContext *aotContext, QV4::Lookup *l, QObject *object, QMetaType type)
1157{
1158 QV4::Scope scope(aotContext->engine->handle());
1159 QV4::PropertyKey id = scope.engine->identifierTable->asPropertyKey(
1160 str: aotContext->compilationUnit->runtimeStrings[l->nameIndex]);
1161
1162 Q_ASSERT(id.isString());
1163
1164 QV4::ScopedString name(scope, id.asStringOrSymbol());
1165
1166 Q_ASSERT(!name->equals(scope.engine->id_toString()));
1167 Q_ASSERT(!name->equals(scope.engine->id_destroy()));
1168
1169 QQmlData *ddata = QQmlData::get(object, create: true);
1170 Q_ASSERT(ddata);
1171 if (ddata->isQueuedForDeletion)
1172 return ObjectLookupResult::Failure;
1173
1174 const QQmlPropertyData *property;
1175 if (!ddata->propertyCache) {
1176 property = QQmlPropertyCache::property(object, name, aotContext->qmlContext, nullptr);
1177 } else {
1178 property = ddata->propertyCache->property(
1179 key: name.getPointer(), object, context: aotContext->qmlContext);
1180 }
1181
1182 const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
1183 if (!property) {
1184 const QMetaObject *metaObject = object->metaObject();
1185 if (!metaObject)
1186 return ObjectLookupResult::Failure;
1187
1188 const int coreIndex = metaObject->indexOfProperty(
1189 name: name->toQStringNoThrow().toUtf8().constData());
1190 if (coreIndex < 0)
1191 return ObjectLookupResult::Failure;
1192
1193 const QMetaProperty property = metaObject->property(index: coreIndex);
1194 if (!doVariantLookup && !isTypeCompatible(lookupType: type, propertyType: property.metaType()))
1195 return ObjectLookupResult::Failure;
1196
1197 l->releasePropertyCache();
1198 // & 1 to tell the gc that this is not heap allocated; see markObjects in qv4lookup_p.h
1199 l->qobjectFallbackLookup.metaObject = quintptr(metaObject) + 1;
1200 l->qobjectFallbackLookup.coreIndex = coreIndex;
1201 l->qobjectFallbackLookup.notifyIndex =
1202 QMetaObjectPrivate::signalIndex(m: property.notifySignal());
1203 l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
1204 return doVariantLookup
1205 ? ObjectLookupResult::FallbackAsVariant
1206 : ObjectLookupResult::Fallback;
1207 }
1208
1209 if (!doVariantLookup && !isTypeCompatible(lookupType: type, propertyType: property->propType()))
1210 return ObjectLookupResult::Failure;
1211
1212 Q_ASSERT(ddata->propertyCache);
1213
1214 QV4::setupQObjectLookup(lookup: l, ddata, propertyData: property);
1215
1216 return doVariantLookup
1217 ? ObjectLookupResult::ObjectAsVariant
1218 : ObjectLookupResult::Object;
1219}
1220
1221static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
1222 const QMetaObject *metaObject, QMetaType type)
1223{
1224 Q_ASSERT(metaObject);
1225 const QByteArray name = compilationUnit->runtimeStrings[l->nameIndex]->toQString().toUtf8();
1226 const int coreIndex = metaObject->indexOfProperty(name: name.constData());
1227 QMetaType lookupType = metaObject->property(index: coreIndex).metaType();
1228 if (!isTypeCompatible(lookupType: type, propertyType: lookupType))
1229 return false;
1230 l->qgadgetLookup.metaObject = quintptr(metaObject) + 1;
1231 l->qgadgetLookup.coreIndex = coreIndex;
1232 l->qgadgetLookup.metaType = lookupType.iface();
1233 return true;
1234}
1235
1236static void amendException(QV4::ExecutionEngine *engine)
1237{
1238 const int missingLineNumber = engine->currentStackFrame->missingLineNumber();
1239 const int lineNumber = engine->currentStackFrame->lineNumber();
1240 Q_ASSERT(missingLineNumber != lineNumber);
1241
1242 auto amendStackTrace = [&](QV4::StackTrace *stackTrace) {
1243 for (auto it = stackTrace->begin(), end = stackTrace->end(); it != end; ++it) {
1244 if (it->line == missingLineNumber) {
1245 it->line = lineNumber;
1246 break;
1247 }
1248 }
1249 };
1250
1251 amendStackTrace(&engine->exceptionStackTrace);
1252
1253 QV4::Scope scope(engine);
1254 QV4::Scoped<QV4::ErrorObject> error(scope, *engine->exceptionValue);
1255 if (error) // else some other value was thrown
1256 amendStackTrace(error->d()->stackTrace);
1257}
1258
1259
1260bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
1261{
1262 if (!object)
1263 return false;
1264
1265 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1266 if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
1267 || l->getter == QV4::Lookup::getterQObject
1268 || l->getter == QV4::Lookup::getterQObjectAsVariant) {
1269 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
1270 QQmlData::flushPendingBinding(object, coreIndex: property->coreIndex());
1271 captureObjectProperty(object, propertyCache: l->qobjectLookup.propertyCache, property, qmlContext);
1272 return true;
1273 }
1274
1275 if (l->getter == QV4::Lookup::getterFallback
1276 || l->getter == QV4::Lookup::getterFallbackAsVariant) {
1277 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1278 QQmlData::flushPendingBinding(object, coreIndex);
1279 captureFallbackProperty(
1280 object, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
1281 isConstant: l->qobjectFallbackLookup.isConstant, qmlContext);
1282 return true;
1283 }
1284
1285 return false;
1286}
1287
1288bool AOTCompiledContext::captureQmlContextPropertyLookup(uint index) const
1289{
1290 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1291 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
1292 && l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty) {
1293 const QQmlPropertyData *property = l->qobjectLookup.propertyData;
1294 QQmlData::flushPendingBinding(object: qmlScopeObject, coreIndex: property->coreIndex());
1295 captureObjectProperty(object: qmlScopeObject, propertyCache: l->qobjectLookup.propertyCache, property, qmlContext);
1296 return true;
1297 }
1298
1299 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
1300 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1301 QQmlData::flushPendingBinding(object: qmlScopeObject, coreIndex);
1302 captureFallbackProperty(object: qmlScopeObject, coreIndex, notifyIndex: l->qobjectFallbackLookup.notifyIndex,
1303 isConstant: l->qobjectFallbackLookup.isConstant, qmlContext);
1304 return true;
1305 }
1306
1307 return false;
1308}
1309
1310void AOTCompiledContext::captureTranslation() const
1311{
1312 if (QQmlPropertyCapture *capture = propertyCapture(qmlContext))
1313 capture->captureTranslation();
1314}
1315
1316QString AOTCompiledContext::translationContext() const
1317{
1318#if QT_CONFIG(translation)
1319 return QV4::GlobalExtensions::currentTranslationContext(engine: engine->handle());
1320#else
1321 return QString();
1322#endif
1323}
1324
1325QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
1326{
1327 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1328 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty
1329 || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
1330 || l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
1331 || l->getter == QV4::Lookup::getterQObject
1332 || l->setter == QV4::Lookup::setterQObject
1333 || l->getter == QV4::Lookup::getterQObjectAsVariant
1334 || l->setter == QV4::Lookup::setterQObjectAsVariant) {
1335 return l->qobjectLookup.propertyData->propType();
1336 } else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
1337 return QMetaType(l->qgadgetLookup.metaType);
1338 } else if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue) {
1339 return QMetaType::fromType<int>();
1340 } else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
1341 || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
1342 || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
1343 || l->getter == QV4::QObjectWrapper::lookupAttached) {
1344 return QMetaType::fromType<QObject *>();
1345 } else if (l->getter == QV4::Lookup::getterFallback
1346 || l->setter == QV4::Lookup::setterFallback
1347 || l->getter == QV4::Lookup::getterFallbackAsVariant
1348 || l->setter == QV4::Lookup::setterFallbackAsVariant
1349 || l->qmlContextPropertyGetter
1350 == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
1351 const QMetaObject *metaObject
1352 = reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
1353 const int coreIndex = l->qobjectFallbackLookup.coreIndex;
1354 return metaObject->property(index: coreIndex).metaType();
1355 }
1356 return QMetaType();
1357}
1358
1359static bool isUndefined(const void *value, QMetaType type)
1360{
1361 if (type == QMetaType::fromType<QVariant>())
1362 return !static_cast<const QVariant *>(value)->isValid();
1363 if (type == QMetaType::fromType<QJSValue>())
1364 return static_cast<const QJSValue *>(value)->isUndefined();
1365 if (type == QMetaType::fromType<QJSPrimitiveValue>()) {
1366 return static_cast<const QJSPrimitiveValue *>(value)->type()
1367 == QJSPrimitiveValue::Undefined;
1368 }
1369 return false;
1370}
1371
1372void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType type) const
1373{
1374 // We don't really use any part of the lookup machinery here.
1375 // The QV4::Lookup is created on the stack to conveniently get the property cache, and through
1376 // the property cache we store a value into the property.
1377
1378 QV4::Lookup l;
1379 l.clear();
1380 l.nameIndex = nameIndex;
1381 l.forCall = false;
1382 ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
1383 switch (initObjectLookup(aotContext: this, l: &l, object: qmlScopeObject, type: QMetaType())) {
1384 case ObjectLookupResult::ObjectAsVariant:
1385 case ObjectLookupResult::Object: {
1386 const QMetaType propType = l.qobjectLookup.propertyData->propType();
1387 if (isTypeCompatible(lookupType: type, propertyType: propType)) {
1388 storeResult = storeObjectProperty(l: &l, object: qmlScopeObject, value);
1389 } else if (isUndefined(value, type)) {
1390 storeResult = resetObjectProperty(l: &l, object: qmlScopeObject);
1391 } else {
1392 QVariant var(propType);
1393 QV4::ExecutionEngine *v4 = engine->handle();
1394 v4->metaTypeFromJS(value: v4->metaTypeToJS(type, data: value), type: propType, data: var.data());
1395 storeResult = storeObjectProperty(l: &l, object: qmlScopeObject, value: var.data());
1396 }
1397
1398 l.qobjectLookup.propertyCache->release();
1399 break;
1400 }
1401 case ObjectLookupResult::FallbackAsVariant:
1402 case ObjectLookupResult::Fallback: {
1403 const QMetaObject *metaObject
1404 = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
1405 const QMetaType propType
1406 = metaObject->property(index: l.qobjectFallbackLookup.coreIndex).metaType();
1407 if (isTypeCompatible(lookupType: type, propertyType: propType)) {
1408 storeResult = storeFallbackProperty(l: &l, object: qmlScopeObject, value);
1409 } else if (isUndefined(value, type)) {
1410 storeResult = resetFallbackProperty(l: &l, object: qmlScopeObject);
1411 } else {
1412 QVariant var(propType);
1413 QV4::ExecutionEngine *v4 = engine->handle();
1414 v4->metaTypeFromJS(value: v4->metaTypeToJS(type, data: value), type: propType, data: var.data());
1415 storeResult = storeFallbackProperty(l: &l, object: qmlScopeObject, value: var.data());
1416 }
1417 break;
1418 }
1419 case ObjectLookupResult::Failure:
1420 engine->handle()->throwTypeError();
1421 return;
1422 }
1423
1424 switch (storeResult) {
1425 case ObjectPropertyResult::NeedsInit:
1426 engine->handle()->throwTypeError();
1427 break;
1428 case ObjectPropertyResult::Deleted:
1429 engine->handle()->throwTypeError(
1430 QStringLiteral("Value is null and could not be converted to an object"));
1431 break;
1432 case ObjectPropertyResult::OK:
1433 break;
1434 }
1435}
1436
1437QJSValue AOTCompiledContext::javaScriptGlobalProperty(uint nameIndex) const
1438{
1439 QV4::Scope scope(engine->handle());
1440 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[nameIndex]);
1441 QV4::ScopedObject global(scope, scope.engine->globalObject);
1442 return QJSValuePrivate::fromReturnedValue(d: global->get(id: name->toPropertyKey()));
1443}
1444
1445const QLoggingCategory *AOTCompiledContext::resolveLoggingCategory(QObject *wrapper, bool *ok) const
1446{
1447 if (wrapper) {
1448 // We have to check this here because you may pass a plain QObject that only
1449 // turns out to be a QQmlLoggingCategory at run time.
1450 if (QQmlLoggingCategory *qQmlLoggingCategory
1451 = qobject_cast<QQmlLoggingCategory *>(object: wrapper)) {
1452 QLoggingCategory *loggingCategory = qQmlLoggingCategory->category();
1453 *ok = true;
1454 if (!loggingCategory) {
1455 engine->handle()->throwError(
1456 QStringLiteral("A QmlLoggingCatgory was provided without a valid name"));
1457 }
1458 return loggingCategory;
1459 }
1460 }
1461
1462 *ok = false;
1463 return qmlEngine() ? &lcQml() : &lcJs();
1464}
1465
1466void AOTCompiledContext::writeToConsole(
1467 QtMsgType type, const QString &message, const QLoggingCategory *loggingCategory) const
1468{
1469 Q_ASSERT(loggingCategory->isEnabled(type));
1470
1471 const QV4::CppStackFrame *frame = engine->handle()->currentStackFrame;
1472 Q_ASSERT(frame);
1473
1474 const QByteArray source(frame->source().toUtf8());
1475 const QByteArray function(frame->function().toUtf8());
1476 QMessageLogger logger(source.constData(), frame->lineNumber(),
1477 function.constData(), loggingCategory->categoryName());
1478
1479 switch (type) {
1480 case QtDebugMsg:
1481 logger.debug(msg: "%s", qUtf8Printable(message));
1482 break;
1483 case QtInfoMsg:
1484 logger.info(msg: "%s", qUtf8Printable(message));
1485 break;
1486 case QtWarningMsg:
1487 logger.warning(msg: "%s", qUtf8Printable(message));
1488 break;
1489 case QtCriticalMsg:
1490 logger.critical(msg: "%s", qUtf8Printable(message));
1491 break;
1492 default:
1493 break;
1494 }
1495}
1496
1497QVariant AOTCompiledContext::constructValueType(
1498 QMetaType resultMetaType, const QMetaObject *resultMetaObject,
1499 int ctorIndex, void *ctorArg) const
1500{
1501 return QQmlValueTypeProvider::constructValueType(
1502 targetMetaType: resultMetaType, targetMetaObject: resultMetaObject, ctorIndex, ctorArg);
1503}
1504
1505bool AOTCompiledContext::callQmlContextPropertyLookup(
1506 uint index, void **args, const QMetaType *types, int argc) const
1507{
1508 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1509 QV4::Scope scope(engine->handle());
1510 QV4::ScopedValue thisObject(scope);
1511 QV4::ScopedFunctionObject function(
1512 scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
1513 if (!function) {
1514 scope.engine->throwTypeError(
1515 QStringLiteral("Property '%1' of object [null] is not a function").arg(
1516 a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
1517 return false;
1518 }
1519
1520 function->call(thisObject: qmlScopeObject, a: args, types, argc);
1521 return !scope.hasException();
1522}
1523
1524void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
1525{
1526 Q_UNUSED(index);
1527 Q_ASSERT(engine->hasError());
1528 amendException(engine: engine->handle());
1529}
1530
1531bool AOTCompiledContext::loadContextIdLookup(uint index, void *target) const
1532{
1533 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1534 int objectId = -1;
1535 QQmlContextData *context = nullptr;
1536 Q_ASSERT(qmlContext);
1537
1538 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject) {
1539 objectId = l->qmlContextIdObjectLookup.objectId;
1540 context = qmlContext;
1541 } else if (l->qmlContextPropertyGetter
1542 == QV4::QQmlContextWrapper::lookupIdObjectInParentContext) {
1543 QV4::Scope scope(engine->handle());
1544 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
1545 for (context = qmlContext; context; context = context->parent().data()) {
1546 objectId = context->propertyIndex(name);
1547 if (objectId != -1 && objectId < context->numIdValues())
1548 break;
1549 }
1550 } else {
1551 return false;
1552 }
1553
1554 Q_ASSERT(objectId >= 0);
1555 Q_ASSERT(context != nullptr);
1556 QQmlEnginePrivate *engine = QQmlEnginePrivate::get(e: qmlEngine());
1557 if (QQmlPropertyCapture *capture = engine->propertyCapture)
1558 capture->captureProperty(context->idValueBindings(index: objectId));
1559 *static_cast<QObject **>(target) = context->idValue(index: objectId);
1560 return true;
1561}
1562
1563void AOTCompiledContext::initLoadContextIdLookup(uint index) const
1564{
1565 Q_ASSERT(!engine->hasError());
1566 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1567 QV4::Scope scope(engine->handle());
1568 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
1569 const QQmlRefPointer<QQmlContextData> ownContext = qmlContext;
1570 for (auto context = ownContext; context; context = context->parent()) {
1571 const int propertyIdx = context->propertyIndex(name);
1572 if (propertyIdx == -1 || propertyIdx >= context->numIdValues())
1573 continue;
1574
1575 if (context.data() == ownContext.data()) {
1576 l->qmlContextIdObjectLookup.objectId = propertyIdx;
1577 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObject;
1578 } else {
1579 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupIdObjectInParentContext;
1580 }
1581
1582 return;
1583 }
1584
1585 Q_UNREACHABLE();
1586}
1587
1588bool AOTCompiledContext::callObjectPropertyLookup(
1589 uint index, QObject *object, void **args, const QMetaType *types, int argc) const
1590{
1591 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1592 QV4::Scope scope(engine->handle());
1593 QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object));
1594 QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
1595 if (!function) {
1596 scope.engine->throwTypeError(
1597 QStringLiteral("Property '%1' of object [object Object] is not a function")
1598 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
1599 return false;
1600 }
1601
1602 function->call(thisObject: object, a: args, types, argc);
1603 return !scope.hasException();
1604}
1605
1606void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
1607{
1608 Q_UNUSED(index);
1609 Q_ASSERT(engine->hasError());
1610 amendException(engine: engine->handle());
1611}
1612
1613bool AOTCompiledContext::callGlobalLookup(
1614 uint index, void **args, const QMetaType *types, int argc) const
1615{
1616 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1617 QV4::Scope scope(engine->handle());
1618 QV4::ScopedFunctionObject function(scope, l->globalGetter(l, scope.engine));
1619 if (!function) {
1620 scope.engine->throwTypeError(
1621 QStringLiteral("Property '%1' of object [null] is not a function")
1622 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
1623 return false;
1624 }
1625
1626 function->call(thisObject: nullptr, a: args, types, argc);
1627 return true;
1628}
1629
1630void AOTCompiledContext::initCallGlobalLookup(uint index) const
1631{
1632 Q_UNUSED(index);
1633 Q_ASSERT(engine->hasError());
1634 amendException(engine: engine->handle());
1635}
1636
1637bool AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const
1638{
1639 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1640 if (!QV4::ExecutionEngine::metaTypeFromJS(value: l->globalGetter(l, engine->handle()), type, data: target)) {
1641 engine->handle()->throwTypeError();
1642 return false;
1643 }
1644 return true;
1645}
1646
1647void AOTCompiledContext::initLoadGlobalLookup(uint index) const
1648{
1649 Q_UNUSED(index);
1650 Q_ASSERT(engine->hasError());
1651 amendException(engine: engine->handle());
1652}
1653
1654bool AOTCompiledContext::loadScopeObjectPropertyLookup(uint index, void *target) const
1655{
1656 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1657
1658 ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
1659 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
1660 result = loadObjectProperty(l, object: qmlScopeObject, target, qmlContext);
1661 else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeFallbackProperty)
1662 result = loadFallbackProperty(l, object: qmlScopeObject, target, qmlContext);
1663 else
1664 return false;
1665
1666 switch (result) {
1667 case ObjectPropertyResult::NeedsInit:
1668 return false;
1669 case ObjectPropertyResult::Deleted:
1670 engine->handle()->throwTypeError(
1671 QStringLiteral("Cannot read property '%1' of null")
1672 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
1673 return false;
1674 case ObjectPropertyResult::OK:
1675 return true;
1676 }
1677
1678 Q_UNREACHABLE_RETURN(false);
1679}
1680
1681void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType type) const
1682{
1683 QV4::ExecutionEngine *v4 = engine->handle();
1684 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1685
1686 if (v4->hasException) {
1687 amendException(engine: v4);
1688 return;
1689 }
1690
1691 switch (initObjectLookup(aotContext: this, l, object: qmlScopeObject, type)) {
1692 case ObjectLookupResult::ObjectAsVariant:
1693 case ObjectLookupResult::Object:
1694 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
1695 break;
1696 case ObjectLookupResult::FallbackAsVariant:
1697 case ObjectLookupResult::Fallback:
1698 l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
1699 break;
1700 case ObjectLookupResult::Failure:
1701 v4->throwTypeError();
1702 break;
1703 }
1704}
1705
1706bool AOTCompiledContext::loadSingletonLookup(uint index, void *target) const
1707{
1708 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1709 QV4::Scope scope(engine->handle());
1710
1711 if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton) {
1712 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
1713 scope, l->qmlContextSingletonLookup.singletonObject);
1714
1715 // We don't handle non-QObject singletons (as those can't be declared in qmltypes anyway)
1716 Q_ASSERT(wrapper);
1717 *static_cast<QObject **>(target) = wrapper->object();
1718 return true;
1719 }
1720
1721 return false;
1722}
1723
1724using QmlContextPropertyGetter
1725 = QV4::ReturnedValue (*)(QV4::Lookup *l, QV4::ExecutionEngine *engine, QV4::Value *thisObject);
1726
1727template<QmlContextPropertyGetter qmlContextPropertyGetter>
1728static void initTypeWrapperLookup(
1729 const AOTCompiledContext *context, QV4::Lookup *l, uint importNamespace)
1730{
1731 Q_ASSERT(!context->engine->hasError());
1732 if (importNamespace != AOTCompiledContext::InvalidStringId) {
1733 QV4::Scope scope(context->engine->handle());
1734 QV4::ScopedString import(scope, context->compilationUnit->runtimeStrings[importNamespace]);
1735 if (const QQmlImportRef *importRef
1736 = context->qmlContext->imports()->query(key: import).importNamespace) {
1737 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
1738 scope, QV4::QQmlTypeWrapper::create(
1739 scope.engine, nullptr, context->qmlContext->imports(), importRef));
1740 wrapper = l->qmlContextPropertyGetter(l, context->engine->handle(), wrapper);
1741 l->qmlContextPropertyGetter = qmlContextPropertyGetter;
1742 if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton)
1743 l->qmlContextSingletonLookup.singletonObject = wrapper->heapObject();
1744 else if (qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType)
1745 l->qmlTypeLookup.qmlTypeWrapper = wrapper->heapObject();
1746 return;
1747 }
1748 scope.engine->throwTypeError();
1749 } else {
1750 QV4::ExecutionEngine *v4 = context->engine->handle();
1751 l->qmlContextPropertyGetter(l, v4, nullptr);
1752 if (l->qmlContextPropertyGetter != qmlContextPropertyGetter) {
1753 const QString error
1754 = QLatin1String(qmlContextPropertyGetter
1755 == QV4::QQmlContextWrapper::lookupSingleton
1756 ? "%1 was a singleton at compile time, "
1757 "but is not a singleton anymore."
1758 : "%1 was not a singleton at compile time, "
1759 "but is a singleton now.")
1760 .arg(args: context->compilationUnit->runtimeStrings[l->nameIndex]->toQString());
1761 v4->throwTypeError(message: error);
1762 }
1763 }
1764}
1765
1766void AOTCompiledContext::initLoadSingletonLookup(uint index, uint importNamespace) const
1767{
1768 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1769 initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupSingleton>(context: this, l, importNamespace);
1770}
1771
1772bool AOTCompiledContext::loadAttachedLookup(uint index, QObject *object, void *target) const
1773{
1774 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1775 if (l->getter != QV4::QObjectWrapper::lookupAttached)
1776 return false;
1777
1778 QV4::Scope scope(engine->handle());
1779 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(scope, l->qmlTypeLookup.qmlTypeWrapper);
1780 Q_ASSERT(wrapper);
1781 *static_cast<QObject **>(target) = qmlAttachedPropertiesObject(
1782 object, func: wrapper->d()->type().attachedPropertiesFunction(
1783 engine: QQmlEnginePrivate::get(e: qmlEngine())));
1784 return true;
1785}
1786
1787void AOTCompiledContext::initLoadAttachedLookup(
1788 uint index, uint importNamespace, QObject *object) const
1789{
1790 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1791 QV4::Scope scope(engine->handle());
1792 QV4::ScopedString name(scope, compilationUnit->runtimeStrings[l->nameIndex]);
1793
1794 QQmlType type;
1795 if (importNamespace != InvalidStringId) {
1796 QV4::ScopedString import(scope, compilationUnit->runtimeStrings[importNamespace]);
1797 if (const QQmlImportRef *importRef = qmlContext->imports()->query(key: import).importNamespace)
1798 type = qmlContext->imports()->query(key: name, importNamespace: importRef).type;
1799 } else {
1800 type = qmlContext->imports()->query<QQmlImport::AllowRecursion>(key: name).type;
1801 }
1802
1803 if (!type.isValid()) {
1804 scope.engine->throwTypeError();
1805 return;
1806 }
1807
1808 QV4::Scoped<QV4::QQmlTypeWrapper> wrapper(
1809 scope, QV4::QQmlTypeWrapper::create(scope.engine, object, type,
1810 QV4::Heap::QQmlTypeWrapper::ExcludeEnums));
1811
1812 l->qmlTypeLookup.qmlTypeWrapper = wrapper->d();
1813 l->getter = QV4::QObjectWrapper::lookupAttached;
1814}
1815
1816bool AOTCompiledContext::loadTypeLookup(uint index, void *target) const
1817{
1818 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1819 if (l->qmlContextPropertyGetter != QV4::QQmlContextWrapper::lookupType)
1820 return false;
1821
1822 const QV4::Heap::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::Heap::QQmlTypeWrapper *>(
1823 l->qmlTypeLookup.qmlTypeWrapper);
1824 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(e: qmlEngine());
1825
1826 QMetaType metaType = typeWrapper->type().typeId();
1827 if (!metaType.isValid()) {
1828 metaType = ep->typeLoader.getType(unNormalizedUrl: typeWrapper->type().sourceUrl())
1829 ->compilationUnit()->typeIds.id;
1830 }
1831
1832 *static_cast<const QMetaObject **>(target)
1833 = QQmlMetaType::metaObjectForType(metaType).metaObject();
1834 return true;
1835}
1836
1837void AOTCompiledContext::initLoadTypeLookup(uint index, uint importNamespace) const
1838{
1839 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1840 initTypeWrapperLookup<QV4::QQmlContextWrapper::lookupType>(context: this, l, importNamespace);
1841}
1842
1843bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *target) const
1844{
1845
1846 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1847 const auto doThrow = [&]() {
1848 engine->handle()->throwTypeError(
1849 QStringLiteral("Cannot read property '%1' of null")
1850 .arg(a: compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
1851 return false;
1852 };
1853
1854 if (!object)
1855 return doThrow();
1856
1857 ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
1858 if (l->getter == QV4::Lookup::getterQObject)
1859 result = loadObjectProperty(l, object, target, qmlContext);
1860 else if (l->getter == QV4::Lookup::getterFallback)
1861 result = loadFallbackProperty(l, object, target, qmlContext);
1862 else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
1863 result = loadObjectAsVariant(l, object, target, qmlContext);
1864 else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
1865 result = loadFallbackAsVariant(l, object, target, qmlContext);
1866 else
1867 return false;
1868
1869 switch (result) {
1870 case ObjectPropertyResult::Deleted:
1871 return doThrow();
1872 case ObjectPropertyResult::NeedsInit:
1873 return false;
1874 case ObjectPropertyResult::OK:
1875 return true;
1876 }
1877
1878 Q_UNREACHABLE_RETURN(false);
1879}
1880
1881void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaType type) const
1882{
1883 QV4::ExecutionEngine *v4 = engine->handle();
1884 if (v4->hasException) {
1885 amendException(engine: v4);
1886 } else {
1887 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1888 switch (initObjectLookup(aotContext: this, l, object, type)) {
1889 case ObjectLookupResult::Object:
1890 l->getter = QV4::Lookup::getterQObject;
1891 break;
1892 case ObjectLookupResult::ObjectAsVariant:
1893 l->getter = QV4::Lookup::getterQObjectAsVariant;
1894 break;
1895 case ObjectLookupResult::Fallback:
1896 l->getter = QV4::Lookup::getterFallback;
1897 break;
1898 case ObjectLookupResult::FallbackAsVariant:
1899 l->getter = QV4::Lookup::getterFallbackAsVariant;
1900 break;
1901 case ObjectLookupResult::Failure:
1902 engine->handle()->throwTypeError();
1903 break;
1904 }
1905 }
1906}
1907
1908bool AOTCompiledContext::getValueLookup(uint index, void *value, void *target) const
1909{
1910 Q_ASSERT(value);
1911
1912 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1913 if (l->getter != QV4::QQmlValueTypeWrapper::lookupGetter)
1914 return false;
1915
1916 const QMetaObject *metaObject
1917 = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
1918 Q_ASSERT(metaObject);
1919
1920 void *args[] = { target, nullptr };
1921 metaObject->d.static_metacall(
1922 reinterpret_cast<QObject*>(value), QMetaObject::ReadProperty,
1923 l->qgadgetLookup.coreIndex, args);
1924 return true;
1925}
1926
1927void AOTCompiledContext::initGetValueLookup(
1928 uint index, const QMetaObject *metaObject, QMetaType type) const
1929{
1930 Q_ASSERT(!engine->hasError());
1931 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1932 if (initValueLookup(l, compilationUnit: compilationUnit, metaObject, type))
1933 l->getter = QV4::QQmlValueTypeWrapper::lookupGetter;
1934 else
1935 engine->handle()->throwTypeError();
1936}
1937
1938bool AOTCompiledContext::getEnumLookup(uint index, void *target) const
1939{
1940 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1941 if (l->getter != QV4::QQmlTypeWrapper::lookupEnumValue)
1942 return false;
1943 const bool isUnsigned
1944 = l->qmlEnumValueLookup.metaType->flags & QMetaType::IsUnsignedEnumeration;
1945 const QV4::ReturnedValue encoded = l->qmlEnumValueLookup.encodedEnumValue;
1946 switch (l->qmlEnumValueLookup.metaType->size) {
1947 case 1:
1948 if (isUnsigned)
1949 *static_cast<quint8 *>(target) = encoded;
1950 else
1951 *static_cast<qint8 *>(target) = encoded;
1952 return true;
1953 case 2:
1954 if (isUnsigned)
1955 *static_cast<quint16 *>(target) = encoded;
1956 else
1957 *static_cast<qint16 *>(target) = encoded;
1958 return true;
1959 case 4:
1960 if (isUnsigned)
1961 *static_cast<quint32 *>(target) = encoded;
1962 else
1963 *static_cast<qint32 *>(target) = encoded;
1964 return true;
1965 case 8:
1966 if (isUnsigned)
1967 *static_cast<quint64 *>(target) = encoded;
1968 else
1969 *static_cast<qint64 *>(target) = encoded;
1970 return true;
1971 default:
1972 break;
1973 }
1974
1975 return false;
1976}
1977
1978void AOTCompiledContext::initGetEnumLookup(
1979 uint index, const QMetaObject *metaObject,
1980 const char *enumerator, const char *enumValue) const
1981{
1982 Q_ASSERT(!engine->hasError());
1983 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
1984 if (!metaObject) {
1985 engine->handle()->throwTypeError(
1986 QStringLiteral("Cannot read property '%1' of undefined")
1987 .arg(a: QString::fromUtf8(utf8: enumValue)));
1988 return;
1989 }
1990 const int enumIndex = metaObject->indexOfEnumerator(name: enumerator);
1991 const QMetaEnum metaEnum = metaObject->enumerator(index: enumIndex);
1992 l->qmlEnumValueLookup.encodedEnumValue = metaEnum.keyToValue(key: enumValue);
1993 l->qmlEnumValueLookup.metaType = metaEnum.metaType().iface();
1994 l->getter = QV4::QQmlTypeWrapper::lookupEnumValue;
1995}
1996
1997bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *value) const
1998{
1999 const auto doThrow = [&]() {
2000 engine->handle()->throwTypeError(
2001 QStringLiteral("Value is null and could not be converted to an object"));
2002 return false;
2003 };
2004
2005 if (!object)
2006 return doThrow();
2007
2008 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2009 ObjectPropertyResult result = ObjectPropertyResult::NeedsInit;
2010 if (l->setter == QV4::Lookup::setterQObject)
2011 result = storeObjectProperty(l, object, value);
2012 else if (l->setter == QV4::Lookup::setterFallback)
2013 result = storeFallbackProperty(l, object, value);
2014 else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
2015 result = storeObjectAsVariant(v4: engine->handle(), l, object, value);
2016 else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
2017 result = storeFallbackAsVariant(v4: engine->handle(), l, object, value);
2018 else
2019 return false;
2020
2021 switch (result) {
2022 case ObjectPropertyResult::Deleted:
2023 return doThrow();
2024 case ObjectPropertyResult::NeedsInit:
2025 return false;
2026 case ObjectPropertyResult::OK:
2027 return true;
2028 }
2029
2030 Q_UNREACHABLE_RETURN(false);
2031}
2032
2033void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaType type) const
2034{
2035 QV4::ExecutionEngine *v4 = engine->handle();
2036 if (v4->hasException) {
2037 amendException(engine: v4);
2038 } else {
2039 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2040 switch (initObjectLookup(aotContext: this, l, object, type)) {
2041 case ObjectLookupResult::Object:
2042 l->setter = QV4::Lookup::setterQObject;
2043 break;
2044 case ObjectLookupResult::ObjectAsVariant:
2045 l->setter = QV4::Lookup::setterQObjectAsVariant;
2046 break;
2047 case ObjectLookupResult::Fallback:
2048 l->setter = QV4::Lookup::setterFallback;
2049 break;
2050 case ObjectLookupResult::FallbackAsVariant:
2051 l->setter = QV4::Lookup::setterFallbackAsVariant;
2052 break;
2053 case ObjectLookupResult::Failure:
2054 engine->handle()->throwTypeError();
2055 break;
2056 }
2057 }
2058}
2059
2060bool AOTCompiledContext::setValueLookup(
2061 uint index, void *target, void *value) const
2062{
2063 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2064 if (l->setter != QV4::QQmlValueTypeWrapper::lookupSetter)
2065 return false;
2066
2067 const QMetaObject *metaObject
2068 = reinterpret_cast<const QMetaObject *>(l->qgadgetLookup.metaObject - 1);
2069
2070 void *args[] = { value, nullptr };
2071 metaObject->d.static_metacall(
2072 reinterpret_cast<QObject*>(target), QMetaObject::WriteProperty,
2073 l->qgadgetLookup.coreIndex, args);
2074 return true;
2075}
2076
2077void AOTCompiledContext::initSetValueLookup(uint index, const QMetaObject *metaObject,
2078 QMetaType type) const
2079{
2080 Q_ASSERT(!engine->hasError());
2081 QV4::Lookup *l = compilationUnit->runtimeLookups + index;
2082 if (initValueLookup(l, compilationUnit: compilationUnit, metaObject, type))
2083 l->setter = QV4::QQmlValueTypeWrapper::lookupSetter;
2084 else
2085 engine->handle()->throwTypeError();
2086}
2087
2088} // namespace QQmlPrivate
2089
2090QT_END_NAMESPACE
2091

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