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
251 if (count < 0)
252 return false;
253
254 // https://262.ecma-international.org/6.0/#sec-array-exotic-objects
255 // [...] whenever an own property is added whose name is an array index, the
256 // value of the length property is changed, if necessary, to be one more
257 // than the numeric value of that array index [...]
258 if (index >= uint(count)) {
259 if (!prop->append)
260 return false;
261
262 for (uint times = uint(count); times < index; ++times)
263 prop->append(prop, nullptr);
264 }
265
266 if (value.isNull()) {
267 prop->replace(prop, index, nullptr);
268 return true;
269 }
270
271 QV4::Scope scope(v4);
272 QV4::ScopedObject so(scope, value.toObject(scope.engine));
273 if (auto *wrapper = so->as<QV4::QObjectWrapper>()) {
274 QObject *object = wrapper->object();
275 if (!object) {
276 prop->replace(prop, index, object);
277 return true;
278 }
279
280 const QMetaType elementType = w->d()->elementType();
281 const QMetaObject *elementMeta = elementType.metaObject();
282 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
283 qCWarning(lcIncompatibleElement)
284 << "Cannot insert" << object << "into a QML list of" << elementType.name();
285 prop->replace(prop, index, nullptr);
286 return true;
287 }
288
289 prop->replace(prop, index, object);
290 return true;
291 }
292
293 return false;
294 }
295
296 return Object::virtualPut(m, id, value, receiver);
297}
298
300{
302 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
303
304};
305
306PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
307{
308 const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
309
310 quint32 count = w->d()->property()->count ? w->d()->property()->count(w->d()->property()) : 0;
311 if (arrayIndex < count) {
312 uint index = arrayIndex;
313 ++arrayIndex;
314 if (attrs)
315 *attrs = QV4::Attr_Data;
316 if (pd) {
317 pd->value = QV4::QObjectWrapper::wrap(
318 w->engine(), w->d()->property()->at(w->d()->property(), index));
319 }
320 return PropertyKey::fromArrayIndex(index);
321 } else if (memberIndex == 0) {
322 ++memberIndex;
323 return o->engine()->id_length()->propertyKey();
324 }
325
326 // You cannot add any own properties via the regular JavaScript interfaces.
327 return PropertyKey::invalid();
328}
329
330OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
331{
332 *target = *m;
333 return new QmlListWrapperOwnPropertyKeyIterator;
334}
335
337{
338 defineDefaultProperty(QStringLiteral("pop"), method_pop, 0);
339 defineDefaultProperty(QStringLiteral("push"), method_push, 1);
340 defineDefaultProperty(QStringLiteral("shift"), method_shift, 0);
341 defineDefaultProperty(QStringLiteral("splice"), method_splice, 2);
342 defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1);
343 defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
344 defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
345 defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
346 defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length);
347}
348
349ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
350{
351 Scope scope(b);
352 ScopedObject instance(scope, thisObject->toObject(scope.engine));
353 if (!instance)
354 RETURN_UNDEFINED();
355
356 QmlListWrapper *w = instance->as<QmlListWrapper>();
357 if (!w)
358 RETURN_UNDEFINED();
359
360 QQmlListProperty<QObject> *property = w->d()->property();
361
362 if (!property->count)
363 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
364 const qsizetype len = property->count(property);
365 if (!len)
366 RETURN_UNDEFINED();
367
368 if (!property->at)
369 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
370 ScopedValue result(
371 scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, len - 1)));
372
373 if (!property->removeLast)
374 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
375 property->removeLast(property);
376
377 return result->asReturnedValue();
378}
379
380ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
381{
382 Scope scope(b);
383 ScopedObject instance(scope, thisObject->toObject(scope.engine));
384 if (!instance)
385 RETURN_UNDEFINED();
386 QmlListWrapper *w = instance->as<QmlListWrapper>();
387 if (!w)
388 RETURN_UNDEFINED();
389
390 QQmlListProperty<QObject> *property = w->d()->property();
391 if (!property->append)
392 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
393 if (!property->count)
394 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
395
396 for (int i = 0; i < argc; ++i) {
397 const Value &arg = argv[i];
398 if (!arg.isNull() && !arg.as<QObjectWrapper>())
399 THROW_TYPE_ERROR();
400 }
401
402 const qsizetype length = property->count(property);
403 if (!qIsAtMostUintLimit(length, std::numeric_limits<uint>::max() - argc))
404 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
405
406 const QMetaType elementType = w->d()->elementType();
407 const QMetaObject *elementMeta = elementType.metaObject();
408 for (int i = 0; i < argc; ++i) {
409 if (argv[i].isNull()) {
410 property->append(property, nullptr);
411 continue;
412 }
413
414 QObject *object = argv[i].as<QV4::QObjectWrapper>()->object();
415 if (!object) {
416 property->append(property, nullptr);
417 continue;
418 }
419
420 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
421 qCWarning(lcIncompatibleElement)
422 << "Cannot append" << object << "to a QML list of" << elementType.name();
423 property->append(property, nullptr);
424 continue;
425 }
426
427 property->append(property, object);
428 }
429
430 const auto actualLength = property->count(property);
431 if (actualLength != length + argc)
432 qmlWarning(property->object) << "List didn't append all objects";
433
434 return Encode(uint(actualLength));
435}
436
437ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
438{
439 Scope scope(b);
440 ScopedObject instance(scope, thisObject->toObject(scope.engine));
441 if (!instance)
442 RETURN_UNDEFINED();
443 QmlListWrapper *w = instance->as<QmlListWrapper>();
444 if (!w)
445 RETURN_UNDEFINED();
446
447 QQmlListProperty<QObject> *property = w->d()->property();
448
449 if (!property->count)
450 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
451 const qsizetype len = property->count(property);
452 if (!len)
453 RETURN_UNDEFINED();
454
455 if (!property->at)
456 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
457 ScopedValue result(scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, 0)));
458
459 if (!property->replace)
460 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
461 if (!property->removeLast)
462 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
463
464 for (qsizetype i = 1; i < len; ++i)
465 property->replace(property, i - 1, property->at(property, i));
466 property->removeLast(property);
467
468 return result->asReturnedValue();
469}
470
471ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
472{
473 Scope scope(b);
474 ScopedObject instance(scope, thisObject->toObject(scope.engine));
475 if (!instance)
476 RETURN_UNDEFINED();
477 QmlListWrapper *w = instance->as<QmlListWrapper>();
478 if (!w)
479 RETURN_UNDEFINED();
480
481 QQmlListProperty<QObject> *property = w->d()->property();
482
483 if (!property->count)
484 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
485 const qsizetype len = property->count(property);
486
487 const double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
488 qsizetype start;
489 if (rs < 0)
490 start = static_cast<qsizetype>(qMax(0., len + rs));
491 else
492 start = static_cast<qsizetype>(qMin(rs, static_cast<double>(len)));
493
494 qsizetype deleteCount = 0;
495 qsizetype itemCount = 0;
496 if (argc == 1) {
497 deleteCount = len - start;
498 } else if (argc > 1){
499 itemCount = argc - 2;
500 double dc = argv[1].toInteger();
501 deleteCount = static_cast<qsizetype>(qMin(qMax(dc, 0.), double(len - start)));
502 }
503
504 if (itemCount > deleteCount
505 && len > std::numeric_limits<qsizetype>::max() - itemCount + deleteCount) {
506 return scope.engine->throwTypeError();
507 }
508
509 if (!qIsAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
510 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
511
512 if (!property->at)
513 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
514
515 for (qsizetype i = 0; i < itemCount; ++i) {
516 const auto arg = argv[i + 2];
517 if (!arg.isNull() && !arg.as<QObjectWrapper>())
518 THROW_TYPE_ERROR();
519 }
520
521 ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
522 newArray->arrayReserve(deleteCount);
523 ScopedValue v(scope);
524 QV4::ScopedValue wrappedObject(scope);
525 for (qsizetype i = 0; i < deleteCount; ++i) {
526 wrappedObject = QObjectWrapper::wrap(scope.engine, property->at(property, start + i));
527 newArray->arrayPut(
528 i, wrappedObject);
529 }
530 newArray->setArrayLengthUnchecked(deleteCount);
531
532 if (!property->replace)
533 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
534 if (!property->removeLast)
535 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
536 if (!property->append)
537 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
538
539 if (itemCount < deleteCount) {
540 for (qsizetype k = start; k < len - deleteCount; ++k)
541 property->replace(property, k + itemCount, property->at(property, k + deleteCount));
542 for (qsizetype k = len; k > len - deleteCount + itemCount; --k)
543 property->removeLast(property);
544 } else if (itemCount > deleteCount) {
545 for (qsizetype k = 0; k < itemCount - deleteCount; ++k)
546 property->append(property, nullptr);
547 for (qsizetype k = len - deleteCount; k > start; --k) {
548 property->replace(
549 property, k + itemCount - 1, property->at(property, k + deleteCount - 1));
550 }
551 }
552
553 const QMetaType elementType = w->d()->elementType();
554 const QMetaObject *elementMeta = elementType.metaObject();
555 for (qsizetype i = 0; i < itemCount; ++i) {
556 const auto arg = argv[i + 2];
557 if (arg.isNull()) {
558 property->replace(property, start + i, nullptr);
559 continue;
560 }
561
562 QObject *object = arg.as<QObjectWrapper>()->object();
563 if (!object) {
564 property->replace(property, start + i, nullptr);
565 continue;
566 }
567
568 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
569 qCWarning(lcIncompatibleElement)
570 << "Cannot splice" << object << "into a QML list of" << elementType.name();
571 property->replace(property, start + i, nullptr);
572 continue;
573 }
574
575 property->replace(property, start + i, object);
576 }
577
578 return newArray->asReturnedValue();
579}
580
581ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
582{
583 Scope scope(b);
584 ScopedObject instance(scope, thisObject->toObject(scope.engine));
585 if (!instance)
586 RETURN_UNDEFINED();
587
588 QmlListWrapper *w = instance->as<QmlListWrapper>();
589 if (!w)
590 RETURN_UNDEFINED();
591
592 QQmlListProperty<QObject> *property = w->d()->property();
593
594 if (!property->count)
595 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
596 const qsizetype len = property->count(property);
597
598 if (std::numeric_limits<qsizetype>::max() - len < argc || !qIsAtMostUintLimit(len + argc))
599 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
600
601 if (!property->append)
602 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
603 if (!property->replace)
604 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
605
606 for (int i = 0; i < argc; ++i) {
607 const auto arg = argv[i];
608 if (!arg.isNull() && !arg.as<QObjectWrapper>())
609 THROW_TYPE_ERROR();
610 }
611
612 for (int i = 0; i < argc; ++i)
613 property->append(property, nullptr);
614 if (property->count(property) != argc + len)
615 return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
616
617 for (qsizetype k = len; k > 0; --k)
618 property->replace(property, k + argc - 1, property->at(property, k - 1));
619
620 const QMetaType elementType = w->d()->elementType();
621 const QMetaObject *elementMeta = elementType.metaObject();
622 for (int i = 0; i < argc; ++i) {
623 const auto *wrapper = argv[i].as<QObjectWrapper>();
624 QObject *object = wrapper ? wrapper->object() : nullptr;
625 if (!object) {
626 property->replace(property, i, object);
627 continue;
628 }
629
630 if (Q_UNLIKELY(!elementMeta || !QQmlMetaObject::canConvert(object, elementMeta))) {
631 qCWarning(lcIncompatibleElement)
632 << "Cannot unshift" << object << "into a QML list of" << elementType.name();
633 property->replace(property, i, nullptr);
634 continue;
635 }
636
637 property->replace(property, i, object);
638 }
639
640 return Encode(uint(len + argc));
641}
642
643template<typename Iterate>
644ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, Iterate iterate)
645{
646 Scope scope(b);
647
648 // Undefined cannot be encoded as QObject*. In particular it's not nullptr.
649 if (argc == 0)
650 THROW_TYPE_ERROR();
651
652 QObject *searchValue;
653 if (argv[0].isNull()) {
654 searchValue = nullptr;
655 } else {
656 Scoped<QObjectWrapper> wrapper(scope, argv[0]);
657 if (wrapper)
658 searchValue = wrapper->object();
659 else
660 THROW_TYPE_ERROR();
661 }
662
663 ScopedObject instance(scope, thisObject->toObject(scope.engine));
664 if (!instance)
665 RETURN_UNDEFINED();
666
667 QmlListWrapper *w = instance->as<QmlListWrapper>();
668 if (!w)
669 RETURN_UNDEFINED();
670
671 QQmlListProperty<QObject> *property = w->d()->property();
672
673 if (!property->count)
674 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
675 const qsizetype len = property->count(property);
676 if (!len)
677 return Encode(-1);
678
679
680 return iterate(scope.engine, property, len, searchValue);
681}
682
683ReturnedValue PropertyListPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
684{
685 return firstOrLastIndexOf(
686 b, thisObject, argv, argc,
687 [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
688 qsizetype len, QObject *searchValue) -> ReturnedValue {
689 qsizetype fromIndex = 0;
690 if (argc >= 2) {
691 double f = argv[1].toInteger();
692 if (hasExceptionOrIsInterrupted(engine))
693 return Encode::undefined();
694 if (f >= len)
695 return Encode(-1);
696 if (f < 0)
697 f = qMax(len + f, 0.);
698 fromIndex = qsizetype(f);
699 }
700
701 for (qsizetype i = fromIndex; i < len; ++i) {
702 if (property->at(property, i) == searchValue) {
703 if (qIsAtMostUintLimit(i))
704 return Encode(uint(i));
705 return engine->throwRangeError(QString::fromLatin1("List length out of range."));
706 }
707 }
708
709 return Encode(-1);
710 });
711}
712
713ReturnedValue PropertyListPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
714{
715 return firstOrLastIndexOf(
716 b, thisObject, argv, argc,
717 [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property,
718 qsizetype len, QObject *searchValue) -> ReturnedValue {
719 qsizetype fromIndex = len - 1;
720 if (argc >= 2) {
721 double f = argv[1].toInteger();
722 if (hasExceptionOrIsInterrupted(engine))
723 return Encode::undefined();
724 if (f > 0)
725 f = qMin(f, (double)(len - 1));
726 else if (f < 0) {
727 f = len + f;
728 if (f < 0)
729 return Encode(-1);
730 }
731 fromIndex = qsizetype(f);
732 }
733
734 for (qsizetype i = fromIndex; i >= 0; --i) {
735 if (property->at(property, i) == searchValue) {
736 if (qIsAtMostUintLimit(i))
737 return Encode(uint(i));
738 return engine->throwRangeError(QString::fromLatin1("List length out of range."));
739 }
740 }
741
742 return Encode(-1);
743 });
744}
745
746ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
747{
748 Scope scope(b);
749 ScopedObject instance(scope, thisObject->toObject(scope.engine));
750 if (!instance)
751 RETURN_UNDEFINED();
752
753 QmlListWrapper *w = instance->as<QmlListWrapper>();
754 if (!w)
755 RETURN_UNDEFINED();
756
757 QQmlListProperty<QObject> *property = w->d()->property();
758
759 if (!property->count)
760 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
761 if (property->count(property) == 0)
762 return thisObject->asReturnedValue();
763 if (!property->at)
764 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
765 if (!property->replace)
766 return scope.engine->throwTypeError(u"List doesn't define a Replace function"_s);
767
768 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
769 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
770 THROW_TYPE_ERROR();
771
772 const ArrayElementLessThan lessThan(scope.engine, comparefn);
773 sortHelper(begin(*property), end(*property), [&](QObject *a, QObject *b) {
774 Scoped<QObjectWrapper> o1(scope, QObjectWrapper::wrap(scope.engine, a));
775 Scoped<QObjectWrapper> o2(scope, QObjectWrapper::wrap(scope.engine, b));
776 return lessThan(o1, o2);
777 });
778
779 return thisObject->asReturnedValue();
780}
781
782ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
783{
784 Scope scope(b);
785 ScopedObject instance(scope, thisObject->toObject(scope.engine));
786 if (!instance)
787 RETURN_UNDEFINED();
788
789 const QmlListWrapper *w = instance->as<QmlListWrapper>();
790 if (!w)
791 RETURN_UNDEFINED();
792
793 QQmlListProperty<QObject> *property = w->d()->property();
794 if (!property->count)
795 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
796
797 qsizetype count = property->count(property);
798 if (qIsAtMostUintLimit(count))
799 return Encode(uint(count));
800
801 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
802}
803
804ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
805{
806 QV4::Scope scope(b);
807 ScopedObject instance(scope, thisObject->toObject(scope.engine));
808 if (!instance)
809 RETURN_UNDEFINED();
810
811 const QmlListWrapper *w = instance->as<QmlListWrapper>();
812 if (!w)
813 RETURN_UNDEFINED();
814
815 QQmlListProperty<QObject> *property = w->d()->property();
816
817 bool ok = false;
818 const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
819 if (!ok)
820 return scope.engine->throwRangeError(QString::fromLatin1("Invalid list length."));
821
822 if (newLength == 0 && property->clear) {
823 property->clear(property);
824 return true;
825 }
826
827 if (!property->count)
828 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
829
830 qsizetype count = property->count(property);
831 if (!qIsAtMostUintLimit(count))
832 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
833
834 if (newLength < uint(count)) {
835 if (!property->removeLast)
836 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
837
838 for (uint i = count; i > newLength; --i)
839 property->removeLast(property);
840
841 return true;
842 }
843
844 if (!property->append)
845 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
846
847 for (uint i = count; i < newLength; ++i)
848 property->append(property, nullptr);
849
850 count = property->count(property);
851 if (!qIsAtMostUintLimit(count))
852 return scope.engine->throwRangeError(QString::fromLatin1("List length out of range."));
853
854 if (uint(count) != newLength)
855 return scope.engine->throwTypeError(u"List doesn't append null objects"_s);
856
857 return true;
858
859}
860
861QT_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