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