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 | |
4 | #include "qqmllistwrapper_p.h" |
5 | |
6 | #include <QtQml/qqmlinfo.h> |
7 | |
8 | #include <private/qqmllist_p.h> |
9 | |
10 | #include <private/qv4arrayiterator_p.h> |
11 | #include <private/qv4arrayobject_p.h> |
12 | #include <private/qv4functionobject_p.h> |
13 | #include <private/qv4objectiterator_p.h> |
14 | #include <private/qv4objectproto_p.h> |
15 | #include <private/qv4qobjectwrapper_p.h> |
16 | #include <private/qv4symbol_p.h> |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | using namespace QV4; |
21 | using namespace Qt::StringLiterals; |
22 | |
23 | DEFINE_OBJECT_VTABLE(QmlListWrapper); |
24 | |
25 | void Heap::QmlListWrapper::init() |
26 | { |
27 | Object::init(); |
28 | object.init(); |
29 | QV4::Scope scope(internalClass->engine); |
30 | QV4::ScopedObject o(scope, this); |
31 | o->setArrayType(Heap::ArrayData::Custom); |
32 | } |
33 | |
34 | void Heap::QmlListWrapper::destroy() |
35 | { |
36 | object.destroy(); |
37 | Object::destroy(); |
38 | } |
39 | |
40 | ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, int propId, QMetaType propType) |
41 | { |
42 | if (!object || propId == -1) |
43 | return Encode::null(); |
44 | |
45 | Scope scope(engine); |
46 | |
47 | Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); |
48 | r->d()->object = object; |
49 | r->d()->propertyType = propType.iface(); |
50 | void *args[] = { &r->d()->property(), nullptr }; |
51 | QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args); |
52 | return r.asReturnedValue(); |
53 | } |
54 | |
55 | ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, QMetaType propType) |
56 | { |
57 | Scope scope(engine); |
58 | |
59 | Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>()); |
60 | r->d()->object = prop.object; |
61 | r->d()->property() = prop; |
62 | r->d()->propertyType = propType.iface(); |
63 | return r.asReturnedValue(); |
64 | } |
65 | |
66 | QVariant QmlListWrapper::toVariant() const |
67 | { |
68 | if (!d()->object) |
69 | return QVariant(); |
70 | |
71 | return QVariant::fromValue(value: toListReference()); |
72 | } |
73 | |
74 | QQmlListReference QmlListWrapper::toListReference() const |
75 | { |
76 | Heap::QmlListWrapper *wrapper = d(); |
77 | return QQmlListReferencePrivate::init(wrapper->property(), QMetaType(wrapper->propertyType)); |
78 | } |
79 | |
80 | |
81 | ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) |
82 | { |
83 | Q_ASSERT(m->as<QmlListWrapper>()); |
84 | const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); |
85 | QV4::ExecutionEngine *v4 = w->engine(); |
86 | |
87 | if (id.isArrayIndex()) { |
88 | uint index = id.asArrayIndex(); |
89 | quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; |
90 | if (index < count && w->d()->property().at) { |
91 | if (hasProperty) |
92 | *hasProperty = true; |
93 | return QV4::QObjectWrapper::wrap(engine: v4, object: w->d()->property().at(&w->d()->property(), index)); |
94 | } |
95 | |
96 | if (hasProperty) |
97 | *hasProperty = false; |
98 | return Value::undefinedValue().asReturnedValue(); |
99 | } |
100 | |
101 | return Object::virtualGet(m, id, receiver, hasProperty); |
102 | } |
103 | |
104 | qint64 QmlListWrapper::virtualGetLength(const Managed *m) |
105 | { |
106 | Q_ASSERT(m->as<QmlListWrapper>()); |
107 | const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m); |
108 | return w->toListReference().size(); |
109 | } |
110 | |
111 | bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) |
112 | { |
113 | Q_ASSERT(m->as<QmlListWrapper>()); |
114 | |
115 | const auto *w = static_cast<const QmlListWrapper *>(m); |
116 | QV4::ExecutionEngine *v4 = w->engine(); |
117 | |
118 | QQmlListProperty<QObject> *prop = &(w->d()->property()); |
119 | |
120 | if (id.isArrayIndex()) { |
121 | if (!prop->count || !prop->replace) |
122 | return false; |
123 | |
124 | const uint index = id.asArrayIndex(); |
125 | const int count = prop->count(prop); |
126 | if (count < 0 || index >= uint(count)) |
127 | return false; |
128 | |
129 | if (value.isNull()) { |
130 | prop->replace(prop, index, nullptr); |
131 | return true; |
132 | } |
133 | |
134 | QV4::Scope scope(v4); |
135 | QV4::ScopedObject so(scope, value.toObject(e: scope.engine)); |
136 | if (auto *wrapper = so->as<QV4::QObjectWrapper>()) { |
137 | prop->replace(prop, index, wrapper->object()); |
138 | return true; |
139 | } |
140 | |
141 | return false; |
142 | } |
143 | |
144 | return Object::virtualPut(m, id, value, receiver); |
145 | } |
146 | |
147 | struct QmlListWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator |
148 | { |
149 | ~QmlListWrapperOwnPropertyKeyIterator() override = default; |
150 | PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; |
151 | |
152 | }; |
153 | |
154 | PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) |
155 | { |
156 | const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o); |
157 | |
158 | quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0; |
159 | if (arrayIndex < count) { |
160 | uint index = arrayIndex; |
161 | ++arrayIndex; |
162 | if (attrs) |
163 | *attrs = QV4::Attr_Data; |
164 | if (pd) |
165 | pd->value = QV4::QObjectWrapper::wrap(engine: w->engine(), object: w->d()->property().at(&w->d()->property(), index)); |
166 | return PropertyKey::fromArrayIndex(idx: index); |
167 | } else if (memberIndex == 0) { |
168 | ++memberIndex; |
169 | return o->engine()->id_length()->propertyKey(); |
170 | } |
171 | |
172 | // You cannot add any own properties via the regular JavaScript interfaces. |
173 | return PropertyKey::invalid(); |
174 | } |
175 | |
176 | OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m, Value *target) |
177 | { |
178 | *target = *m; |
179 | return new QmlListWrapperOwnPropertyKeyIterator; |
180 | } |
181 | |
182 | void PropertyListPrototype::init() |
183 | { |
184 | defineDefaultProperty(QStringLiteral("pop" ), code: method_pop, argumentCount: 0); |
185 | defineDefaultProperty(QStringLiteral("push" ), code: method_push, argumentCount: 1); |
186 | defineDefaultProperty(QStringLiteral("shift" ), code: method_shift, argumentCount: 0); |
187 | defineDefaultProperty(QStringLiteral("splice" ), code: method_splice, argumentCount: 2); |
188 | defineDefaultProperty(QStringLiteral("unshift" ), code: method_unshift, argumentCount: 1); |
189 | defineDefaultProperty(QStringLiteral("indexOf" ), code: method_indexOf, argumentCount: 1); |
190 | defineDefaultProperty(QStringLiteral("lastIndexOf" ), code: method_lastIndexOf, argumentCount: 1); |
191 | defineDefaultProperty(QStringLiteral("sort" ), code: method_sort, argumentCount: 1); |
192 | defineAccessorProperty(QStringLiteral("length" ), getter: method_get_length, setter: method_set_length); |
193 | } |
194 | |
195 | ReturnedValue PropertyListPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int) |
196 | { |
197 | Scope scope(b); |
198 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
199 | if (!instance) |
200 | RETURN_UNDEFINED(); |
201 | |
202 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
203 | if (!w) |
204 | RETURN_UNDEFINED(); |
205 | |
206 | QQmlListProperty<QObject> *property = &w->d()->property(); |
207 | |
208 | if (!property->count) |
209 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
210 | const qsizetype len = property->count(property); |
211 | if (!len) |
212 | RETURN_UNDEFINED(); |
213 | |
214 | if (!property->at) |
215 | return scope.engine->throwTypeError(message: u"List doesn't define an At function"_s ); |
216 | ScopedValue result( |
217 | scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: property->at(property, len - 1))); |
218 | |
219 | if (!property->removeLast) |
220 | return scope.engine->throwTypeError(message: u"List doesn't define a RemoveLast function"_s ); |
221 | property->removeLast(property); |
222 | |
223 | return result->asReturnedValue(); |
224 | } |
225 | |
226 | ReturnedValue PropertyListPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
227 | { |
228 | Scope scope(b); |
229 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
230 | if (!instance) |
231 | RETURN_UNDEFINED(); |
232 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
233 | if (!w) |
234 | RETURN_UNDEFINED(); |
235 | |
236 | QQmlListProperty<QObject> *property = &w->d()->property(); |
237 | if (!property->append) |
238 | return scope.engine->throwTypeError(message: u"List doesn't define an Append function"_s ); |
239 | if (!property->count) |
240 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
241 | |
242 | for (int i = 0; i < argc; ++i) { |
243 | const Value &arg = argv[i]; |
244 | if (!arg.isNull() && !arg.as<QObjectWrapper>()) |
245 | THROW_TYPE_ERROR(); |
246 | } |
247 | |
248 | const qsizetype length = property->count(property); |
249 | if (!qIsAtMostUintLimit(length, limit: std::numeric_limits<uint>::max() - argc)) |
250 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
251 | |
252 | for (int i = 0; i < argc; ++i) { |
253 | if (argv[i].isNull()) |
254 | property->append(property, nullptr); |
255 | else |
256 | property->append(property, argv[i].as<QV4::QObjectWrapper>()->object()); |
257 | } |
258 | |
259 | const auto actualLength = property->count(property); |
260 | if (actualLength != length + argc) |
261 | qmlWarning(me: property->object) << "List didn't append all objects" ; |
262 | |
263 | return Encode(uint(actualLength)); |
264 | } |
265 | |
266 | ReturnedValue PropertyListPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int) |
267 | { |
268 | Scope scope(b); |
269 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
270 | if (!instance) |
271 | RETURN_UNDEFINED(); |
272 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
273 | if (!w) |
274 | RETURN_UNDEFINED(); |
275 | |
276 | QQmlListProperty<QObject> *property = &w->d()->property(); |
277 | |
278 | if (!property->count) |
279 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
280 | const qsizetype len = property->count(property); |
281 | if (!len) |
282 | RETURN_UNDEFINED(); |
283 | |
284 | if (!property->at) |
285 | return scope.engine->throwTypeError(message: u"List doesn't define an At function"_s ); |
286 | ScopedValue result(scope, QV4::QObjectWrapper::wrap(engine: scope.engine, object: property->at(property, 0))); |
287 | |
288 | if (!property->replace) |
289 | return scope.engine->throwTypeError(message: u"List doesn't define a Replace function"_s ); |
290 | if (!property->removeLast) |
291 | return scope.engine->throwTypeError(message: u"List doesn't define a RemoveLast function"_s ); |
292 | |
293 | for (qsizetype i = 1; i < len; ++i) |
294 | property->replace(property, i - 1, property->at(property, i)); |
295 | property->removeLast(property); |
296 | |
297 | return result->asReturnedValue(); |
298 | } |
299 | |
300 | ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
301 | { |
302 | Scope scope(b); |
303 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
304 | if (!instance) |
305 | RETURN_UNDEFINED(); |
306 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
307 | if (!w) |
308 | RETURN_UNDEFINED(); |
309 | |
310 | QQmlListProperty<QObject> *property = &w->d()->property(); |
311 | |
312 | if (!property->count) |
313 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
314 | const qsizetype len = property->count(property); |
315 | |
316 | const double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger(); |
317 | qsizetype start; |
318 | if (rs < 0) |
319 | start = static_cast<qsizetype>(qMax(a: 0., b: len + rs)); |
320 | else |
321 | start = static_cast<qsizetype>(qMin(a: rs, b: static_cast<double>(len))); |
322 | |
323 | qsizetype deleteCount = 0; |
324 | qsizetype itemCount = 0; |
325 | if (argc == 1) { |
326 | deleteCount = len - start; |
327 | } else if (argc > 1){ |
328 | itemCount = argc - 2; |
329 | double dc = argv[1].toInteger(); |
330 | deleteCount = static_cast<qsizetype>(qMin(a: qMax(a: dc, b: 0.), b: double(len - start))); |
331 | } |
332 | |
333 | if (itemCount > deleteCount |
334 | && len > std::numeric_limits<qsizetype>::max() - itemCount + deleteCount) { |
335 | return scope.engine->throwTypeError(); |
336 | } |
337 | |
338 | if (!qIsAtMostUintLimit(length: deleteCount, limit: std::numeric_limits<uint>::max() - 1)) |
339 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
340 | |
341 | if (!property->at) |
342 | return scope.engine->throwTypeError(message: u"List doesn't define an At function"_s ); |
343 | |
344 | for (qsizetype i = 0; i < itemCount; ++i) { |
345 | const auto arg = argv[i + 2]; |
346 | if (!arg.isNull() && !arg.as<QObjectWrapper>()) |
347 | THROW_TYPE_ERROR(); |
348 | } |
349 | |
350 | ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); |
351 | newArray->arrayReserve(n: deleteCount); |
352 | ScopedValue v(scope); |
353 | for (qsizetype i = 0; i < deleteCount; ++i) { |
354 | newArray->arrayPut( |
355 | index: i, value: QObjectWrapper::wrap(engine: scope.engine, object: property->at(property, start + i))); |
356 | } |
357 | newArray->setArrayLengthUnchecked(deleteCount); |
358 | |
359 | if (!property->replace) |
360 | return scope.engine->throwTypeError(message: u"List doesn't define a Replace function"_s ); |
361 | if (!property->removeLast) |
362 | return scope.engine->throwTypeError(message: u"List doesn't define a RemoveLast function"_s ); |
363 | if (!property->append) |
364 | return scope.engine->throwTypeError(message: u"List doesn't define an Append function"_s ); |
365 | |
366 | if (itemCount < deleteCount) { |
367 | for (qsizetype k = start; k < len - deleteCount; ++k) |
368 | property->replace(property, k + itemCount, property->at(property, k + deleteCount)); |
369 | for (qsizetype k = len; k > len - deleteCount + itemCount; --k) |
370 | property->removeLast(property); |
371 | } else if (itemCount > deleteCount) { |
372 | for (qsizetype k = 0; k < itemCount - deleteCount; ++k) |
373 | property->append(property, nullptr); |
374 | for (qsizetype k = len - deleteCount; k > start; --k) { |
375 | property->replace( |
376 | property, k + itemCount - 1, property->at(property, k + deleteCount - 1)); |
377 | } |
378 | } |
379 | |
380 | for (qsizetype i = 0; i < itemCount; ++i) { |
381 | const auto arg = argv[i + 2]; |
382 | if (arg.isNull()) |
383 | property->replace(property, start + i, nullptr); |
384 | else |
385 | property->replace(property, start + i, arg.as<QObjectWrapper>()->object()); |
386 | } |
387 | |
388 | return newArray->asReturnedValue(); |
389 | } |
390 | |
391 | ReturnedValue PropertyListPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
392 | { |
393 | Scope scope(b); |
394 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
395 | if (!instance) |
396 | RETURN_UNDEFINED(); |
397 | |
398 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
399 | if (!w) |
400 | RETURN_UNDEFINED(); |
401 | |
402 | QQmlListProperty<QObject> *property = &w->d()->property(); |
403 | |
404 | if (!property->count) |
405 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
406 | const qsizetype len = property->count(property); |
407 | |
408 | if (std::numeric_limits<qsizetype>::max() - len < argc || !qIsAtMostUintLimit(length: len + argc)) |
409 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
410 | |
411 | if (!property->append) |
412 | return scope.engine->throwTypeError(message: u"List doesn't define an Append function"_s ); |
413 | if (!property->replace) |
414 | return scope.engine->throwTypeError(message: u"List doesn't define a Replace function"_s ); |
415 | |
416 | for (int i = 0; i < argc; ++i) { |
417 | const auto arg = argv[i]; |
418 | if (!arg.isNull() && !arg.as<QObjectWrapper>()) |
419 | THROW_TYPE_ERROR(); |
420 | } |
421 | |
422 | for (int i = 0; i < argc; ++i) |
423 | property->append(property, nullptr); |
424 | if (property->count(property) != argc + len) |
425 | return scope.engine->throwTypeError(message: u"List doesn't append null objects"_s ); |
426 | |
427 | for (qsizetype k = len; k > 0; --k) |
428 | property->replace(property, k + argc - 1, property->at(property, k - 1)); |
429 | |
430 | for (int i = 0; i < argc; ++i) { |
431 | const auto *wrapper = argv[i].as<QObjectWrapper>(); |
432 | property->replace(property, i, wrapper ? wrapper->object() : nullptr); |
433 | } |
434 | |
435 | return Encode(uint(len + argc)); |
436 | } |
437 | |
438 | template<typename Iterate> |
439 | ReturnedValue firstOrLastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, Iterate iterate) |
440 | { |
441 | Scope scope(b); |
442 | |
443 | // Undefined cannot be encoded as QObject*. In particular it's not nullptr. |
444 | if (argc == 0) |
445 | THROW_TYPE_ERROR(); |
446 | |
447 | QObject *searchValue; |
448 | if (argv[0].isNull()) { |
449 | searchValue = nullptr; |
450 | } else { |
451 | Scoped<QObjectWrapper> wrapper(scope, argv[0]); |
452 | if (wrapper) |
453 | searchValue = wrapper->object(); |
454 | else |
455 | THROW_TYPE_ERROR(); |
456 | } |
457 | |
458 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
459 | if (!instance) |
460 | RETURN_UNDEFINED(); |
461 | |
462 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
463 | if (!w) |
464 | RETURN_UNDEFINED(); |
465 | |
466 | QQmlListProperty<QObject> *property = &w->d()->property(); |
467 | |
468 | if (!property->count) |
469 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
470 | const qsizetype len = property->count(property); |
471 | if (!len) |
472 | return Encode(-1); |
473 | |
474 | |
475 | return iterate(scope.engine, property, len, searchValue); |
476 | } |
477 | |
478 | ReturnedValue PropertyListPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
479 | { |
480 | return firstOrLastIndexOf( |
481 | b, thisObject, argv, argc, |
482 | iterate: [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property, |
483 | qsizetype len, QObject *searchValue) -> ReturnedValue { |
484 | qsizetype fromIndex = 0; |
485 | if (argc >= 2) { |
486 | double f = argv[1].toInteger(); |
487 | if (hasExceptionOrIsInterrupted(engine)) |
488 | return Encode::undefined(); |
489 | if (f >= len) |
490 | return Encode(-1); |
491 | if (f < 0) |
492 | f = qMax(a: len + f, b: 0.); |
493 | fromIndex = qsizetype(f); |
494 | } |
495 | |
496 | for (qsizetype i = fromIndex; i < len; ++i) { |
497 | if (property->at(property, i) == searchValue) { |
498 | if (qIsAtMostUintLimit(length: i)) |
499 | return Encode(uint(i)); |
500 | return engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
501 | } |
502 | } |
503 | |
504 | return Encode(-1); |
505 | }); |
506 | } |
507 | |
508 | ReturnedValue PropertyListPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
509 | { |
510 | return firstOrLastIndexOf( |
511 | b, thisObject, argv, argc, |
512 | iterate: [argc, argv](ExecutionEngine *engine, QQmlListProperty<QObject> *property, |
513 | qsizetype len, QObject *searchValue) -> ReturnedValue { |
514 | qsizetype fromIndex = len - 1; |
515 | if (argc >= 2) { |
516 | double f = argv[1].toInteger(); |
517 | if (hasExceptionOrIsInterrupted(engine)) |
518 | return Encode::undefined(); |
519 | if (f > 0) |
520 | f = qMin(a: f, b: (double)(len - 1)); |
521 | else if (f < 0) { |
522 | f = len + f; |
523 | if (f < 0) |
524 | return Encode(-1); |
525 | } |
526 | fromIndex = qsizetype(f); |
527 | } |
528 | |
529 | for (qsizetype i = fromIndex; i >= 0; --i) { |
530 | if (property->at(property, i) == searchValue) { |
531 | if (qIsAtMostUintLimit(length: i)) |
532 | return Encode(uint(i)); |
533 | return engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
534 | } |
535 | } |
536 | |
537 | return Encode(-1); |
538 | }); |
539 | } |
540 | |
541 | ReturnedValue PropertyListPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
542 | { |
543 | Scope scope(b); |
544 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
545 | if (!instance) |
546 | RETURN_UNDEFINED(); |
547 | |
548 | QmlListWrapper *w = instance->as<QmlListWrapper>(); |
549 | if (!w) |
550 | RETURN_UNDEFINED(); |
551 | |
552 | QQmlListProperty<QObject> *property = &w->d()->property(); |
553 | |
554 | if (!property->count) |
555 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
556 | if (property->count(property) == 0) |
557 | return thisObject->asReturnedValue(); |
558 | if (!property->at) |
559 | return scope.engine->throwTypeError(message: u"List doesn't define an At function"_s ); |
560 | if (!property->replace) |
561 | return scope.engine->throwTypeError(message: u"List doesn't define a Replace function"_s ); |
562 | |
563 | ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue()); |
564 | if (!comparefn->isUndefined() && !comparefn->isFunctionObject()) |
565 | THROW_TYPE_ERROR(); |
566 | |
567 | const ArrayElementLessThan lessThan(scope.engine, comparefn); |
568 | sortHelper(start: begin(list&: *property), end: end(list&: *property), lessThan: [&](QObject *a, QObject *b) { |
569 | Scoped<QObjectWrapper> o1(scope, QObjectWrapper::wrap(engine: scope.engine, object: a)); |
570 | Scoped<QObjectWrapper> o2(scope, QObjectWrapper::wrap(engine: scope.engine, object: b)); |
571 | return lessThan(o1, o2); |
572 | }); |
573 | |
574 | return thisObject->asReturnedValue(); |
575 | } |
576 | |
577 | ReturnedValue PropertyListPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int) |
578 | { |
579 | Scope scope(b); |
580 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
581 | if (!instance) |
582 | RETURN_UNDEFINED(); |
583 | |
584 | const QmlListWrapper *w = instance->as<QmlListWrapper>(); |
585 | if (!w) |
586 | RETURN_UNDEFINED(); |
587 | |
588 | QQmlListProperty<QObject> *property = &w->d()->property(); |
589 | if (!property->count) |
590 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
591 | |
592 | qsizetype count = property->count(property); |
593 | if (qIsAtMostUintLimit(length: count)) |
594 | return Encode(uint(count)); |
595 | |
596 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
597 | } |
598 | |
599 | ReturnedValue PropertyListPrototype::method_set_length(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
600 | { |
601 | QV4::Scope scope(b); |
602 | ScopedObject instance(scope, thisObject->toObject(e: scope.engine)); |
603 | if (!instance) |
604 | RETURN_UNDEFINED(); |
605 | |
606 | const QmlListWrapper *w = instance->as<QmlListWrapper>(); |
607 | if (!w) |
608 | RETURN_UNDEFINED(); |
609 | |
610 | QQmlListProperty<QObject> *property = &w->d()->property(); |
611 | |
612 | bool ok = false; |
613 | const uint newLength = argc ? argv[0].asArrayLength(ok: &ok) : 0; |
614 | if (!ok) |
615 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "Invalid list length." )); |
616 | |
617 | if (newLength == 0 && property->clear) { |
618 | property->clear(property); |
619 | return true; |
620 | } |
621 | |
622 | if (!property->count) |
623 | return scope.engine->throwTypeError(message: u"List doesn't define a Count function"_s ); |
624 | |
625 | qsizetype count = property->count(property); |
626 | if (!qIsAtMostUintLimit(length: count)) |
627 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
628 | |
629 | if (newLength < uint(count)) { |
630 | if (!property->removeLast) |
631 | return scope.engine->throwTypeError(message: u"List doesn't define a RemoveLast function"_s ); |
632 | |
633 | for (uint i = count; i > newLength; --i) |
634 | property->removeLast(property); |
635 | |
636 | return true; |
637 | } |
638 | |
639 | if (!property->append) |
640 | return scope.engine->throwTypeError(message: u"List doesn't define an Append function"_s ); |
641 | |
642 | for (uint i = count; i < newLength; ++i) |
643 | property->append(property, nullptr); |
644 | |
645 | count = property->count(property); |
646 | if (!qIsAtMostUintLimit(length: count)) |
647 | return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "List length out of range." )); |
648 | |
649 | if (uint(count) != newLength) |
650 | return scope.engine->throwTypeError(message: u"List doesn't append null objects"_s ); |
651 | |
652 | return true; |
653 | |
654 | } |
655 | |
656 | QT_END_NAMESPACE |
657 | |