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 uint 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 (uint 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 uint len = eltAsObj->getLength();
397 if (scope.hasException())
398 return Encode::undefined();
399
400 for (uint 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 (int 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 uint 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 (uint 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 uint 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 (uint 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(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 uint 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 uint 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 (uint 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 uint len = o->getLength();
819 double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
820 uint start;
821 if (s < 0)
822 start = (uint)qMax(len + s, 0.);
823 else if (s > len)
824 start = len;
825 else
826 start = (uint) s;
827 uint end = len;
828 if (argc > 1 && !argv[1].isUndefined()) {
829 double e = argv[1].toInteger();
830 if (e < 0)
831 end = (uint)qMax(len + e, 0.);
832 else if (e > len)
833 end = len;
834 else
835 end = (uint) e;
836 }
837
838 ScopedValue v(scope);
839 uint n = 0;
840 for (uint i = start; i < end; ++i) {
841 bool exists;
842 v = o->get(i, &exists);
844 if (exists)
845 result->arraySet(n, v);
846 ++n;
847 }
848 return result->asReturnedValue();
849}
850
851ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
852{
853 // Based on https://tc39.es/ecma262/#sec-array.prototype.sort
854
855 Scope scope(b);
856
857 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
858
859 // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
860 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
861 return scope.engine->throwTypeError(QStringLiteral("The provided comparison function is not callable."));
862
863 // 2. Let obj be ? ToObject(this value).
864 ScopedObject instance(scope, thisObject->toObject(scope.engine));
865 if (!instance)
867
868 // 3. Let len be ? LengthOfArrayLike(obj).
869 uint len = instance->getLength();
870
871 if (instance->arrayData() && instance->arrayData()->length()) {
872 ArrayData::sort(scope.engine, instance, comparefn, len);
873 } else {
874 // Generic implementation that does not require a populated
875 // ArrayData, this is used, for example, by `Sequences` which
876 // store their data in a different way.
877
878 // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes)
879 Value* sorted = scope.constructUndefined(scope.engine->safeForAllocLength(len));
881
882 uint written = 0;
883 for (uint index = 0; index < len; ++index) {
884 bool hasProperty = false;
885 auto element = instance->get(index, &hasProperty);
886
887 if (hasProperty) {
888 sorted[written] = element;
889 ++written;
890 }
891 }
892
893 std::stable_sort(sorted, sorted + written, ArrayElementLessThan(scope.engine, comparefn));
894
895 // [...]
896 // 8. Repeat, while j < itemCount,
897 // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true).
898 // [...]
899 for (uint index = 0; index < written; ++index) {
900 instance->setIndexed(index, sorted[index], QV4::Object::DoThrowOnRejection);
902 }
903
904 // [...]
905 // 10. Repeat, while j < len,
906 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).
907 // [...]
908 while (written < len) {
909 if (!instance->deleteProperty(PropertyKey::fromArrayIndex(written)))
910 return scope.engine->throwTypeError();
911 ++written;
912 }
913 }
914
915 // 11. Return obj
916 return thisObject->asReturnedValue();
917}
918
919ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
920{
921 Scope scope(b);
922 ScopedObject instance(scope, thisObject->toObject(scope.engine));
923 if (!instance)
925
926 qint64 len = instance->getLength();
927
928 double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
929 qint64 start;
930 if (rs < 0)
931 start = static_cast<qint64>(qMax(0., len + rs));
932 else
933 start = static_cast<qint64>(qMin(rs, static_cast<double>(len)));
934
935 qint64 deleteCount = 0;
936 qint64 itemCount = 0;
937 if (argc == 1) {
938 deleteCount = len - start;
939 } else if (argc > 1){
940 itemCount = argc - 2;
941 double dc = argv[1].toInteger();
942 deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start)));
943 }
944
945 if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
946 return scope.engine->throwTypeError();
947 if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
948 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
949
950 ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
951 newArray->arrayReserve(deleteCount);
952 ScopedValue v(scope);
953 for (uint i = 0; i < deleteCount; ++i) {
954 bool exists;
955 v = instance->get(start + i, &exists);
957 if (exists)
958 newArray->arrayPut(i, v);
959 }
960 newArray->setArrayLengthUnchecked(deleteCount);
961
962
963 if (itemCount < deleteCount) {
964 for (uint k = start; k < len - deleteCount; ++k) {
965 bool exists;
966 v = instance->get(k + deleteCount, &exists);
968 bool ok;
969 if (exists)
970 ok = instance->put(k + itemCount, v);
971 else
972 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount));
973 if (!ok)
974 return scope.engine->throwTypeError();
975 }
976 for (uint k = len; k > len - deleteCount + itemCount; --k) {
977 if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)))
978 return scope.engine->throwTypeError();
979 }
980 } else if (itemCount > deleteCount) {
981 uint k = len - deleteCount;
982 while (k > start) {
983 bool exists;
984 v = instance->get(k + deleteCount - 1, &exists);
986 bool ok;
987 if (exists)
988 ok = instance->put(k + itemCount - 1, v);
989 else
990 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1));
991 if (!ok)
992 return scope.engine->throwTypeError();
993 --k;
994 }
995 }
996
997 for (uint i = 0; i < itemCount; ++i)
998 instance->put(start + i, argv[i + 2]);
999
1000 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - deleteCount + itemCount))))
1001 return scope.engine->throwTypeError();
1002
1003 return newArray->asReturnedValue();
1004}
1005
1006ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1007{
1008 Scope scope(b);
1009 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1010 if (!instance)
1012
1013 instance->arrayCreate();
1014 Q_ASSERT(instance->arrayData());
1015
1016 uint len = instance->getLength();
1017
1018 if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len &&
1019 instance->arrayData()->type != Heap::ArrayData::Custom) {
1020 instance->arrayData()->vtable()->push_front(instance, argv, argc);
1021 } else {
1022 ScopedValue v(scope);
1023 for (uint k = len; k > 0; --k) {
1024 bool exists;
1025 v = instance->get(k - 1, &exists);
1026 bool ok;
1027 if (exists)
1028 ok = instance->put(k + argc - 1, v);
1029 else
1030 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1));
1031 if (!ok)
1032 return scope.engine->throwTypeError();
1033 }
1034 for (int i = 0, ei = argc; i < ei; ++i) {
1035 bool ok = instance->put(i, argv[i]);
1036 if (!ok)
1037 return scope.engine->throwTypeError();
1038 }
1039 }
1040
1041 uint newLen = len + argc;
1042 if (instance->isArrayObject())
1043 instance->setArrayLengthUnchecked(newLen);
1044 else {
1045 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(newLen))))
1046 return scope.engine->throwTypeError();
1047 }
1048
1049 return Encode(newLen);
1050}
1051
1052ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1053{
1054 Scope scope(b);
1055 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1056 if (!instance)
1058
1059 qint64 len = instance->getLength();
1060 if (len == 0) {
1061 return Encode(false);
1062 }
1063
1064 double n = 0;
1065 if (argc > 1 && !argv[1].isUndefined()) {
1066 n = argv[1].toInteger();
1067 }
1068
1069 double k = 0;
1070 if (n >= 0) {
1071 k = n;
1072 } else {
1073 k = len + n;
1074 if (k < 0) {
1075 k = 0;
1076 }
1077 }
1078
1079 ScopedValue val(scope);
1080 while (k < len) {
1081 val = instance->get(k);
1082 if (val->sameValueZero(argv[0])) {
1083 return Encode(true);
1084 }
1085 k++;
1086 }
1087
1088 return Encode(false);
1089}
1090
1091ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1092{
1093 Scope scope(b);
1094 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1095 if (!instance)
1097
1098 uint len = instance->getLength();
1099 if (!len)
1100 return Encode(-1);
1101
1102 ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
1103 uint fromIndex = 0;
1104
1105 if (argc >= 2) {
1106 double f = argv[1].toInteger();
1108 if (f >= len)
1109 return Encode(-1);
1110 if (f < 0)
1111 f = qMax(len + f, 0.);
1112 fromIndex = (uint) f;
1113 }
1114
1115 if (instance->isStringObject()) {
1116 ScopedValue v(scope);
1117 for (uint k = fromIndex; k < len; ++k) {
1118 bool exists;
1119 v = instance->get(k, &exists);
1120 if (exists && RuntimeHelpers::strictEqual(v, searchValue))
1121 return Encode(k);
1122 }
1123 return Encode(-1);
1124 }
1125
1126 ScopedValue value(scope);
1127
1128 if (ArgumentsObject::isNonStrictArgumentsObject(instance) ||
1129 (instance->arrayType() >= Heap::ArrayData::Sparse) || instance->protoHasArray()) {
1130 // lets be safe and slow
1131 for (uint i = fromIndex; i < len; ++i) {
1132 bool exists;
1133 value = instance->get(i, &exists);
1135 if (exists && RuntimeHelpers::strictEqual(value, searchValue))
1136 return Encode(i);
1137 }
1138 } else if (!instance->arrayData()) {
1139 return Encode(-1);
1140 } else {
1141 Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple);
1142 Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>();
1143 if (len > sa->values.size)
1144 len = sa->values.size;
1145 uint idx = fromIndex;
1146 while (idx < len) {
1147 value = sa->data(idx);
1149 if (RuntimeHelpers::strictEqual(value, searchValue))
1150 return Encode(idx);
1151 ++idx;
1152 }
1153 }
1154 return Encode(-1);
1155}
1156
1157ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
1158{
1159 Scope scope(f);
1160 ScopedObject O(scope, thisObject->toObject(scope.engine));
1161 if (!O)
1163
1164 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
1165 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
1166 return ao->asReturnedValue();
1167}
1168
1169ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1170{
1171 Scope scope(b);
1172 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1173 if (!instance)
1175
1176 uint len = instance->getLength();
1177 if (!len)
1178 return Encode(-1);
1179
1180 ScopedValue searchValue(scope);
1181 uint fromIndex = len;
1182
1183 if (argc >= 1)
1184 searchValue = argv[0];
1185 else
1186 searchValue = Value::undefinedValue();
1187
1188 if (argc >= 2) {
1189 double f = argv[1].toInteger();
1191 if (f > 0)
1192 f = qMin(f, (double)(len - 1));
1193 else if (f < 0) {
1194 f = len + f;
1195 if (f < 0)
1196 return Encode(-1);
1197 }
1198 fromIndex = (uint) f + 1;
1199 }
1200
1201 ScopedValue v(scope);
1202 for (uint k = fromIndex; k > 0;) {
1203 --k;
1204 bool exists;
1205 v = instance->get(k, &exists);
1207 if (exists && RuntimeHelpers::strictEqual(v, searchValue))
1208 return Encode(k);
1209 }
1210 return Encode(-1);
1211}
1212
1213ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1214{
1215 Scope scope(b);
1216 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1217 if (!instance)
1219
1220 uint len = instance->getLength();
1221
1222 if (!argc || !argv->isFunctionObject())
1224 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1225
1226 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1227 ScopedValue r(scope);
1228 Value *arguments = scope.constructUndefined(3);
1229
1230 bool ok = true;
1231 for (uint k = 0; ok && k < len; ++k) {
1232 bool exists;
1233 arguments[0] = instance->get(k, &exists);
1234 if (!exists)
1235 continue;
1236
1237 arguments[1] = Value::fromDouble(k);
1238 arguments[2] = instance;
1239 r = callback->call(that, arguments, 3);
1241 ok = r->toBoolean();
1242 }
1243 return Encode(ok);
1244}
1245
1246ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1247{
1248 Scope scope(b);
1249 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1250 if (!instance)
1252
1253 const qsizetype len = instance->getLength();
1254 Q_ASSERT(len >= 0);
1255
1256 const qsizetype relativeStart = argc > 1 ? argv[1].toInteger() : 0;
1257 qsizetype relativeEnd = len;
1258 if (argc > 2 && !argv[2].isUndefined())
1259 relativeEnd = argv[2].toInteger();
1260
1261 qsizetype k = 0;
1262 qsizetype fin = 0;
1263
1264 if (relativeStart < 0) {
1265 if (relativeStart > -len)
1266 k = std::max(len + relativeStart, qsizetype(0));
1267 } else {
1268 k = std::min(relativeStart, len);
1269 }
1270 Q_ASSERT(k >= 0);
1271
1272 if (relativeEnd < 0) {
1273 if (relativeEnd > -len)
1274 fin = std::max(len + relativeEnd, qsizetype(0));
1275 } else {
1276 fin = std::min(relativeEnd, len);
1277 }
1278 Q_ASSERT(fin >= 0);
1279
1280 if (sizeof(qsizetype) > sizeof(uint) && fin > qsizetype(std::numeric_limits<uint>::max()))
1281 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
1282
1283 for (; k < fin; ++k)
1284 instance->setIndexed(uint(k), argv[0], QV4::Object::DoThrowOnRejection);
1285
1286 return instance.asReturnedValue();
1287}
1288
1289ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1290{
1291 Scope scope(b);
1292 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1293 if (!instance)
1295
1296 uint len = instance->getLength();
1297
1298 if (!argc || !argv->isFunctionObject())
1300 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1301
1302 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1303 ScopedValue result(scope);
1304 Value *arguments = scope.constructUndefined(3);
1305
1306 for (uint k = 0; k < len; ++k) {
1307 bool exists;
1308 arguments[0] = instance->get(k, &exists);
1309 if (!exists)
1310 continue;
1311
1312 arguments[1] = Value::fromDouble(k);
1313 arguments[2] = instance;
1314 result = callback->call(that, arguments, 3);
1316 if (result->toBoolean())
1317 return Encode(true);
1318 }
1319 return Encode(false);
1320}
1321
1322ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1323{
1324 Scope scope(b);
1325 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1326 if (!instance)
1328
1329 uint len = instance->getLength();
1330
1331 if (!argc || !argv->isFunctionObject())
1333 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1334
1335 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1336 Value *arguments = scope.constructUndefined(3);
1337
1338 for (uint k = 0; k < len; ++k) {
1339 bool exists;
1340 arguments[0] = instance->get(k, &exists);
1341 if (!exists)
1342 continue;
1343
1344 arguments[1] = Value::fromDouble(k);
1345 arguments[2] = instance;
1346 callback->call(that, arguments, 3);
1347 }
1349}
1350
1351ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1352{
1353 Scope scope(b);
1354 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1355 if (!instance)
1357
1358 qint64 len = instance->getLength();
1359
1360 if (!argc || !argv->isFunctionObject())
1362 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1363
1364 if (len > UINT_MAX - 1)
1365 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
1366
1367 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1368 a->arrayReserve(len);
1369 a->setArrayLengthUnchecked(len);
1370
1371 ScopedValue v(scope);
1372 ScopedValue mapped(scope);
1373 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1374 Value *arguments = scope.constructUndefined(3);
1375
1376 for (uint k = 0; k < len; ++k) {
1377 bool exists;
1378 arguments[0] = instance->get(k, &exists);
1379 if (!exists)
1380 continue;
1381
1382 arguments[1] = Value::fromDouble(k);
1383 arguments[2] = instance;
1384 mapped = callback->call(that, arguments, 3);
1386 a->arraySet(k, mapped);
1387 }
1388 return a.asReturnedValue();
1389}
1390
1391ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1392{
1393 Scope scope(b);
1394 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1395 if (!instance)
1397
1398 uint len = instance->getLength();
1399
1400 if (!argc || !argv->isFunctionObject())
1402 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1403
1404 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1405 a->arrayReserve(len);
1406
1407 ScopedValue selected(scope);
1408 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1409 Value *arguments = scope.constructUndefined(3);
1410
1411 uint to = 0;
1412 for (uint k = 0; k < len; ++k) {
1413 bool exists;
1414 arguments[0] = instance->get(k, &exists);
1415 if (!exists)
1416 continue;
1417
1418 arguments[1] = Value::fromDouble(k);
1419 arguments[2] = instance;
1420 selected = callback->call(that, arguments, 3);
1422 if (selected->toBoolean()) {
1423 a->arraySet(to, arguments[0]);
1424 ++to;
1425 }
1426 }
1427 return a.asReturnedValue();
1428}
1429
1430ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1431{
1432 Scope scope(b);
1433 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1434 if (!instance)
1436
1437 uint len = instance->getLength();
1438
1439 if (!argc || !argv->isFunctionObject())
1441 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1442
1443 uint k = 0;
1444 ScopedValue acc(scope);
1445 ScopedValue v(scope);
1446
1447 if (argc > 1) {
1448 acc = argv[1];
1449 } else {
1450 bool kPresent = false;
1451 while (k < len && !kPresent) {
1452 v = instance->get(k, &kPresent);
1453 if (kPresent)
1454 acc = v;
1455 ++k;
1456 }
1457 if (!kPresent)
1459 }
1460
1461 Value *arguments = scope.constructUndefined(4);
1462
1463 while (k < len) {
1464 bool kPresent;
1465 v = instance->get(k, &kPresent);
1466 if (kPresent) {
1467 arguments[0] = acc;
1468 arguments[1] = v;
1469 arguments[2] = Value::fromDouble(k);
1470 arguments[3] = instance;
1471 acc = callback->call(nullptr, arguments, 4);
1473 }
1474 ++k;
1475 }
1476 return acc->asReturnedValue();
1477}
1478
1479ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1480{
1481 Scope scope(b);
1482 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1483 if (!instance)
1485
1486 uint len = instance->getLength();
1487
1488 if (!argc || !argv->isFunctionObject())
1490 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1491
1492 if (len == 0) {
1493 if (argc == 1)
1495 return argv[1].asReturnedValue();
1496 }
1497
1498 uint k = len;
1499 ScopedValue acc(scope);
1500 ScopedValue v(scope);
1501 if (argc > 1) {
1502 acc = argv[1];
1503 } else {
1504 bool kPresent = false;
1505 while (k > 0 && !kPresent) {
1506 v = instance->get(k - 1, &kPresent);
1507 if (kPresent)
1508 acc = v;
1509 --k;
1510 }
1511 if (!kPresent)
1513 }
1514
1515 Value *arguments = scope.constructUndefined(4);
1516
1517 while (k > 0) {
1518 bool kPresent;
1519 v = instance->get(k - 1, &kPresent);
1520 if (kPresent) {
1521 arguments[0] = acc;
1522 arguments[1] = v;
1523 arguments[2] = Value::fromDouble(k - 1);
1524 arguments[3] = instance;
1525 acc = callback->call(nullptr, arguments, 4);
1527 }
1528 --k;
1529 }
1530 return acc->asReturnedValue();
1531}
1532
1533ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
1534{
1535 Scope scope(b);
1536 ScopedObject O(scope, thisObject->toObject(scope.engine));
1537 if (!O)
1539
1540 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
1541 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
1542 return ao->asReturnedValue();
1543}
1544
1545ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
1546{
1547 return thisObject->asReturnedValue();
1548}
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()