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();
255 for (; i < end; ++i) {
256 value = arrayData->get(i);
258 if (value->equals(scopedKey))
259 break;
260 }
261
262 if (i == end) {
265 }
266 } else {
268 }
269
270 return scope.engine->fromVariant(
271 *it, this, i,
273 });
274}
275
276} // namespace Heap
277
279 const Managed *that, PropertyKey id, const Value *, bool *hasProperty)
280{
282 = static_cast<const VariantAssociationObject *>(that)->d();
283
284 // If this is detached we rely on the element wrappers to hold the value
285 if (!heapAssociation->object())
287
288 bool found = false;
290 if (hasProperty)
291 *hasProperty = true;
292 return result;
293 }
294
296}
297
300{
302 = static_cast<VariantAssociationObject *>(that)->d();
303
304 if (!heapAssociation->object())
306
307 // Convert before dispatching on the association type. toVariant() reads the
308 // properties of the value, which runs any accessors it has, which can change
309 // the association object itself between QVariantMap and QVariantHash. We
310 // can't let that happen while we are holding a pointer to the the map or hash.
311 const QString key = id.toQString();
312 const QVariant converted
314
317 });
318
320 return true;
321}
322
324{
326 = static_cast<VariantAssociationObject *>(that)->d();
327
328 if (!heapAssociation->object())
330
331 const QString key = id.toQString();
333 return association->remove(key);
334 })) {
335 return false;
336 }
337
339 return true;
340}
341
343 const Object *m, Value *target)
344{
346 = static_cast<const VariantAssociationObject *>(m)->d();
347
348 if (!heapAssociation->object())
350
352 {
354
356
358 const Object *o, Property *pd = nullptr,
360 {
362 = static_cast<const VariantAssociationObject *>(o)->d();
363
364 if (memberIndex == 0) {
366 heapAssociation, [](const auto *association) {
367 return association->keys();
368 });
369 keys.sort();
370 }
371
372 if (static_cast<qsizetype>(memberIndex) < keys.count()) {
376
377 if (attrs)
378 *attrs = QV4::Attr_Data;
379 if (pd) {
380 bool found = false;
383 }
384
385 ++memberIndex;
386
387 return id;
388 }
389
390 return PropertyKey::invalid();
391 }
392 };
393
395
396 *target = *m;
398}
399
421
423 Object *object, QMetaObject::Call call, int index, void **a)
424{
426 = static_cast<VariantAssociationObject *>(object)->d();
427
428 // We only create attached wrappers if this variant association is itself attached.
429 // When detaching, we re-create everything. Therefore, we can't get a metaCall if
430 // we are detached.
432
434
437 if (index < 0 || uint(index) >= arrayData->length())
438 return 0;
439
441
442 switch (call) {
445
446 if (!visitVariantAssociation<bool>(heapAssociation, [&](const auto *association) {
447 const auto it = association->constFind(scopedKey->toQString());
448 if (it == association->constEnd())
449 return false;
450
451 *static_cast<QVariant *>(a[0]) = *it;
452 return true;
453 })) {
454 return 0;
455 }
456
457 break;
458 }
461 const auto it = association->find(scopedKey->toQString());
462 if (it == association->end())
463 return false;
464
465 *it = *static_cast<const QVariant *>(a[0]);
466 return true;
467 })) {
468 return 0;
469 }
470
472 break;
473 }
474 default:
475 return 0; // not supported
476 }
477
478 return -1;
479}
480
481} // namespace QV4
482
483QT_END_NAMESPACE
Combined button and popup list for selecting options.
VariantAssociationObject * createDetached(QV4::ExecutionEngine *engine, const Association &association)
Definition qjsvalue.h:24
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)