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