Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
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
5
#
include
"qv4variantassociationobject_p.h"
6
7
#
include
<
private
/
qqmlengine_p
.
h
>
8
9
QT_BEGIN_NAMESPACE
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
42
template
<
typename
Return,
typename
MapCallable,
typename
HashCallable>
43
Return 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
60
template
<
typename
Return,
typename
MapCallable,
typename
HashCallable>
61
Return
visitVariantAssociation
(
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
78
template
<
typename
Return,
typename
Callable>
79
Return
visitVariantAssociation
(
80
const
QV4::Heap::VariantAssociationObject *association, Callable &&callable)
81
{
82
return
visitVariantAssociation<Return>(association, callable, callable);
83
}
84
85
template
<
typename
Return,
typename
Callable>
86
Return
visitVariantAssociation
(
87
QV4::Heap::VariantAssociationObject *association, Callable &&callable)
88
{
89
return
visitVariantAssociation<Return>(association, callable, callable);
90
}
91
92
namespace
QV4
{
93
94
DEFINE_OBJECT_VTABLE
(
VariantAssociationObject
);
95
96
ReturnedValue
VariantAssociationPrototype
::
fromQVariantMap
(
97
ExecutionEngine
*
engine
,
const
QVariantMap
&
variantMap
,
QV4
::
Heap
::
Object
*
container
,
98
int
property
,
Heap
::
ReferenceObject
::
Flags
flags
)
99
{
100
return
engine
->
memoryManager
->
allocate
<
VariantAssociationObject
>(
101
variantMap
,
container
,
property
,
flags
)->
asReturnedValue
();
102
}
103
104
ReturnedValue
VariantAssociationPrototype
::
fromQVariantHash
(
105
ExecutionEngine
*
engine
,
const
QVariantHash
&
variantHash
,
QV4
::
Heap
::
Object
*
container
,
106
int
property
,
Heap
::
ReferenceObject
::
Flags
flags
)
107
{
108
return
engine
->
memoryManager
->
allocate
<
VariantAssociationObject
>(
109
variantHash
,
container
,
property
,
flags
)->
asReturnedValue
();
110
}
111
112
namespace
Heap
{
113
114
void
VariantAssociationObject
::
init
(
115
const
QVariantMap
&
variantMap
,
QV4
::
Heap
::
Object
*
container
,
int
property
,
116
Heap
::
ReferenceObject
::
Flags
flags
)
117
{
118
ReferenceObject
::
init
(
container
,
property
,
flags
);
119
m_type
=
AssociationType
::
VariantMap
;
120
121
if
(
container
)
122
new
(
m_variantAssociation
)
QVariantMap
(
variantMap
);
123
else
124
createElementWrappers
(
variantMap
);
125
}
126
127
void
VariantAssociationObject
::
init
(
128
const
QVariantHash
&
variantHash
,
QV4
::
Heap
::
Object
*
container
,
int
property
,
129
Heap
::
ReferenceObject
::
Flags
flags
)
130
{
131
ReferenceObject
::
init
(
container
,
property
,
flags
);
132
m_type
=
AssociationType
::
VariantHash
;
133
134
if
(
container
)
135
new
(
m_variantAssociation
)
QVariantHash
(
variantHash
);
136
else
137
createElementWrappers
(
variantHash
);
138
}
139
140
void
VariantAssociationObject
::
destroy
()
141
{
142
if
(
object
()) {
143
visitVariantAssociation
<
void
>(
144
this
,
std
::
destroy_at
<
QVariantMap
>,
std
::
destroy_at
<
QVariantHash
>);
145
}
146
ReferenceObject
::
destroy
();
147
}
148
149
QVariant
VariantAssociationObject
::
toVariant
()
150
{
151
if
(
object
()) {
152
return
visitVariantAssociation
<
QVariant
>(
153
this
, [](
const
auto
*
association
){
return
QVariant
(*
association
); });
154
}
155
156
QV4
::
Scope
scope
(
internalClass
->
engine
);
157
QV4
::
ScopedObject
self
(
scope
,
this
);
158
159
switch
(
m_type
) {
160
case
AssociationType
::
VariantMap
:
161
return
QV4
::
ExecutionEngine
::
variantMapFromJS
(
self
);
162
case
AssociationType
::
VariantHash
:
163
return
QV4
::
ExecutionEngine
::
variantHashFromJS
(
self
);
164
default
:
165
break
;
166
}
167
168
Q_UNREACHABLE_RETURN
(
QVariant
());
169
}
170
171
bool
VariantAssociationObject
::
setVariant
(
const
QVariant
&
variant
)
172
{
173
// Should only happen from readReference(). Therefore we are attached.
174
Q_ASSERT
(
object
());
175
176
auto
metatypeId
=
variant
.
metaType
().
id
();
177
178
if
(
metatypeId
!=
QMetaType
::
QVariantMap
&&
metatypeId
!=
QMetaType
::
QVariantHash
)
179
return
false
;
180
181
if
(
metatypeId
==
QMetaType
::
QVariantMap
&&
m_type
==
AssociationType
::
VariantMap
) {
182
*
reinterpret_cast
<
QVariantMap
*>(&
m_variantAssociation
) =
variant
.
toMap
();
183
}
else
if
(
metatypeId
==
QMetaType
::
QVariantMap
&&
m_type
==
AssociationType
::
VariantHash
) {
184
std
::
destroy_at
(
reinterpret_cast
<
QVariantHash
*>(&
m_variantAssociation
));
185
new
(
m_variantAssociation
)
QVariantMap
(
variant
.
toMap
());
186
m_type
=
AssociationType
::
VariantMap
;
187
}
else
if
(
metatypeId
==
QMetaType
::
QVariantHash
&&
m_type
==
AssociationType
::
VariantHash
) {
188
*
reinterpret_cast
<
QVariantHash
*>(&
m_variantAssociation
) =
variant
.
toHash
();
189
}
else
if
(
metatypeId
==
QMetaType
::
QVariantHash
&&
m_type
==
AssociationType
::
VariantMap
) {
190
std
::
destroy_at
(
reinterpret_cast
<
QVariantMap
*>(&
m_variantAssociation
));
191
new
(
m_variantAssociation
)
QVariantHash
(
variant
.
toHash
());
192
m_type
=
AssociationType
::
VariantHash
;
193
}
194
195
return
true
;
196
}
197
198
template
<
typename
Association>
199
VariantAssociationObject
*
createDetached
(
200
QV4::ExecutionEngine *engine,
const
Association &association)
201
{
202
return
engine->memoryManager->allocate<
QV4
::
VariantAssociationObject
>(
203
association,
nullptr
, -1, ReferenceObject::Flag::NoFlag);
204
}
205
206
VariantAssociationObject
*
VariantAssociationObject
::
detached
()
207
{
208
if
(
object
()) {
209
return
visitVariantAssociation
<
VariantAssociationObject
*>(
210
this
, [
this
](
const
auto
*
association
) {
211
return
createDetached
(
internalClass
->
engine
, *
association
);
212
});
213
}
214
215
QV4
::
Scope
scope
(
internalClass
->
engine
);
216
QV4
::
ScopedObject
self
(
scope
,
this
);
217
218
switch
(
m_type
) {
219
case
AssociationType
::
VariantMap
:
220
return
createDetached
(
scope
.
engine
,
QV4
::
ExecutionEngine
::
variantMapFromJS
(
self
));
221
case
AssociationType
::
VariantHash
:
222
return
createDetached
(
scope
.
engine
,
QV4
::
ExecutionEngine
::
variantHashFromJS
(
self
));
223
default
:
224
break
;
225
}
226
227
Q_UNREACHABLE_RETURN
(
nullptr
);
228
}
229
230
QV4
::
ReturnedValue
VariantAssociationObject
::
getElement
(
const
QString
&
key
,
bool
*
hasProperty
)
231
{
232
// Must be attached. Otherwise we should use memberData/arrayData
233
Q_ASSERT
(
object
());
234
Q_ASSERT
(
hasProperty
);
235
236
QV4
::
ReferenceObject
::
readReference
(
this
);
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
247
Scope
scope
(
internalClass
->
engine
);
248
ScopedString
scopedKey
(
scope
,
scope
.
engine
->
newString
(
key
));
249
250
uint
i
= 0;
251
if
(
propertyIndexMapping
) {
252
Scoped
<
QV4
::
ArrayData
>
arrayData
(
scope
,
propertyIndexMapping
->
arrayData
);
253
const
uint
end
=
arrayData
->
length
();
254
QV4
::
ScopedString
value
(
scope
);
255
for
(;
i
<
end
; ++
i
) {
256
value
=
arrayData
->
get
(
i
);
257
Q_ASSERT
(
value
);
258
if
(
value
->
equals
(
scopedKey
))
259
break
;
260
}
261
262
if
(
i
==
end
) {
263
ScopedArrayObject
mapping
(
scope
,
propertyIndexMapping
);
264
mapping
->
push_back
(
scopedKey
);
265
}
266
}
else
{
267
propertyIndexMapping
.
set
(
scope
.
engine
,
scope
.
engine
->
newArrayObject
(
scopedKey
, 1));
268
}
269
270
return
scope
.
engine
->
fromVariant
(
271
*
it
,
this
,
i
,
272
ReferenceObject
::
Flag
::
CanWriteBack
|
ReferenceObject
::
Flag
::
IsVariant
);
273
});
274
}
275
276
}
// namespace Heap
277
278
ReturnedValue
VariantAssociationObject
::
virtualGet
(
279
const
Managed
*
that
,
PropertyKey
id
,
const
Value
*,
bool
*
hasProperty
)
280
{
281
Heap
::
VariantAssociationObject
*
heapAssociation
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
())
286
return
ReferenceObject
::
virtualGet
(
that
,
id
,
that
,
hasProperty
);
287
288
bool
found
=
false
;
289
if
(
ReturnedValue
result
=
heapAssociation
->
getElement
(
id
.
toQString
(), &
found
);
found
) {
290
if
(
hasProperty
)
291
*
hasProperty
=
true
;
292
return
result
;
293
}
294
295
return
ReferenceObject
::
virtualGet
(
that
,
id
,
that
,
hasProperty
);
296
}
297
298
bool
VariantAssociationObject
::
virtualPut
(
299
Managed
*
that
,
PropertyKey
id
,
const
Value
&
value
,
Value
*)
300
{
301
Heap
::
VariantAssociationObject
*
heapAssociation
302
=
static_cast
<
VariantAssociationObject
*>(
that
)->
d
();
303
304
if
(!
heapAssociation
->
object
())
305
return
ReferenceObject
::
virtualPut
(
that
,
id
,
value
,
that
);
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
313
=
heapAssociation
->
internalClass
->
engine
->
toVariant
(
value
,
QMetaType
(),
false
);
314
315
visitVariantAssociation
<
void
>(
heapAssociation
, [&](
auto
*
association
) {
316
association
->
insert
(
key
,
converted
);
317
});
318
319
QV4
::
ReferenceObject
::
writeBack
(
heapAssociation
);
320
return
true
;
321
}
322
323
bool
VariantAssociationObject
::
virtualDeleteProperty
(
Managed
*
that
,
PropertyKey
id
)
324
{
325
Heap
::
VariantAssociationObject
*
heapAssociation
326
=
static_cast
<
VariantAssociationObject
*>(
that
)->
d
();
327
328
if
(!
heapAssociation
->
object
())
329
return
ReferenceObject
::
virtualDeleteProperty
(
that
,
id
);
330
331
const
QString
key
=
id
.
toQString
();
332
if
(!
visitVariantAssociation
<
bool
>(
heapAssociation
, [&](
auto
*
association
) {
333
return
association
->
remove
(
key
);
334
})) {
335
return
false
;
336
}
337
338
QV4
::
ReferenceObject
::
writeBack
(
heapAssociation
);
339
return
true
;
340
}
341
342
OwnPropertyKeyIterator
*
VariantAssociationObject
::
virtualOwnPropertyKeys
(
343
const
Object
*
m
,
Value
*
target
)
344
{
345
Heap
::
VariantAssociationObject
*
heapAssociation
346
=
static_cast
<
const
VariantAssociationObject
*>(
m
)->
d
();
347
348
if
(!
heapAssociation
->
object
())
349
return
ReferenceObject
::
virtualOwnPropertyKeys
(
m
,
target
);
350
351
struct
VariantAssociationOwnPropertyKeyIterator
:
ObjectOwnPropertyKeyIterator
352
{
353
QStringList
keys
;
354
355
~
VariantAssociationOwnPropertyKeyIterator
()
override
=
default
;
356
357
PropertyKey
next
(
358
const
Object
*
o
,
Property
*
pd
=
nullptr
,
359
PropertyAttributes
*
attrs
=
nullptr
)
override
360
{
361
Heap
::
VariantAssociationObject
*
heapAssociation
362
=
static_cast
<
const
VariantAssociationObject
*>(
o
)->
d
();
363
364
if
(
memberIndex
== 0) {
365
keys
=
visitVariantAssociation
<
QStringList
>(
366
heapAssociation
, [](
const
auto
*
association
) {
367
return
association
->
keys
();
368
});
369
keys
.
sort
();
370
}
371
372
if
(
static_cast
<
qsizetype
>(
memberIndex
) <
keys
.
count
()) {
373
Scope
scope
(
heapAssociation
->
internalClass
->
engine
);
374
ScopedString
propertyName
(
scope
,
scope
.
engine
->
newString
(
keys
[
memberIndex
]));
375
ScopedPropertyKey
id
(
scope
,
propertyName
->
toPropertyKey
());
376
377
if
(
attrs
)
378
*
attrs
=
QV4
::
Attr_Data
;
379
if
(
pd
) {
380
bool
found
=
false
;
381
pd
->
value
=
heapAssociation
->
getElement
(
keys
[
memberIndex
], &
found
);
382
Q_ASSERT
(
found
);
383
}
384
385
++
memberIndex
;
386
387
return
id
;
388
}
389
390
return
PropertyKey
::
invalid
();
391
}
392
};
393
394
QV4
::
ReferenceObject
::
readReference
(
heapAssociation
);
395
396
*
target
= *
m
;
397
return
new
VariantAssociationOwnPropertyKeyIterator
;
398
}
399
400
PropertyAttributes
VariantAssociationObject
::
virtualGetOwnProperty
(
401
const
Managed
*
m
,
PropertyKey
id
,
Property
*
p
)
402
{
403
Heap
::
VariantAssociationObject
*
heapAssociation
404
=
static_cast
<
const
VariantAssociationObject
*>(
m
)->
d
();
405
406
if
(!
heapAssociation
->
object
())
407
return
ReferenceObject
::
virtualGetOwnProperty
(
m
,
id
,
p
);
408
409
bool
hasElement
=
false
;
410
Scope
scope
(
heapAssociation
->
internalClass
->
engine
);
411
ScopedValue
element
(
scope
,
heapAssociation
->
getElement
(
id
.
toQString
(), &
hasElement
));
412
413
if
(!
hasElement
)
414
return
Attr_Invalid
;
415
416
if
(
p
)
417
p
->
value
=
element
->
asReturnedValue
();
418
419
return
Attr_Data
;
420
}
421
422
int
VariantAssociationObject
::
virtualMetacall
(
423
Object
*
object
,
QMetaObject
::
Call
call
,
int
index
,
void
**
a
)
424
{
425
Heap
::
VariantAssociationObject
*
heapAssociation
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.
431
Q_ASSERT
(
heapAssociation
->
object
());
432
433
Q_ASSERT
(
heapAssociation
->
propertyIndexMapping
);
434
435
Scope
scope
(
heapAssociation
->
internalClass
->
engine
);
436
Scoped
<
QV4
::
ArrayData
>
arrayData
(
scope
,
heapAssociation
->
propertyIndexMapping
->
arrayData
);
437
if
(
index
< 0 ||
uint
(
index
) >=
arrayData
->
length
())
438
return
0;
439
440
ScopedString
scopedKey
(
scope
,
arrayData
->
get
(
index
));
441
442
switch
(
call
) {
443
case
QMetaObject
::
ReadProperty
: {
444
QV4
::
ReferenceObject
::
readReference
(
heapAssociation
);
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
}
459
case
QMetaObject
::
WriteProperty
: {
460
if
(!
visitVariantAssociation
<
bool
>(
heapAssociation
, [&](
auto
*
association
) {
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
471
QV4
::
ReferenceObject
::
writeBack
(
heapAssociation
);
472
break
;
473
}
474
default
:
475
return
0;
// not supported
476
}
477
478
return
-1;
479
}
480
481
}
// namespace QV4
482
483
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
QV4::Heap
Definition
qv4compileddata_p.h:76
QV4::Heap::createDetached
VariantAssociationObject * createDetached(QV4::ExecutionEngine *engine, const Association &association)
Definition
qv4variantassociationobject.cpp:199
QV4
Definition
qjsvalue.h:24
visitVariantAssociation
Return visitVariantAssociation(const QV4::Heap::VariantAssociationObject *association, Callable &&callable)
Definition
qv4variantassociationobject.cpp:79
visitVariantAssociation
Return visitVariantAssociation(QV4::Heap::VariantAssociationObject *association, MapCallable &&mapCallable, HashCallable &&hashCallable)
Definition
qv4variantassociationobject.cpp:61
visitVariantAssociation
Return visitVariantAssociation(QV4::Heap::VariantAssociationObject *association, Callable &&callable)
Definition
qv4variantassociationobject.cpp:86
QV4::VariantAssociationObject
Definition
qv4variantassociationobject_p.h:107
qtdeclarative
src
qml
jsruntime
qv4variantassociationobject.cpp
Generated on
for Qt by
1.18.0