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 ScopedObject valueAsObject(scope);
347 ScopedFunctionObject function(scope);
348
349 for (qint64 k = 0; k < len; ++k) {
350 if (k)
351 R += separator;
352
353 v = instance->get(k);
354 if (v->isNullOrUndefined())
355 continue;
356
357 valueAsObject = v->toObject(scope.engine);
358 Q_ASSERT(valueAsObject); // null and undefined handled above
359
360 function = valueAsObject->get(tolocaleString);
361 if (!function)
362 return scope.engine->throwTypeError();
363
364 v = function->call(valueAsObject, nullptr, 0);
365 if (scope.hasException())
366 return Encode::undefined();
367
368 s = v->toString(scope.engine);
369 if (scope.hasException())
370 return Encode::undefined();
371
372 R += s->toQString();
373 }
374 return scope.engine->newString(R)->asReturnedValue();
375}
376
377ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc)
378{
379 Scope scope(b);
380 ScopedObject thisObject(scope, that->toObject(scope.engine));
381 if (!thisObject)
383
384 ScopedArrayObject result(scope, scope.engine->newArrayObject());
385
386 ScopedArrayObject elt(scope);
387 ScopedObject eltAsObj(scope);
388 ScopedValue entry(scope);
389 for (int i = -1; i < argc; ++i) {
390 const Value *v = i == -1 ? thisObject.getPointer() : argv + i;
391 eltAsObj = *v;
392 elt = *v;
393 if (elt) {
394 uint n = elt->getLength();
395 uint newLen = ArrayData::append(result, elt, n);
396 result->setArrayLengthUnchecked(newLen);
397 } else if (eltAsObj && eltAsObj->isConcatSpreadable()) {
398 const uint startIndex = result->getLength();
399 const qint64 len = eltAsObj->getLength();
400 if (scope.hasException())
401 return Encode::undefined();
402
403 for (qint64 i = 0; i < len; ++i) {
404 bool hasProperty = false;
405 entry = eltAsObj->get(i, &hasProperty);
406 if (hasProperty) {
407 if (!result->put(startIndex + i, entry))
408 return scope.engine->throwTypeError();
409 }
410 }
411 } else if (eltAsObj && eltAsObj->isArrayLike()) {
412 const uint startIndex = result->getLength();
413 for (qint64 i = 0, len = eltAsObj->getLength(); i < len; ++i) {
414 entry = eltAsObj->get(i);
415 // spec says not to throw if this fails
416 result->put(startIndex + i, entry);
417 }
418 } else {
419 result->arraySet(result->getLength(), *v);
420 }
421 }
422
423 return result.asReturnedValue();
424}
425
426ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
427{
428 Scope scope(b);
429 ScopedObject instance(scope, thisObject->toObject(scope.engine));
430 if (!instance)
432
433 double len = instance->getLength();
434 double target = argv[0].toInteger();
435 double start = argc > 1 ? argv[1].toInteger() : 0;
436 double end = len;
437
438 if (argc > 2 && !argv[2].isUndefined()) {
439 end = argv[2].toInteger();
440 }
441
442 double relativeTarget = target;
443 double relativeStart = start;
444 double relativeEnd = end;
445 double from = 0;
446 double to = 0;
447
448 if (relativeTarget < 0) {
449 to = std::max(len+relativeTarget, 0.0);
450 } else {
451 to = std::min(relativeTarget, len);
452 }
453 if (relativeStart < 0) {
454 from = std::max(len+relativeStart, 0.0);
455 } else {
456 from = std::min(relativeStart, len);
457 }
458
459 double fin = 0;
460 if (relativeEnd < 0) {
461 fin = std::max(len+relativeEnd, 0.0);
462 } else {
463 fin = std::min(relativeEnd, len);
464 }
465 double count = std::min(fin-from, len-to);
466 double direction = 1;
467 if (from < to && to < from+count) {
468 direction = -1;
469 from = from + count - 1;
470 to = to + count - 1;
471 }
472
473 ScopedValue fromVal(scope);
474 while (count > 0) {
475 bool fromPresent = false;
476 fromVal = instance->get(from, &fromPresent);
477
478 if (fromPresent) {
479 instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection);
481 } else {
482 bool didDelete = instance->deleteProperty(PropertyKey::fromArrayIndex(to));
484 if (!didDelete) {
485 return scope.engine->throwTypeError();
486 }
487 }
488
489 from = from + direction;
490 to = to + direction;
491 count = count - 1;
492 }
493
494 return instance.asReturnedValue();
495}
496
497ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
498{
499 Scope scope(b);
500 ScopedObject O(scope, thisObject->toObject(scope.engine));
501 if (!O)
503
504 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
505 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
506 return ao->asReturnedValue();
507}
508
509ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
510{
511 Scope scope(b);
512 ScopedObject instance(scope, thisObject->toObject(scope.engine));
513 if (!instance)
515
516 qint64 len = instance->getLength();
517
518 if (!argc || !argv[0].isFunctionObject())
520 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
521
522 ScopedValue result(scope);
523 Value *arguments = scope.constructUndefined(3);
524
525 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
526
527 for (qint64 k = 0; k < len; ++k) {
528 arguments[0] = instance->get(k);
530
531 arguments[1] = Value::fromDouble(k);
532 arguments[2] = instance;
533 result = callback->call(that, arguments, 3);
534
536 if (result->toBoolean())
537 return arguments[0].asReturnedValue();
538 }
539
541}
542
543ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
544{
545 Scope scope(b);
546 ScopedObject instance(scope, thisObject->toObject(scope.engine));
547 if (!instance)
549
550 qint64 len = instance->getLength();
551
552 if (!argc || !argv[0].isFunctionObject())
554 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
555
556 ScopedValue result(scope);
557 Value *arguments = scope.constructUndefined(3);
558
559 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
560
561 for (qint64 k = 0; k < len; ++k) {
562 arguments[0] = instance->get(k);
564
565 arguments[1] = Value::fromDouble(k);
566 arguments[2] = instance;
567 result = callback->call(that, arguments, 3);
568
570 if (result->toBoolean())
571 return Encode(double(k));
572 }
573
574 return Encode(-1);
575}
576
577ReturnedValue ArrayPrototype::method_join(const FunctionObject *functionObject,
578 const Value *thisObject, const Value *argv, int argc)
579{
580 Scope scope(functionObject);
581 CHECK_STACK_LIMITS(scope.engine)
582 ScopedObject instance(scope, thisObject->toObject(scope.engine));
583
584 if (!instance)
585 return Encode(scope.engine->newString());
586
587 // We cannot optimize the resolution of the argument away in case of length == 0
588 // It may have side effects.
589 ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue());
590 const QString separator = argument->isUndefined()
591 ? QStringLiteral(",")
592 : argument->toQString();
593
594 ScopedValue scopedLength(scope, instance->get(scope.engine->id_length()));
595 const quint32 genericLength = scopedLength->isUndefined() ? 0 : scopedLength->toUInt32();
596 if (!genericLength)
597 return Encode(scope.engine->newString());
598
599 QString result;
600 if (auto *arrayObject = instance->as<ArrayObject>()) {
601 ScopedValue entry(scope);
602 const qint64 arrayLength = arrayObject->getLength();
603 Q_ASSERT(arrayLength >= 0);
604 Q_ASSERT(arrayLength <= std::numeric_limits<quint32>::max());
605 for (quint32 i = 0; i < quint32(arrayLength); ++i) {
606 if (i)
607 result += separator;
608
609 entry = arrayObject->get(i);
611 if (!entry->isNullOrUndefined())
612 result += entry->toQString();
613 }
614 } else {
615 ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
616 ScopedValue value(scope, instance->get(name));
618
619 if (!value->isNullOrUndefined())
620 result = value->toQString();
621
622 for (quint32 i = 1; i < genericLength; ++i) {
623 result += separator;
624
625 name = Value::fromDouble(i).toString(scope.engine);
626 value = instance->get(name);
628
629 if (!value->isNullOrUndefined())
630 result += value->toQString();
631 }
632 }
633
634 return Encode(scope.engine->newString(result));
635}
636
637ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *thisObject, const Value *, int)
638{
639 Scope scope(b);
640 ScopedObject instance(scope, thisObject->toObject(scope.engine));
641 if (!instance)
643
644 qint64 len = instance->getLength();
645
646 if (!len) {
647 if (!instance->isArrayObject())
648 instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromInt32(0)));
650 }
651
652 ScopedValue result(scope, instance->get(len - 1));
654
655 if (!instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1)))
656 return scope.engine->throwTypeError();
657
658 if (instance->isArrayObject())
659 instance->setArrayLength(len - 1);
660 else {
661 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - 1))))
662 return scope.engine->throwTypeError();
663 }
664 return result->asReturnedValue();
665}
666
667ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
668{
669 Scope scope(b);
670 ScopedObject instance(scope, thisObject->toObject(scope.engine));
671 if (!instance)
673
674 instance->arrayCreate();
675 Q_ASSERT(instance->arrayData());
676
677 qint64 len = instance->getLength();
678
679 if (len + quint64(argc) >= UINT_MAX) {
680 // ughh... this goes beyond UINT_MAX
681 double l = len;
682 ScopedString s(scope);
683 for (int i = 0, ei = argc; i < ei; ++i) {
684 s = Value::fromDouble(l + i).toString(scope.engine);
685 if (!instance->put(s, argv[i]))
686 return scope.engine->throwTypeError();
687 }
688 double newLen = l + argc;
689 if (!instance->isArrayObject()) {
690 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(newLen))))
691 return scope.engine->throwTypeError();
692 } else {
693 ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow")));
694 return scope.engine->throwRangeError(str);
695 }
696 return Encode(newLen);
697 }
698
699 if (!argc)
700 ;
701 else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) {
702 instance->arrayData()->vtable()->putArray(instance, len, argv, argc);
703 len = instance->arrayData()->length();
704 } else {
705 for (int i = 0, ei = argc; i < ei; ++i) {
706 if (!instance->put(len + i, argv[i]))
707 return scope.engine->throwTypeError();
708 }
709 len += argc;
710 }
711 if (instance->isArrayObject())
712 instance->setArrayLengthUnchecked(len);
713 else {
714 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len))))
715 return scope.engine->throwTypeError();
716 }
717
718 return Encode(uint(len));
719}
720
721ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
722{
723 Scope scope(b);
724 ScopedObject instance(scope, thisObject->toObject(scope.engine));
725 if (!instance)
727
728 qint64 length = instance->getLength();
729 // ### FIXME
730 if (length >= UINT_MAX)
731 return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range."));
732
733 int lo = 0, hi = length - 1;
734
735 ScopedValue lval(scope);
736 ScopedValue hval(scope);
737 for (; lo < hi; ++lo, --hi) {
738 bool loExists, hiExists;
739 lval = instance->get(lo, &loExists);
740 hval = instance->get(hi, &hiExists);
742 bool ok;
743 if (hiExists)
744 ok = instance->put(lo, hval);
745 else
746 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(lo));
747 if (ok) {
748 if (loExists)
749 ok = instance->put(hi, lval);
750 else
751 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(hi));
752 }
753 if (!ok)
754 return scope.engine->throwTypeError();
755 }
756 return instance->asReturnedValue();
757}
758
759ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value *thisObject, const Value *, int)
760{
761 Scope scope(b);
762 ScopedObject instance(scope, thisObject->toObject(scope.engine));
763 if (!instance)
765
766 instance->arrayCreate();
767 Q_ASSERT(instance->arrayData());
768
769 qint64 len = instance->getLength();
770
771 if (!len) {
772 if (!instance->isArrayObject())
773 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromInt32(0))))
774 return scope.engine->throwTypeError();
776 }
777
778 ScopedValue result(scope);
779 if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) {
780 result = instance->arrayData()->vtable()->pop_front(instance);
781 } else {
782 result = instance->get(uint(0));
784 ScopedValue v(scope);
785 // do it the slow way
786 for (qint64 k = 1; k < len; ++k) {
787 bool exists;
788 v = instance->get(k, &exists);
790 bool ok;
791 if (exists)
792 ok = instance->put(k - 1, v);
793 else
794 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1));
795 if (!ok)
796 return scope.engine->throwTypeError();
797 }
798 bool ok = instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1));
799 if (!ok)
800 return scope.engine->throwTypeError();
801 }
802
803 if (instance->isArrayObject())
804 instance->setArrayLengthUnchecked(len - 1);
805 else {
806 bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - 1)));
807 if (!ok)
808 return scope.engine->throwTypeError();
809 }
810
811 return result->asReturnedValue();
812}
813
814ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
815{
816 Scope scope(b);
817 ScopedObject o(scope, thisObject->toObject(scope.engine));
818 if (!o)
820
821 ScopedArrayObject result(scope, scope.engine->newArrayObject());
822 qint64 len = o->getLength();
823 double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
824 qint64 start;
825 if (s < 0)
826 start = static_cast<qint64>(qMax(double(len) + s, 0.));
827 else if (s > len)
828 start = len;
829 else
830 start = qint64(s);
831 qint64 end = len;
832 if (argc > 1 && !argv[1].isUndefined()) {
833 double e = argv[1].toInteger();
834 if (e < 0)
835 end = static_cast<qint64>(qMax(double(len) + e, 0.));
836 else if (e > len)
837 end = len;
838 else
839 end = qint64(e);
840 }
841
842 if (end > start && qint64(std::numeric_limits<quint32>::max()) < end - start)
843 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
844
845 ScopedValue v(scope);
846 qint64 n = 0;
847 for (qint64 i = start; i < end; ++i) {
848 bool exists;
849 v = o->get(i, &exists);
851 if (exists)
852 result->arraySet(n, v);
853 ++n;
854 }
855 return result->asReturnedValue();
856}
857
858ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
859{
860 // Based on https://tc39.es/ecma262/#sec-array.prototype.sort
861
862 Scope scope(b);
863
864 ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
865
866 // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
867 if (!comparefn->isUndefined() && !comparefn->isFunctionObject())
868 return scope.engine->throwTypeError(QStringLiteral("The provided comparison function is not callable."));
869
870 // 2. Let obj be ? ToObject(this value).
871 ScopedObject instance(scope, thisObject->toObject(scope.engine));
872 if (!instance)
874
875 // 3. Let len be ? LengthOfArrayLike(obj).
876 qint64 len = instance->getLength();
877
878 if (instance->arrayData() && instance->arrayData()->length()) {
879 ArrayData::sort(scope.engine, instance, comparefn, len);
880 } else {
881 // Generic implementation that does not require a populated
882 // ArrayData, this is used, for example, by `Sequences` which
883 // store their data in a different way.
884
885 // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes)
886 Value* sorted = scope.constructUndefined(scope.engine->safeForAllocLength(len));
888
889 uint written = 0;
890 for (uint index = 0; index < len; ++index) {
891 bool hasProperty = false;
892 auto element = instance->get(index, &hasProperty);
893
894 if (hasProperty) {
895 sorted[written] = element;
896 ++written;
897 }
898 }
899
900 std::stable_sort(sorted, sorted + written, ArrayElementLessThan(scope.engine, comparefn));
901
902 // [...]
903 // 8. Repeat, while j < itemCount,
904 // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true).
905 // [...]
906 for (uint index = 0; index < written; ++index) {
907 instance->setIndexed(index, sorted[index], QV4::Object::DoThrowOnRejection);
909 }
910
911 // [...]
912 // 10. Repeat, while j < len,
913 // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).
914 // [...]
915 while (written < len) {
916 if (!instance->deleteProperty(PropertyKey::fromArrayIndex(written)))
917 return scope.engine->throwTypeError();
918 ++written;
919 }
920 }
921
922 // 11. Return obj
923 return thisObject->asReturnedValue();
924}
925
926ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
927{
928 Scope scope(b);
929 ScopedObject instance(scope, thisObject->toObject(scope.engine));
930 if (!instance)
932
933 qint64 len = instance->getLength();
934
935 double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
936 qint64 start;
937 if (rs < 0)
938 start = static_cast<qint64>(qMax(0., len + rs));
939 else
940 start = static_cast<qint64>(qMin(rs, static_cast<double>(len)));
941
942 qint64 deleteCount = 0;
943 qint64 itemCount = 0;
944 if (argc == 1) {
945 deleteCount = len - start;
946 } else if (argc > 1){
947 itemCount = argc - 2;
948 double dc = argv[1].toInteger();
949 deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start)));
950 }
951
952 if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
953 return scope.engine->throwTypeError();
954 if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
955 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
956
957 ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
958 newArray->arrayReserve(deleteCount);
959 ScopedValue v(scope);
960 for (uint i = 0; i < deleteCount; ++i) {
961 bool exists;
962 v = instance->get(start + i, &exists);
964 if (exists)
965 newArray->arrayPut(i, v);
966 }
967 newArray->setArrayLengthUnchecked(deleteCount);
968
969
970 if (itemCount < deleteCount) {
971 for (uint k = start; k < len - deleteCount; ++k) {
972 bool exists;
973 v = instance->get(k + deleteCount, &exists);
975 bool ok;
976 if (exists)
977 ok = instance->put(k + itemCount, v);
978 else
979 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount));
980 if (!ok)
981 return scope.engine->throwTypeError();
982 }
983 for (uint k = len; k > len - deleteCount + itemCount; --k) {
984 if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)))
985 return scope.engine->throwTypeError();
986 }
987 } else if (itemCount > deleteCount) {
988 uint k = len - deleteCount;
989 while (k > start) {
990 bool exists;
991 v = instance->get(k + deleteCount - 1, &exists);
993 bool ok;
994 if (exists)
995 ok = instance->put(k + itemCount - 1, v);
996 else
997 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1));
998 if (!ok)
999 return scope.engine->throwTypeError();
1000 --k;
1001 }
1002 }
1003
1004 for (uint i = 0; i < itemCount; ++i)
1005 instance->put(start + i, argv[i + 2]);
1006
1007 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - deleteCount + itemCount))))
1008 return scope.engine->throwTypeError();
1009
1010 return newArray->asReturnedValue();
1011}
1012
1013ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1014{
1015 Scope scope(b);
1016 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1017 if (!instance)
1019
1020 instance->arrayCreate();
1021 Q_ASSERT(instance->arrayData());
1022
1023 qint64 len = instance->getLength();
1024
1025 if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len &&
1026 instance->arrayData()->type != Heap::ArrayData::Custom) {
1027 instance->arrayData()->vtable()->push_front(instance, argv, argc);
1028 } else {
1029 ScopedValue v(scope);
1030 for (qint64 k = len; k > 0; --k) {
1031 bool exists;
1032 v = instance->get(k - 1, &exists);
1033 bool ok;
1034 if (exists)
1035 ok = instance->put(k + argc - 1, v);
1036 else
1037 ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1));
1038 if (!ok)
1039 return scope.engine->throwTypeError();
1040 }
1041 for (int i = 0, ei = argc; i < ei; ++i) {
1042 bool ok = instance->put(i, argv[i]);
1043 if (!ok)
1044 return scope.engine->throwTypeError();
1045 }
1046 }
1047
1048 qint64 newLen = len + argc;
1049 if (instance->isArrayObject())
1050 instance->setArrayLengthUnchecked(newLen);
1051 else {
1052 if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(newLen))))
1053 return scope.engine->throwTypeError();
1054 }
1055
1056 return Encode(double(newLen));
1057}
1058
1059ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1060{
1061 Scope scope(b);
1062 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1063 if (!instance)
1065
1066 qint64 len = instance->getLength();
1067 if (len == 0) {
1068 return Encode(false);
1069 }
1070
1071 double n = 0;
1072 if (argc > 1 && !argv[1].isUndefined()) {
1073 n = argv[1].toInteger();
1074 }
1075
1076 double k = 0;
1077 if (n >= 0) {
1078 k = n;
1079 } else {
1080 k = len + n;
1081 if (k < 0) {
1082 k = 0;
1083 }
1084 }
1085
1086 ScopedValue val(scope);
1087 while (k < len) {
1088 val = instance->get(k);
1089 if (val->sameValueZero(argv[0])) {
1090 return Encode(true);
1091 }
1092 k++;
1093 }
1094
1095 return Encode(false);
1096}
1097
1098ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1099{
1100 Scope scope(b);
1101 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1102 if (!instance)
1104
1105 qint64 len = instance->getLength();
1106 if (!len)
1107 return Encode(-1);
1108
1109 ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
1110 qint64 fromIndex = 0;
1111
1112 if (argc >= 2) {
1113 double f = argv[1].toInteger();
1115 if (f >= len)
1116 return Encode(-1);
1117 if (f < 0)
1118 f = qMax(double(len) + f, 0.);
1119 fromIndex = qint64(f);
1120 }
1121
1122 if (instance->isStringObject()) {
1123 ScopedValue v(scope);
1124 for (qint64 k = fromIndex; k < len; ++k) {
1125 bool exists;
1126 v = instance->get(k, &exists);
1127 if (exists && RuntimeHelpers::strictEqual(v, searchValue))
1128 return Encode(double(k));
1129 }
1130 return Encode(-1);
1131 }
1132
1133 ScopedValue value(scope);
1134
1135 if (ArgumentsObject::isNonStrictArgumentsObject(instance) ||
1136 (instance->arrayType() >= Heap::ArrayData::Sparse) || instance->protoHasArray()) {
1137 // lets be safe and slow
1138 for (qint64 i = fromIndex; i < len; ++i) {
1139 bool exists;
1140 value = instance->get(i, &exists);
1142 if (exists && RuntimeHelpers::strictEqual(value, searchValue))
1143 return Encode(double(i));
1144 }
1145 } else if (!instance->arrayData()) {
1146 return Encode(-1);
1147 } else {
1148 Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple);
1149 Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>();
1150 if (len > sa->values.size)
1151 len = sa->values.size;
1152 uint idx = fromIndex;
1153 while (idx < len) {
1154 value = sa->data(idx);
1156 if (RuntimeHelpers::strictEqual(value, searchValue))
1157 return Encode(idx);
1158 ++idx;
1159 }
1160 }
1161 return Encode(-1);
1162}
1163
1164ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
1165{
1166 Scope scope(f);
1167 ScopedObject O(scope, thisObject->toObject(scope.engine));
1168 if (!O)
1170
1171 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
1172 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
1173 return ao->asReturnedValue();
1174}
1175
1176ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1177{
1178 Scope scope(b);
1179 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1180 if (!instance)
1182
1183 qint64 len = instance->getLength();
1184 if (!len)
1185 return Encode(-1);
1186
1187 ScopedValue searchValue(scope);
1188 qint64 fromIndex = len;
1189
1190 if (argc >= 1)
1191 searchValue = argv[0];
1192 else
1193 searchValue = Value::undefinedValue();
1194
1195 if (argc >= 2) {
1196 double f = argv[1].toInteger();
1198 if (f > 0)
1199 f = qMin(f, double(len - 1));
1200 else if (f < 0) {
1201 f = double(len) + f;
1202 if (f < 0)
1203 return Encode(-1);
1204 }
1205 fromIndex = qint64(f) + 1;
1206 }
1207
1208 ScopedValue v(scope);
1209 for (qint64 k = fromIndex; k > 0;) {
1210 --k;
1211 bool exists;
1212 v = instance->get(k, &exists);
1214 if (exists && RuntimeHelpers::strictEqual(v, searchValue))
1215 return Encode(double(k));
1216 }
1217 return Encode(-1);
1218}
1219
1220ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1221{
1222 Scope scope(b);
1223 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1224 if (!instance)
1226
1227 qint64 len = instance->getLength();
1228
1229 if (!argc || !argv->isFunctionObject())
1231 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1232
1233 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1234 ScopedValue r(scope);
1235 Value *arguments = scope.constructUndefined(3);
1236
1237 bool ok = true;
1238 for (qint64 k = 0; ok && k < len; ++k) {
1239 bool exists;
1240 arguments[0] = instance->get(k, &exists);
1241 if (!exists)
1242 continue;
1243
1244 arguments[1] = Value::fromDouble(k);
1245 arguments[2] = instance;
1246 r = callback->call(that, arguments, 3);
1248 ok = r->toBoolean();
1249 }
1250 return Encode(ok);
1251}
1252
1253ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1254{
1255 Scope scope(b);
1256 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1257 if (!instance)
1259
1260 const qsizetype len = instance->getLength();
1261 Q_ASSERT(len >= 0);
1262
1263 const qsizetype relativeStart = argc > 1 ? argv[1].toInteger() : 0;
1264 qsizetype relativeEnd = len;
1265 if (argc > 2 && !argv[2].isUndefined())
1266 relativeEnd = argv[2].toInteger();
1267
1268 qsizetype k = 0;
1269 qsizetype fin = 0;
1270
1271 if (relativeStart < 0) {
1272 if (relativeStart > -len)
1273 k = std::max(len + relativeStart, qsizetype(0));
1274 } else {
1275 k = std::min(relativeStart, len);
1276 }
1277 Q_ASSERT(k >= 0);
1278
1279 if (relativeEnd < 0) {
1280 if (relativeEnd > -len)
1281 fin = std::max(len + relativeEnd, qsizetype(0));
1282 } else {
1283 fin = std::min(relativeEnd, len);
1284 }
1285 Q_ASSERT(fin >= 0);
1286
1287 if (sizeof(qsizetype) > sizeof(uint) && fin > qsizetype(std::numeric_limits<uint>::max()))
1288 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
1289
1290 for (; k < fin; ++k)
1291 instance->setIndexed(uint(k), argv[0], QV4::Object::DoThrowOnRejection);
1292
1293 return instance.asReturnedValue();
1294}
1295
1296ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1297{
1298 Scope scope(b);
1299 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1300 if (!instance)
1302
1303 qint64 len = instance->getLength();
1304
1305 if (!argc || !argv->isFunctionObject())
1307 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1308
1309 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1310 ScopedValue result(scope);
1311 Value *arguments = scope.constructUndefined(3);
1312
1313 for (qint64 k = 0; k < len; ++k) {
1314 bool exists;
1315 arguments[0] = instance->get(k, &exists);
1316 if (!exists)
1317 continue;
1318
1319 arguments[1] = Value::fromDouble(k);
1320 arguments[2] = instance;
1321 result = callback->call(that, arguments, 3);
1323 if (result->toBoolean())
1324 return Encode(true);
1325 }
1326 return Encode(false);
1327}
1328
1329ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1330{
1331 Scope scope(b);
1332 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1333 if (!instance)
1335
1336 qint64 len = instance->getLength();
1337
1338 if (!argc || !argv->isFunctionObject())
1340 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1341
1342 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1343 Value *arguments = scope.constructUndefined(3);
1344
1345 for (qint64 k = 0; k < len; ++k) {
1346 bool exists;
1347 arguments[0] = instance->get(k, &exists);
1348 if (!exists)
1349 continue;
1350
1351 arguments[1] = Value::fromDouble(k);
1352 arguments[2] = instance;
1353 callback->call(that, arguments, 3);
1354 }
1356}
1357
1358ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1359{
1360 Scope scope(b);
1361 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1362 if (!instance)
1364
1365 qint64 len = instance->getLength();
1366
1367 if (!argc || !argv->isFunctionObject())
1369 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1370
1371 if (len > UINT_MAX - 1)
1372 return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
1373
1374 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1375 a->arrayReserve(len);
1376 a->setArrayLengthUnchecked(len);
1377
1378 ScopedValue v(scope);
1379 ScopedValue mapped(scope);
1380 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1381 Value *arguments = scope.constructUndefined(3);
1382
1383 for (uint k = 0; k < len; ++k) {
1384 bool exists;
1385 arguments[0] = instance->get(k, &exists);
1386 if (!exists)
1387 continue;
1388
1389 arguments[1] = Value::fromDouble(k);
1390 arguments[2] = instance;
1391 mapped = callback->call(that, arguments, 3);
1393 a->arraySet(k, mapped);
1394 }
1395 return a.asReturnedValue();
1396}
1397
1398ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1399{
1400 Scope scope(b);
1401 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1402 if (!instance)
1404
1405 qint64 len = instance->getLength();
1406
1407 if (!argc || !argv->isFunctionObject())
1409 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1410
1411 ScopedArrayObject a(scope, scope.engine->newArrayObject());
1412 a->arrayReserve(len);
1413
1414 ScopedValue selected(scope);
1415 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1416 Value *arguments = scope.constructUndefined(3);
1417
1418 uint to = 0;
1419 for (qint64 k = 0; k < len; ++k) {
1420 bool exists;
1421 arguments[0] = instance->get(k, &exists);
1422 if (!exists)
1423 continue;
1424
1425 arguments[1] = Value::fromDouble(k);
1426 arguments[2] = instance;
1427 selected = callback->call(that, arguments, 3);
1429 if (selected->toBoolean()) {
1430 a->arraySet(to, arguments[0]);
1431 ++to;
1432 }
1433 }
1434 return a.asReturnedValue();
1435}
1436
1437ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1438{
1439 Scope scope(b);
1440 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1441 if (!instance)
1443
1444 qint64 len = instance->getLength();
1445
1446 if (!argc || !argv->isFunctionObject())
1448 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1449
1450 qint64 k = 0;
1451 ScopedValue acc(scope);
1452 ScopedValue v(scope);
1453
1454 if (argc > 1) {
1455 acc = argv[1];
1456 } else {
1457 bool kPresent = false;
1458 while (k < len && !kPresent) {
1459 v = instance->get(k, &kPresent);
1460 if (kPresent)
1461 acc = v;
1462 ++k;
1463 }
1464 if (!kPresent)
1466 }
1467
1468 Value *arguments = scope.constructUndefined(4);
1469
1470 while (k < len) {
1471 bool kPresent;
1472 v = instance->get(k, &kPresent);
1473 if (kPresent) {
1474 arguments[0] = acc;
1475 arguments[1] = v;
1476 arguments[2] = Value::fromDouble(k);
1477 arguments[3] = instance;
1478 acc = callback->call(nullptr, arguments, 4);
1480 }
1481 ++k;
1482 }
1483 return acc->asReturnedValue();
1484}
1485
1486ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1487{
1488 Scope scope(b);
1489 ScopedObject instance(scope, thisObject->toObject(scope.engine));
1490 if (!instance)
1492
1493 qint64 len = instance->getLength();
1494
1495 if (!argc || !argv->isFunctionObject())
1497 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1498
1499 if (len == 0) {
1500 if (argc == 1)
1502 return argv[1].asReturnedValue();
1503 }
1504
1505 qint64 k = len;
1506 ScopedValue acc(scope);
1507 ScopedValue v(scope);
1508 if (argc > 1) {
1509 acc = argv[1];
1510 } else {
1511 bool kPresent = false;
1512 while (k > 0 && !kPresent) {
1513 v = instance->get(k - 1, &kPresent);
1514 if (kPresent)
1515 acc = v;
1516 --k;
1517 }
1518 if (!kPresent)
1520 }
1521
1522 Value *arguments = scope.constructUndefined(4);
1523
1524 while (k > 0) {
1525 bool kPresent;
1526 v = instance->get(k - 1, &kPresent);
1527 if (kPresent) {
1528 arguments[0] = acc;
1529 arguments[1] = v;
1530 arguments[2] = Value::fromDouble(k - 1);
1531 arguments[3] = instance;
1532 acc = callback->call(nullptr, arguments, 4);
1534 }
1535 --k;
1536 }
1537 return acc->asReturnedValue();
1538}
1539
1540ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
1541{
1542 Scope scope(b);
1543 ScopedObject O(scope, thisObject->toObject(scope.engine));
1544 if (!O)
1546
1547 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
1548 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
1549 return ao->asReturnedValue();
1550}
1551
1552ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
1553{
1554 return thisObject->asReturnedValue();
1555}
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()