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