1// Copyright (C) 2018 Crimson AS <info@crimson.no>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qv4arrayobject_p.h"
6#include "qv4arrayiterator_p.h"
7#include "qv4objectproto_p.h"
8#include "qv4argumentsobject_p.h"
9#include "qv4runtime_p.h"
10#include "qv4symbol_p.h"
11#include <QtCore/qscopedvaluerollback.h>
12
13using namespace QV4;
14
15DEFINE_OBJECT_VTABLE(ArrayCtor);
16
17void Heap::ArrayCtor::init(QV4::ExecutionEngine *engine)
18{
19 Heap::FunctionObject::init(engine, QStringLiteral("Array"));
20}
21
22ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
23{
24 ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine();
25 Scope scope(v4);
26 ScopedArrayObject a(scope, v4->newArrayObject());
27 if (newTarget)
28 a->setProtoFromNewTarget(newTarget);
29 uint len;
30 if (argc == 1 && argv[0].isNumber()) {
31 bool ok;
32 len = argv[0].asArrayLength(ok: &ok);
33
34 if (!ok)
35 return v4->throwRangeError(value: argv[0]);
36
37 if (len < 0x1000)
38 a->arrayReserve(n: len);
39 } else {
40 len = argc;
41 a->arrayReserve(n: len);
42 a->arrayPut(index: 0, values: argv, n: len);
43 }
44 a->setArrayLengthUnchecked(len);
45
46 return a.asReturnedValue();
47}
48
49ReturnedValue ArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
50{
51 return virtualCallAsConstructor(f, argv, argc, newTarget: f);
52}
53
54void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
55{
56 Scope scope(engine);
57 ScopedObject o(scope);
58 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 1));
59 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
60 ctor->defineDefaultProperty(QStringLiteral("isArray"), code: method_isArray, argumentCount: 1);
61 ctor->defineDefaultProperty(QStringLiteral("of"), code: method_of, argumentCount: 0);
62 ctor->defineDefaultProperty(QStringLiteral("from"), code: method_from, argumentCount: 1);
63 ctor->addSymbolSpecies();
64
65 Scoped<InternalClass> ic(scope, engine->classes[EngineBase::Class_Empty]
66 ->changeVTable(vt: QV4::Object::staticVTable()));
67 ScopedObject unscopables(scope, engine->newObject(internalClass: ic->d()));
68 ScopedString name(scope);
69 defineDefaultProperty(QStringLiteral("constructor"), value: (o = ctor));
70 defineDefaultProperty(name: engine->id_toString(), code: method_toString, argumentCount: 0);
71 defineDefaultProperty(name: engine->id_toLocaleString(), code: method_toLocaleString, argumentCount: 0);
72 defineDefaultProperty(QStringLiteral("concat"), code: method_concat, argumentCount: 1);
73 name = engine->newIdentifier(QStringLiteral("copyWithin"));
74 unscopables->put(name, v: Value::fromBoolean(b: true));
75 defineDefaultProperty(name, code: method_copyWithin, argumentCount: 2);
76 name = engine->newIdentifier(QStringLiteral("entries"));
77 unscopables->put(name, v: Value::fromBoolean(b: true));
78 defineDefaultProperty(name, code: method_entries, argumentCount: 0);
79 name = engine->newIdentifier(QStringLiteral("fill"));
80 unscopables->put(name, v: Value::fromBoolean(b: true));
81 defineDefaultProperty(name, code: method_fill, argumentCount: 1);
82 name = engine->newIdentifier(QStringLiteral("find"));
83 unscopables->put(name, v: Value::fromBoolean(b: true));
84 defineDefaultProperty(name, code: method_find, argumentCount: 1);
85 name = engine->newIdentifier(QStringLiteral("findIndex"));
86 unscopables->put(name, v: Value::fromBoolean(b: true));
87 defineDefaultProperty(name, code: method_findIndex, argumentCount: 1);
88 name = engine->newIdentifier(QStringLiteral("includes"));
89 unscopables->put(name, v: Value::fromBoolean(b: true));
90 defineDefaultProperty(name, code: method_includes, argumentCount: 1);
91 defineDefaultProperty(QStringLiteral("join"), code: method_join, argumentCount: 1);
92 name = engine->newIdentifier(QStringLiteral("keys"));
93 unscopables->put(name, v: Value::fromBoolean(b: true));
94 defineDefaultProperty(name, code: method_keys, argumentCount: 0);
95 defineDefaultProperty(QStringLiteral("pop"), code: method_pop, argumentCount: 0);
96 defineDefaultProperty(QStringLiteral("push"), code: method_push, argumentCount: 1);
97 defineDefaultProperty(QStringLiteral("reverse"), code: method_reverse, argumentCount: 0);
98 defineDefaultProperty(QStringLiteral("shift"), code: method_shift, argumentCount: 0);
99 defineDefaultProperty(QStringLiteral("slice"), code: method_slice, argumentCount: 2);
100 defineDefaultProperty(QStringLiteral("sort"), code: method_sort, argumentCount: 1);
101 defineDefaultProperty(QStringLiteral("splice"), code: method_splice, argumentCount: 2);
102 defineDefaultProperty(QStringLiteral("unshift"), code: method_unshift, argumentCount: 1);
103 defineDefaultProperty(QStringLiteral("indexOf"), code: method_indexOf, argumentCount: 1);
104 defineDefaultProperty(QStringLiteral("lastIndexOf"), code: method_lastIndexOf, argumentCount: 1);
105 defineDefaultProperty(QStringLiteral("every"), code: method_every, argumentCount: 1);
106 defineDefaultProperty(QStringLiteral("some"), code: method_some, argumentCount: 1);
107 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
108 defineDefaultProperty(QStringLiteral("map"), code: method_map, argumentCount: 1);
109 defineDefaultProperty(QStringLiteral("filter"), code: method_filter, argumentCount: 1);
110 defineDefaultProperty(QStringLiteral("reduce"), code: method_reduce, argumentCount: 1);
111 defineDefaultProperty(QStringLiteral("reduceRight"), code: method_reduceRight, argumentCount: 1);
112 ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
113 ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valuesString, code: method_values, argumentCount: 0));
114 engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values;
115 unscopables->put(name: valuesString, v: Value::fromBoolean(b: true));
116 defineDefaultProperty(name: valuesString, value: values);
117 defineDefaultProperty(name: engine->symbol_iterator(), value: values);
118
119 defineReadonlyConfigurableProperty(name: engine->symbol_unscopables(), value: unscopables);
120}
121
122ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
123{
124 if (!argc || !argv->objectValue())
125 return Encode(false);
126 return Encode(argv->objectValue()->isArray());
127}
128
129static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
130{
131 ScopedObject a(scope, Value::undefinedValue());
132
133 if (ctor && ctor->isConstructor()) {
134 // this isn't completely kosher. for instance:
135 // Array.from.call(Object, []).constructor == Object
136 // is expected by the tests, but naturally, we get Number.
137 ScopedValue argument(scope, useLen ? Value::fromReturnedValue(val: QV4::Encode(len))
138 : Value::undefinedValue());
139 a = ctor->callAsConstructor(argv: argument, argc: useLen ? 1 : 0);
140 } else {
141 a = scope.engine->newArrayObject(count: len);
142 }
143
144 return a;
145}
146
147ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
148{
149 Scope scope(builtin);
150 ScopedFunctionObject thatCtor(scope, thisObject);
151 ScopedObject itemsObject(scope, argv[0]);
152 bool usingIterator = false;
153
154 if (itemsObject) {
155 // If the object claims to support iterators, then let's try use them.
156 ScopedValue it(scope, itemsObject->get(name: scope.engine->symbol_iterator()));
157 if (!it->isNullOrUndefined()) {
158 ScopedFunctionObject itfunc(scope, it);
159 if (!itfunc)
160 return scope.engine->throwTypeError();
161 usingIterator = true;
162 }
163 }
164
165 ScopedFunctionObject mapfn(scope, Value::undefinedValue());
166 Value *mapArguments = nullptr;
167 if (argc > 1) {
168 mapfn = ScopedFunctionObject(scope, argv[1]);
169 if (!mapfn)
170 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "%1 is not a function").arg(a: argv[1].toQStringNoThrow()));
171 mapArguments = scope.alloc(nValues: 2);
172 }
173
174 ScopedValue thisArg(scope);
175 if (argc > 2)
176 thisArg = argv[2];
177
178 if (usingIterator) {
179 // Item iteration supported, so let's go ahead and try use that.
180 ScopedObject a(createObjectFromCtorOrArray(scope, ctor: thatCtor, useLen: false, len: 0));
181 CHECK_EXCEPTION();
182 ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
183 CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
184 if (!iterator) {
185 return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
186 }
187
188 qint64 k = 0;
189 ScopedValue mappedValue(scope);
190 Value *nextValue = scope.alloc(nValues: 1);
191 ScopedValue done(scope);
192
193 // The loop below pulls out all the properties using the iterator, and
194 // sets them into the created array.
195 forever {
196 if (k > (static_cast<qint64>(1) << 53) - 1) {
197 ScopedValue error(scope, scope.engine->throwTypeError());
198 return Runtime::IteratorClose::call(scope.engine, iterator);
199 }
200
201 // Retrieve the next value. If the iteration ends, we're done here.
202 done = Value::fromReturnedValue(val: Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
203 CHECK_EXCEPTION();
204 if (done->toBoolean()) {
205 if (ArrayObject *ao = a->as<ArrayObject>()) {
206 ao->setArrayLengthUnchecked(k);
207 } else {
208 a->set(name: scope.engine->id_length(), v: Value::fromDouble(d: k), shouldThrow: QV4::Object::DoThrowOnRejection);
209 CHECK_EXCEPTION();
210 }
211 return a.asReturnedValue();
212 }
213
214 if (mapfn) {
215 Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc
216 mapArguments[0] = *nextValue;
217 mapArguments[1] = Value::fromDouble(d: k);
218 mappedValue = mapfn->call(thisObject: thisArg, argv: mapArguments, argc: 2);
219 if (scope.hasException())
220 return Runtime::IteratorClose::call(scope.engine, iterator);
221 } else {
222 mappedValue = *nextValue;
223 }
224
225 if (a->getOwnProperty(id: PropertyKey::fromArrayIndex(idx: k)) == Attr_Invalid) {
226 a->arraySet(index: k, value: mappedValue);
227 } else {
228 // Don't return: we need to close the iterator.
229 scope.engine->throwTypeError(message: QString::fromLatin1(ba: "Cannot redefine property: %1").arg(a: k));
230 }
231
232 if (scope.hasException())
233 return Runtime::IteratorClose::call(scope.engine, iterator);
234
235 k++;
236 }
237
238 // the return is hidden up in the loop above, when iteration finishes.
239 } else {
240 // Array-like fallback. We request properties by index, and set them on
241 // the return object.
242 ScopedObject arrayLike(scope, argv[0].toObject(e: scope.engine));
243 if (!arrayLike)
244 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "Cannot convert %1 to object").arg(a: argv[0].toQStringNoThrow()));
245 qint64 len = arrayLike->getLength();
246 ScopedObject a(createObjectFromCtorOrArray(scope, ctor: thatCtor, useLen: true, len));
247 CHECK_EXCEPTION();
248
249 qint64 k = 0;
250 ScopedValue mappedValue(scope, Value::undefinedValue());
251 ScopedValue kValue(scope);
252 while (k < len) {
253 kValue = arrayLike->get(idx: k);
254 CHECK_EXCEPTION();
255
256 if (mapfn) {
257 Q_ASSERT(mapArguments); // if mapfn is set, we always setup mapArguments with scope.alloc
258 mapArguments[0] = kValue;
259 mapArguments[1] = Value::fromDouble(d: k);
260 mappedValue = mapfn->call(thisObject: thisArg, argv: mapArguments, argc: 2);
261 CHECK_EXCEPTION();
262 } else {
263 mappedValue = kValue;
264 }
265
266 if (a->getOwnProperty(id: PropertyKey::fromArrayIndex(idx: k)) != Attr_Invalid)
267 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "Cannot redefine property: %1").arg(a: k));
268
269 a->arraySet(index: k, value: mappedValue);
270 CHECK_EXCEPTION();
271
272 k++;
273 }
274
275 if (ArrayObject *ao = a->as<ArrayObject>()) {
276 ao->setArrayLengthUnchecked(k);
277 } else {
278 a->set(name: scope.engine->id_length(), v: Value::fromDouble(d: k), shouldThrow: QV4::Object::DoThrowOnRejection);
279 CHECK_EXCEPTION();
280 }
281 return a.asReturnedValue();
282 }
283
284}
285
286ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
287{
288 Scope scope(builtin);
289 ScopedFunctionObject that(scope, thisObject);
290 ScopedObject a(createObjectFromCtorOrArray(scope, ctor: that, useLen: true, len: argc));
291 CHECK_EXCEPTION();
292
293 int k = 0;
294 while (k < argc) {
295 if (a->getOwnProperty(id: PropertyKey::fromArrayIndex(idx: k)) != Attr_Invalid) {
296 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "Cannot redefine property: %1").arg(a: k));
297 }
298 a->arraySet(index: k, value: argv[k]);
299 CHECK_EXCEPTION();
300
301 k++;
302 }
303
304 // ArrayObject updates its own length, and will throw if we try touch it.
305 if (!a->as<ArrayObject>()) {
306 a->set(name: scope.engine->id_length(), v: Value::fromDouble(d: argc), shouldThrow: QV4::Object::DoThrowOnRejection);
307 CHECK_EXCEPTION();
308 }
309
310 return a.asReturnedValue();
311}
312
313ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
314{
315 Scope scope(builtin);
316 ScopedObject that(scope, thisObject->toObject(e: scope.engine));
317 if (scope.hasException())
318 return QV4::Encode::undefined();
319
320 ScopedString string(scope, scope.engine->newString(QStringLiteral("join")));
321 ScopedFunctionObject f(scope, that->get(name: string));
322 if (f)
323 return checkedResult(v4: scope.engine, result: f->call(thisObject: that, argv, argc));
324 return ObjectPrototype::method_toString(builtin, thisObject: that, argv, argc);
325}
326
327ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
328{
329 Scope scope(b);
330 ScopedObject instance(scope, thisObject);
331 if (!instance)
332 return scope.engine->throwTypeError();
333
334 uint len = instance->getLength();
335 const QString separator = QStringLiteral(",");
336
337 QString R;
338
339 ScopedValue v(scope);
340 ScopedString s(scope);
341
342 ScopedPropertyKey tolocaleString(scope, scope.engine->id_toLocaleString()->toPropertyKey());
343 Q_ASSERT(!scope.engine->hasException);
344
345 for (uint k = 0; k < len; ++k) {
346 if (k)
347 R += separator;
348
349 v = instance->get(idx: k);
350 if (v->isNullOrUndefined())
351 continue;
352
353 ScopedObject valueAsObject(scope, v->toObject(e: scope.engine));
354 Q_ASSERT(valueAsObject); // null and undefined handled above
355
356 ScopedFunctionObject function(scope, valueAsObject->get(id: tolocaleString));
357 if (!function)
358 return scope.engine->throwTypeError();
359
360 v = function->call(thisObject: valueAsObject, argv: nullptr, argc: 0);
361 if (scope.hasException())
362 return Encode::undefined();
363
364 s = v->toString(e: scope.engine);
365 if (scope.hasException())
366 return Encode::undefined();
367
368 R += s->toQString();
369 }
370 return scope.engine->newString(s: R)->asReturnedValue();
371}
372
373ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc)
374{
375 Scope scope(b);
376 ScopedObject thisObject(scope, that->toObject(e: scope.engine));
377 if (!thisObject)
378 RETURN_UNDEFINED();
379
380 ScopedArrayObject result(scope, scope.engine->newArrayObject());
381
382 ScopedArrayObject elt(scope);
383 ScopedObject eltAsObj(scope);
384 ScopedValue entry(scope);
385 for (int i = -1; i < argc; ++i) {
386 const Value *v = i == -1 ? thisObject.getPointer() : argv + i;
387 eltAsObj = *v;
388 elt = *v;
389 if (elt) {
390 uint n = elt->getLength();
391 uint newLen = ArrayData::append(obj: result, otherObj: elt, n);
392 result->setArrayLengthUnchecked(newLen);
393 } else if (eltAsObj && eltAsObj->isConcatSpreadable()) {
394 const uint startIndex = result->getLength();
395 const uint len = eltAsObj->getLength();
396 if (scope.hasException())
397 return Encode::undefined();
398
399 for (uint i = 0; i < len; ++i) {
400 bool hasProperty = false;
401 entry = eltAsObj->get(idx: i, hasProperty: &hasProperty);
402 if (hasProperty) {
403 if (!result->put(idx: startIndex + i, v: entry))
404 return scope.engine->throwTypeError();
405 }
406 }
407 } else if (eltAsObj && eltAsObj->isArrayLike()) {
408 const uint startIndex = result->getLength();
409 for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
410 entry = eltAsObj->get(idx: i);
411 // spec says not to throw if this fails
412 result->put(idx: startIndex + i, v: entry);
413 }
414 } else {
415 result->arraySet(index: result->getLength(), value: *v);
416 }
417 }
418
419 return result.asReturnedValue();
420}
421
422ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
423{
424 Scope scope(b);
425 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
426 if (!instance)
427 RETURN_UNDEFINED();
428
429 double len = instance->getLength();
430 double target = argv[0].toInteger();
431 double start = argc > 1 ? argv[1].toInteger() : 0;
432 double end = len;
433
434 if (argc > 2 && !argv[2].isUndefined()) {
435 end = argv[2].toInteger();
436 }
437
438 double relativeTarget = target;
439 double relativeStart = start;
440 double relativeEnd = end;
441 double from = 0;
442 double to = 0;
443
444 if (relativeTarget < 0) {
445 to = std::max(a: len+relativeTarget, b: 0.0);
446 } else {
447 to = std::min(a: relativeTarget, b: len);
448 }
449 if (relativeStart < 0) {
450 from = std::max(a: len+relativeStart, b: 0.0);
451 } else {
452 from = std::min(a: relativeStart, b: len);
453 }
454
455 double fin = 0;
456 if (relativeEnd < 0) {
457 fin = std::max(a: len+relativeEnd, b: 0.0);
458 } else {
459 fin = std::min(a: relativeEnd, b: len);
460 }
461 double count = std::min(a: fin-from, b: len-to);
462 double direction = 1;
463 if (from < to && to < from+count) {
464 direction = -1;
465 from = from + count - 1;
466 to = to + count - 1;
467 }
468
469 while (count > 0) {
470 bool fromPresent = false;
471 ScopedValue fromVal(scope, instance->get(idx: from, hasProperty: &fromPresent));
472
473 if (fromPresent) {
474 instance->setIndexed(idx: to, v: fromVal, shouldThrow: QV4::Object::DoThrowOnRejection);
475 CHECK_EXCEPTION();
476 } else {
477 bool didDelete = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: to));
478 CHECK_EXCEPTION();
479 if (!didDelete) {
480 return scope.engine->throwTypeError();
481 }
482 }
483
484 from = from + direction;
485 to = to + direction;
486 count = count - 1;
487 }
488
489 return instance.asReturnedValue();
490}
491
492ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
493{
494 Scope scope(b);
495 ScopedObject O(scope, thisObject->toObject(e: scope.engine));
496 if (!O)
497 RETURN_UNDEFINED();
498
499 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: O));
500 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
501 return ao->asReturnedValue();
502}
503
504ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
505{
506 Scope scope(b);
507 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
508 if (!instance)
509 RETURN_UNDEFINED();
510
511 uint len = instance->getLength();
512
513 if (!argc || !argv[0].isFunctionObject())
514 THROW_TYPE_ERROR();
515 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
516
517 ScopedValue result(scope);
518 Value *arguments = scope.alloc(nValues: 3);
519
520 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
521
522 for (uint k = 0; k < len; ++k) {
523 arguments[0] = instance->get(idx: k);
524 CHECK_EXCEPTION();
525
526 arguments[1] = Value::fromDouble(d: k);
527 arguments[2] = instance;
528 result = callback->call(thisObject: that, argv: arguments, argc: 3);
529
530 CHECK_EXCEPTION();
531 if (result->toBoolean())
532 return arguments[0].asReturnedValue();
533 }
534
535 RETURN_UNDEFINED();
536}
537
538ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
539{
540 Scope scope(b);
541 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
542 if (!instance)
543 RETURN_UNDEFINED();
544
545 uint len = instance->getLength();
546
547 if (!argc || !argv[0].isFunctionObject())
548 THROW_TYPE_ERROR();
549 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
550
551 ScopedValue result(scope);
552 Value *arguments = scope.alloc(nValues: 3);
553
554 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
555
556 for (uint k = 0; k < len; ++k) {
557 arguments[0] = instance->get(idx: k);
558 CHECK_EXCEPTION();
559
560 arguments[1] = Value::fromDouble(d: k);
561 arguments[2] = instance;
562 result = callback->call(thisObject: that, argv: arguments, argc: 3);
563
564 CHECK_EXCEPTION();
565 if (result->toBoolean())
566 return Encode(k);
567 }
568
569 return Encode(-1);
570}
571
572ReturnedValue ArrayPrototype::method_join(const FunctionObject *functionObject,
573 const Value *thisObject, const Value *argv, int argc)
574{
575 Scope scope(functionObject);
576 CHECK_STACK_LIMITS(scope.engine)
577 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
578
579 if (!instance)
580 return Encode(scope.engine->newString());
581
582 // We cannot optimize the resolution of the argument away in case of length == 0
583 // It may have side effects.
584 ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue());
585 const QString separator = argument->isUndefined()
586 ? QStringLiteral(",")
587 : argument->toQString();
588
589 ScopedValue scopedLength(scope, instance->get(name: scope.engine->id_length()));
590 const quint32 genericLength = scopedLength->isUndefined() ? 0 : scopedLength->toUInt32();
591 if (!genericLength)
592 return Encode(scope.engine->newString());
593
594 QString result;
595 if (auto *arrayObject = instance->as<ArrayObject>()) {
596 ScopedValue entry(scope);
597 const qint64 arrayLength = arrayObject->getLength();
598 Q_ASSERT(arrayLength >= 0);
599 Q_ASSERT(arrayLength <= std::numeric_limits<quint32>::max());
600 for (quint32 i = 0; i < quint32(arrayLength); ++i) {
601 if (i)
602 result += separator;
603
604 entry = arrayObject->get(idx: i);
605 CHECK_EXCEPTION();
606 if (!entry->isNullOrUndefined())
607 result += entry->toQString();
608 }
609 } else {
610 ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
611 ScopedValue value(scope, instance->get(name));
612 CHECK_EXCEPTION();
613
614 if (!value->isNullOrUndefined())
615 result = value->toQString();
616
617 for (quint32 i = 1; i < genericLength; ++i) {
618 result += separator;
619
620 name = Value::fromDouble(d: i).toString(e: scope.engine);
621 value = instance->get(name);
622 CHECK_EXCEPTION();
623
624 if (!value->isNullOrUndefined())
625 result += value->toQString();
626 }
627 }
628
629 return Encode(scope.engine->newString(s: result));
630}
631
632ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
633{
634 Scope scope(b);
635 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
636 if (!instance)
637 RETURN_UNDEFINED();
638
639 uint len = instance->getLength();
640
641 if (!len) {
642 if (!instance->isArrayObject())
643 instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromInt32(i: 0)));
644 RETURN_UNDEFINED();
645 }
646
647 ScopedValue result(scope, instance->get(idx: len - 1));
648 CHECK_EXCEPTION();
649
650 if (!instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: len - 1)))
651 return scope.engine->throwTypeError();
652
653 if (instance->isArrayObject())
654 instance->setArrayLength(len - 1);
655 else {
656 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: len - 1))))
657 return scope.engine->throwTypeError();
658 }
659 return result->asReturnedValue();
660}
661
662ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
663{
664 Scope scope(b);
665 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
666 if (!instance)
667 RETURN_UNDEFINED();
668
669 instance->arrayCreate();
670 Q_ASSERT(instance->arrayData());
671
672 qint64 len = instance->getLength();
673
674 if (len + quint64(argc) >= UINT_MAX) {
675 // ughh... this goes beyond UINT_MAX
676 double l = len;
677 ScopedString s(scope);
678 for (int i = 0, ei = argc; i < ei; ++i) {
679 s = Value::fromDouble(d: l + i).toString(e: scope.engine);
680 if (!instance->put(name: s, v: argv[i]))
681 return scope.engine->throwTypeError();
682 }
683 double newLen = l + argc;
684 if (!instance->isArrayObject()) {
685 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: newLen))))
686 return scope.engine->throwTypeError();
687 } else {
688 ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow")));
689 return scope.engine->throwRangeError(value: str);
690 }
691 return Encode(newLen);
692 }
693
694 if (!argc)
695 ;
696 else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) {
697 instance->arrayData()->vtable()->putArray(instance, len, argv, argc);
698 len = instance->arrayData()->length();
699 } else {
700 for (int i = 0, ei = argc; i < ei; ++i) {
701 if (!instance->put(idx: len + i, v: argv[i]))
702 return scope.engine->throwTypeError();
703 }
704 len += argc;
705 }
706 if (instance->isArrayObject())
707 instance->setArrayLengthUnchecked(len);
708 else {
709 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: len))))
710 return scope.engine->throwTypeError();
711 }
712
713 return Encode(uint(len));
714}
715
716ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
717{
718 Scope scope(b);
719 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
720 if (!instance)
721 RETURN_UNDEFINED();
722
723 qint64 length = instance->getLength();
724 // ### FIXME
725 if (length >= UINT_MAX)
726 return scope.engine->throwRangeError(message: QLatin1String("Array.prototype.reverse: Length out of range."));
727
728 int lo = 0, hi = length - 1;
729
730 ScopedValue lval(scope);
731 ScopedValue hval(scope);
732 for (; lo < hi; ++lo, --hi) {
733 bool loExists, hiExists;
734 lval = instance->get(idx: lo, hasProperty: &loExists);
735 hval = instance->get(idx: hi, hasProperty: &hiExists);
736 CHECK_EXCEPTION();
737 bool ok;
738 if (hiExists)
739 ok = instance->put(idx: lo, v: hval);
740 else
741 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: lo));
742 if (ok) {
743 if (loExists)
744 ok = instance->put(idx: hi, v: lval);
745 else
746 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: hi));
747 }
748 if (!ok)
749 return scope.engine->throwTypeError();
750 }
751 return instance->asReturnedValue();
752}
753
754ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
755{
756 Scope scope(b);
757 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
758 if (!instance)
759 RETURN_UNDEFINED();
760
761 instance->arrayCreate();
762 Q_ASSERT(instance->arrayData());
763
764 uint len = instance->getLength();
765
766 if (!len) {
767 if (!instance->isArrayObject())
768 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromInt32(i: 0))))
769 return scope.engine->throwTypeError();
770 RETURN_UNDEFINED();
771 }
772
773 ScopedValue result(scope);
774 if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) {
775 result = instance->arrayData()->vtable()->pop_front(instance);
776 } else {
777 result = instance->get(idx: uint(0));
778 CHECK_EXCEPTION();
779 ScopedValue v(scope);
780 // do it the slow way
781 for (uint k = 1; k < len; ++k) {
782 bool exists;
783 v = instance->get(idx: k, hasProperty: &exists);
784 CHECK_EXCEPTION();
785 bool ok;
786 if (exists)
787 ok = instance->put(idx: k - 1, v);
788 else
789 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: k - 1));
790 if (!ok)
791 return scope.engine->throwTypeError();
792 }
793 bool ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: len - 1));
794 if (!ok)
795 return scope.engine->throwTypeError();
796 }
797
798 if (instance->isArrayObject())
799 instance->setArrayLengthUnchecked(len - 1);
800 else {
801 bool ok = instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: len - 1)));
802 if (!ok)
803 return scope.engine->throwTypeError();
804 }
805
806 return result->asReturnedValue();
807}
808
809ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
810{
811 Scope scope(b);
812 ScopedObject o(scope, thisObject->toObject(e: scope.engine));
813 if (!o)
814 RETURN_UNDEFINED();
815
816 ScopedArrayObject result(scope, scope.engine->newArrayObject());
817 uint len = o->getLength();
818 double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
819 uint start;
820 if (s < 0)
821 start = (uint)qMax(a: len + s, b: 0.);
822 else if (s > len)
823 start = len;
824 else
825 start = (uint) s;
826 uint end = len;
827 if (argc > 1 && !argv[1].isUndefined()) {
828 double e = argv[1].toInteger();
829 if (e < 0)
830 end = (uint)qMax(a: len + e, b: 0.);
831 else if (e > len)
832 end = len;
833 else
834 end = (uint) e;
835 }
836
837 ScopedValue v(scope);
838 uint n = 0;
839 for (uint i = start; i < end; ++i) {
840 bool exists;
841 v = o->get(idx: i, hasProperty: &exists);
842 CHECK_EXCEPTION();
843 if (exists)
844 result->arraySet(index: n, value: v);
845 ++n;
846 }
847 return result->asReturnedValue();
848}
849
850ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
851{
852 // Based on https://tc39.es/ecma262/#sec-array.prototype.sort
853
854 Scope scope(b);
855
856 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
857
858 // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
859 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
860 return scope.engine->throwTypeError(QStringLiteral("The provided comparison function is not callable."));
861
862 // 2. Let obj be ? ToObject(this value).
863 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
864 if (!instance)
865 RETURN_UNDEFINED();
866
867 // 3. Let len be ? LengthOfArrayLike(obj).
868 uint len = instance->getLength();
869
870 if (instance->arrayData() && instance->arrayData()->length()) {
871 ArrayData::sort(engine: scope.engine, thisObject: instance, comparefn, dataLen: len);
872 } else {
873 // Generic implementation that does not require a populated
874 // ArrayData, this is used, for example, by `Sequences` which
875 // store their data in a different way.
876
877 // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes)
878 Value* sorted = scope.alloc(nValues: scope.engine->safeForAllocLength(len64: len));
879 CHECK_EXCEPTION();
880
881 uint written = 0;
882 for (uint index = 0; index < len; ++index) {
883 bool hasProperty = false;
884 auto element = instance->get(idx: index, hasProperty: &hasProperty);
885
886 if (hasProperty) {
887 sorted[written] = element;
888 ++written;
889 }
890 }
891
892 std::stable_sort(first: sorted, last: sorted + written, comp: ArrayElementLessThan(scope.engine, comparefn));
893
894 // [...]
895 // 8. Repeat, while j < itemCount,
896 // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true).
897 // [...]
898 for (uint index = 0; index < written; ++index) {
899 instance->setIndexed(idx: index, v: sorted[index], shouldThrow: QV4::Object::DoThrowOnRejection);
900 CHECK_EXCEPTION();
901 }
902
903 // [...]
904 // 10. Repeat, while j < len,
905 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).
906 // [...]
907 while (written < len) {
908 if (!instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: written)))
909 return scope.engine->throwTypeError();
910 ++written;
911 }
912 }
913
914 // 11. Return obj
915 return thisObject->asReturnedValue();
916}
917
918ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
919{
920 Scope scope(b);
921 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
922 if (!instance)
923 RETURN_UNDEFINED();
924
925 qint64 len = instance->getLength();
926
927 double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
928 qint64 start;
929 if (rs < 0)
930 start = static_cast<qint64>(qMax(a: 0., b: len + rs));
931 else
932 start = static_cast<qint64>(qMin(a: rs, b: static_cast<double>(len)));
933
934 qint64 deleteCount = 0;
935 qint64 itemCount = 0;
936 if (argc == 1) {
937 deleteCount = len - start;
938 } else if (argc > 1){
939 itemCount = argc - 2;
940 double dc = argv[1].toInteger();
941 deleteCount = static_cast<qint64>(qMin(a: qMax(a: dc, b: 0.), b: double(len - start)));
942 }
943
944 if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
945 return scope.engine->throwTypeError();
946 if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
947 return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "Array length out of range."));
948
949 ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
950 newArray->arrayReserve(n: deleteCount);
951 ScopedValue v(scope);
952 for (uint i = 0; i < deleteCount; ++i) {
953 bool exists;
954 v = instance->get(idx: start + i, hasProperty: &exists);
955 CHECK_EXCEPTION();
956 if (exists)
957 newArray->arrayPut(index: i, value: v);
958 }
959 newArray->setArrayLengthUnchecked(deleteCount);
960
961
962 if (itemCount < deleteCount) {
963 for (uint k = start; k < len - deleteCount; ++k) {
964 bool exists;
965 v = instance->get(idx: k + deleteCount, hasProperty: &exists);
966 CHECK_EXCEPTION();
967 bool ok;
968 if (exists)
969 ok = instance->put(idx: k + itemCount, v);
970 else
971 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: k + itemCount));
972 if (!ok)
973 return scope.engine->throwTypeError();
974 }
975 for (uint k = len; k > len - deleteCount + itemCount; --k) {
976 if (!instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: k - 1)))
977 return scope.engine->throwTypeError();
978 }
979 } else if (itemCount > deleteCount) {
980 uint k = len - deleteCount;
981 while (k > start) {
982 bool exists;
983 v = instance->get(idx: k + deleteCount - 1, hasProperty: &exists);
984 CHECK_EXCEPTION();
985 bool ok;
986 if (exists)
987 ok = instance->put(idx: k + itemCount - 1, v);
988 else
989 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: k + itemCount - 1));
990 if (!ok)
991 return scope.engine->throwTypeError();
992 --k;
993 }
994 }
995
996 for (uint i = 0; i < itemCount; ++i)
997 instance->put(idx: start + i, v: argv[i + 2]);
998
999 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: len - deleteCount + itemCount))))
1000 return scope.engine->throwTypeError();
1001
1002 return newArray->asReturnedValue();
1003}
1004
1005ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1006{
1007 Scope scope(b);
1008 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1009 if (!instance)
1010 RETURN_UNDEFINED();
1011
1012 instance->arrayCreate();
1013 Q_ASSERT(instance->arrayData());
1014
1015 uint len = instance->getLength();
1016
1017 if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len &&
1018 instance->arrayData()->type != Heap::ArrayData::Custom) {
1019 instance->arrayData()->vtable()->push_front(instance, argv, argc);
1020 } else {
1021 ScopedValue v(scope);
1022 for (uint k = len; k > 0; --k) {
1023 bool exists;
1024 v = instance->get(idx: k - 1, hasProperty: &exists);
1025 bool ok;
1026 if (exists)
1027 ok = instance->put(idx: k + argc - 1, v);
1028 else
1029 ok = instance->deleteProperty(id: PropertyKey::fromArrayIndex(idx: k + argc - 1));
1030 if (!ok)
1031 return scope.engine->throwTypeError();
1032 }
1033 for (int i = 0, ei = argc; i < ei; ++i) {
1034 bool ok = instance->put(idx: i, v: argv[i]);
1035 if (!ok)
1036 return scope.engine->throwTypeError();
1037 }
1038 }
1039
1040 uint newLen = len + argc;
1041 if (instance->isArrayObject())
1042 instance->setArrayLengthUnchecked(newLen);
1043 else {
1044 if (!instance->put(name: scope.engine->id_length(), v: ScopedValue(scope, Value::fromDouble(d: newLen))))
1045 return scope.engine->throwTypeError();
1046 }
1047
1048 return Encode(newLen);
1049}
1050
1051ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1052{
1053 Scope scope(b);
1054 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1055 if (!instance)
1056 RETURN_UNDEFINED();
1057
1058 qint64 len = instance->getLength();
1059 if (len == 0) {
1060 return Encode(false);
1061 }
1062
1063 double n = 0;
1064 if (argc > 1 && !argv[1].isUndefined()) {
1065 n = argv[1].toInteger();
1066 }
1067
1068 double k = 0;
1069 if (n >= 0) {
1070 k = n;
1071 } else {
1072 k = len + n;
1073 if (k < 0) {
1074 k = 0;
1075 }
1076 }
1077
1078 ScopedValue val(scope);
1079 while (k < len) {
1080 val = instance->get(idx: k);
1081 if (val->sameValueZero(other: argv[0])) {
1082 return Encode(true);
1083 }
1084 k++;
1085 }
1086
1087 return Encode(false);
1088}
1089
1090ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1091{
1092 Scope scope(b);
1093 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1094 if (!instance)
1095 RETURN_UNDEFINED();
1096
1097 uint len = instance->getLength();
1098 if (!len)
1099 return Encode(-1);
1100
1101 ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
1102 uint fromIndex = 0;
1103
1104 if (argc >= 2) {
1105 double f = argv[1].toInteger();
1106 CHECK_EXCEPTION();
1107 if (f >= len)
1108 return Encode(-1);
1109 if (f < 0)
1110 f = qMax(a: len + f, b: 0.);
1111 fromIndex = (uint) f;
1112 }
1113
1114 if (instance->isStringObject()) {
1115 ScopedValue v(scope);
1116 for (uint k = fromIndex; k < len; ++k) {
1117 bool exists;
1118 v = instance->get(idx: k, hasProperty: &exists);
1119 if (exists && RuntimeHelpers::strictEqual(x: v, y: searchValue))
1120 return Encode(k);
1121 }
1122 return Encode(-1);
1123 }
1124
1125 ScopedValue value(scope);
1126
1127 if (ArgumentsObject::isNonStrictArgumentsObject(m: instance) ||
1128 (instance->arrayType() >= Heap::ArrayData::Sparse) || instance->protoHasArray()) {
1129 // lets be safe and slow
1130 for (uint i = fromIndex; i < len; ++i) {
1131 bool exists;
1132 value = instance->get(idx: i, hasProperty: &exists);
1133 CHECK_EXCEPTION();
1134 if (exists && RuntimeHelpers::strictEqual(x: value, y: searchValue))
1135 return Encode(i);
1136 }
1137 } else if (!instance->arrayData()) {
1138 return Encode(-1);
1139 } else {
1140 Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple);
1141 Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>();
1142 if (len > sa->values.size)
1143 len = sa->values.size;
1144 uint idx = fromIndex;
1145 while (idx < len) {
1146 value = sa->data(index: idx);
1147 CHECK_EXCEPTION();
1148 if (RuntimeHelpers::strictEqual(x: value, y: searchValue))
1149 return Encode(idx);
1150 ++idx;
1151 }
1152 }
1153 return Encode(-1);
1154}
1155
1156ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
1157{
1158 Scope scope(f);
1159 ScopedObject O(scope, thisObject->toObject(e: scope.engine));
1160 if (!O)
1161 RETURN_UNDEFINED();
1162
1163 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: O));
1164 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
1165 return ao->asReturnedValue();
1166}
1167
1168ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1169{
1170 Scope scope(b);
1171 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1172 if (!instance)
1173 RETURN_UNDEFINED();
1174
1175 uint len = instance->getLength();
1176 if (!len)
1177 return Encode(-1);
1178
1179 ScopedValue searchValue(scope);
1180 uint fromIndex = len;
1181
1182 if (argc >= 1)
1183 searchValue = argv[0];
1184 else
1185 searchValue = Value::undefinedValue();
1186
1187 if (argc >= 2) {
1188 double f = argv[1].toInteger();
1189 CHECK_EXCEPTION();
1190 if (f > 0)
1191 f = qMin(a: f, b: (double)(len - 1));
1192 else if (f < 0) {
1193 f = len + f;
1194 if (f < 0)
1195 return Encode(-1);
1196 }
1197 fromIndex = (uint) f + 1;
1198 }
1199
1200 ScopedValue v(scope);
1201 for (uint k = fromIndex; k > 0;) {
1202 --k;
1203 bool exists;
1204 v = instance->get(idx: k, hasProperty: &exists);
1205 CHECK_EXCEPTION();
1206 if (exists && RuntimeHelpers::strictEqual(x: v, y: searchValue))
1207 return Encode(k);
1208 }
1209 return Encode(-1);
1210}
1211
1212ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1213{
1214 Scope scope(b);
1215 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1216 if (!instance)
1217 RETURN_UNDEFINED();
1218
1219 uint len = instance->getLength();
1220
1221 if (!argc || !argv->isFunctionObject())
1222 THROW_TYPE_ERROR();
1223 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1224
1225 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1226 ScopedValue r(scope);
1227 Value *arguments = scope.alloc(nValues: 3);
1228
1229 bool ok = true;
1230 for (uint k = 0; ok && k < len; ++k) {
1231 bool exists;
1232 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1233 if (!exists)
1234 continue;
1235
1236 arguments[1] = Value::fromDouble(d: k);
1237 arguments[2] = instance;
1238 r = callback->call(thisObject: that, argv: arguments, argc: 3);
1239 CHECK_EXCEPTION();
1240 ok = r->toBoolean();
1241 }
1242 return Encode(ok);
1243}
1244
1245ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1246{
1247 Scope scope(b);
1248 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1249 if (!instance)
1250 RETURN_UNDEFINED();
1251
1252 const qsizetype len = instance->getLength();
1253 Q_ASSERT(len >= 0);
1254
1255 const qsizetype relativeStart = argc > 1 ? argv[1].toInteger() : 0;
1256 qsizetype relativeEnd = len;
1257 if (argc > 2 && !argv[2].isUndefined())
1258 relativeEnd = argv[2].toInteger();
1259
1260 qsizetype k = 0;
1261 qsizetype fin = 0;
1262
1263 if (relativeStart < 0) {
1264 if (relativeStart > -len)
1265 k = std::max(a: len + relativeStart, b: qsizetype(0));
1266 } else {
1267 k = std::min(a: relativeStart, b: len);
1268 }
1269 Q_ASSERT(k >= 0);
1270
1271 if (relativeEnd < 0) {
1272 if (relativeEnd > -len)
1273 fin = std::max(a: len + relativeEnd, b: qsizetype(0));
1274 } else {
1275 fin = std::min(a: relativeEnd, b: len);
1276 }
1277 Q_ASSERT(fin >= 0);
1278
1279 if (sizeof(qsizetype) > sizeof(uint) && fin > qsizetype(std::numeric_limits<uint>::max()))
1280 return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "Array length out of range."));
1281
1282 for (; k < fin; ++k)
1283 instance->setIndexed(idx: uint(k), v: argv[0], shouldThrow: QV4::Object::DoThrowOnRejection);
1284
1285 return instance.asReturnedValue();
1286}
1287
1288ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1289{
1290 Scope scope(b);
1291 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1292 if (!instance)
1293 RETURN_UNDEFINED();
1294
1295 uint len = instance->getLength();
1296
1297 if (!argc || !argv->isFunctionObject())
1298 THROW_TYPE_ERROR();
1299 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1300
1301 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1302 ScopedValue result(scope);
1303 Value *arguments = scope.alloc(nValues: 3);
1304
1305 for (uint k = 0; k < len; ++k) {
1306 bool exists;
1307 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1308 if (!exists)
1309 continue;
1310
1311 arguments[1] = Value::fromDouble(d: k);
1312 arguments[2] = instance;
1313 result = callback->call(thisObject: that, argv: arguments, argc: 3);
1314 CHECK_EXCEPTION();
1315 if (result->toBoolean())
1316 return Encode(true);
1317 }
1318 return Encode(false);
1319}
1320
1321ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1322{
1323 Scope scope(b);
1324 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1325 if (!instance)
1326 RETURN_UNDEFINED();
1327
1328 uint len = instance->getLength();
1329
1330 if (!argc || !argv->isFunctionObject())
1331 THROW_TYPE_ERROR();
1332 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1333
1334 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1335 Value *arguments = scope.alloc(nValues: 3);
1336
1337 for (uint k = 0; k < len; ++k) {
1338 bool exists;
1339 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1340 if (!exists)
1341 continue;
1342
1343 arguments[1] = Value::fromDouble(d: k);
1344 arguments[2] = instance;
1345 callback->call(thisObject: that, argv: arguments, argc: 3);
1346 }
1347 RETURN_UNDEFINED();
1348}
1349
1350ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1351{
1352 Scope scope(b);
1353 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1354 if (!instance)
1355 RETURN_UNDEFINED();
1356
1357 qint64 len = instance->getLength();
1358
1359 if (!argc || !argv->isFunctionObject())
1360 THROW_TYPE_ERROR();
1361 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1362
1363 if (len > UINT_MAX - 1)
1364 return scope.engine->throwRangeError(message: QString::fromLatin1(ba: "Array length out of range."));
1365
1366 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1367 a->arrayReserve(n: len);
1368 a->setArrayLengthUnchecked(len);
1369
1370 ScopedValue v(scope);
1371 ScopedValue mapped(scope);
1372 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1373 Value *arguments = scope.alloc(nValues: 3);
1374
1375 for (uint k = 0; k < len; ++k) {
1376 bool exists;
1377 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1378 if (!exists)
1379 continue;
1380
1381 arguments[1] = Value::fromDouble(d: k);
1382 arguments[2] = instance;
1383 mapped = callback->call(thisObject: that, argv: arguments, argc: 3);
1384 CHECK_EXCEPTION();
1385 a->arraySet(index: k, value: mapped);
1386 }
1387 return a.asReturnedValue();
1388}
1389
1390ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1391{
1392 Scope scope(b);
1393 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1394 if (!instance)
1395 RETURN_UNDEFINED();
1396
1397 uint len = instance->getLength();
1398
1399 if (!argc || !argv->isFunctionObject())
1400 THROW_TYPE_ERROR();
1401 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1402
1403 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1404 a->arrayReserve(n: len);
1405
1406 ScopedValue selected(scope);
1407 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1408 Value *arguments = scope.alloc(nValues: 3);
1409
1410 uint to = 0;
1411 for (uint k = 0; k < len; ++k) {
1412 bool exists;
1413 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1414 if (!exists)
1415 continue;
1416
1417 arguments[1] = Value::fromDouble(d: k);
1418 arguments[2] = instance;
1419 selected = callback->call(thisObject: that, argv: arguments, argc: 3);
1420 CHECK_EXCEPTION();
1421 if (selected->toBoolean()) {
1422 a->arraySet(index: to, value: arguments[0]);
1423 ++to;
1424 }
1425 }
1426 return a.asReturnedValue();
1427}
1428
1429ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1430{
1431 Scope scope(b);
1432 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1433 if (!instance)
1434 RETURN_UNDEFINED();
1435
1436 uint len = instance->getLength();
1437
1438 if (!argc || !argv->isFunctionObject())
1439 THROW_TYPE_ERROR();
1440 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1441
1442 uint k = 0;
1443 ScopedValue acc(scope);
1444 ScopedValue v(scope);
1445
1446 if (argc > 1) {
1447 acc = argv[1];
1448 } else {
1449 bool kPresent = false;
1450 while (k < len && !kPresent) {
1451 v = instance->get(idx: k, hasProperty: &kPresent);
1452 if (kPresent)
1453 acc = v;
1454 ++k;
1455 }
1456 if (!kPresent)
1457 THROW_TYPE_ERROR();
1458 }
1459
1460 Value *arguments = scope.alloc(nValues: 4);
1461
1462 while (k < len) {
1463 bool kPresent;
1464 v = instance->get(idx: k, hasProperty: &kPresent);
1465 if (kPresent) {
1466 arguments[0] = acc;
1467 arguments[1] = v;
1468 arguments[2] = Value::fromDouble(d: k);
1469 arguments[3] = instance;
1470 acc = callback->call(thisObject: nullptr, argv: arguments, argc: 4);
1471 CHECK_EXCEPTION();
1472 }
1473 ++k;
1474 }
1475 return acc->asReturnedValue();
1476}
1477
1478ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1479{
1480 Scope scope(b);
1481 ScopedObject instance(scope, thisObject->toObject(e: scope.engine));
1482 if (!instance)
1483 RETURN_UNDEFINED();
1484
1485 uint len = instance->getLength();
1486
1487 if (!argc || !argv->isFunctionObject())
1488 THROW_TYPE_ERROR();
1489 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1490
1491 if (len == 0) {
1492 if (argc == 1)
1493 THROW_TYPE_ERROR();
1494 return argv[1].asReturnedValue();
1495 }
1496
1497 uint k = len;
1498 ScopedValue acc(scope);
1499 ScopedValue v(scope);
1500 if (argc > 1) {
1501 acc = argv[1];
1502 } else {
1503 bool kPresent = false;
1504 while (k > 0 && !kPresent) {
1505 v = instance->get(idx: k - 1, hasProperty: &kPresent);
1506 if (kPresent)
1507 acc = v;
1508 --k;
1509 }
1510 if (!kPresent)
1511 THROW_TYPE_ERROR();
1512 }
1513
1514 Value *arguments = scope.alloc(nValues: 4);
1515
1516 while (k > 0) {
1517 bool kPresent;
1518 v = instance->get(idx: k - 1, hasProperty: &kPresent);
1519 if (kPresent) {
1520 arguments[0] = acc;
1521 arguments[1] = v;
1522 arguments[2] = Value::fromDouble(d: k - 1);
1523 arguments[3] = instance;
1524 acc = callback->call(thisObject: nullptr, argv: arguments, argc: 4);
1525 CHECK_EXCEPTION();
1526 }
1527 --k;
1528 }
1529 return acc->asReturnedValue();
1530}
1531
1532ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
1533{
1534 Scope scope(b);
1535 ScopedObject O(scope, thisObject->toObject(e: scope.engine));
1536 if (!O)
1537 RETURN_UNDEFINED();
1538
1539 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: O));
1540 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
1541 return ao->asReturnedValue();
1542}
1543
1544ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
1545{
1546 return thisObject->asReturnedValue();
1547}
1548

source code of qtdeclarative/src/qml/jsruntime/qv4arrayobject.cpp