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
qv4sequenceobject.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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 <QtCore/qsequentialiterable.h>
6
8
9#include <private/qv4functionobject_p.h>
10#include <private/qv4arrayobject_p.h>
11#include <private/qqmlengine_p.h>
12#include <private/qv4scopedvalue_p.h>
13#include <private/qv4jscall_p.h>
14#include <private/qqmlmetatype_p.h>
15#include <private/qqmltype_p_p.h>
16#include <private/qqmlvaluetypewrapper_p.h>
17
19
20/*!
21 * \class QV4::Sequence
22 * \internal
23 *
24 * A Sequence stores the contents of a sequential container and makes them acccessible
25 * to the JavaScript engine. It behaves mostly like a regular JavaScript array. The
26 * entries of the container are exposed as array elements.
27 *
28 * Sequence is a ReferenceObject. Therefore it writes back its contents to the property
29 * it was retrieved from whenever it changes. It also re-reads the property whenever
30 * that one changes.
31 *
32 * As long as a Sequence is attached to a property this way, it is the responsibility of
33 * the property's surrounding (C++) object to keep the contents valid. It has to, for
34 * example, track pointers to QObjects potentially deleted in other places so that they
35 * don't become dangling.
36 *
37 * However, the Sequence can also be detached. This happens predominantly by assigning
38 * it to a QML-declared property. In that case, it becomes the Sequence's responsibility
39 * to track its contents. To do so, it does not necessarily keep an actual instance of
40 * the original container in this case, but may rather stores its contents as actual
41 * JavaScript array elements. This includes QObjectWrappers for all QObject pointers it
42 * may contain. The contents are then marked like all JavaScript array elements when the
43 * garbage collector runs, and QObjectWrapper also guards against external deletion.
44 * There is no property to read or write back in this case, and neither does the
45 * internal container need to be updated. Therefore, the objects stored as array elements
46 * are created detached and won't read or write back.
47 *
48 * For element types that don't need to be marked, Sequence will still use the original
49 * container for storage, even in the detached case. This is usually more efficient
50 * because because it saves some data conversion. The only types that need to be marked
51 * are pointers to QObject-derived types, either stored as-is or hidden inside QVariant.
52 * Whenever the container cannot possibly hold such elements (directly or indirectly),
53 * the original container is used.
54 */
55
56Q_STATIC_LOGGING_CATEGORY(lcListValueConversion, "qt.qml.listvalueconversion")
57
58namespace QV4 {
59
61
62static ReturnedValue doGetIndexed(Heap::Sequence *p, qsizetype index)
63{
64 Q_ASSERT(p->isStoredInline());
65 QV4::Scope scope(p->internalClass->engine);
66
67 const QMetaType valueMetaType = p->valueMetaType();
68 const QMetaSequence metaSequence = p->metaSequence();
69
70 Heap::ReferenceObject::Flags flags = Heap::ReferenceObject::EnforcesLocation;
71 if (metaSequence.canSetValueAtIndex())
72 flags |= Heap::ReferenceObject::CanWriteBack;
73
74 const void *container = p->storagePointer();
75 Q_ASSERT(container); // Must readReference() before
76
77 QVariant result;
78 if (valueMetaType == QMetaType::fromType<QVariant>()) {
79 flags |= Heap::ReferenceObject::IsVariant;
80 metaSequence.valueAtIndex(container, index, &result);
81 } else {
82 result = QVariant(valueMetaType);
83 metaSequence.valueAtIndex(container, index, result.data());
84 }
85
86 QV4::ScopedValue v(scope, scope.engine->fromVariant(result, p, index, flags));
87 if (QQmlValueTypeWrapper *ref = v->as<QQmlValueTypeWrapper>()) {
88 if (CppStackFrame *frame = scope.engine->currentStackFrame)
89 ref->d()->setLocation(frame->v4Function, frame->statementNumber());
90 // No need to read the reference. We've done that above already.
91 }
92 return v->asReturnedValue();
93}
94
95static void *createVariantData(QMetaType type, QVariant *variant)
96{
97 if (type == QMetaType::fromType<QVariant>())
98 return variant;
99 *variant = QVariant(type);
100 return variant->data();
101}
102
103static const void *retrieveVariantData(QMetaType type, const QVariant *variant)
104{
105 if (type == QMetaType::fromType<QVariant>())
106 return variant;
107 return variant->constData();
108}
109
110// helper function to generate valid warnings if errors occur during sequence operations.
111static void generateWarning(QV4::ExecutionEngine *v4, const QString& description)
112{
113 QQmlEngine *engine = v4->qmlEngine();
114 if (!engine)
115 return;
116 QQmlError retn;
117 retn.setDescription(description);
118
119 QV4::CppStackFrame *stackFrame = v4->currentStackFrame;
120
121 retn.setLine(stackFrame->lineNumber());
122 retn.setUrl(QUrl(stackFrame->source()));
123 QQmlEnginePrivate::warning(engine, retn);
124}
125
127{
128 Q_ASSERT(p->isStoredInline());
129
130 if (const void *container = p->storagePointer())
131 return p->metaSequence().size(container);
132
133 // It can be stored inline, and the container can still be nullptr.
134 // This happens if we construct it from a nullptr in the first place and never update it.
135 // It means it's empty.
136 return 0;
137}
138
140{
142 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
143 {
144 Heap::Sequence *p = static_cast<const Sequence *>(o)->d();
145 Q_ASSERT(p->isStoredInline());
146
147 if (p->isReference() && !p->loadReference())
148 return PropertyKey::invalid();
149
150 const qsizetype size = sizeInline(p);
151 if (size > 0 && qIsAtMostSizetypeLimit(arrayIndex, size - 1)) {
152 const uint index = arrayIndex;
153 ++arrayIndex;
154 if (attrs)
155 *attrs = QV4::Attr_Data;
156 if (pd)
157 pd->value = doGetIndexed(p, index);
158 return PropertyKey::fromArrayIndex(index);
159 }
160
161 if (memberIndex == 0) {
162 ++memberIndex;
163 return o->engine()->id_length()->propertyKey();
164 }
165
166 // You cannot add any own properties via the regular JavaScript interfaces.
167 return PropertyKey::invalid();
168 }
169};
170
171void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence)
172{
173 m_listType = listType.iface();
174 Q_ASSERT(m_listType);
175 m_metaSequence = metaSequence.iface();
176 Q_ASSERT(m_metaSequence);
177}
178
179void Heap::Sequence::init(QMetaType listType, QMetaSequence metaSequence, const void *container)
180{
181 ReferenceObject::init(nullptr, -1, NoFlag);
182 initTypes(listType, metaSequence);
184 createInlineStorage(container);
185 else
186 createElementWrappers(container);
187}
188
189void Heap::Sequence::init(
190 QMetaType listType, QMetaSequence metaSequence, const void *container,
191 Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
192{
193 ReferenceObject::init(object, propertyIndex, flags | IsDirty);
194 initTypes(listType, metaSequence);
195
196 if (isStoredInline()) {
197 if (CppStackFrame *frame = internalClass->engine->currentStackFrame)
198 setLocation(frame->v4Function, frame->statementNumber());
199 createInlineStorage(container);
200 if (!container && (flags & EnforcesLocation))
201 QV4::ReferenceObject::readReference(this);
202 } else {
203 createElementWrappers(container);
204 }
205}
206
207void Heap::Sequence::createInlineStorage(const void *container)
208{
209 Q_ASSERT(isStoredInline());
210
211 QV4::Scope scope(internalClass->engine);
212 QV4::Scoped<QV4::Sequence> o(scope, this);
213 o->setArrayType(Heap::ArrayData::Custom);
214 if (container)
215 m_container = listType().create(container);
216}
217
218void Heap::Sequence::createElementWrappers(const void *container)
219{
220 Q_ASSERT(!isStoredInline());
221
222 if (!container)
223 return;
224
225 const QMetaSequence metaSequence(m_metaSequence);
226 const QMetaType valueMetaType = metaSequence.valueMetaType();
227 const qsizetype size = metaSequence.size(container);
228
229 QV4::Scope scope(internalClass->engine);
230 if (!qIsAtMostUintLimit(size)) {
231 generateWarning(scope.engine, QLatin1String("Sequence length out of range"));
232 return;
233 }
234
235 QV4::Scoped<QV4::Sequence> self(scope, this);
236 self->arrayReserve(size);
237 QV4::ScopedValue v(scope);
238 if (valueMetaType == QMetaType::fromType<QVariant>()) {
239 QVariant var;
240 for (qsizetype i = 0; i < size; ++i) {
241 metaSequence.valueAtIndex(container, i, &var);
242 v = scope.engine->metaTypeToJS(var.metaType(), var.constData());
243 self->arraySet(i, v);
244 }
245 } else {
246 QVariant var(valueMetaType);
247 for (qsizetype i = 0; i < size; ++i) {
248 metaSequence.valueAtIndex(container, i, var.data());
249 v = scope.engine->metaTypeToJS(valueMetaType, var.constData());
250 self->arraySet(i, v);
251 }
252 }
253
254 m_size = size;
255}
256
258{
259 const QMetaType listType(m_listType);
260 const QMetaSequence metaSequence(m_metaSequence);
261 if (isStoredInline()) {
262 return internalClass->engine->memoryManager->allocate<QV4::Sequence>(
263 listType, metaSequence, m_container);
264 }
265
266 QVariant list(listType);
267
268 const QMetaType valueMetaType(m_metaSequence->valueMetaType);
269 QVariant element;
270 void *elementData = createVariantData(valueMetaType, &element);
271
272 QV4::Scope scope(internalClass->engine);
273 if (qIsAtMostSizetypeLimit(m_size)) {
274 QV4::Scoped<QV4::Sequence> self(scope, this);
275 QV4::ScopedValue v(scope);
276 for (uint i = 0; i < m_size; ++i) {
277 v = self->get(PropertyKey::fromArrayIndex(i));
278 ExecutionEngine::metaTypeFromJS(v, valueMetaType, elementData);
279 metaSequence.addValue(list.data(), elementData);
280 }
281 } else {
282 generateWarning(scope.engine, QLatin1String("Index out of range during toVariant()"));
283 }
284
285 return scope.engine->memoryManager->allocate<QV4::Sequence>(
286 listType, metaSequence, list.constData());
287}
288
290{
291 if (isStoredInline() && m_container)
292 listType().destroy(m_container);
293 ReferenceObject::destroy();
294}
295
297{
298 if (!isStoredInline())
299 return nullptr;
300 if (!m_container)
301 m_container = listType().create();
302 return m_container;
303}
304
305bool Heap::Sequence::setVariant(const QVariant &variant)
306{
307 // Should only happen from readReference(). Therefore we are attached.
308 Q_ASSERT(isStoredInline());
309
310 const QMetaType variantReferenceType = variant.metaType();
311 if (variantReferenceType != listType()) {
312 // This is a stale reference. That is, the property has been
313 // overwritten with a different type in the meantime.
314 // We need to modify this reference to the updated type, if
315 // possible, or return false if it is not a sequence.
316 const QQmlType newType = QQmlMetaType::qmlListType(variantReferenceType);
317 if (newType.isSequentialContainer()) {
318 if (m_container)
319 listType().destroy(m_container);
320 m_listType = newType.qListTypeId().iface();
321 m_metaSequence = newType.listMetaSequence().iface();
322 m_container = listType().create(variant.constData());
323 return true;
324 } else {
325 return false;
326 }
327 }
328 if (m_container) {
329 variantReferenceType.destruct(m_container);
330 variantReferenceType.construct(m_container, variant.constData());
331 } else {
332 m_container = variantReferenceType.create(variant.constData());
333 }
334 return true;
335}
337{
338 // Should only happen from readReference(). Therefore we are attached.
339 Q_ASSERT(isStoredInline());
340
341 return QVariant(listType(), m_container);
342}
343
344template<typename Action>
345void convertAndDo(const QVariant &item, const QMetaType v, Action action)
346{
347 if (item.metaType() == v) {
348 action(item.constData());
349 } else if (v == QMetaType::fromType<QVariant>()) {
350 action(&item);
351 } else {
352 QVariant converted = item;
353 if (!converted.convert(v))
354 converted = QVariant(v);
355 action(converted.constData());
356 }
357}
358
359static void appendInline(Heap::Sequence *p, const QVariant &item)
360{
361 convertAndDo(item, p->valueMetaType(), [p](const void *data) {
362 p->metaSequence().addValueAtEnd(p->storagePointer(), data);
363 });
364}
365
366static void appendDefaultConstructedInline(Heap::Sequence *p, qsizetype num)
367{
368 QVariant item;
369 const void *data = createVariantData(p->valueMetaType(), &item);
370 const QMetaSequence m = p->metaSequence();
371 void *container = p->storagePointer();
372 for (qsizetype i = 0; i < num; ++i)
373 m.addValueAtEnd(container, data);
374}
375
376static void replaceInline(Heap::Sequence *p, qsizetype index, const QVariant &item)
377{
378 convertAndDo(item, p->valueMetaType(), [p, index](const void *data) {
379 p->metaSequence().setValueAtIndex(p->storagePointer(), index, data);
380 });
381}
382
383static void removeLastInline(Heap::Sequence *p, qsizetype num)
384{
385 const QMetaSequence m = p->metaSequence();
386 void *container = p->storagePointer();
387
388 if (m.canEraseRangeAtIterator() && m.hasRandomAccessIterator() && num > 1) {
389 void *i = m.end(container);
390 m.advanceIterator(i, -num);
391 void *j = m.end(container);
392 m.eraseRangeAtIterator(container, i, j);
393 m.destroyIterator(i);
394 m.destroyIterator(j);
395 } else {
396 for (int i = 0; i < num; ++i)
397 m.removeValueAtEnd(container);
398 }
399}
400
401bool Heap::Sequence::loadReference()
402{
403 Q_ASSERT(isReference());
404 // If locations are enforced we only read once
405 return enforcesLocation() || QV4::ReferenceObject::readReference(this);
406}
407
408bool Heap::Sequence::storeReference()
409{
410 Q_ASSERT(isReference());
411 return isAttachedToProperty() && QV4::ReferenceObject::writeBack(this);
412}
413
415{
416 Heap::Sequence *p = static_cast<const Sequence *>(that)->d();
417 if (!p->isStoredInline() || !id.isArrayIndex())
419
420 const uint arrayIndex = id.asArrayIndex();
422 generateWarning(that->engine(), QLatin1String("Index out of range during indexed get"));
423 return false;
424 }
425
426 if (p->isReference() && !p->loadReference())
427 return Encode::undefined();
428
429 const qsizetype index = arrayIndex;
430 if (index < sizeInline(p)) {
431 if (hasProperty)
432 *hasProperty = true;
433 return doGetIndexed(p, index);
434 }
435
436 if (hasProperty)
437 *hasProperty = false;
438 return Encode::undefined();
439}
440
442{
443 Heap::Sequence *p = static_cast<const Sequence *>(m)->d();
444 if (!p->isStoredInline())
445 return p->m_size;
446 if (p->isReference() && !p->loadReference())
447 return 0;
448 return sizeInline(p);
449}
450
452{
453 if (!id.isArrayIndex())
455
456 const uint arrayIndex = id.asArrayIndex();
457 Sequence *s = static_cast<Sequence *>(that);
458 Heap::Sequence *p = s->d();
459
460 if (!p->isStoredInline()) {
462 if (p->m_size <= arrayIndex)
463 p->m_size = arrayIndex + 1;
464 return true;
465 }
466
468 generateWarning(that->engine(), QLatin1String("Index out of range during indexed set"));
469 return false;
470 }
471
472 if (p->isReadOnly()) {
474 QLatin1String("Cannot insert into a readonly container"));
475 return false;
476 }
477
478 if (p->isReference() && !p->loadReference())
479 return false;
480
481 const qsizetype index = arrayIndex;
482 const qsizetype count = sizeInline(p);
485
486 if (index == count) {
488 } else if (index < count) {
490 } else {
491 /* according to ECMA262r3 we need to insert */
492 /* the value at the given index, increasing length to index+1. */
495 }
496
497 if (p->isReference())
498 p->storeReference();
499 return true;
500}
501
503{
504 Heap::Sequence *p = static_cast<const Sequence *>(that)->d();
505 if (!p->isStoredInline() || !id.isArrayIndex())
507
508 const uint arrayIndex = id.asArrayIndex();
510 generateWarning(that->engine(), QLatin1String("Index out of range during indexed delete"));
511 return false;
512 }
513
514 if (p->isReadOnly()) {
516 QLatin1String("Cannot delete from a readonly container"));
517 return false;
518 }
519
520 if (p->isReference() && !p->loadReference())
521 return false;
522
523 const qsizetype index = arrayIndex;
524 if (index >= sizeInline(p))
525 return false;
526
527 /* according to ECMA262r3 it should be Undefined, */
528 /* but we cannot, so we insert a default-value instead. */
530
531 if (p->isReference())
532 p->storeReference();
533
534 return true;
535}
536
538{
539 if (!other)
540 return false;
541
542 const Sequence *otherS = other->as<Sequence>();
543 if (!otherS)
544 return false;
545
546 const Sequence *s = static_cast<Sequence *>(that);
547 const Heap::Sequence *p = s->d();
548 const Heap::Sequence *otherP = otherS->d();
549
550 const Heap::Object *object = p->object();
551 const Heap::Object *otherObject = otherP->object();
552
553 if (object && otherObject)
554 return object == otherObject && p->property() == otherP->property();
555
556 if (!object && !otherObject)
557 return s == otherS;
558
559 return false;
560}
561
563{
564 Heap::Sequence *p = static_cast<const Sequence *>(m)->d();
565 if (!p->isStoredInline())
567
568 *target = *m;
570}
571
573{
574 Heap::Sequence *p = static_cast<Sequence *>(object)->d();
575 Q_ASSERT(p);
576
577 // We only create attached wrappers if this sequence is stored inline.
578 // When detaching, we re-create everything. Therefore, we can't get a metaCall if
579 // we aren't stored inline.
581
582 switch (call) {
585 if (p->isReference() && !p->loadReference())
586 return 0;
589 return 0; // value metatype is not what the caller expects anymore.
590
591 const void *storagePointer = p->storagePointer();
593 return 0;
595 break;
596 }
598 if (p->isReadOnly())
599 return 0;
600
604 return 0;
606 if (p->isReference())
607 p->storeReference();
608 break;
609 }
610 default:
611 return 0; // not supported
612 }
613
614 return -1;
615}
616
618 const FunctionObject *b, const Value *thisObject, const Value *, int)
619{
620 QV4::Scope scope(b);
622 if (!This)
624
625 Heap::Sequence *p = This->d();
626 if (!p->isStoredInline())
628
629 if (p->isReference() && !p->loadReference())
630 return Encode::undefined();
631
632 const qsizetype size = sizeInline(p);
633 if (!qIsAtMostUintLimit(size)) {
634 generateWarning(scope.engine, QLatin1String("Sequence length out of range"));
636 }
637
639}
640
642 const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
643{
644 QV4::Scope scope(f);
646 if (!This)
648
649 Heap::Sequence *p = This->d();
650
651 bool ok = false;
652 const quint32 argv0 = argc ? argv[0].asArrayLength(&ok) : 0;
653 if (!ok) {
654 generateWarning(scope.engine, QLatin1String("Index out of range during length set"));
656 }
657
658 if (!p->isStoredInline()) {
659 if (argv0 < p->m_size)
661 p->m_size = argv0;
663 }
664
666 generateWarning(scope.engine, QLatin1String("Sequence length out of range"));
668 }
669
670 if (p->isReadOnly())
672
674
675 /* Read the sequence from the QObject property if we're a reference */
676 if (p->isReference() && !p->loadReference())
678
679 /* Determine whether we need to modify the sequence */
680 const qsizetype count = sizeInline(p);
681 if (newCount == count) {
683 } else if (newCount > count) {
684 /* according to ECMA262r3 we need to insert */
685 /* undefined values increasing length to newLength. */
686 /* We cannot, so we insert default-values instead. */
688 } else {
689 /* according to ECMA262r3 we need to remove */
690 /* elements until the sequence is the required length. */
693 }
694
695 /* write back if required. */
696 if (p->isReference())
697 p->storeReference();
698
700}
701
711
713 const FunctionObject *f, const Value *thisObject, const Value *, int)
714{
715 return Encode(thisObject->toString(f->engine()));
716}
717
719 const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
720{
721 Scope scope(b);
723 if (!s)
725
726 Heap::Sequence *p = s->d();
727 if (p->isReadOnly())
729
730 if (!p->isStoredInline())
732
733 if (p->isReference() && !p->loadReference())
735
736 const qsizetype len = sizeInline(p);
737 if (!len)
739
740 void *storage = p->storagePointer();
741 Q_ASSERT(storage); // Must readReference() before
742 const QMetaType v = p->valueMetaType();
743 const QMetaSequence m = p->metaSequence();
744
748
749 if (m.canRemoveValueAtBegin()) {
751 } else {
752 QVariant t;
753 void *tData = createVariantData(v, &t);
754 for (qsizetype i = 1, end = m.size(storage); i < end; ++i) {
757 }
759 }
760
761 if (p->isReference())
762 p->storeReference();
763
765}
766
768 const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
769{
770 Scope scope(f);
772 if (!s)
774
775 Heap::Sequence *p = s->d();
776 if (p->isReadOnly())
778
779 if (!p->isStoredInline())
781
782 if (p->isReference() && !p->loadReference())
784
787 generateWarning(scope.engine, QLatin1String("Index out of range during unshift"));
789 }
790
791 void *storage = p->storagePointer();
792 Q_ASSERT(storage); // Must readReference() before
793 const QMetaType v = p->valueMetaType();
794 const QMetaSequence m = p->metaSequence();
795
796 if (m.canAddValueAtBegin()) {
797 for (int i = argc - 1; i >= 0; --i) {
798 const QVariant item = scope.engine->toVariant(argv[i], p->valueMetaType(), false);
800 }
801 } else {
802 QVariant t;
803 void *tData = createVariantData(v, &t);
804
805 const qsizetype oldSize = m.size(storage);
806
807 // Resize array by appending values to the end
808 for (qsizetype i = argc; i > 0; --i) {
809 if (i < oldSize)
812 }
813
814 // Move other existing values into now vacant storage
815 for (qsizetype i = oldSize - argc; i >= 0; --i) {
818 }
819
820 // Insert new values into vacant storage at front
821 for (qsizetype i = 0; i < argc; ++i) {
822 const QVariant item = scope.engine->toVariant(argv[i], p->valueMetaType(), false);
824 }
825 }
826
827 if (p->isReference())
828 p->storeReference();
829 return Encode(uint(size));
830}
831
833 const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
834{
835 Scope scope(f);
837 if (!s)
839
840 Heap::Sequence *p = s->d();
841 if (p->isReadOnly())
843
844 if (!p->isStoredInline())
846
847 if (p->isReference() && !p->loadReference())
849
852 generateWarning(scope.engine, QLatin1String("Index out of range during push"));
854 }
855
856 for (int i = 0; i < argc; ++i)
858
859 if (p->isReference())
860 p->storeReference();
861 return Encode(uint(size));
862}
863
865 const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
866{
867 Scope scope(f);
869 if (!s)
871
872 Heap::Sequence *p = s->d();
873 if (p->isReadOnly())
875
876 if (!p->isStoredInline())
878
879 if (p->isReference() && !p->loadReference())
881
882 const qsizetype len = sizeInline(p);
883 if (!len)
885
888
889 if (p->isReference())
890 p->storeReference();
891 return result->asReturnedValue();
892}
893
897{
898 // This function is called when the property is a QObject Q_PROPERTY of
899 // the given sequence type. Internally we store a sequence
900 // (as well as object ptr + property index for updated-read and write-back)
901 // and so access/mutate avoids variant conversion.
902
905}
906
922
925{
926 // This function is called when assigning a sequence value to a normal JS var
927 // in a JS block. Internally, we store a sequence of the specified type.
928 // Access and mutation is extremely fast since it will not need to modify any
929 // QObject property.
930
932}
933
934/*!
935 * \internal
936 *
937 * Produce a QVariant containing a copy of the list stored in \a object.
938 */
940{
942 Heap::Sequence *p = object->d();
943
944 if (p->isStoredInline()) {
945 // Note: For historical reasons, we ignore the result of loadReference()
946 // here. This allows us to retain sequences whose objects have vaninshed
947 // as "var" properties. It comes at the price of potentially returning
948 // outdated data. This is the behavior sequences have always shown.
949 if (p->isReference())
950 p->loadReference();
951
952 if (const void *storage = p->m_container)
953 return QVariant(p->listType(), storage);
954
955 return QVariant();
956 }
957
960 return toVariant(q, p->listType());
961}
962
963bool convertToIterable(QMetaType metaType, void *data, QV4::Object *sequence)
964{
965 QSequentialIterable iterable;
966 if (!QMetaType::view(metaType, data, QMetaType::fromType<QSequentialIterable>(), &iterable))
967 return false;
968
969 const QMetaType elementMetaType = iterable.valueMetaType();
970 QV4::Scope scope(sequence->engine());
971 QV4::ScopedValue v(scope);
972 for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) {
973 QVariant element(elementMetaType);
974 v = sequence->get(i);
975 ExecutionEngine::metaTypeFromJS(v, elementMetaType, element.data());
976 iterable.addValue(element, QSequentialIterable::AtEnd);
977 }
978 return true;
979}
980
981/*!
982 * \internal
983 *
984 * Convert the Object \a array to \a targetType. If \a targetType is not a sequential container,
985 * or if \a array is not an Object, return an invalid QVariant. Otherwise return a QVariant of
986 * \a targetType with the converted data. It is assumed that this actual requires a conversion. If
987 * the conversion is not needed, the single-argument toVariant() is faster.
988 */
990{
991 if (!array.as<Object>())
992 return QVariant();
993
998 // If the QML type declares a custom sequential container, use that.
1000 } else if (QSequentialIterable iterable;
1002 &iterable)) {
1003 // Otherwise try to convert to QSequentialIterable via QMetaType conversion.
1005 }
1006
1007 if (!meta.canAddValue())
1008 return QVariant();
1009
1012
1014 const qint64 length = a->getLength();
1015 Q_ASSERT(length >= 0);
1017
1019 for (quint32 i = 0; i < quint32(length); ++i) {
1022
1023 // Note: We can convert to any sequence type here, even those that don't have a specified
1024 // order. Therefore the meta.addValue() below. meta.addValue() preferably adds to the
1025 // end, but will also add in other places if that's not possible.
1026
1027 // If the target type is QVariant itself, let the conversion produce any interanl type.
1031 continue;
1032 }
1033
1034 // Try to convert to the specific type requested ...
1038 continue;
1039 }
1040
1041 // If that doesn't work, produce any QVariant and try to convert it afterwards.
1042 // toVariant() is free to ignore the type hint and can produce the "original" type.
1045
1046 // Try value type constructors.
1049 if (converted.isValid()) {
1051 continue;
1052 }
1053
1054 const auto warn = [&](QLatin1String base) {
1055 // If the original type was void, we're converting a "hole" in a sparse
1056 // array. There is no point in warning about that.
1057 if (!originalType.isValid())
1058 return;
1059
1063 };
1064
1065 // Note: Ideally you should use constructible value types for everything below, but we'd
1066 // probably get a lot of pushback for warning about that.
1067
1068 // Before attempting a conversion from the concrete types, check if there exists a
1069 // conversion from QJSValue to the result type. Prefer that one for compatibility reasons.
1070 // This is a rather surprising "feature". Therefore, warn if a concrete conversion wouldn't
1071 // be possible. You should at least make your type conversions consistent.
1077 warn(QLatin1String("Converting array value at position %1 from %2 to %3 via "
1078 "QJSValue even though they are not directly convertible"));
1079 }
1081 continue;
1082 }
1083 }
1084
1085 // Last ditch effort: Try QVariant::convert()
1087 warn(QLatin1String("Could not convert array value at position %1 from %2 to %3"));
1088
1090 }
1091 return result;
1092
1093}
1094
1095static Heap::Sequence *resolveHeapSequence(const Sequence *sequence, QMetaType typeHint)
1096{
1097 if (!sequence)
1098 return nullptr;
1099 Heap::Sequence *p = sequence->d();
1100 if (p->listType() != typeHint)
1101 return nullptr;
1102 return p;
1103}
1104
1106{
1108 if (!p || !p->isStoredInline())
1109 return nullptr;
1110
1111 return p->storagePointer();
1112}
1113
1132
1135{
1137 if (!p)
1138 return TypeMismatch;
1139
1140 if (p->isStoredInline()) {
1142 return WasEqual;
1145 return Copied;
1146 }
1147
1150
1154 const QMetaType metaType = p->valueMetaType();
1155 const qsizetype size = p->m_size;
1156
1158
1159 if (metaType == QMetaType::fromType<QVariant>()) {
1160 for (qsizetype i = 0; i < size; ++i) {
1164 }
1165 return Copied;
1166 }
1167
1169 for (qsizetype i = 0; i < size; ++i) {
1173 }
1174 return Copied;
1175}
1176
1181
1182} // namespace QV4
1183
1184QT_END_NAMESPACE
1185
1186#include "moc_qv4sequenceobject_p.cpp"
Definition qjsvalue.h:23
static Heap::Sequence * resolveHeapSequence(const Sequence *sequence, QMetaType typeHint)
static void removeLastInline(Heap::Sequence *p, qsizetype num)
static void * createVariantData(QMetaType type, QVariant *variant)
static void generateWarning(QV4::ExecutionEngine *v4, const QString &description)
static void appendInline(Heap::Sequence *p, const QVariant &item)
void convertAndDo(const QVariant &item, const QMetaType v, Action action)
bool convertToIterable(QMetaType metaType, void *data, QV4::Object *sequence)
static const void * retrieveVariantData(QMetaType type, const QVariant *variant)
static void appendDefaultConstructedInline(Heap::Sequence *p, qsizetype num)
static void replaceInline(Heap::Sequence *p, qsizetype index, const QVariant &item)
static qsizetype sizeInline(const Heap::Sequence *p)
static ReturnedValue doGetIndexed(Heap::Sequence *p, qsizetype index)
void init(QMetaType listType, QMetaSequence metaSequence, const void *container)
QVariant toVariant() const
void init(QMetaType listType, QMetaSequence metaSequence, const void *container, Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
const void * storagePointer() const
bool setVariant(const QVariant &variant)
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~SequenceOwnPropertyKeyIterator() override=default