57 ListWrapperObject object(p);
58 Heap::ArrayData *arrayData = object.arrayData();
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."));
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);
167ReturnedValue QmlListWrapper::createOwned(ExecutionEngine *engine,
const QQmlProperty &prop)
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);
175 Scoped<QV4::QmlListWrapper> listWrapper(scope, QV4::QmlListWrapper::create(engine, privProp->propertyType()));
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);
186 return listWrapper.asReturnedValue();
207ReturnedValue QmlListWrapper::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
209 Q_ASSERT(m->as<QmlListWrapper>());
210 const QmlListWrapper *w =
static_cast<
const QmlListWrapper *>(m);
211 QV4::ExecutionEngine *v4 = w->engine();
213 if (id.isArrayIndex()) {
214 if (w->d()->hasDeadOwner()) {
216 *hasProperty =
false;
217 return Value::undefinedValue().asReturnedValue();
220 const uint index = id.asArrayIndex();
221 const quint32 count = w->d()->property()->count
222 ? w->d()->property()->count(w->d()->property())
224 if (index < count && w->d()->property()->at) {
227 return QV4::QObjectWrapper::wrap(v4, w->d()->property()->at(w->d()->property(), index));
231 *hasProperty =
false;
232 return Value::undefinedValue().asReturnedValue();
235 return Object::virtualGet(m, id, receiver, hasProperty);
249bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
251 Q_ASSERT(m->as<QmlListWrapper>());
253 const auto *w =
static_cast<
const QmlListWrapper *>(m);
254 QV4::ExecutionEngine *v4 = w->engine();
256 QQmlListProperty<QObject> *prop = w->d()->property();
258 if (id.isArrayIndex()) {
259 if (w->d()->hasDeadOwner())
262 if (!prop->count || !prop->replace)
265 const uint index = id.asArrayIndex();
266 const int count = prop->count(prop);
275 if (index >= uint(count)) {
279 for (uint times = uint(count); times <= index; ++times)
280 prop->append(prop,
nullptr);
283 if (value.isNull()) {
284 prop->replace(prop, index,
nullptr);
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();
293 prop->replace(prop, index, object);
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);
306 prop->replace(prop, index, object);
313 return Object::virtualPut(m, id, value, receiver);
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);
370ReturnedValue
PropertyListPrototype::method_pop(
const FunctionObject *b,
const Value *thisObject,
const Value *,
int)
373 ScopedObject instance(scope, thisObject->toObject(scope.engine));
377 QmlListWrapper *w = instance->as<QmlListWrapper>();
381 if (w->d()->hasDeadOwner())
384 QQmlListProperty<QObject> *property = w->d()->property();
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);
393 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
395 scope, QV4::QObjectWrapper::wrap(scope.engine, property->at(property, len - 1)));
397 if (!property->removeLast)
398 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
399 property->removeLast(property);
401 return result->asReturnedValue();
404ReturnedValue
PropertyListPrototype::method_push(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
407 ScopedObject instance(scope, thisObject->toObject(scope.engine));
410 QmlListWrapper *w = instance->as<QmlListWrapper>();
414 if (w->d()->hasDeadOwner())
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);
423 for (
int i = 0; i < argc; ++i) {
424 const Value &arg = argv[i];
425 if (!arg.isNull() && !arg.as<QObjectWrapper>())
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."));
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);
441 QObject *object = argv[i].as<QV4::QObjectWrapper>()->object();
443 property->append(property,
nullptr);
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);
454 property->append(property, object);
457 const auto actualLength = property->count(property);
458 if (actualLength != length + argc)
459 qmlWarning(property->object) <<
"List didn't append all objects";
461 return Encode(uint(actualLength));
464ReturnedValue
PropertyListPrototype::method_shift(
const FunctionObject *b,
const Value *thisObject,
const Value *,
int)
467 ScopedObject instance(scope, thisObject->toObject(scope.engine));
470 QmlListWrapper *w = instance->as<QmlListWrapper>();
474 if (w->d()->hasDeadOwner())
477 QQmlListProperty<QObject> *property = w->d()->property();
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);
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)));
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);
494 for (qsizetype i = 1; i < len; ++i)
495 property->replace(property, i - 1, property->at(property, i));
496 property->removeLast(property);
498 return result->asReturnedValue();
501ReturnedValue
PropertyListPrototype::method_splice(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
504 ScopedObject instance(scope, thisObject->toObject(scope.engine));
507 QmlListWrapper *w = instance->as<QmlListWrapper>();
511 if (w->d()->hasDeadOwner())
514 QQmlListProperty<QObject> *property = w->d()->property();
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);
520 const double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
523 start =
static_cast<qsizetype>(qMax(0., len + rs));
525 start =
static_cast<qsizetype>(qMin(rs,
static_cast<
double>(len)));
527 qsizetype deleteCount = 0;
528 qsizetype itemCount = 0;
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)));
537 if (itemCount > deleteCount
538 && len > std::numeric_limits<qsizetype>::max() - itemCount + deleteCount) {
539 return scope.engine->throwTypeError();
542 if (!qIsAtMostUintLimit(deleteCount, std::numeric_limits<uint>::max() - 1))
543 return scope.engine->throwRangeError(QString::fromLatin1(
"List length out of range."));
546 return scope.engine->throwTypeError(u"List doesn't define an At function"_s);
548 for (qsizetype i = 0; i < itemCount; ++i) {
549 const auto arg = argv[i + 2];
550 if (!arg.isNull() && !arg.as<QObjectWrapper>())
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));
563 newArray->setArrayLengthUnchecked(deleteCount);
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);
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) {
582 property, k + itemCount - 1, property->at(property, k + deleteCount - 1));
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];
591 property->replace(property, start + i,
nullptr);
595 QObject *object = arg.as<QObjectWrapper>()->object();
597 property->replace(property, start + i,
nullptr);
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);
608 property->replace(property, start + i, object);
611 return newArray->asReturnedValue();
614ReturnedValue
PropertyListPrototype::method_unshift(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
617 ScopedObject instance(scope, thisObject->toObject(scope.engine));
621 QmlListWrapper *w = instance->as<QmlListWrapper>();
625 if (w->d()->hasDeadOwner())
628 QQmlListProperty<QObject> *property = w->d()->property();
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);
634 if (std::numeric_limits<qsizetype>::max() - len < argc || !qIsAtMostUintLimit(len + argc))
635 return scope.engine->throwRangeError(QString::fromLatin1(
"List length out of range."));
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);
642 for (
int i = 0; i < argc; ++i) {
643 const auto arg = argv[i];
644 if (!arg.isNull() && !arg.as<QObjectWrapper>())
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);
653 for (qsizetype k = len; k > 0; --k)
654 property->replace(property, k + argc - 1, property->at(property, k - 1));
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;
662 property->replace(property, i, object);
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);
673 property->replace(property, i, object);
676 return Encode(uint(len + argc));
688 QObject *searchValue;
689 if (argv[0].isNull()) {
690 searchValue =
nullptr;
692 Scoped<QObjectWrapper> wrapper(scope, argv[0]);
694 searchValue = wrapper->object();
699 ScopedObject instance(scope, thisObject->toObject(scope.engine));
703 QmlListWrapper *w = instance->as<QmlListWrapper>();
707 if (w->d()->hasDeadOwner())
710 QQmlListProperty<QObject> *property = w->d()->property();
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);
719 return iterate(scope.engine, property, len, searchValue);
722ReturnedValue
PropertyListPrototype::method_indexOf(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
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;
730 double f = argv[1].toInteger();
731 if (hasExceptionOrIsInterrupted(engine))
732 return Encode::undefined();
736 f = qMax(len + f, 0.);
737 fromIndex = qsizetype(f);
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."));
752ReturnedValue
PropertyListPrototype::method_lastIndexOf(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
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;
760 double f = argv[1].toInteger();
761 if (hasExceptionOrIsInterrupted(engine))
762 return Encode::undefined();
764 f = qMin(f, (
double)(len - 1));
770 fromIndex = qsizetype(f);
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."));
785ReturnedValue
PropertyListPrototype::method_sort(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
788 ScopedObject instance(scope, thisObject->toObject(scope.engine));
792 QmlListWrapper *w = instance->as<QmlListWrapper>();
796 if (w->d()->hasDeadOwner())
799 QQmlListProperty<QObject> *property = w->d()->property();
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();
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);
810 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
811 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
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);
821 return thisObject->asReturnedValue();
849ReturnedValue
PropertyListPrototype::method_set_length(
const FunctionObject *b,
const Value *thisObject,
const Value *argv,
int argc)
852 ScopedObject instance(scope, thisObject->toObject(scope.engine));
856 const QmlListWrapper *w = instance->as<QmlListWrapper>();
860 if (w->d()->hasDeadOwner())
863 QQmlListProperty<QObject> *property = w->d()->property();
866 const uint newLength = argc ? argv[0].asArrayLength(&ok) : 0;
868 return scope.engine->throwRangeError(QString::fromLatin1(
"Invalid list length."));
870 if (newLength == 0 && property->clear) {
871 property->clear(property);
875 if (!property->count)
876 return scope.engine->throwTypeError(u"List doesn't define a Count function"_s);
878 qsizetype count = property->count(property);
879 if (!qIsAtMostUintLimit(count))
880 return scope.engine->throwRangeError(QString::fromLatin1(
"List length out of range."));
882 if (newLength < uint(count)) {
883 if (!property->removeLast)
884 return scope.engine->throwTypeError(u"List doesn't define a RemoveLast function"_s);
886 for (uint i = count; i > newLength; --i)
887 property->removeLast(property);
892 if (!property->append)
893 return scope.engine->throwTypeError(u"List doesn't define an Append function"_s);
895 for (uint i = count; i < newLength; ++i)
896 property->append(property,
nullptr);
898 count = property->count(property);
899 if (!qIsAtMostUintLimit(count))
900 return scope.engine->throwRangeError(QString::fromLatin1(
"List length out of range."));
902 if (uint(count) != newLength)
903 return scope.engine->throwTypeError(u"List doesn't append null objects"_s);