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
qqmllistwrapper.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
5
6#include <QtQml/qqmlinfo.h>
7#include <QtQml/private/qqmlproperty_p.h>
8
9#include <private/qqmllist_p.h>
10
11#include <private/qv4arrayiterator_p.h>
12#include <private/qv4arrayobject_p.h>
13#include <private/qv4functionobject_p.h>
14#include <private/qv4objectiterator_p.h>
15#include <private/qv4objectproto_p.h>
16#include <private/qv4qobjectwrapper_p.h>
17#include <private/qv4symbol_p.h>
18
20
21Q_LOGGING_CATEGORY(lcIncompatibleElement, "qt.qml.list.incompatible")
22
23using namespace QV4;
24using namespace Qt::StringLiterals;
25
27
28static void setArrayData(Heap::QmlListWrapper *d)
29{
30 QV4::Scope scope(d->internalClass->engine);
31 QV4::ScopedObject o(scope, d);
32 o->arrayCreate();
33}
34
36{
39
40 ListWrapperObject(QQmlListProperty<QObject> *p)
41 : scope(static_cast<Heap::QmlListWrapper *>(p->data)->internalClass->engine)
42 , object(scope, static_cast<Heap::QmlListWrapper *>(p->data))
43 {
46 }
47
49 {
50 return object->arrayData();
51 }
52};
53
54static void appendWrapped(QQmlListProperty<QObject> *p, QObject *o)
55{
56 ListWrapperObject object(p);
57 Heap::ArrayData *arrayData = object.arrayData();
58
59 const uint length = arrayData->length();
60 if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
61 object.scope.engine->throwRangeError(QLatin1String("Too many elements."));
62 return;
63 }
64
65 ArrayData::realloc(object.object, Heap::ArrayData::Simple, length + 1, false);
66 QV4::Scope scope(object.scope.engine);
67 QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(object.scope.engine, o));
68 arrayData->vtable()->put(
69 object.object, length, wrappedObject);
70}
71
72static qsizetype countWrapped(QQmlListProperty<QObject> *p)
73{
74 ListWrapperObject object(p);
75 return object.arrayData()->length();
76}
77
78static QObject *atWrapped(QQmlListProperty<QObject> *p, qsizetype i)
79{
80 ListWrapperObject object(p);
81 QV4::Scoped<QObjectWrapper> result(object.scope, object.arrayData()->get(i));
82 return result ? result->object() : nullptr;
83}
84
85static void clearWrapped(QQmlListProperty<QObject> *p)
86{
87 ListWrapperObject object(p);
88 object.arrayData()->vtable()->truncate(object.object, 0);
89}
90
91static void replaceWrapped(QQmlListProperty<QObject> *p, qsizetype i, QObject *o)
92{
93 ListWrapperObject object(p);
94 QV4::Scope scope(object.scope.engine);
95 QV4::ScopedObject wrappedObject(scope, QV4::QObjectWrapper::wrap(object.scope.engine, o));
96 object.arrayData()->vtable()->put(
97 object.object, i, wrappedObject);
98}
99
100static void removeLastWrapped(QQmlListProperty<QObject> *p)
101{
102 ListWrapperObject object(p);
103 Heap::ArrayData *arrayData = object.arrayData();
104 const uint length = arrayData->length();
105 if (length > 0)
106 arrayData->vtable()->truncate(object.object, length - 1);
107}
108
109void Heap::QmlListWrapper::init(QMetaType propertyType)
110{
111 Object::init();
112 m_object.init();
113 m_propertyType = propertyType.iface();
114 setArrayData(this);
115 *property() = QQmlListProperty<QObject>(
116 nullptr, this,
117 appendWrapped, countWrapped, atWrapped,
118 clearWrapped, replaceWrapped, removeLastWrapped);
119}
120
121void Heap::QmlListWrapper::init(QObject *object, int propertyId, QMetaType propertyType)
122{
123 Object::init();
124 m_object.init(object);
125 m_propertyType = propertyType.iface();
126 void *args[] = { property(), nullptr };
127 QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyId, args);
128}
129
130void Heap::QmlListWrapper::init(
131 QObject *object, const QQmlListProperty<QObject> &list, QMetaType propertyType)
132{
133 Object::init();
134 m_object.init(object);
135 m_propertyType = propertyType.iface();
136 *property() = list;
137}
138
139void Heap::QmlListWrapper::destroy()
140{
141 m_object.destroy();
142 Object::destroy();
143}
144
145ReturnedValue QmlListWrapper::create(
146 ExecutionEngine *engine, QObject *object, int propId, QMetaType propType)
147{
148 if (!object || propId == -1)
149 return Encode::null();
150 return engine->memoryManager->allocate<QmlListWrapper>(object, propId, propType)
151 ->asReturnedValue();
152}
153
154ReturnedValue QmlListWrapper::create(
155 ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType)
156{
157 return engine->memoryManager->allocate<QmlListWrapper>(prop.object, prop, propType)
158 ->asReturnedValue();
159}
160
161ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QMetaType propType)
162{
163 return engine->memoryManager->allocate<QmlListWrapper>(propType)->asReturnedValue();
164}
165
166ReturnedValue QmlListWrapper::createOwned(ExecutionEngine *engine, const QQmlProperty &prop)
167{
168 Q_ASSERT(prop.propertyMetaType().flags() & QMetaType::IsQmlList);
169 QQmlPropertyPrivate *privProp = QQmlPropertyPrivate::get(prop);
170 QQmlListProperty<QObject> listProp;
171 privProp->core.readProperty(privProp->object, &listProp);
172 const qsizetype listSize = listProp.count(&listProp);
173 Scope scope(engine);
174 Scoped<QV4::QmlListWrapper> listWrapper(scope, QV4::QmlListWrapper::create(engine, privProp->propertyType()));
175 // compare with appendWrapped; we do not call appendWrapped directly as that would put the setup code
176 // into the loop body
177 Heap::ArrayData *arrayData = listWrapper->arrayData();
178 ArrayData::realloc(listWrapper, Heap::ArrayData::Simple, listSize, false);
179 for (qsizetype i = 0; i != listSize; ++i) {
180 QObject *o = listProp.at(&listProp, i);
181 ScopedObject scopedO(scope, QV4::QObjectWrapper::wrap(engine, o));
182 arrayData->vtable()->put(listWrapper, i, scopedO);
183 }
184 return listWrapper.asReturnedValue();
185}
186
187QVariant QmlListWrapper::toVariant() const
188{
189 Heap::QmlListWrapper *p = d();
190 return p->object()
191 ? QVariant::fromValue(toListReference())
192 : QVariant::fromValue(p->property()->toList<QObjectList>());
193}
194
195QQmlListReference QmlListWrapper::toListReference() const
196{
197 const Heap::QmlListWrapper *wrapper = d();
198 return QQmlListReferencePrivate::init(*wrapper->property(), wrapper->propertyType());
199}
200
201ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
202{
203 Q_ASSERT(m->as<QmlListWrapper>());
204 const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
205 QV4::ExecutionEngine *v4 = w->engine();
206
207 if (id.isArrayIndex()) {
208 const uint index = id.asArrayIndex();
209 const quint32 count = w->d()->property()->count
210 ? w->d()->property()->count(w->d()->property())
211 : 0;
212 if (index < count && w->d()->property()->at) {
213 if (hasProperty)
214 *hasProperty = true;
215 return QV4::QObjectWrapper::wrap(v4, w->d()->property()->at(w->d()->property(), index));
216 }
217
218 if (hasProperty)
219 *hasProperty = false;
220 return Value::undefinedValue().asReturnedValue();
221 }
222
223 return Object::virtualGet(m, id, receiver, hasProperty);
224}
225
226qint64 QmlListWrapper::virtualGetLength(const Managed *m)
227{
228 Q_ASSERT(m->as<QmlListWrapper>());
229 QQmlListProperty<QObject> *property = static_cast<const QmlListWrapper *>(m)->d()->property();
230 Q_ASSERT(property);
231 return property->count ? property->count(property) : 0;
232}
233
234bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
235{
236 Q_ASSERT(m->as<QmlListWrapper>());
237
238 const auto *w = static_cast<const QmlListWrapper *>(m);
239 QV4::ExecutionEngine *v4 = w->engine();
240
241 QQmlListProperty<QObject> *prop = w->d()->property();
242
243 if (id.isArrayIndex()) {
244 if (!prop->count || !prop->replace)
245 return false;
246
247 const uint index = id.asArrayIndex();
248 const int count = prop->count(prop);
249 if (count < 0 || index >= uint(count))
250 return false;
251
252 if (value.isNull()) {
253 prop->replace(prop, index, nullptr);
254 return true;
255 }
256
257 QV4::Scope scope(v4);
258 QV4::ScopedObject so(scope, value.toObject(scope.engine));
259 if (auto *wrapper = so->as<QV4::QObjectWrapper>()) {
260 QObject *object = wrapper->object();
261 if (!object) {
262 prop->replace(prop, index, object);
263 return true;
264 }
265
266 const QMetaType elementType = w->d()->elementType();
267 const QMetaObject *elementMeta = elementType.metaObject();
268 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
269 qCWarning(lcIncompatibleElement)
270 << "Cannot insert" << object << "into a QML list of" << elementType.name();
271 prop->replace(prop, index, nullptr);
272 return true;
273 }
274
275 prop->replace(prop, index, object);
276 return true;
277 }
278
279 return false;
280 }
281
282 return Object::virtualPut(m, id, value, receiver);
283}
284
286{
288 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
289
290};
291
292PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
293{
294 const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
295
296 quint32 count = w->d()->property()->count ? w->d()->property()->count(w->d()->property()) : 0;
297 if (arrayIndex < count) {
298 uint index = arrayIndex;
299 ++arrayIndex;
300 if (attrs)
301 *attrs = QV4::Attr_Data;
302 if (pd) {
303 pd->value = QV4::QObjectWrapper::wrap(
304 w->engine(), w->d()->property()->at(w->d()->property(), index));
305 }
306 return PropertyKey::fromArrayIndex(index);
307 } else if (memberIndex == 0) {
308 ++memberIndex;
309 return o->engine()->id_length()->propertyKey();
310 }
311
312 // You cannot add any own properties via the regular JavaScript interfaces.
313 return PropertyKey::invalid();
314}
315
316OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
317{
318 *target = *m;
319 return new QmlListWrapperOwnPropertyKeyIterator;
320}
321
323{
324 defineDefaultProperty(QStringLiteral("pop"), method_pop, 0);
325 defineDefaultProperty(QStringLiteral("push"), method_push, 1);
326 defineDefaultProperty(QStringLiteral("shift"), method_shift, 0);
327 defineDefaultProperty(QStringLiteral("splice"), method_splice, 2);
328 defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1);
329 defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
330 defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
331 defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
332 defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length);
333}
334
335ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
336{
337 Scope scope(b);
338 ScopedObject instance(scope, thisObject->toObject(scope.engine));
339 if (!instance)
340 RETURN_UNDEFINED();
341
342 QmlListWrapper *w = instance->as<QmlListWrapper>();
343 if (!w)
344 RETURN_UNDEFINED();
345
346 QQmlListProperty<QObject> *property = w->d()->property();
347
348 if (!property->count)
349 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
350 const qsizetype len = property->count(property);
351 if (!len)
352 RETURN_UNDEFINED();
353
354 if (!property->at)
355 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
356 ScopedValue result(
357 scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, len - 1)));
358
359 if (!property->removeLast)
360 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
361 property->removeLast(property);
362
363 return result->asReturnedValue();
364}
365
366ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
367{
368 Scope scope(b);
369 ScopedObject instance(scope, thisObject->toObject(scope.engine));
370 if (!instance)
371 RETURN_UNDEFINED();
372 QmlListWrapper *w = instance->as<QmlListWrapper>();
373 if (!w)
374 RETURN_UNDEFINED();
375
376 QQmlListProperty<QObject> *property = w->d()->property();
377 if (!property->append)
378 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
379 if (!property->count)
380 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
381
382 for (int i = 0; i < argc; ++i) {
383 const Value &arg = argv[i];
384 if (!arg.isNull() && !arg.as<QObjectWrapper>())
385 THROW_TYPE_ERROR();
386 }
387
388 const qsizetype length = property->count(property);
389 if (!qIsAtMostUintLimit(length, std::numeric_limits<uint>::max() - argc))
390 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
391
392 const QMetaType elementType = w->d()->elementType();
393 const QMetaObject *elementMeta = elementType.metaObject();
394 for (int i = 0; i < argc; ++i) {
395 if (argv[i].isNull()) {
396 property->append(property, nullptr);
397 continue;
398 }
399
400 QObject *object = argv[i].as<QV4::QObjectWrapper>()->object();
401 if (!object) {
402 property->append(property, nullptr);
403 continue;
404 }
405
406 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
407 qCWarning(lcIncompatibleElement)
408 << "Cannot append" << object << "to a QML list of" << elementType.name();
409 property->append(property, nullptr);
410 continue;
411 }
412
413 property->append(property, object);
414 }
415
416 const auto actualLength = property->count(property);
417 if (actualLength != length + argc)
418 qmlWarning(property->object) << "List didn't append all objects";
419
420 return Encode(uint(actualLength));
421}
422
423ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
424{
425 Scope scope(b);
426 ScopedObject instance(scope, thisObject->toObject(scope.engine));
427 if (!instance)
428 RETURN_UNDEFINED();
429 QmlListWrapper *w = instance->as<QmlListWrapper>();
430 if (!w)
431 RETURN_UNDEFINED();
432
433 QQmlListProperty<QObject> *property = w->d()->property();
434
435 if (!property->count)
436 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
437 const qsizetype len = property->count(property);
438 if (!len)
439 RETURN_UNDEFINED();
440
441 if (!property->at)
442 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
443 ScopedValue result(scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, 0)));
444
445 if (!property->replace)
446 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
447 if (!property->removeLast)
448 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
449
450 for (qsizetype i = 1; i < len; ++i)
451 property->replace(property, i - 1, property->at(property, i));
452 property->removeLast(property);
453
454 return result->asReturnedValue();
455}
456
457ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
458{
459 Scope scope(b);
460 ScopedObject instance(scope, thisObject->toObject(scope.engine));
461 if (!instance)
462 RETURN_UNDEFINED();
463 QmlListWrapper *w = instance->as<QmlListWrapper>();
464 if (!w)
465 RETURN_UNDEFINED();
466
467 QQmlListProperty<QObject> *property = w->d()->property();
468
469 if (!property->count)
470 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
471 const qsizetype len = property->count(property);
472
473 const double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
474 qsizetype start;
475 if (rs < 0)
476 start = static_cast<qsizetype>(qMax(0., len + rs));
477 else
478 start = static_cast<qsizetype>(qMin(rs, static_cast<double>(len)));
479
480 qsizetype deleteCount = 0;
481 qsizetype itemCount = 0;
482 if (argc == 1) {
483 deleteCount = len - start;
484 } else if (argc > 1){
485 itemCount = argc - 2;
486 double dc = argv[1].toInteger();
487 deleteCount = static_cast<qsizetype>(qMin(qMax(dc, 0.), double(len - start)));
488 }
489
490 if (itemCount > deleteCount
491 && len > std::numeric_limits<qsizetype>::max() - itemCount + deleteCount) {
492 return scope.engine->throwTypeError();
493 }
494
495 if (!qIsAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
496 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
497
498 if (!property->at)
499 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
500
501 for (qsizetype i = 0; i < itemCount; ++i) {
502 const auto arg = argv[i + 2];
503 if (!arg.isNull() && !arg.as<QObjectWrapper>())
504 THROW_TYPE_ERROR();
505 }
506
507 ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
508 newArray->arrayReserve(deleteCount);
509 ScopedValue v(scope);
510 QV4::ScopedValue wrappedObject(scope);
511 for (qsizetype i = 0; i < deleteCount; ++i) {
512 wrappedObject = QObjectWrapper::wrap(scope.engine, property->at(property, start + i));
513 newArray->arrayPut(
514 i, wrappedObject);
515 }
516 newArray->setArrayLengthUnchecked(deleteCount);
517
518 if (!property->replace)
519 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
520 if (!property->removeLast)
521 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
522 if (!property->append)
523 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
524
525 if (itemCount < deleteCount) {
526 for (qsizetype k = start; k < len - deleteCount; ++k)
527 property->replace(property, k + itemCount, property->at(property, k + deleteCount));
528 for (qsizetype k = len; k > len - deleteCount + itemCount; --k)
529 property->removeLast(property);
530 } else if (itemCount > deleteCount) {
531 for (qsizetype k = 0; k < itemCount - deleteCount; ++k)
532 property->append(property, nullptr);
533 for (qsizetype k = len - deleteCount; k > start; --k) {
534 property->replace(
535 property, k + itemCount - 1, property->at(property, k + deleteCount - 1));
536 }
537 }
538
539 const QMetaType elementType = w->d()->elementType();
540 const QMetaObject *elementMeta = elementType.metaObject();
541 for (qsizetype i = 0; i < itemCount; ++i) {
542 const auto arg = argv[i + 2];
543 if (arg.isNull()) {
544 property->replace(property, start + i, nullptr);
545 continue;
546 }
547
548 QObject *object = arg.as<QObjectWrapper>()->object();
549 if (!object) {
550 property->replace(property, start + i, nullptr);
551 continue;
552 }
553
554 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
555 qCWarning(lcIncompatibleElement)
556 << "Cannot splice" << object << "into a QML list of" << elementType.name();
557 property->replace(property, start + i, nullptr);
558 continue;
559 }
560
561 property->replace(property, start + i, object);
562 }
563
564 return newArray->asReturnedValue();
565}
566
567ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
568{
569 Scope scope(b);
570 ScopedObject instance(scope, thisObject->toObject(scope.engine));
571 if (!instance)
572 RETURN_UNDEFINED();
573
574 QmlListWrapper *w = instance->as<QmlListWrapper>();
575 if (!w)
576 RETURN_UNDEFINED();
577
578 QQmlListProperty<QObject> *property = w->d()->property();
579
580 if (!property->count)
581 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
582 const qsizetype len = property->count(property);
583
584 if (std::numeric_limits<qsizetype>::max() - len < argc || !qIsAtMostUintLimit(len + argc))
585 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
586
587 if (!property->append)
588 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
589 if (!property->replace)
590 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
591
592 for (int i = 0; i < argc; ++i) {
593 const auto arg = argv[i];
594 if (!arg.isNull() && !arg.as<QObjectWrapper>())
595 THROW_TYPE_ERROR();
596 }
597
598 for (int i = 0; i < argc; ++i)
599 property->append(property, nullptr);
600 if (property->count(property) != argc + len)
601 return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
602
603 for (qsizetype k = len; k > 0; --k)
604 property->replace(property, k + argc - 1, property->at(property, k - 1));
605
606 const QMetaType elementType = w->d()->elementType();
607 const QMetaObject *elementMeta = elementType.metaObject();
608 for (int i = 0; i < argc; ++i) {
609 const auto *wrapper = argv[i].as<QObjectWrapper>();
610 QObject *object = wrapper ? wrapper->object() : nullptr;
611 if (!object) {
612 property->replace(property, i, object);
613 continue;
614 }
615
616 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
617 qCWarning(lcIncompatibleElement)
618 << "Cannot unshift" << object << "into a QML list of" << elementType.name();
619 property->replace(property, i, nullptr);
620 continue;
621 }
622
623 property->replace(property, i, object);
624 }
625
626 return Encode(uint(len + argc));
627}
628
629template<typename Iterate>
630ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, Iterate iterate)
631{
632 Scope scope(b);
633
634 // Undefined cannot be encoded as QObject*. In particular it's not nullptr.
635 if (argc == 0)
636 THROW_TYPE_ERROR();
637
638 QObject *searchValue;
639 if (argv[0].isNull()) {
640 searchValue = nullptr;
641 } else {
642 Scoped<QObjectWrapper> wrapper(scope, argv[0]);
643 if (wrapper)
644 searchValue = wrapper->object();
645 else
646 THROW_TYPE_ERROR();
647 }
648
649 ScopedObject instance(scope, thisObject->toObject(scope.engine));
650 if (!instance)
651 RETURN_UNDEFINED();
652
653 QmlListWrapper *w = instance->as<QmlListWrapper>();
654 if (!w)
655 RETURN_UNDEFINED();
656
657 QQmlListProperty<QObject> *property = w->d()->property();
658
659 if (!property->count)
660 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
661 const qsizetype len = property->count(property);
662 if (!len)
663 return Encode(-1);
664
665
666 return iterate(scope.engine, property, len, searchValue);
667}
668
669ReturnedValue PropertyListPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
670{
671 return firstOrLastIndexOf(
672 b, thisObject, argv, argc,
673 [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
674 qsizetype len, QObject *searchValue) -> ReturnedValue {
675 qsizetype fromIndex = 0;
676 if (argc >= 2) {
677 double f = argv[1].toInteger();
678 if (hasExceptionOrIsInterrupted(engine))
679 return Encode::undefined();
680 if (f >= len)
681 return Encode(-1);
682 if (f < 0)
683 f = qMax(len + f, 0.);
684 fromIndex = qsizetype(f);
685 }
686
687 for (qsizetype i = fromIndex; i < len; ++i) {
688 if (property->at(property, i) == searchValue) {
689 if (qIsAtMostUintLimit(i))
690 return Encode(uint(i));
691 return engine->throwRangeError(QString::fromLatin1("List length out of range."));
692 }
693 }
694
695 return Encode(-1);
696 });
697}
698
699ReturnedValue PropertyListPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
700{
701 return firstOrLastIndexOf(
702 b, thisObject, argv, argc,
703 [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
704 qsizetype len, QObject *searchValue) -> ReturnedValue {
705 qsizetype fromIndex = len - 1;
706 if (argc >= 2) {
707 double f = argv[1].toInteger();
708 if (hasExceptionOrIsInterrupted(engine))
709 return Encode::undefined();
710 if (f > 0)
711 f = qMin(f, (double)(len - 1));
712 else if (f < 0) {
713 f = len + f;
714 if (f < 0)
715 return Encode(-1);
716 }
717 fromIndex = qsizetype(f);
718 }
719
720 for (qsizetype i = fromIndex; i >= 0; --i) {
721 if (property->at(property, i) == searchValue) {
722 if (qIsAtMostUintLimit(i))
723 return Encode(uint(i));
724 return engine->throwRangeError(QString::fromLatin1("List length out of range."));
725 }
726 }
727
728 return Encode(-1);
729 });
730}
731
732ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
733{
734 Scope scope(b);
735 ScopedObject instance(scope, thisObject->toObject(scope.engine));
736 if (!instance)
737 RETURN_UNDEFINED();
738
739 QmlListWrapper *w = instance->as<QmlListWrapper>();
740 if (!w)
741 RETURN_UNDEFINED();
742
743 QQmlListProperty<QObject> *property = w->d()->property();
744
745 if (!property->count)
746 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
747 if (property->count(property) == 0)
748 return thisObject->asReturnedValue();
749 if (!property->at)
750 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
751 if (!property->replace)
752 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
753
754 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
755 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
756 THROW_TYPE_ERROR();
757
758 const ArrayElementLessThan lessThan(scope.engine, comparefn);
759 sortHelper(begin(*property), end(*property), [&](QObject *a, QObject *b) {
760 Scoped<QObjectWrapper> o1(scope, QObjectWrapper::wrap(scope.engine, a));
761 Scoped<QObjectWrapper> o2(scope, QObjectWrapper::wrap(scope.engine, b));
762 return lessThan(o1, o2);
763 });
764
765 return thisObject->asReturnedValue();
766}
767
768ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
769{
770 Scope scope(b);
771 ScopedObject instance(scope, thisObject->toObject(scope.engine));
772 if (!instance)
773 RETURN_UNDEFINED();
774
775 const QmlListWrapper *w = instance->as<QmlListWrapper>();
776 if (!w)
777 RETURN_UNDEFINED();
778
779 QQmlListProperty<QObject> *property = w->d()->property();
780 if (!property->count)
781 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
782
783 qsizetype count = property->count(property);
784 if (qIsAtMostUintLimit(count))
785 return Encode(uint(count));
786
787 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
788}
789
790ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
791{
792 QV4::Scope scope(b);
793 ScopedObject instance(scope, thisObject->toObject(scope.engine));
794 if (!instance)
795 RETURN_UNDEFINED();
796
797 const QmlListWrapper *w = instance->as<QmlListWrapper>();
798 if (!w)
799 RETURN_UNDEFINED();
800
801 QQmlListProperty<QObject> *property = w->d()->property();
802
803 bool ok = false;
804 const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
805 if (!ok)
806 return scope.engine->throwRangeError(QString::fromLatin1("Invalid list length."));
807
808 if (newLength == 0 && property->clear) {
809 property->clear(property);
810 return true;
811 }
812
813 if (!property->count)
814 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
815
816 qsizetype count = property->count(property);
817 if (!qIsAtMostUintLimit(count))
818 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
819
820 if (newLength < uint(count)) {
821 if (!property->removeLast)
822 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
823
824 for (uint i = count; i > newLength; --i)
825 property->removeLast(property);
826
827 return true;
828 }
829
830 if (!property->append)
831 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
832
833 for (uint i = count; i < newLength; ++i)
834 property->append(property, nullptr);
835
836 count = property->count(property);
837 if (!qIsAtMostUintLimit(count))
838 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
839
840 if (uint(count) != newLength)
841 return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
842
843 return true;
844
845}
846
847QT_END_NAMESPACE
Combined button and popup list for selecting options.
static void appendWrapped(QQmlListProperty< QObject > *p, QObject *o)
static QObject * atWrapped(QQmlListProperty< QObject > *p, qsizetype i)
static void clearWrapped(QQmlListProperty< QObject > *p)
DEFINE_OBJECT_VTABLE(QmlListWrapper)
static void removeLastWrapped(QQmlListProperty< QObject > *p)
static qsizetype countWrapped(QQmlListProperty< QObject > *p)
static void replaceWrapped(QQmlListProperty< QObject > *p, qsizetype i, QObject *o)
ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, Iterate iterate)
static void setArrayData(Heap::QmlListWrapper *d)
QV4::ScopedObject object
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~QmlListWrapperOwnPropertyKeyIterator() override=default