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 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | Q_DECLARE_LOGGING_CATEGORY(lcQml); |
30 | Q_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 | */ |
48 | void 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 | |
71 | QQmlContext *qmlContext(const QObject *obj) |
72 | { |
73 | return QQmlEngine::contextForObject(obj); |
74 | } |
75 | |
76 | QQmlEngine *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 | |
84 | static 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 | |
102 | QQmlAttachedPropertiesFunc 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 | |
110 | QObject *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 | |
125 | QObject *qmlExtendedObject(QObject *object) |
126 | { |
127 | return QQmlPrivate::qmlExtendedObject(object, 0); |
128 | } |
129 | |
130 | QObject *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 | |
149 | void 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 | |
174 | int 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 | |
209 | void 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 |
217 | bool 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 |
224 | void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor) |
225 | { |
226 | QQmlMetaType::registerModule(uri, version: QTypeRevision::fromVersion(majorVersion: versionMajor, minorVersion: versionMinor)); |
227 | } |
228 | |
229 | static 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 | |
240 | static 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 | */ |
307 | void 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 | */ |
327 | void 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 |
336 | int 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 | |
341 | static 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) |
364 | QObject *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 | |
383 | QObject *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 | |
401 | static 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 | |
430 | template<typename Registration> |
431 | void 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 | |
442 | static QVector<QTypeRevision> prepareRevisions(const QMetaObject *metaObject, QTypeRevision added) |
443 | { |
444 | auto revisions = availableRevisions(metaObject); |
445 | revisions.append(t: added); |
446 | return revisions; |
447 | } |
448 | |
449 | static 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 | /* |
479 | This method is "over generalized" to allow us to (potentially) register more types of things in |
480 | the future without adding exported symbols. |
481 | */ |
482 | int 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 | |
714 | void 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 | |
746 | QList<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 | |
758 | int 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 | |
766 | namespace QQmlPrivate { |
767 | template<> |
768 | void 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 | |
808 | QObject *AOTCompiledContext::thisObject() const |
809 | { |
810 | return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame) |
811 | ->thisObject(); |
812 | } |
813 | |
814 | QQmlEngine *AOTCompiledContext::qmlEngine() const |
815 | { |
816 | return qmlContext ? qmlContext->engine() : nullptr; |
817 | } |
818 | |
819 | QJSValue AOTCompiledContext::jsMetaType(int index) const |
820 | { |
821 | return QJSValuePrivate::fromReturnedValue( |
822 | d: compilationUnit->runtimeClasses[index]->asReturnedValue()); |
823 | } |
824 | |
825 | void AOTCompiledContext::setInstructionPointer(int offset) const |
826 | { |
827 | if (auto *frame = engine->handle()->currentStackFrame) |
828 | frame->instructionPointer = offset; |
829 | } |
830 | |
831 | void 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 | |
839 | static 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 | |
851 | static 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 | |
862 | static 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 | |
873 | static 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 | |
882 | enum class ObjectPropertyResult { OK, NeedsInit, Deleted }; |
883 | |
884 | template<bool StrictType = false> |
885 | ObjectPropertyResult 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 | |
912 | static 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 | |
939 | ObjectPropertyResult 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 | |
951 | ObjectPropertyResult 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 | |
967 | template<bool StrictType, typename Op> |
968 | static 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 | |
988 | template<bool StrictType = false> |
989 | static 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 | |
996 | template<bool StrictType = false> |
997 | static 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 | |
1004 | template<typename Op> |
1005 | static 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 | |
1024 | static 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 | |
1032 | static 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 | |
1040 | static 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 | |
1104 | static 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 | |
1123 | static 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 | |
1147 | enum class ObjectLookupResult { |
1148 | Failure, |
1149 | Object, |
1150 | Fallback, |
1151 | ObjectAsVariant, |
1152 | FallbackAsVariant, |
1153 | }; |
1154 | |
1155 | static 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 | |
1221 | static 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 | |
1236 | static 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 | |
1260 | bool 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 | |
1288 | bool 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 | |
1310 | void AOTCompiledContext::captureTranslation() const |
1311 | { |
1312 | if (QQmlPropertyCapture *capture = propertyCapture(qmlContext)) |
1313 | capture->captureTranslation(); |
1314 | } |
1315 | |
1316 | QString 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 | |
1325 | QMetaType 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 | |
1359 | static 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 | |
1372 | void 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 | |
1437 | QJSValue 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 | |
1445 | const 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 | |
1466 | void 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 | |
1497 | QVariant 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 | |
1505 | bool 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 | |
1524 | void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const |
1525 | { |
1526 | Q_UNUSED(index); |
1527 | Q_ASSERT(engine->hasError()); |
1528 | amendException(engine: engine->handle()); |
1529 | } |
1530 | |
1531 | bool 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 | |
1563 | void 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 | |
1588 | bool 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 | |
1606 | void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const |
1607 | { |
1608 | Q_UNUSED(index); |
1609 | Q_ASSERT(engine->hasError()); |
1610 | amendException(engine: engine->handle()); |
1611 | } |
1612 | |
1613 | bool 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 | |
1630 | void AOTCompiledContext::initCallGlobalLookup(uint index) const |
1631 | { |
1632 | Q_UNUSED(index); |
1633 | Q_ASSERT(engine->hasError()); |
1634 | amendException(engine: engine->handle()); |
1635 | } |
1636 | |
1637 | bool 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 | |
1647 | void AOTCompiledContext::initLoadGlobalLookup(uint index) const |
1648 | { |
1649 | Q_UNUSED(index); |
1650 | Q_ASSERT(engine->hasError()); |
1651 | amendException(engine: engine->handle()); |
1652 | } |
1653 | |
1654 | bool 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 | |
1681 | void 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 | |
1706 | bool 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 | |
1724 | using QmlContextPropertyGetter |
1725 | = QV4::ReturnedValue (*)(QV4::Lookup *l, QV4::ExecutionEngine *engine, QV4::Value *thisObject); |
1726 | |
1727 | template<QmlContextPropertyGetter qmlContextPropertyGetter> |
1728 | static 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 | |
1766 | void 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 | |
1772 | bool 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 | |
1787 | void 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 | |
1816 | bool 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 | |
1837 | void 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 | |
1843 | bool 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 | |
1881 | void 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 | |
1908 | bool 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 | |
1927 | void 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 | |
1938 | bool 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 | |
1978 | void 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 | |
1997 | bool 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 | |
2033 | void 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 | |
2060 | bool 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 | |
2077 | void 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 | |
2090 | QT_END_NAMESPACE |
2091 | |