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