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