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
4#include <QtCore/qsequentialiterable.h>
5
6#include "qv4sequenceobject_p.h"
7
8#include <private/qv4functionobject_p.h>
9#include <private/qv4arrayobject_p.h>
10#include <private/qqmlengine_p.h>
11#include <private/qv4scopedvalue_p.h>
12#include <private/qv4jscall_p.h>
13#include <private/qqmlmetatype_p.h>
14#include <private/qqmltype_p_p.h>
15#include <private/qqmlvaluetypewrapper_p.h>
16
17#include <algorithm>
18
19QT_BEGIN_NAMESPACE
20
21namespace QV4 {
22
23DEFINE_OBJECT_VTABLE(Sequence);
24
25static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) {
26 QV4::Scope scope(s->engine());
27
28 Heap::ReferenceObject::Flags flags =
29 Heap::ReferenceObject::EnforcesLocation;
30 if (s->d()->typePrivate()->extraData.ld->canSetValueAtIndex())
31 flags |= Heap::ReferenceObject::CanWriteBack;
32 if (Sequence::valueMetaType(p: s->d()) == QMetaType::fromType<QVariant>())
33 flags |= Heap::ReferenceObject::IsVariant;
34
35 QV4::ScopedValue v(scope, scope.engine->fromVariant(
36 variant: s->at(index), parent: s->d(), property: index, flags));
37 if (QQmlValueTypeWrapper *ref = v->as<QQmlValueTypeWrapper>()) {
38 if (CppStackFrame *frame = scope.engine->currentStackFrame)
39 ref->d()->setLocation(function: frame->v4Function, statement: frame->statementNumber());
40 // No need to read the reference. at() has done that already.
41 }
42 return v->asReturnedValue();
43}
44
45static const QMetaSequence *metaSequence(const Heap::Sequence *p)
46{
47 return p->typePrivate()->extraData.ld;
48}
49
50template<typename Compare>
51void sortSequence(Sequence *sequence, const Compare &compare)
52{
53 auto *p = sequence->d();
54 const auto *m = metaSequence(p);
55
56 QSequentialIterable iterable(*m, p->typePrivate()->listId, p->storagePointer());
57 if (iterable.canRandomAccessIterate()) {
58 std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()),
59 QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()),
60 compare);
61 } else if (iterable.canReverseIterate()) {
62 std::sort(QSequentialIterable::BidirectionalIterator(iterable.mutableBegin()),
63 QSequentialIterable::BidirectionalIterator(iterable.mutableEnd()),
64 compare);
65 } else {
66 qWarning() << "Container has no suitable iterator for sorting";
67 }
68}
69
70// helper function to generate valid warnings if errors occur during sequence operations.
71static void generateWarning(QV4::ExecutionEngine *v4, const QString& description)
72{
73 QQmlEngine *engine = v4->qmlEngine();
74 if (!engine)
75 return;
76 QQmlError retn;
77 retn.setDescription(description);
78
79 QV4::CppStackFrame *stackFrame = v4->currentStackFrame;
80
81 retn.setLine(stackFrame->lineNumber());
82 retn.setUrl(QUrl(stackFrame->source()));
83 QQmlEnginePrivate::warning(engine, retn);
84}
85
86struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
87{
88 ~SequenceOwnPropertyKeyIterator() override = default;
89 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
90 {
91 const Sequence *s = static_cast<const Sequence *>(o);
92
93 if (s->d()->isReference() && !s->loadReference())
94 return PropertyKey::invalid();
95
96 const qsizetype size = s->size();
97 if (size > 0 && qIsAtMostSizetypeLimit(length: arrayIndex, limit: size - 1)) {
98 const uint index = arrayIndex;
99 ++arrayIndex;
100 if (attrs)
101 *attrs = QV4::Attr_Data;
102 if (pd)
103 pd->value = doGetIndexed(s, index);
104 return PropertyKey::fromArrayIndex(idx: index);
105 }
106
107 if (memberIndex == 0) {
108 ++memberIndex;
109 return o->engine()->id_length()->propertyKey();
110 }
111
112 // You cannot add any own properties via the regular JavaScript interfaces.
113 return PropertyKey::invalid();
114 }
115};
116
117struct SequenceCompareFunctor
118{
119 SequenceCompareFunctor(QV4::ExecutionEngine *v4, const QV4::Value &compareFn)
120 : m_v4(v4), m_compareFn(&compareFn)
121 {}
122
123 bool operator()(const QVariant &lhs, const QVariant &rhs)
124 {
125 QV4::Scope scope(m_v4);
126 ScopedFunctionObject compare(scope, m_compareFn);
127 if (!compare)
128 return m_v4->throwTypeError();
129 Value *argv = scope.alloc(nValues: 2);
130 argv[0] = m_v4->fromVariant(lhs);
131 argv[1] = m_v4->fromVariant(rhs);
132 QV4::ScopedValue result(scope, compare->call(thisObject: m_v4->globalObject, argv, argc: 2));
133 if (scope.hasException())
134 return false;
135 return result->toNumber() < 0;
136 }
137
138private:
139 QV4::ExecutionEngine *m_v4;
140 const QV4::Value *m_compareFn;
141};
142
143struct SequenceDefaultCompareFunctor
144{
145 bool operator()(const QVariant &lhs, const QVariant &rhs)
146 {
147 return lhs.toString() < rhs.toString();
148 }
149};
150
151void Heap::Sequence::init(const QQmlType &qmlType, const void *container)
152{
153 ReferenceObject::init(object: nullptr, property: -1, flags: NoFlag);
154
155 Q_ASSERT(qmlType.isSequentialContainer());
156 m_typePrivate = qmlType.priv();
157 QQmlType::refHandle(priv: m_typePrivate);
158
159 m_container = m_typePrivate->listId.create(copy: container);
160
161 QV4::Scope scope(internalClass->engine);
162 QV4::Scoped<QV4::Sequence> o(scope, this);
163 o->setArrayType(Heap::ArrayData::Custom);
164}
165
166void Heap::Sequence::init(
167 const QQmlType &qmlType, const void *container,
168 Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
169{
170 ReferenceObject::init(object, property: propertyIndex, flags);
171
172 Q_ASSERT(qmlType.isSequentialContainer());
173 m_typePrivate = qmlType.priv();
174 QQmlType::refHandle(priv: m_typePrivate);
175 QV4::Scope scope(internalClass->engine);
176 QV4::Scoped<QV4::Sequence> o(scope, this);
177 o->setArrayType(Heap::ArrayData::Custom);
178 if (CppStackFrame *frame = scope.engine->currentStackFrame)
179 setLocation(function: frame->v4Function, statement: frame->statementNumber());
180 if (container)
181 m_container = QMetaType(m_typePrivate->listId).create(copy: container);
182 else if (flags & EnforcesLocation)
183 QV4::ReferenceObject::readReference(ref: this);
184}
185
186Heap::Sequence *Heap::Sequence::detached() const
187{
188 return internalClass->engine->memoryManager->allocate<QV4::Sequence>(
189 args: QQmlType(m_typePrivate), args: m_container);
190}
191
192void Heap::Sequence::destroy()
193{
194 if (m_container)
195 m_typePrivate->listId.destroy(data: m_container);
196 QQmlType::derefHandle(priv: m_typePrivate);
197 ReferenceObject::destroy();
198}
199
200void *Heap::Sequence::storagePointer()
201{
202 if (!m_container)
203 m_container = m_typePrivate->listId.create();
204 return m_container;
205}
206
207bool Heap::Sequence::setVariant(const QVariant &variant)
208{
209 const QMetaType variantReferenceType = variant.metaType();
210 if (variantReferenceType != m_typePrivate->listId) {
211 // This is a stale reference. That is, the property has been
212 // overwritten with a different type in the meantime.
213 // We need to modify this reference to the updated type, if
214 // possible, or return false if it is not a sequence.
215 const QQmlType newType = QQmlMetaType::qmlListType(metaType: variantReferenceType);
216 if (newType.isSequentialContainer()) {
217 if (m_container)
218 m_typePrivate->listId.destroy(data: m_container);
219 QQmlType::derefHandle(priv: m_typePrivate);
220 m_typePrivate = newType.priv();
221 QQmlType::refHandle(priv: m_typePrivate);
222 m_container = m_typePrivate->listId.create(copy: variant.constData());
223 return true;
224 } else {
225 return false;
226 }
227 }
228 if (m_container) {
229 variantReferenceType.destruct(data: m_container);
230 variantReferenceType.construct(where: m_container, copy: variant.constData());
231 } else {
232 m_container = variantReferenceType.create(copy: variant.constData());
233 }
234 return true;
235}
236QVariant Heap::Sequence::toVariant() const
237{
238 return QVariant(m_typePrivate->listId, m_container);
239}
240
241const QMetaType Sequence::valueMetaType(const Heap::Sequence *p)
242{
243 return p->typePrivate()->typeId;
244}
245
246qsizetype Sequence::size() const
247{
248 const auto *p = d();
249 Q_ASSERT(p->storagePointer()); // Must readReference() before
250 return metaSequence(p)->size(container: p->storagePointer());
251}
252
253QVariant Sequence::at(qsizetype index) const
254{
255 const auto *p = d();
256 Q_ASSERT(p->storagePointer()); // Must readReference() before
257 const QMetaType v = valueMetaType(p);
258 QVariant result;
259 if (v == QMetaType::fromType<QVariant>()) {
260 metaSequence(p)->valueAtIndex(container: p->storagePointer(), index, result: &result);
261 } else {
262 result = QVariant(v);
263 metaSequence(p)->valueAtIndex(container: p->storagePointer(), index, result: result.data());
264 }
265 return result;
266}
267
268
269template<typename Action>
270void convertAndDo(const QVariant &item, const QMetaType v, Action action)
271{
272 if (item.metaType() == v) {
273 action(item.constData());
274 } else if (v == QMetaType::fromType<QVariant>()) {
275 action(&item);
276 } else {
277 QVariant converted = item;
278 if (!converted.convert(type: v))
279 converted = QVariant(v);
280 action(converted.constData());
281 }
282}
283
284void Sequence::append(const QVariant &item)
285{
286 Heap::Sequence *p = d();
287 convertAndDo(item, v: valueMetaType(p), action: [p](const void *data) {
288 metaSequence(p)->addValueAtEnd(container: p->storagePointer(), value: data);
289 });
290}
291
292void Sequence::append(qsizetype num, const QVariant &item)
293{
294 Heap::Sequence *p = d();
295 convertAndDo(item, v: valueMetaType(p), action: [p, num](const void *data) {
296 const QMetaSequence *m = metaSequence(p);
297 void *container = p->storagePointer();
298 for (qsizetype i = 0; i < num; ++i)
299 m->addValueAtEnd(container, value: data);
300 });
301}
302
303void Sequence::replace(qsizetype index, const QVariant &item)
304{
305 Heap::Sequence *p = d();
306 convertAndDo(item, v: valueMetaType(p), action: [p, index](const void *data) {
307 metaSequence(p)->setValueAtIndex(container: p->storagePointer(), index, value: data);
308 });
309}
310
311void Sequence::removeLast(qsizetype num)
312{
313 auto *p = d();
314 const auto *m = metaSequence(p);
315
316 if (m->canEraseRangeAtIterator() && m->hasRandomAccessIterator() && num > 1) {
317 void *i = m->end(container: p->storagePointer());
318 m->advanceIterator(iterator: i, step: -num);
319 void *j = m->end(container: p->storagePointer());
320 m->eraseRangeAtIterator(container: p->storagePointer(), iterator1: i, iterator2: j);
321 m->destroyIterator(iterator: i);
322 m->destroyIterator(iterator: j);
323 } else {
324 for (int i = 0; i < num; ++i)
325 m->removeValueAtEnd(container: p->storagePointer());
326 }
327}
328
329ReturnedValue Sequence::containerGetIndexed(qsizetype index, bool *hasProperty) const
330{
331 if (d()->isReference() && !loadReference())
332 return Encode::undefined();
333
334 if (index >= 0 && index < size()) {
335 if (hasProperty)
336 *hasProperty = true;
337 return doGetIndexed(s: this, index);
338 }
339 if (hasProperty)
340 *hasProperty = false;
341 return Encode::undefined();
342}
343
344bool Sequence::containerPutIndexed(qsizetype index, const Value &value)
345{
346 if (internalClass()->engine->hasException)
347 return false;
348
349 if (d()->isReadOnly()) {
350 engine()->throwTypeError(message: QLatin1String("Cannot insert into a readonly container"));
351 return false;
352 }
353
354 if (d()->isReference() && !loadReference())
355 return false;
356
357 const qsizetype count = size();
358 const QMetaType valueType = valueMetaType(p: d());
359 const QVariant element = ExecutionEngine::toVariant(value, typeHint: valueType, createJSValueForObjectsAndSymbols: false);
360
361 if (index < 0)
362 return false;
363
364 if (index == count) {
365 append(item: element);
366 } else if (index < count) {
367 replace(index, item: element);
368 } else {
369 /* according to ECMA262r3 we need to insert */
370 /* the value at the given index, increasing length to index+1. */
371 append(num: index - count,
372 item: valueType == QMetaType::fromType<QVariant>() ? QVariant() : QVariant(valueType));
373 append(item: element);
374 }
375
376 if (d()->object())
377 storeReference();
378 return true;
379}
380
381SequenceOwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target)
382{
383 *target = *m;
384 return new SequenceOwnPropertyKeyIterator;
385}
386
387bool Sequence::containerDeleteIndexedProperty(qsizetype index)
388{
389 if (d()->isReadOnly())
390 return false;
391 if (d()->isReference() && !loadReference())
392 return false;
393 if (index < 0 || index >= size())
394 return false;
395
396 /* according to ECMA262r3 it should be Undefined, */
397 /* but we cannot, so we insert a default-value instead. */
398 replace(index, item: QVariant());
399
400 if (d()->object())
401 storeReference();
402
403 return true;
404}
405
406bool Sequence::containerIsEqualTo(Managed *other)
407{
408 if (!other)
409 return false;
410 Sequence *otherSequence = other->as<Sequence>();
411 if (!otherSequence)
412 return false;
413 if (d()->object() && otherSequence->d()->object()) {
414 return d()->object() == otherSequence->d()->object()
415 && d()->property() == otherSequence->d()->property();
416 } else if (!d()->object() && !otherSequence->d()->object()) {
417 return this == otherSequence;
418 }
419 return false;
420}
421
422bool Sequence::sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
423{
424 if (d()->isReadOnly())
425 return false;
426 if (d()->isReference() && !loadReference())
427 return false;
428
429 if (argc == 1 && argv[0].as<FunctionObject>())
430 sortSequence(sequence: this, compare: SequenceCompareFunctor(f->engine(), argv[0]));
431 else
432 sortSequence(sequence: this, compare: SequenceDefaultCompareFunctor());
433
434 if (d()->object())
435 storeReference();
436
437 return true;
438}
439
440void *Sequence::getRawContainerPtr() const
441{ return d()->storagePointer(); }
442
443bool Sequence::loadReference() const
444{
445 Q_ASSERT(d()->object());
446 // If locations are enforced we only read once
447 return d()->enforcesLocation() || QV4::ReferenceObject::readReference(ref: d());
448}
449
450bool Sequence::storeReference()
451{
452 Q_ASSERT(d()->object());
453 return d()->isAttachedToProperty() && QV4::ReferenceObject::writeBack(ref: d());
454}
455
456ReturnedValue Sequence::virtualGet(const Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty)
457{
458 if (id.isArrayIndex()) {
459 const uint index = id.asArrayIndex();
460 if (qIsAtMostSizetypeLimit(length: index))
461 return static_cast<const Sequence *>(that)->containerGetIndexed(index: qsizetype(index), hasProperty);
462
463 generateWarning(v4: that->engine(), description: QLatin1String("Index out of range during indexed get"));
464 return false;
465 }
466
467 return Object::virtualGet(m: that, id, receiver, hasProperty);
468}
469
470qint64 Sequence::virtualGetLength(const Managed *m)
471{
472 const Sequence *s = static_cast<const Sequence *>(m);
473 if (s->d()->isReference() && !s->loadReference())
474 return 0;
475 return s->size();
476}
477
478bool Sequence::virtualPut(Managed *that, PropertyKey id, const Value &value, Value *receiver)
479{
480 if (id.isArrayIndex()) {
481 const uint index = id.asArrayIndex();
482 if (qIsAtMostSizetypeLimit(length: index))
483 return static_cast<Sequence *>(that)->containerPutIndexed(index: qsizetype(index), value);
484
485 generateWarning(v4: that->engine(), description: QLatin1String("Index out of range during indexed set"));
486 return false;
487 }
488 return Object::virtualPut(m: that, id, value, receiver);
489}
490
491bool Sequence::virtualDeleteProperty(Managed *that, PropertyKey id)
492{
493 if (id.isArrayIndex()) {
494 const uint index = id.asArrayIndex();
495 if (qIsAtMostSizetypeLimit(length: index))
496 return static_cast<Sequence *>(that)->containerDeleteIndexedProperty(index: qsizetype(index));
497
498 generateWarning(v4: that->engine(), description: QLatin1String("Index out of range during indexed delete"));
499 return false;
500 }
501 return Object::virtualDeleteProperty(m: that, id);
502}
503
504bool Sequence::virtualIsEqualTo(Managed *that, Managed *other)
505{
506 return static_cast<Sequence *>(that)->containerIsEqualTo(other);
507}
508
509OwnPropertyKeyIterator *Sequence::virtualOwnPropertyKeys(const Object *m, Value *target)
510{
511 return containerOwnPropertyKeys(m, target);
512}
513
514int Sequence::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
515{
516 Sequence *sequence = static_cast<Sequence *>(object);
517 Q_ASSERT(sequence);
518
519 switch (call) {
520 case QMetaObject::ReadProperty: {
521 const QMetaType valueType = valueMetaType(p: sequence->d());
522 if (!sequence->loadReference())
523 return 0;
524 const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld;
525 if (metaSequence->valueMetaType() != valueType)
526 return 0; // value metatype is not what the caller expects anymore.
527
528 const void *storagePointer = sequence->d()->storagePointer();
529 if (index < 0 || index >= metaSequence->size(container: storagePointer))
530 return 0;
531 metaSequence->valueAtIndex(container: storagePointer, index, result: a[0]);
532 break;
533 }
534 case QMetaObject::WriteProperty: {
535 void *storagePointer = sequence->d()->storagePointer();
536 const QMetaSequence *metaSequence = sequence->d()->typePrivate()->extraData.ld;
537 if (index < 0 || index >= metaSequence->size(container: storagePointer))
538 return 0;
539 metaSequence->setValueAtIndex(container: storagePointer, index, value: a[0]);
540 sequence->storeReference();
541 break;
542 }
543 default:
544 return 0; // not supported
545 }
546
547 return -1;
548}
549
550static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
551{
552 QV4::Scope scope(b);
553 QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>());
554 if (!This)
555 THROW_TYPE_ERROR();
556
557 if (This->d()->isReference() && !This->loadReference())
558 return Encode::undefined();
559
560 const qsizetype size = This->size();
561 if (qIsAtMostUintLimit(length: size))
562 RETURN_RESULT(Encode(uint(size)));
563
564 return scope.engine->throwRangeError(message: QLatin1String("Sequence length out of range"));
565}
566
567static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
568{
569 QV4::Scope scope(f);
570 QV4::Scoped<Sequence> This(scope, thisObject->as<Sequence>());
571 if (!This)
572 THROW_TYPE_ERROR();
573
574 bool ok = false;
575 const quint32 argv0 = argc ? argv[0].asArrayLength(ok: &ok) : 0;
576 if (!ok || !qIsAtMostSizetypeLimit(length: argv0)) {
577 generateWarning(v4: scope.engine, description: QLatin1String("Index out of range during length set"));
578 RETURN_UNDEFINED();
579 }
580
581 if (This->d()->isReadOnly())
582 THROW_TYPE_ERROR();
583
584 const qsizetype newCount = qsizetype(argv0);
585
586 /* Read the sequence from the QObject property if we're a reference */
587 if (This->d()->isReference() && !This->loadReference())
588 RETURN_UNDEFINED();
589
590 /* Determine whether we need to modify the sequence */
591 const qsizetype count = This->size();
592 if (newCount == count) {
593 RETURN_UNDEFINED();
594 } else if (newCount > count) {
595 const QMetaType valueMetaType = metaSequence(p: This->d())->valueMetaType();
596 /* according to ECMA262r3 we need to insert */
597 /* undefined values increasing length to newLength. */
598 /* We cannot, so we insert default-values instead. */
599 This->append(num: newCount - count, item: QVariant(valueMetaType));
600 } else {
601 /* according to ECMA262r3 we need to remove */
602 /* elements until the sequence is the required length. */
603 Q_ASSERT(newCount < count);
604 This->removeLast(num: count - newCount);
605 }
606
607 /* write back if required. */
608 if (This->d()->object())
609 This->storeReference();
610
611 RETURN_UNDEFINED();
612}
613
614void SequencePrototype::init()
615{
616 defineDefaultProperty(QStringLiteral("sort"), code: method_sort, argumentCount: 1);
617 defineDefaultProperty(name: engine()->id_valueOf(), code: method_valueOf, argumentCount: 0);
618 defineAccessorProperty(QStringLiteral("length"), getter: method_get_length, setter: method_set_length);
619}
620
621ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
622{
623 return Encode(thisObject->toString(e: f->engine()));
624}
625
626ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
627{
628 Scope scope(b);
629 QV4::ScopedObject o(scope, thisObject);
630 if (!o || !o->isV4SequenceType())
631 THROW_TYPE_ERROR();
632
633 if (argc >= 2)
634 return o.asReturnedValue();
635
636 if (auto *s = o->as<Sequence>()) {
637 if (!s->sort(f: b, thisObject, argv, argc))
638 THROW_TYPE_ERROR();
639 }
640
641 return o.asReturnedValue();
642}
643
644ReturnedValue SequencePrototype::newSequence(
645 QV4::ExecutionEngine *engine, QMetaType sequenceType, const void *data,
646 Heap::Object *object, int propertyIndex, Heap::ReferenceObject::Flags flags)
647{
648 // This function is called when the property is a QObject Q_PROPERTY of
649 // the given sequence type. Internally we store a sequence
650 // (as well as object ptr + property index for updated-read and write-back)
651 // and so access/mutate avoids variant conversion.
652
653 const QQmlType qmlType = QQmlMetaType::qmlListType(metaType: sequenceType);
654 if (qmlType.isSequentialContainer()) {
655 return engine->memoryManager->allocate<Sequence>(
656 args: qmlType, args&: data, args&: object, args&: propertyIndex, args&: flags)->asReturnedValue();
657 }
658
659 return Encode::undefined();
660}
661
662ReturnedValue SequencePrototype::fromVariant(QV4::ExecutionEngine *engine, const QVariant &v)
663{
664 return fromData(engine, type: v.metaType(), data: v.constData());
665}
666
667ReturnedValue SequencePrototype::fromData(ExecutionEngine *engine, QMetaType type, const void *data)
668{
669 // This function is called when assigning a sequence value to a normal JS var
670 // in a JS block. Internally, we store a sequence of the specified type.
671 // Access and mutation is extremely fast since it will not need to modify any
672 // QObject property.
673
674 const QQmlType qmlType = QQmlMetaType::qmlListType(metaType: type);
675 if (qmlType.isSequentialContainer())
676 return engine->memoryManager->allocate<Sequence>(args: qmlType, args&: data)->asReturnedValue();
677
678 return Encode::undefined();
679}
680
681QVariant SequencePrototype::toVariant(const Sequence *object)
682{
683 Q_ASSERT(object->isV4SequenceType());
684 const auto *p = object->d();
685
686 // Note: For historical reasons, we ignore the result of loadReference()
687 // here. This allows us to retain sequences whose objects have vaninshed
688 // as "var" properties. It comes at the price of potentially returning
689 // outdated data. This is the behavior sequences have always shown.
690 if (p->isReference())
691 object->loadReference();
692 if (!p->hasData())
693 return QVariant();
694
695 return QVariant(p->typePrivate()->listId, p->storagePointer());
696}
697
698QVariant SequencePrototype::toVariant(const QV4::Value &array, QMetaType typeHint)
699{
700 if (!array.as<ArrayObject>())
701 return QVariant();
702
703 QV4::Scope scope(array.as<Object>()->engine());
704 QV4::ScopedArrayObject a(scope, array);
705
706 const QQmlType type = QQmlMetaType::qmlListType(metaType: typeHint);
707 if (type.isSequentialContainer()) {
708 const QQmlTypePrivate *priv = type.priv();
709 const QMetaSequence *meta = priv->extraData.ld;
710 const QMetaType containerMetaType(priv->listId);
711 QVariant result(containerMetaType);
712 qint64 length = a->getLength();
713 Q_ASSERT(length >= 0);
714 Q_ASSERT(length <= qint64(std::numeric_limits<quint32>::max()));
715
716 QV4::ScopedValue v(scope);
717 for (quint32 i = 0; i < quint32(length); ++i) {
718 const QMetaType valueMetaType = priv->typeId;
719 QVariant variant = ExecutionEngine::toVariant(value: a->get(idx: i), typeHint: valueMetaType, createJSValueForObjectsAndSymbols: false);
720 if (valueMetaType == QMetaType::fromType<QVariant>()) {
721 meta->addValueAtEnd(container: result.data(), value: &variant);
722 } else {
723 const QMetaType originalType = variant.metaType();
724 if (originalType != valueMetaType) {
725 const QVariant converted = QQmlValueTypeProvider::createValueType(
726 variant, valueMetaType);
727 if (converted.isValid()) {
728 variant = converted;
729 } else if (!variant.convert(type: valueMetaType)) {
730 qWarning().noquote()
731 << QLatin1String("Could not convert array value "
732 "at position %1 from %2 to %3")
733 .arg(args: QString::number(i),
734 args: QString::fromUtf8(utf8: originalType.name()),
735 args: QString::fromUtf8(utf8: valueMetaType.name()));
736 variant = QVariant(valueMetaType);
737 }
738 }
739 meta->addValueAtEnd(container: result.data(), value: variant.constData());
740 }
741 }
742 return result;
743 }
744
745 return QVariant();
746}
747
748void *SequencePrototype::getRawContainerPtr(const Sequence *object, QMetaType typeHint)
749{
750 if (object->d()->typePrivate()->listId == typeHint)
751 return object->getRawContainerPtr();
752 return nullptr;
753}
754
755QMetaType SequencePrototype::metaTypeForSequence(const Sequence *object)
756{
757 return object->d()->typePrivate()->listId;
758}
759
760} // namespace QV4
761
762QT_END_NAMESPACE
763
764#include "moc_qv4sequenceobject_p.cpp"
765

source code of qtdeclarative/src/qml/jsruntime/qv4sequenceobject.cpp