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