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