Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4variantassociationobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
6
7#include <private/qqmlengine_p.h>
8
10
11/*!
12 * \class QV4::VariantAssociationObject
13 * \internal
14 *
15 * A VariantAssociationObject stores the contents of a QVariantMap or QVariantHash
16 * and makes them acccessible to the JavaScript engine. It behaves mostly like a
17 * regular JavaScript object. The entries of the QVariantMap or QVariantHash are
18 * exposed as properties.
19 *
20 * VariantAssociationObject is a ReferenceObject. Therefore it writes back its contents
21 * to the property it was retrieved from whenever it changes. It also re-reads
22 * the property whenever that one changes.
23 *
24 * As long as a VariantAssociationObject is attached to a property this way, it is the
25 * responsibility of the property's surrounding (C++) object to keep the contents valid.
26 * It has to, for example, track pointers to QObjects potentially deleted in other places
27 * so that they don't become dangling.
28 *
29 * However, the VariantAssociation can also be detached. This happens predominantly by
30 * assigning it to a QML-declared property. In that case, it becomes the
31 * VariantAssociationObject's responsibility to track its contents. To do so, it does not
32 * keep an actual QVariantMap or QVariantHash in this case, but rather stores its contents
33 * as actual JavaScript object properties. This includes QObjectWrappers for all QObject
34 * pointers it may contain. The contents are then marked like all JavaScript properties
35 * when the garbage collector runs, and QObjectWrapper also guards against external
36 * deletion. There is no property to read or write back in this case, and neither
37 * does the internal QVariantMap or QVariantHash need to be updated. Therefore, the
38 * objects stored in the individual properties are also created detached and won't
39 * read or write back.
40 */
41
42template<typename Return, typename MapCallable, typename HashCallable>
43Return visitVariantAssociation(
44 const QV4::Heap::VariantAssociationObject *association,
45 MapCallable &&mapCallable, HashCallable &&hashCallable)
46{
47 switch (association->m_type) {
48 case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
49 return std::invoke(
50 std::forward<MapCallable>(mapCallable),
51 reinterpret_cast<const QVariantMap *>(&association->m_variantAssociation));
52 case QV4::Heap::VariantAssociationObject::AssociationType::VariantHash:
53 return std::invoke(
54 std::forward<HashCallable>(hashCallable),
55 reinterpret_cast<const QVariantHash *>(&association->m_variantAssociation));
56 default: Q_UNREACHABLE();
57 };
58}
59
60template<typename Return, typename MapCallable, typename HashCallable>
62 QV4::Heap::VariantAssociationObject *association,
63 MapCallable &&mapCallable, HashCallable &&hashCallable)
64{
65 switch (association->m_type) {
66 case QV4::Heap::VariantAssociationObject::AssociationType::VariantMap:
67 return std::invoke(
68 std::forward<MapCallable>(mapCallable),
69 reinterpret_cast<QVariantMap *>(&association->m_variantAssociation));
70 case QV4::Heap::VariantAssociationObject::AssociationType::VariantHash:
71 return std::invoke(
72 std::forward<HashCallable>(hashCallable),
73 reinterpret_cast<QVariantHash *>(&association->m_variantAssociation));
74 default: Q_UNREACHABLE();
75 };
76}
77
78template<typename Return, typename Callable>
80 const QV4::Heap::VariantAssociationObject *association, Callable &&callable)
81{
82 return visitVariantAssociation<Return>(association, callable, callable);
83}
84
85template<typename Return, typename Callable>
87 QV4::Heap::VariantAssociationObject *association, Callable &&callable)
88{
89 return visitVariantAssociation<Return>(association, callable, callable);
90}
91
92namespace QV4 {
93
95
103
111
112namespace Heap {
113
117{
120
121 if (container)
123 else
125}
126
130{
133
134 if (container)
136 else
138}
139
141{
142 if (object()) {
145 }
147}
148
150{
151 if (object()) {
153 this, [](const auto *association){ return QVariant(*association); });
154 }
155
157 QV4::ScopedObject self(scope, this);
158
159 switch (m_type) {
164 default:
165 break;
166 }
167
169}
170
172{
173 // Should only happen from readReference(). Therefore we are attached.
174 Q_ASSERT(object());
175
176 auto metatypeId = variant.metaType().id();
177
179 return false;
180
182 *reinterpret_cast<QVariantMap *>(&m_variantAssociation) = variant.toMap();
184 std::destroy_at(reinterpret_cast<QVariantHash *>(&m_variantAssociation));
188 *reinterpret_cast<QVariantHash *>(&m_variantAssociation) = variant.toHash();
190 std::destroy_at(reinterpret_cast<QVariantMap *>(&m_variantAssociation));
193 }
194
195 return true;
196}
197
198template<typename Association>
200 QV4::ExecutionEngine *engine, const Association &association)
201{
202 return engine->memoryManager->allocate<QV4::VariantAssociationObject>(
203 association, nullptr, -1, ReferenceObject::Flag::NoFlag);
204}
205
207{
208 if (object()) {
210 this, [this](const auto *association) {
212 });
213 }
214
216 QV4::ScopedObject self(scope, this);
217
218 switch (m_type) {
223 default:
224 break;
225 }
226
227 Q_UNREACHABLE_RETURN(nullptr);
228}
229
231{
232 // Must be attached. Otherwise we should use memberData/arrayData
233 Q_ASSERT(object());
235
237
238 return visitVariantAssociation<QV4::ReturnedValue>(this, [&](const auto *association) {
239 auto it = association->constFind(key);
240 if (it == association->constEnd()) {
241 *hasProperty = false;
242 return Encode::undefined();
243 }
244
245 *hasProperty = true;
246
249
250 uint i = 0;
253 const uint end = arrayData->length();
254 for (; i < end; ++i) {
257 if (value->equals(scopedKey))
258 break;
259 }
260
261 if (i == end) {
264 }
265 } else {
267 }
268
269 return scope.engine->fromVariant(
270 *it, this, i,
272 });
273}
274
275} // namespace Heap
276
278 const Managed *that, PropertyKey id, const Value *, bool *hasProperty)
279{
281 = static_cast<const VariantAssociationObject *>(that)->d();
282
283 // If this is detached we rely on the element wrappers to hold the value
284 if (!heapAssociation->object())
286
287 bool found = false;
289 if (hasProperty)
290 *hasProperty = true;
291 return result;
292 }
293
295}
296
315
317{
319 = static_cast<VariantAssociationObject *>(that)->d();
320
321 if (!heapAssociation->object())
323
325 return association->remove(id.toQString());
326 })) {
327 return false;
328 }
329
331 return true;
332}
333
335 const Object *m, Value *target)
336{
338 = static_cast<const VariantAssociationObject *>(m)->d();
339
340 if (!heapAssociation->object())
342
344 {
346
348
350 const Object *o, Property *pd = nullptr,
352 {
354 = static_cast<const VariantAssociationObject *>(o)->d();
355
356 if (memberIndex == 0) {
358 heapAssociation, [](const auto *association) {
359 return association->keys();
360 });
361 keys.sort();
362 }
363
364 if (static_cast<qsizetype>(memberIndex) < keys.count()) {
368
369 if (attrs)
370 *attrs = QV4::Attr_Data;
371 if (pd) {
372 bool found = false;
375 }
376
377 ++memberIndex;
378
379 return id;
380 }
381
382 return PropertyKey::invalid();
383 }
384 };
385
387
388 *target = *m;
390}
391
413
415 Object *object, QMetaObject::Call call, int index, void **a)
416{
418 = static_cast<VariantAssociationObject *>(object)->d();
419
420 // We only create attached wrappers if this variant association is itself attached.
421 // When detaching, we re-create everything. Therefore, we can't get a metaCall if
422 // we are detached.
424
426
429 if (index < 0 || uint(index) >= arrayData->length())
430 return 0;
431
433
434 switch (call) {
437
438 if (!visitVariantAssociation<bool>(heapAssociation, [&](const auto *association) {
439 const auto it = association->constFind(scopedKey->toQString());
440 if (it == association->constEnd())
441 return false;
442
443 *static_cast<QVariant *>(a[0]) = *it;
444 return true;
445 })) {
446 return 0;
447 }
448
449 break;
450 }
453 const auto it = association->find(scopedKey->toQString());
454 if (it == association->end())
455 return false;
456
457 *it = *static_cast<const QVariant *>(a[0]);
458 return true;
459 })) {
460 return 0;
461 }
462
464 break;
465 }
466 default:
467 return 0; // not supported
468 }
469
470 return -1;
471}
472
473} // namespace QV4
474
475QT_END_NAMESPACE
VariantAssociationObject * createDetached(QV4::ExecutionEngine *engine, const Association &association)
Definition qjsvalue.h:23
Return visitVariantAssociation(const QV4::Heap::VariantAssociationObject *association, Callable &&callable)
Return visitVariantAssociation(QV4::Heap::VariantAssociationObject *association, MapCallable &&mapCallable, HashCallable &&hashCallable)
Return visitVariantAssociation(QV4::Heap::VariantAssociationObject *association, Callable &&callable)