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

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