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

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