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