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