1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4typedarray_p.h"
41#include "qv4arrayiterator_p.h"
42#include "qv4arraybuffer_p.h"
43#include "qv4string_p.h"
44#include "qv4jscall_p.h"
45#include "qv4symbol_p.h"
46#include "qv4runtime_p.h"
47#include <QtCore/qatomic.h>
48
49#include <cmath>
50
51using namespace QV4;
52
53DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor);
54DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype);
55DEFINE_OBJECT_VTABLE(TypedArrayCtor);
56DEFINE_OBJECT_VTABLE(TypedArrayPrototype);
57DEFINE_OBJECT_VTABLE(TypedArray);
58
59Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)NTypedArrayTypes);
60
61static inline int toInt32(Value v)
62{
63 Q_ASSERT(v.isNumber());
64 if (v.isInteger())
65 return v.integerValue();
66 return Double::toInt32(d: v.doubleValue());
67}
68
69static inline double toDouble(Value v)
70{
71 Q_ASSERT(v.isNumber());
72 if (v.isInteger())
73 return v.integerValue();
74 return v.doubleValue();
75}
76
77struct ClampedUInt8 {
78 quint8 c;
79};
80
81template <typename T>
82ReturnedValue typeToValue(T t) {
83 return Encode(t);
84}
85
86template <>
87ReturnedValue typeToValue(ClampedUInt8 t) {
88 return Encode(t.c);
89}
90
91template <typename T>
92T valueToType(Value value)
93{
94 Q_ASSERT(value.isNumber());
95 int n = toInt32(v: value);
96 return static_cast<T>(n);
97}
98
99template <>
100ClampedUInt8 valueToType(Value value)
101{
102 Q_ASSERT(value.isNumber());
103 if (value.isInteger())
104 return { .c: static_cast<quint8>(qBound(min: 0, val: value.integerValue(), max: 255)) };
105 Q_ASSERT(value.isDouble());
106 double d = value.doubleValue();
107 // ### is there a way to optimise this?
108 if (d <= 0 || std::isnan(x: d))
109 return { .c: 0 };
110 if (d >= 255)
111 return { .c: 255 };
112 double f = std::floor(x: d);
113 if (f + 0.5 < d)
114 return { .c: (quint8)(f + 1) };
115 if (d < f + 0.5)
116 return { .c: (quint8)(f) };
117 if (int(f) % 2)
118 // odd number
119 return { .c: (quint8)(f + 1) };
120 return { .c: (quint8)(f) };
121}
122
123template <>
124float valueToType(Value value)
125{
126 Q_ASSERT(value.isNumber());
127 double d = toDouble(v: value);
128 return static_cast<float>(d);
129}
130
131template <>
132double valueToType(Value value)
133{
134 Q_ASSERT(value.isNumber());
135 return toDouble(v: value);
136}
137
138template <typename T>
139ReturnedValue read(const char *data) {
140 return typeToValue(*reinterpret_cast<const T *>(data));
141}
142template <typename T>
143void write(char *data, Value value)
144{
145 *reinterpret_cast<T *>(data) = valueToType<T>(value);
146}
147
148template <typename T>
149ReturnedValue atomicAdd(char *data, Value v)
150{
151 T value = valueToType<T>(v);
152 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
153 value = QAtomicOps<T>::fetchAndAddOrdered(*mem, value);
154 return typeToValue(value);
155}
156
157template <typename T>
158ReturnedValue atomicAnd(char *data, Value v)
159{
160 T value = valueToType<T>(v);
161 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
162 value = QAtomicOps<T>::fetchAndAndOrdered(*mem, value);
163 return typeToValue(value);
164}
165
166template <typename T>
167ReturnedValue atomicExchange(char *data, Value v)
168{
169 T value = valueToType<T>(v);
170 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
171 value = QAtomicOps<T>::fetchAndStoreOrdered(*mem, value);
172 return typeToValue(value);
173}
174
175template <typename T>
176ReturnedValue atomicOr(char *data, Value v)
177{
178 T value = valueToType<T>(v);
179 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
180 value = QAtomicOps<T>::fetchAndOrOrdered(*mem, value);
181 return typeToValue(value);
182}
183
184template <typename T>
185ReturnedValue atomicSub(char *data, Value v)
186{
187 T value = valueToType<T>(v);
188 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
189 value = QAtomicOps<T>::fetchAndSubOrdered(*mem, value);
190 return typeToValue(value);
191}
192
193template <typename T>
194ReturnedValue atomicXor(char *data, Value v)
195{
196 T value = valueToType<T>(v);
197 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
198 value = QAtomicOps<T>::fetchAndXorOrdered(*mem, value);
199 return typeToValue(value);
200}
201
202template <typename T>
203ReturnedValue atomicCompareExchange(char *data, Value expected, Value v)
204{
205 T value = valueToType<T>(v);
206 T exp = valueToType<T>(expected);
207 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
208 T old;
209 QAtomicOps<T>::testAndSetOrdered(*mem, exp, value, &old);
210 return typeToValue(old);
211}
212
213template <typename T>
214ReturnedValue atomicLoad(char *data)
215{
216 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
217 T val = QAtomicOps<T>::loadRelaxed(*mem);
218 return typeToValue(val);
219}
220
221template <typename T>
222ReturnedValue atomicStore(char *data, Value v)
223{
224 T value = valueToType<T>(v);
225 typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
226 QAtomicOps<T>::storeRelaxed(*mem, value);
227 return typeToValue(value);
228}
229
230
231template<typename T>
232constexpr TypedArrayOperations TypedArrayOperations::create(const char *name)
233{
234 return { sizeof(T),
235 name,
236 ::read<T>,
237 ::write<T>,
238 { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr },
239 nullptr,
240 nullptr,
241 nullptr
242 };
243}
244
245template<typename T>
246constexpr TypedArrayOperations TypedArrayOperations::createWithAtomics(const char *name)
247{
248 return { sizeof(T),
249 name,
250 ::read<T>,
251 ::write<T>,
252 { ::atomicAdd<T>, ::atomicAnd<T>, ::atomicExchange<T>, ::atomicOr<T>, ::atomicSub<T>, ::atomicXor<T> },
253 ::atomicCompareExchange<T>,
254 ::atomicLoad<T>,
255 ::atomicStore<T>
256 };
257}
258
259const TypedArrayOperations operations[NTypedArrayTypes] = {
260#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
261 TypedArrayOperations::createWithAtomics<qint8>(name: "Int8Array"),
262 TypedArrayOperations::createWithAtomics<quint8>(name: "Uint8Array"),
263#else
264 TypedArrayOperations::create<qint8>("Int8Array"),
265 TypedArrayOperations::create<quint8>("Uint8Array"),
266#endif
267 TypedArrayOperations::createWithAtomics<qint16>(name: "Int16Array"),
268 TypedArrayOperations::createWithAtomics<quint16>(name: "Uint16Array"),
269 TypedArrayOperations::createWithAtomics<qint32>(name: "Int32Array"),
270 TypedArrayOperations::createWithAtomics<quint32>(name: "Uint32Array"),
271 TypedArrayOperations::create<ClampedUInt8>(name: "Uint8ClampedArray"),
272 TypedArrayOperations::create<float>(name: "Float32Array"),
273 TypedArrayOperations::create<double>(name: "Float64Array")
274};
275
276
277void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t)
278{
279 Heap::FunctionObject::init(scope, name: QLatin1String(operations[t].name));
280 type = t;
281}
282
283ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
284{
285 Scope scope(f->engine());
286 const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f);
287
288 auto updateProto = [=](Scope &scope, Scoped<TypedArray> &a) {
289 if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
290 const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
291 ScopedObject o(scope, nt->protoProperty());
292 if (o)
293 a->setPrototypeOf(o);
294 }
295 };
296
297 if (!argc || !argv[0].isObject()) {
298 // ECMA 6 22.2.1.1
299 qint64 l = argc ? argv[0].toIndex() : 0;
300 if (scope.engine->hasException)
301 return Encode::undefined();
302 // ### lift UINT_MAX restriction
303 if (l < 0 || l > UINT_MAX)
304 return scope.engine->throwRangeError(message: QLatin1String("Index out of range."));
305 uint len = (uint)l;
306 if (l != len)
307 scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
308 uint byteLength = len * operations[that->d()->type].bytesPerElement;
309 Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(length: byteLength));
310 if (scope.engine->hasException)
311 return Encode::undefined();
312
313 Scoped<TypedArray> array(scope, TypedArray::create(e: scope.engine, t: that->d()->type));
314 array->d()->buffer.set(e: scope.engine, newVal: buffer->d());
315 array->d()->byteLength = byteLength;
316 array->d()->byteOffset = 0;
317
318 updateProto(scope, array);
319 return array.asReturnedValue();
320 }
321 Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Value::undefinedValue());
322 if (!!typedArray) {
323 // ECMA 6 22.2.1.2
324 Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer);
325 if (!buffer || buffer->isDetachedBuffer())
326 return scope.engine->throwTypeError();
327 uint srcElementSize = typedArray->d()->type->bytesPerElement;
328 uint destElementSize = operations[that->d()->type].bytesPerElement;
329 uint byteLength = typedArray->d()->byteLength;
330 uint destByteLength = byteLength*destElementSize/srcElementSize;
331
332 Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(length: destByteLength));
333 if (scope.engine->hasException)
334 return Encode::undefined();
335
336 Scoped<TypedArray> array(scope, TypedArray::create(e: scope.engine, t: that->d()->type));
337 array->d()->buffer.set(e: scope.engine, newVal: newBuffer->d());
338 array->d()->byteLength = destByteLength;
339 array->d()->byteOffset = 0;
340
341 const char *src = buffer->d()->data->data() + typedArray->d()->byteOffset;
342 char *dest = newBuffer->d()->data->data();
343
344 // check if src and new type have the same size. In that case we can simply memcpy the data
345 if (srcElementSize == destElementSize) {
346 memcpy(dest: dest, src: src, n: byteLength);
347 } else {
348 // not same size, we need to loop
349 uint l = typedArray->length();
350 TypedArrayOperations::Read read = typedArray->d()->type->read;
351 TypedArrayOperations::Write write =array->d()->type->write;
352 for (uint i = 0; i < l; ++i) {
353 Value val;
354 val.setRawValue(read(src + i*srcElementSize));
355 write(dest + i*destElementSize, val);
356 }
357 }
358
359 updateProto(scope, array);
360 return array.asReturnedValue();
361 }
362 Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
363 if (!!buffer) {
364 // ECMA 6 22.2.1.4
365
366 double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0;
367
368 if (buffer->isDetachedBuffer())
369 return scope.engine->throwTypeError();
370
371 uint byteOffset = (uint)dbyteOffset;
372 uint elementSize = operations[that->d()->type].bytesPerElement;
373 if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength())
374 return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
375
376 uint byteLength;
377 if (argc < 3 || argv[2].isUndefined()) {
378 byteLength = buffer->byteLength() - byteOffset;
379 if (buffer->byteLength() < byteOffset || byteLength % elementSize)
380 return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
381 } else {
382 double l = qBound(min: 0., val: argv[2].toInteger(), max: (double)UINT_MAX);
383 if (scope.engine->hasException)
384 return Encode::undefined();
385 if (buffer->isDetachedBuffer())
386 return scope.engine->throwTypeError();
387 l *= elementSize;
388 if (buffer->byteLength() - byteOffset < l)
389 return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
390 byteLength = (uint)l;
391 }
392
393 Scoped<TypedArray> array(scope, TypedArray::create(e: scope.engine, t: that->d()->type));
394 array->d()->buffer.set(e: scope.engine, newVal: buffer->d());
395 array->d()->byteLength = byteLength;
396 array->d()->byteOffset = byteOffset;
397
398 updateProto(scope, array);
399 return array.asReturnedValue();
400 }
401
402 // ECMA 6 22.2.1.3
403
404 ScopedObject o(scope, argc ? argv[0] : Value::undefinedValue());
405 uint l = (uint) qBound(min: 0., val: ScopedValue(scope, o->get(name: scope.engine->id_length()))->toInteger(), max: (double)UINT_MAX);
406 if (scope.engine->hasException)
407 return scope.engine->throwTypeError();
408
409 uint elementSize = operations[that->d()->type].bytesPerElement;
410 size_t bufferSize;
411 if (mul_overflow(v1: size_t(l), v2: size_t(elementSize), r: &bufferSize))
412 return scope.engine->throwRangeError(message: QLatin1String("new TypedArray: invalid length"));
413 Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(length: bufferSize));
414 if (scope.engine->hasException)
415 return Encode::undefined();
416
417 Scoped<TypedArray> array(scope, TypedArray::create(e: scope.engine, t: that->d()->type));
418 array->d()->buffer.set(e: scope.engine, newVal: newBuffer->d());
419 array->d()->byteLength = l * elementSize;
420 array->d()->byteOffset = 0;
421
422 uint idx = 0;
423 char *b = newBuffer->d()->data->data();
424 ScopedValue val(scope);
425 while (idx < l) {
426 val = o->get(idx);
427 val = val->convertedToNumber();
428 if (scope.engine->hasException)
429 return Encode::undefined();
430 array->d()->type->write(b, val);
431 if (scope.engine->hasException)
432 return Encode::undefined();
433 ++idx;
434 b += elementSize;
435 }
436
437 updateProto(scope, array);
438 return array.asReturnedValue();
439}
440
441ReturnedValue TypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
442{
443 return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid"));
444}
445
446void Heap::TypedArray::init(Type t)
447{
448 Object::init();
449 type = operations + static_cast<int>(t);
450 arrayType = static_cast<int>(t);
451}
452
453Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
454{
455 Scope scope(e);
456 Scoped<InternalClass> ic(scope, e->newInternalClass(vtable: staticVTable(), prototype: e->typedArrayPrototype + static_cast<int>(t)));
457 return e->memoryManager->allocObject<TypedArray>(ic: ic->d(), args: t);
458}
459
460ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
461{
462 const bool isArrayIndex = id.isArrayIndex();
463 if (!isArrayIndex && !id.isCanonicalNumericIndexString())
464 return Object::virtualGet(m, id, receiver, hasProperty);
465
466 Scope scope(static_cast<const Object *>(m)->engine());
467 Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
468 if (a->d()->buffer->isDetachedBuffer())
469 return scope.engine->throwTypeError();
470
471 if (!isArrayIndex || id.asArrayIndex() >= a->length()) {
472 if (hasProperty)
473 *hasProperty = false;
474 return Encode::undefined();
475 }
476
477 uint bytesPerElement = a->d()->type->bytesPerElement;
478 uint byteOffset = a->d()->byteOffset + id.asArrayIndex() * bytesPerElement;
479 Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
480
481 if (hasProperty)
482 *hasProperty = true;
483 return a->d()->type->read(a->d()->buffer->data->data() + byteOffset);
484}
485
486bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
487{
488 const bool isArrayIndex = id.isArrayIndex();
489 if (!isArrayIndex && !id.isCanonicalNumericIndexString())
490 return Object::virtualHasProperty(m, id);
491
492 const TypedArray *a = static_cast<const TypedArray *>(m);
493 if (a->d()->buffer->isDetachedBuffer()) {
494 a->engine()->throwTypeError();
495 return false;
496 }
497 return isArrayIndex && id.asArrayIndex() < a->length();
498}
499
500PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
501{
502 if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString())
503 return Object::virtualGetOwnProperty(m, id, p);
504
505 bool hasProperty = false;
506 ReturnedValue v = virtualGet(m, id, receiver: m, hasProperty: &hasProperty);
507 if (p)
508 p->value = v;
509 return hasProperty ? Attr_NotConfigurable : PropertyAttributes();
510}
511
512bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
513{
514 const bool isArrayIndex = id.isArrayIndex();
515 if (!isArrayIndex && !id.isCanonicalNumericIndexString())
516 return Object::virtualPut(m, id, value, receiver);
517
518 ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
519 if (v4->hasException)
520 return false;
521
522 Scope scope(v4);
523 Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m));
524 if (a->d()->buffer->isDetachedBuffer())
525 return scope.engine->throwTypeError();
526
527 if (!isArrayIndex)
528 return false;
529
530 const uint index = id.asArrayIndex();
531 if (index >= a->length())
532 return false;
533
534 uint bytesPerElement = a->d()->type->bytesPerElement;
535 uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
536 Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
537
538 Value v = Value::fromReturnedValue(val: value.convertedToNumber());
539 if (scope.hasException() || a->d()->buffer->isDetachedBuffer())
540 return scope.engine->throwTypeError();
541 a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
542 return true;
543}
544
545bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
546{
547 if (!id.isArrayIndex()) {
548 return !id.isCanonicalNumericIndexString()
549 && Object::virtualDefineOwnProperty(m, id, p, attrs);
550 }
551
552 const uint index = id.asArrayIndex();
553 TypedArray *a = static_cast<TypedArray *>(m);
554 if (index >= a->length() || attrs.isAccessor())
555 return false;
556
557 if (attrs.hasConfigurable() && attrs.isConfigurable())
558 return false;
559 if (attrs.hasEnumerable() && !attrs.isEnumerable())
560 return false;
561 if (attrs.hasWritable() && !attrs.isWritable())
562 return false;
563 if (!p->value.isEmpty()) {
564 ExecutionEngine *engine = a->engine();
565
566 Value v = Value::fromReturnedValue(val: p->value.convertedToNumber());
567 if (engine->hasException || a->d()->buffer->isDetachedBuffer())
568 return engine->throwTypeError();
569 uint bytesPerElement = a->d()->type->bytesPerElement;
570 uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
571 Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
572 a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
573 }
574 return true;
575}
576
577struct TypedArrayOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
578{
579 ~TypedArrayOwnPropertyKeyIterator() override = default;
580 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
581
582};
583
584PropertyKey TypedArrayOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
585{
586 const TypedArray *a = static_cast<const TypedArray *>(o);
587 if (arrayIndex < a->length()) {
588 if (attrs)
589 *attrs = Attr_NotConfigurable;
590 PropertyKey id = PropertyKey::fromArrayIndex(idx: arrayIndex);
591 if (pd) {
592 bool hasProperty = false;
593 pd->value = TypedArray::virtualGet(m: a, id, receiver: a, hasProperty: &hasProperty);
594 }
595 ++arrayIndex;
596 return id;
597 }
598
599 arrayIndex = UINT_MAX;
600 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
601}
602
603OwnPropertyKeyIterator *TypedArray::virtualOwnPropertyKeys(const Object *m, Value *target)
604{
605 *target = *m;
606 return new TypedArrayOwnPropertyKeyIterator();
607}
608
609void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
610{
611 Scope scope(engine);
612 ScopedObject o(scope);
613
614 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 3));
615 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: *this);
616 ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), value: Value::fromInt32(i: operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
617 ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor());
618
619 setPrototypeOf(engine->intrinsicTypedArrayPrototype());
620 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
621 defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), value: Value::fromInt32(i: operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
622}
623
624ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
625{
626 ExecutionEngine *v4 = b->engine();
627 const TypedArray *v = thisObject->as<TypedArray>();
628 if (!v)
629 return v4->throwTypeError();
630
631 return v->d()->buffer->asReturnedValue();
632}
633
634ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
635{
636 ExecutionEngine *v4 = b->engine();
637 const TypedArray *v = thisObject->as<TypedArray>();
638 if (!v)
639 return v4->throwTypeError();
640
641 if (v->d()->buffer->isDetachedBuffer())
642 return Encode(0);
643
644 return Encode(v->d()->byteLength);
645}
646
647ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
648{
649 ExecutionEngine *v4 = b->engine();
650 const TypedArray *v = thisObject->as<TypedArray>();
651 if (!v)
652 return v4->throwTypeError();
653
654 if (v->d()->buffer->isDetachedBuffer())
655 return Encode(0);
656
657 return Encode(v->d()->byteOffset);
658}
659
660ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
661{
662 ExecutionEngine *v4 = b->engine();
663 const TypedArray *v = thisObject->as<TypedArray>();
664 if (!v)
665 return v4->throwTypeError();
666
667 if (v->d()->buffer->isDetachedBuffer())
668 return Encode(0);
669
670 return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
671}
672
673ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
674{
675 Scope scope(f);
676 Scoped<TypedArray> O(scope, thisObject);
677 if (!O || O->d()->buffer->isDetachedBuffer())
678 return scope.engine->throwTypeError();
679
680 if (!argc)
681 return O->asReturnedValue();
682
683 qint64 len = static_cast<uint>(O->length());
684
685 qint64 to = static_cast<qint64>(argv[0].toInteger());
686 if (to < 0)
687 to = qMax(a: len + to, b: 0ll);
688 else
689 to = qMin(a: to, b: len);
690
691 qint64 from = (argc > 1) ? static_cast<qint64>(argv[1].toInteger()) : 0ll;
692 if (from < 0)
693 from = qMax(a: len + from, b: 0ll);
694 else
695 from = qMin(a: from, b: len);
696
697 double fend = argv[2].toInteger();
698 if (fend > len)
699 fend = len;
700 qint64 end = (argc > 2 && !argv[2].isUndefined()) ? static_cast<qint64>(fend) : len;
701 if (end < 0)
702 end = qMax(a: len + end, b: 0ll);
703 else
704 end = qMin(a: end, b: len);
705
706 qint64 count = qMin(a: end - from, b: len - to);
707
708 if (count <= 0)
709 return O->asReturnedValue();
710
711 if (O->d()->buffer->isDetachedBuffer())
712 return scope.engine->throwTypeError();
713
714 if (from != to) {
715 int elementSize = O->d()->type->bytesPerElement;
716 char *data = O->d()->buffer->data->data() + O->d()->byteOffset;
717 memmove(dest: data + to*elementSize, src: data + from*elementSize, n: count*elementSize);
718 }
719
720 return O->asReturnedValue();
721}
722
723ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
724{
725 Scope scope(b);
726 Scoped<TypedArray> v(scope, thisObject);
727 if (!v || v->d()->buffer->isDetachedBuffer())
728 return scope.engine->throwTypeError();
729
730 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: v));
731 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
732 return ao->asReturnedValue();
733}
734
735ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
736{
737 Scope scope(b);
738 Scoped<TypedArray> v(scope, thisObject);
739 if (!v || v->d()->buffer->isDetachedBuffer())
740 return scope.engine->throwTypeError();
741
742 uint len = v->length();
743
744 if (!argc || !argv->isFunctionObject())
745 THROW_TYPE_ERROR();
746 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
747
748 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
749 ScopedValue r(scope);
750 Value *arguments = scope.alloc(nValues: 3);
751
752 const char *data = v->d()->buffer->data->data();
753 uint bytesPerElement = v->d()->type->bytesPerElement;
754 uint byteOffset = v->d()->byteOffset;
755
756 bool ok = true;
757 for (uint k = 0; ok && k < len; ++k) {
758 if (v->d()->buffer->isDetachedBuffer())
759 return scope.engine->throwTypeError();
760
761 arguments[0] = v->d()->type->read(data + byteOffset + k * bytesPerElement);
762
763 arguments[1] = Value::fromDouble(d: k);
764 arguments[2] = v;
765 r = callback->call(thisObject: that, argv: arguments, argc: 3);
766 CHECK_EXCEPTION();
767 ok = r->toBoolean();
768 }
769 return Encode(ok);
770}
771
772ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
773{
774 Scope scope(b);
775 Scoped<TypedArray> v(scope, thisObject);
776 if (!v || v->d()->buffer->isDetachedBuffer())
777 return scope.engine->throwTypeError();
778
779 uint len = v->length();
780 double dlen = len;
781 double relativeStart = argc > 1 ? argv[1].toInteger() : 0.;
782 double relativeEnd = len;
783 if (argc > 2 && !argv[2].isUndefined())
784 relativeEnd = argv[2].toInteger();
785
786 uint k = 0;
787 uint fin = 0;
788
789 if (relativeStart < 0) {
790 k = static_cast<uint>(std::max(a: len+relativeStart, b: 0.));
791 } else {
792 k = static_cast<uint>(std::min(a: relativeStart, b: dlen));
793 }
794
795 if (relativeEnd < 0) {
796 fin = static_cast<uint>(std::max(a: len + relativeEnd, b: 0.));
797 } else {
798 fin = static_cast<uint>(std::min(a: relativeEnd, b: dlen));
799 }
800
801 double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN();
802 Value value = Value::fromDouble(d: val);
803 if (scope.hasException() || v->d()->buffer->isDetachedBuffer())
804 return scope.engine->throwTypeError();
805
806 char *data = v->d()->buffer->data->data();
807 uint bytesPerElement = v->d()->type->bytesPerElement;
808 uint byteOffset = v->d()->byteOffset;
809
810 while (k < fin) {
811 v->d()->type->write(data + byteOffset + k * bytesPerElement, value);
812 k++;
813 }
814
815 return v.asReturnedValue();
816}
817
818static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *instance, uint len)
819{
820 const FunctionObject *constructor = instance->speciesConstructor(scope, defaultConstructor: scope.engine->typedArrayCtors + instance->d()->arrayType);
821 if (!constructor) {
822 scope.engine->throwTypeError();
823 return nullptr;
824 }
825
826 Value *arguments = scope.alloc(nValues: 1);
827 arguments[0] = Encode(len);
828 Scoped<TypedArray> a(scope, constructor->callAsConstructor(argv: arguments, argc: 1));
829 if (!a || a->d()->buffer->isDetachedBuffer() || a->length() < len) {
830 scope.engine->throwTypeError();
831 return nullptr;
832 }
833 return a;
834}
835
836ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
837{
838 Scope scope(b);
839 Scoped<TypedArray> instance(scope, thisObject);
840 if (!instance || instance->d()->buffer->isDetachedBuffer())
841 return scope.engine->throwTypeError();
842
843 uint len = instance->length();
844
845 if (!argc || !argv->isFunctionObject())
846 THROW_TYPE_ERROR();
847 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
848
849 ScopedValue selected(scope);
850 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
851 Value *arguments = scope.alloc(nValues: 3);
852 Value *list = arguments;
853
854 uint to = 0;
855 for (uint k = 0; k < len; ++k) {
856 if (instance->d()->buffer->isDetachedBuffer())
857 return scope.engine->throwTypeError();
858 bool exists;
859 arguments[0] = instance->get(idx: k, hasProperty: &exists);
860 if (!exists)
861 continue;
862
863 arguments[1] = Value::fromDouble(d: k);
864 arguments[2] = instance;
865 selected = callback->call(thisObject: that, argv: arguments, argc: 3);
866 CHECK_EXCEPTION();
867 if (selected->toBoolean()) {
868 ++arguments;
869 scope.alloc(nValues: 1);
870 ++to;
871 }
872 }
873
874 TypedArray *a = typedArraySpeciesCreate(scope, instance, len: to);
875 if (!a)
876 return Encode::undefined();
877
878 for (uint i = 0; i < to; ++i)
879 a->put(idx: i, v: list[i]);
880
881 return a->asReturnedValue();
882}
883
884ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
885{
886 Scope scope(b);
887 Scoped<TypedArray> v(scope, thisObject);
888 if (!v || v->d()->buffer->isDetachedBuffer())
889 return scope.engine->throwTypeError();
890
891 uint len = v->length();
892
893 if (!argc || !argv[0].isFunctionObject())
894 THROW_TYPE_ERROR();
895 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
896
897 ScopedValue result(scope);
898 Value *arguments = scope.alloc(nValues: 3);
899
900 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
901
902 for (uint k = 0; k < len; ++k) {
903 if (v->d()->buffer->isDetachedBuffer())
904 return scope.engine->throwTypeError();
905 arguments[0] = v->get(idx: k);
906 CHECK_EXCEPTION();
907
908 arguments[1] = Value::fromDouble(d: k);
909 arguments[2] = v;
910 result = callback->call(thisObject: that, argv: arguments, argc: 3);
911
912 CHECK_EXCEPTION();
913 if (result->toBoolean())
914 return arguments[0].asReturnedValue();
915 }
916
917 RETURN_UNDEFINED();
918}
919
920ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
921{
922 Scope scope(b);
923 Scoped<TypedArray> v(scope, thisObject);
924 if (!v || v->d()->buffer->isDetachedBuffer())
925 return scope.engine->throwTypeError();
926
927 uint len = v->length();
928
929 if (!argc || !argv[0].isFunctionObject())
930 THROW_TYPE_ERROR();
931 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
932
933 ScopedValue result(scope);
934 Value *arguments = scope.alloc(nValues: 3);
935
936 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
937
938 for (uint k = 0; k < len; ++k) {
939 if (v->d()->buffer->isDetachedBuffer())
940 return scope.engine->throwTypeError();
941 arguments[0] = v->get(idx: k);
942 CHECK_EXCEPTION();
943
944 arguments[1] = Value::fromDouble(d: k);
945 arguments[2] = v;
946 result = callback->call(thisObject: that, argv: arguments, argc: 3);
947
948 CHECK_EXCEPTION();
949 if (result->toBoolean())
950 return Encode(k);
951 }
952
953 return Encode(-1);
954}
955
956ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
957{
958 Scope scope(b);
959 Scoped<TypedArray> v(scope, thisObject);
960 if (!v || v->d()->buffer->isDetachedBuffer())
961 return scope.engine->throwTypeError();
962
963 uint len = v->length();
964
965 if (!argc || !argv->isFunctionObject())
966 THROW_TYPE_ERROR();
967 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
968
969 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
970 Value *arguments = scope.alloc(nValues: 3);
971
972 for (uint k = 0; k < len; ++k) {
973 if (v->d()->buffer->isDetachedBuffer())
974 return scope.engine->throwTypeError();
975 bool exists;
976 arguments[0] = v->get(idx: k, hasProperty: &exists);
977 if (!exists)
978 continue;
979
980 arguments[1] = Value::fromDouble(d: k);
981 arguments[2] = v;
982 callback->call(thisObject: that, argv: arguments, argc: 3);
983 }
984 RETURN_UNDEFINED();
985}
986
987
988ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
989{
990 Scope scope(b);
991 Scoped<TypedArray> v(scope, thisObject);
992 if (!v || v->d()->buffer->isDetachedBuffer())
993 return scope.engine->throwTypeError();
994
995 uint len = v->length();
996 if (len == 0) {
997 return Encode(false);
998 }
999
1000 double n = 0;
1001 if (argc > 1 && !argv[1].isUndefined()) {
1002 n = argv[1].toInteger();
1003 }
1004
1005 double k = 0;
1006 if (n >= 0) {
1007 k = n;
1008 } else {
1009 k = len + n;
1010 if (k < 0) {
1011 k = 0;
1012 }
1013 }
1014
1015 while (k < len) {
1016 ScopedValue val(scope, v->get(idx: k));
1017 if (val->sameValueZero(other: argv[0])) {
1018 return Encode(true);
1019 }
1020 k++;
1021 }
1022
1023 return Encode(false);
1024}
1025
1026ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1027{
1028 Scope scope(b);
1029 Scoped<TypedArray> v(scope, thisObject);
1030 if (!v || v->d()->buffer->isDetachedBuffer())
1031 return scope.engine->throwTypeError();
1032
1033 uint len = v->length();
1034 if (!len)
1035 return Encode(-1);
1036
1037 ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
1038 uint fromIndex = 0;
1039
1040 if (argc >= 2) {
1041 double f = argv[1].toInteger();
1042 CHECK_EXCEPTION();
1043 if (f >= len)
1044 return Encode(-1);
1045 if (f < 0)
1046 f = qMax(a: len + f, b: 0.);
1047 fromIndex = (uint) f;
1048 }
1049
1050 if (v->isStringObject()) {
1051 ScopedValue value(scope);
1052 for (uint k = fromIndex; k < len; ++k) {
1053 bool exists;
1054 value = v->get(idx: k, hasProperty: &exists);
1055 if (exists && RuntimeHelpers::strictEqual(x: value, y: searchValue))
1056 return Encode(k);
1057 }
1058 return Encode(-1);
1059 }
1060
1061 ScopedValue value(scope);
1062
1063 for (uint i = fromIndex; i < len; ++i) {
1064 bool exists;
1065 value = v->get(idx: i, hasProperty: &exists);
1066 CHECK_EXCEPTION();
1067 if (exists && RuntimeHelpers::strictEqual(x: value, y: searchValue))
1068 return Encode(i);
1069 }
1070 return Encode(-1);
1071}
1072
1073ReturnedValue IntrinsicTypedArrayPrototype::method_join(
1074 const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc)
1075{
1076 Scope scope(functionObject);
1077 Scoped<TypedArray> typedArray(scope, thisObject);
1078 if (!typedArray || typedArray->d()->buffer->isDetachedBuffer())
1079 return scope.engine->throwTypeError();
1080
1081 // We cannot optimize the resolution of the argument away if length is 0.
1082 // It may have side effects.
1083 ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue());
1084 const QString separator = argument->isUndefined()
1085 ? QStringLiteral(",")
1086 : argument->toQString();
1087
1088 const quint32 length = typedArray->length();
1089 if (!length)
1090 return Encode(scope.engine->newString());
1091
1092 QString result;
1093
1094 ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
1095 ScopedValue value(scope, typedArray->get(name));
1096 if (!value->isNullOrUndefined())
1097 result = value->toQString();
1098
1099 for (quint32 i = 1; i < length; ++i) {
1100 result += separator;
1101
1102 name = Value::fromDouble(d: i).toString(e: scope.engine);
1103 value = typedArray->get(name);
1104 CHECK_EXCEPTION();
1105
1106 if (!value->isNullOrUndefined())
1107 result += value->toQString();
1108 }
1109
1110 return Encode(scope.engine->newString(s: result));
1111}
1112
1113ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
1114{
1115 Scope scope(b);
1116 Scoped<TypedArray> v(scope, thisObject);
1117 if (!v || v->d()->buffer->isDetachedBuffer())
1118 return scope.engine->throwTypeError();
1119
1120 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: v));
1121 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
1122 return ao->asReturnedValue();
1123}
1124
1125
1126ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1127{
1128 Scope scope(b);
1129 Scoped<TypedArray> instance(scope, thisObject);
1130 if (!instance || instance->d()->buffer->isDetachedBuffer())
1131 return scope.engine->throwTypeError();
1132
1133 uint len = instance->length();
1134 if (!len)
1135 return Encode(-1);
1136
1137 ScopedValue searchValue(scope);
1138 uint fromIndex = len;
1139
1140 if (argc >= 1)
1141 searchValue = argv[0];
1142 else
1143 searchValue = Value::undefinedValue();
1144
1145 if (argc >= 2) {
1146 double f = argv[1].toInteger();
1147 CHECK_EXCEPTION();
1148 if (f > 0)
1149 f = qMin(a: f, b: (double)(len - 1));
1150 else if (f < 0) {
1151 f = len + f;
1152 if (f < 0)
1153 return Encode(-1);
1154 }
1155 fromIndex = (uint) f + 1;
1156 }
1157
1158 ScopedValue value(scope);
1159 for (uint k = fromIndex; k > 0;) {
1160 --k;
1161 bool exists;
1162 value = instance->get(idx: k, hasProperty: &exists);
1163 if (exists && RuntimeHelpers::strictEqual(x: value, y: searchValue))
1164 return Encode(k);
1165 }
1166 return Encode(-1);
1167}
1168
1169ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1170{
1171 Scope scope(b);
1172 Scoped<TypedArray> instance(scope, thisObject);
1173 if (!instance || instance->d()->buffer->isDetachedBuffer())
1174 return scope.engine->throwTypeError();
1175
1176 uint len = instance->length();
1177
1178 if (!argc || !argv->isFunctionObject())
1179 THROW_TYPE_ERROR();
1180 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1181
1182 TypedArray *a = typedArraySpeciesCreate(scope, instance, len);
1183 if (!a)
1184 return Encode::undefined();
1185
1186 ScopedValue v(scope);
1187 ScopedValue mapped(scope);
1188 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1189 Value *arguments = scope.alloc(nValues: 3);
1190
1191 for (uint k = 0; k < len; ++k) {
1192 if (instance->d()->buffer->isDetachedBuffer())
1193 return scope.engine->throwTypeError();
1194 arguments[0] = instance->get(idx: k);
1195
1196 arguments[1] = Value::fromDouble(d: k);
1197 arguments[2] = instance;
1198 mapped = callback->call(thisObject: that, argv: arguments, argc: 3);
1199 CHECK_EXCEPTION();
1200 a->put(idx: k, v: mapped);
1201 }
1202 return a->asReturnedValue();
1203}
1204
1205ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1206{
1207 Scope scope(b);
1208 Scoped<TypedArray> instance(scope, thisObject);
1209 if (!instance || instance->d()->buffer->isDetachedBuffer())
1210 return scope.engine->throwTypeError();
1211
1212 uint len = instance->length();
1213
1214 if (!argc || !argv->isFunctionObject())
1215 THROW_TYPE_ERROR();
1216 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1217
1218 uint k = 0;
1219 ScopedValue acc(scope);
1220 ScopedValue v(scope);
1221
1222 if (argc > 1) {
1223 acc = argv[1];
1224 } else {
1225 bool kPresent = false;
1226 while (k < len && !kPresent) {
1227 v = instance->get(idx: k, hasProperty: &kPresent);
1228 if (kPresent)
1229 acc = v;
1230 ++k;
1231 }
1232 if (!kPresent)
1233 THROW_TYPE_ERROR();
1234 }
1235
1236 Value *arguments = scope.alloc(nValues: 4);
1237
1238 while (k < len) {
1239 if (instance->d()->buffer->isDetachedBuffer())
1240 return scope.engine->throwTypeError();
1241 bool kPresent;
1242 v = instance->get(idx: k, hasProperty: &kPresent);
1243 if (kPresent) {
1244 arguments[0] = acc;
1245 arguments[1] = v;
1246 arguments[2] = Value::fromDouble(d: k);
1247 arguments[3] = instance;
1248 acc = callback->call(thisObject: nullptr, argv: arguments, argc: 4);
1249 CHECK_EXCEPTION();
1250 }
1251 ++k;
1252 }
1253 return acc->asReturnedValue();
1254}
1255
1256ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1257{
1258 Scope scope(b);
1259 Scoped<TypedArray> instance(scope, thisObject);
1260 if (!instance || instance->d()->buffer->isDetachedBuffer())
1261 return scope.engine->throwTypeError();
1262
1263 uint len = instance->length();
1264
1265 if (!argc || !argv->isFunctionObject())
1266 THROW_TYPE_ERROR();
1267 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1268
1269 if (len == 0) {
1270 if (argc == 1)
1271 THROW_TYPE_ERROR();
1272 return argv[1].asReturnedValue();
1273 }
1274
1275 uint k = len;
1276 ScopedValue acc(scope);
1277 ScopedValue v(scope);
1278 if (argc > 1) {
1279 acc = argv[1];
1280 } else {
1281 bool kPresent = false;
1282 while (k > 0 && !kPresent) {
1283 v = instance->get(idx: k - 1, hasProperty: &kPresent);
1284 if (kPresent)
1285 acc = v;
1286 --k;
1287 }
1288 if (!kPresent)
1289 THROW_TYPE_ERROR();
1290 }
1291
1292 Value *arguments = scope.alloc(nValues: 4);
1293
1294 while (k > 0) {
1295 if (instance->d()->buffer->isDetachedBuffer())
1296 return scope.engine->throwTypeError();
1297 bool kPresent;
1298 v = instance->get(idx: k - 1, hasProperty: &kPresent);
1299 if (kPresent) {
1300 arguments[0] = acc;
1301 arguments[1] = v;
1302 arguments[2] = Value::fromDouble(d: k - 1);
1303 arguments[3] = instance;
1304 acc = callback->call(thisObject: nullptr, argv: arguments, argc: 4);
1305 CHECK_EXCEPTION();
1306 }
1307 --k;
1308 }
1309 return acc->asReturnedValue();
1310}
1311
1312ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
1313{
1314 Scope scope(b);
1315 Scoped<TypedArray> instance(scope, thisObject);
1316 if (!instance || instance->d()->buffer->isDetachedBuffer())
1317 return scope.engine->throwTypeError();
1318
1319 uint length = instance->length();
1320
1321 int lo = 0, hi = length - 1;
1322
1323 ScopedValue lval(scope);
1324 ScopedValue hval(scope);
1325 for (; lo < hi; ++lo, --hi) {
1326 bool loExists, hiExists;
1327 lval = instance->get(idx: lo, hasProperty: &loExists);
1328 hval = instance->get(idx: hi, hasProperty: &hiExists);
1329 Q_ASSERT(hiExists && loExists);
1330 bool ok;
1331 ok = instance->put(idx: lo, v: hval);
1332 Q_ASSERT(ok);
1333 ok = instance->put(idx: hi, v: lval);
1334 Q_ASSERT(ok);
1335 }
1336 return instance->asReturnedValue();
1337}
1338
1339ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1340{
1341 Scope scope(b);
1342 Scoped<TypedArray> instance(scope, thisObject);
1343 if (!instance || instance->d()->buffer->isDetachedBuffer())
1344 return scope.engine->throwTypeError();
1345
1346 uint len = instance->length();
1347
1348 if (!argc || !argv->isFunctionObject())
1349 THROW_TYPE_ERROR();
1350 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1351
1352 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1353 ScopedValue result(scope);
1354 Value *arguments = scope.alloc(nValues: 3);
1355
1356 for (uint k = 0; k < len; ++k) {
1357 if (instance->d()->buffer->isDetachedBuffer())
1358 return scope.engine->throwTypeError();
1359 bool exists;
1360 arguments[0] = instance->get(idx: k, hasProperty: &exists);
1361 if (!exists)
1362 continue;
1363
1364 arguments[1] = Value::fromDouble(d: k);
1365 arguments[2] = instance;
1366 result = callback->call(thisObject: that, argv: arguments, argc: 3);
1367 CHECK_EXCEPTION();
1368 if (result->toBoolean())
1369 return Encode(true);
1370 }
1371 return Encode(false);
1372}
1373
1374
1375ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
1376{
1377 Scope scope(b);
1378 Scoped<TypedArray> v(scope, thisObject);
1379 if (!v || v->d()->buffer->isDetachedBuffer())
1380 return scope.engine->throwTypeError();
1381
1382 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(o: v));
1383 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
1384 return ao->asReturnedValue();
1385}
1386
1387ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1388{
1389 Scope scope(b);
1390 Scoped<TypedArray> a(scope, *thisObject);
1391 if (!a)
1392 return scope.engine->throwTypeError();
1393 Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
1394
1395 double doffset = argc >= 2 ? argv[1].toInteger() : 0;
1396 if (scope.engine->hasException)
1397 RETURN_UNDEFINED();
1398 if (!buffer || buffer->isDetachedBuffer())
1399 return scope.engine->throwTypeError();
1400
1401 if (doffset < 0 || doffset >= UINT_MAX)
1402 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1403 uint offset = (uint)doffset;
1404 uint elementSize = a->d()->type->bytesPerElement;
1405
1406 Scoped<TypedArray> srcTypedArray(scope, argv[0]);
1407 if (!srcTypedArray) {
1408 // src is a regular object
1409 ScopedObject o(scope, argv[0].toObject(e: scope.engine));
1410 if (scope.engine->hasException || !o)
1411 return scope.engine->throwTypeError();
1412
1413 double len = ScopedValue(scope, o->get(name: scope.engine->id_length()))->toNumber();
1414 uint l = (uint)len;
1415 if (scope.engine->hasException || l != len)
1416 return scope.engine->throwTypeError();
1417
1418 const uint aLength = a->length();
1419 if (offset > aLength || l > aLength - offset)
1420 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1421
1422 uint idx = 0;
1423 if (buffer->isDetachedBuffer())
1424 return scope.engine->throwTypeError();
1425 char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
1426 ScopedValue val(scope);
1427 while (idx < l) {
1428 val = o->get(idx);
1429 if (scope.hasException())
1430 return Encode::undefined();
1431 val = val->convertedToNumber();
1432 if (scope.hasException() || buffer->isDetachedBuffer())
1433 return scope.engine->throwTypeError();
1434 a->d()->type->write(b, val);
1435 if (scope.engine->hasException)
1436 RETURN_UNDEFINED();
1437 ++idx;
1438 b += elementSize;
1439 }
1440 RETURN_UNDEFINED();
1441 }
1442
1443 // src is a typed array
1444 Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
1445 if (!srcBuffer || srcBuffer->isDetachedBuffer())
1446 return scope.engine->throwTypeError();
1447
1448 uint l = srcTypedArray->length();
1449
1450 const uint aLength = a->length();
1451 if (offset > aLength || l > aLength - offset)
1452 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1453
1454 char *dest = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
1455 const char *src = srcBuffer->d()->data->data() + srcTypedArray->d()->byteOffset;
1456 if (srcTypedArray->d()->type == a->d()->type) {
1457 // same type of typed arrays, use memmove (as srcbuffer and buffer could be the same)
1458 memmove(dest: dest, src: src, n: srcTypedArray->d()->byteLength);
1459 RETURN_UNDEFINED();
1460 }
1461
1462 char *srcCopy = nullptr;
1463 if (buffer->d() == srcBuffer->d()) {
1464 // same buffer, need to take a temporary copy, to not run into problems
1465 srcCopy = new char[srcTypedArray->d()->byteLength];
1466 memcpy(dest: srcCopy, src: src, n: srcTypedArray->d()->byteLength);
1467 src = srcCopy;
1468 }
1469
1470 // typed arrays of different kind, need to manually loop
1471 uint srcElementSize = srcTypedArray->d()->type->bytesPerElement;
1472 TypedArrayOperations::Read read = srcTypedArray->d()->type->read;
1473 TypedArrayOperations::Write write = a->d()->type->write;
1474 for (uint i = 0; i < l; ++i) {
1475 Value val;
1476 val.setRawValue(read(src + i*srcElementSize));
1477 write(dest + i*elementSize, val);
1478 }
1479
1480 if (srcCopy)
1481 delete [] srcCopy;
1482
1483 RETURN_UNDEFINED();
1484}
1485
1486ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1487{
1488 Scope scope(b);
1489 Scoped<TypedArray> instance(scope, thisObject);
1490 if (!instance || instance->d()->buffer->isDetachedBuffer())
1491 return scope.engine->throwTypeError();
1492
1493 uint len = instance->length();
1494
1495 double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
1496 uint start;
1497 if (s < 0)
1498 start = (uint)qMax(a: len + s, b: 0.);
1499 else if (s > len)
1500 start = len;
1501 else
1502 start = (uint) s;
1503 uint end = len;
1504 if (argc > 1 && !argv[1].isUndefined()) {
1505 double e = argv[1].toInteger();
1506 if (e < 0)
1507 end = (uint)qMax(a: len + e, b: 0.);
1508 else if (e > len)
1509 end = len;
1510 else
1511 end = (uint) e;
1512 }
1513 uint count = start > end ? 0 : end - start;
1514
1515 TypedArray *a = typedArraySpeciesCreate(scope, instance, len: count);
1516 if (!a)
1517 return Encode::undefined();
1518
1519 ScopedValue v(scope);
1520 uint n = 0;
1521 for (uint i = start; i < end; ++i) {
1522 if (instance->d()->buffer->isDetachedBuffer())
1523 return scope.engine->throwTypeError();
1524 v = instance->get(idx: i);
1525 if (a->d()->buffer->isDetachedBuffer())
1526 return scope.engine->throwTypeError();
1527 a->put(idx: n, v);
1528 ++n;
1529 }
1530 return a->asReturnedValue();
1531}
1532
1533ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
1534{
1535 Scope scope(builtin);
1536 Scoped<TypedArray> a(scope, *thisObject);
1537
1538 if (!a)
1539 return scope.engine->throwTypeError();
1540
1541 Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
1542 Q_ASSERT(buffer);
1543
1544 int len = a->length();
1545 double b = argc > 0 ? argv[0].toInteger() : 0;
1546 if (b < 0)
1547 b = len + b;
1548 uint begin = (uint)qBound(min: 0., val: b, max: (double)len);
1549
1550 double e = argc < 2 || argv[1].isUndefined() ? len : argv[1].toInteger();
1551 if (e < 0)
1552 e = len + e;
1553 uint end = (uint)qBound(min: 0., val: e, max: (double)len);
1554 if (end < begin)
1555 end = begin;
1556
1557 if (scope.engine->hasException)
1558 RETURN_UNDEFINED();
1559
1560 int newLen = end - begin;
1561
1562 ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, defaultConstructor: scope.engine->typedArrayCtors + a->d()->arrayType));
1563 if (!constructor)
1564 return scope.engine->throwTypeError();
1565
1566 Value *arguments = scope.alloc(nValues: 3);
1567 arguments[0] = buffer;
1568 arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
1569 arguments[2] = Encode(newLen);
1570 a = constructor->callAsConstructor(argv: arguments, argc: 3);
1571 if (!a || a->d()->buffer->isDetachedBuffer())
1572 return scope.engine->throwTypeError();
1573 return a->asReturnedValue();
1574}
1575
1576ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1577{
1578 Scope scope(b);
1579 Scoped<TypedArray> instance(scope, thisObject);
1580 if (!instance || instance->d()->buffer->isDetachedBuffer())
1581 return scope.engine->throwTypeError();
1582
1583 uint len = instance->length();
1584 const QString separator = QStringLiteral(",");
1585
1586 QString R;
1587
1588 ScopedValue v(scope);
1589 ScopedString s(scope);
1590
1591 for (uint k = 0; k < len; ++k) {
1592 if (instance->d()->buffer->isDetachedBuffer())
1593 return scope.engine->throwTypeError();
1594 if (k)
1595 R += separator;
1596
1597 v = instance->get(idx: k);
1598 v = Runtime::CallElement::call(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
1599 s = v->toString(e: scope.engine);
1600 if (scope.hasException())
1601 return Encode::undefined();
1602
1603 R += s->toQString();
1604 }
1605 return scope.engine->newString(s: R)->asReturnedValue();
1606}
1607
1608ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
1609{
1610 const TypedArray *a = thisObject->as<TypedArray>();
1611 if (!a)
1612 return Encode::undefined();
1613
1614 return a->engine()->newString(s: QString::fromLatin1(str: a->d()->type->name))->asReturnedValue();
1615}
1616
1617static bool validateTypedArray(const Object *o)
1618{
1619 const TypedArray *a = o->as<TypedArray>();
1620 if (!a)
1621 return false;
1622 if (a->d()->buffer->isDetachedBuffer())
1623 return false;
1624 return true;
1625}
1626
1627ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1628{
1629 Scope scope(f);
1630 int len = argc;
1631 const Value *items = argv;
1632 const FunctionObject *C = thisObject->as<FunctionObject>();
1633 if (!C || !C->isConstructor())
1634 return scope.engine->throwTypeError();
1635
1636 Value lenValue = Value::fromInt32(i: len);
1637 ScopedObject newObj(scope, C->callAsConstructor(argv: &lenValue, argc: 1));
1638 if (scope.hasException())
1639 return Encode::undefined();
1640 if (!::validateTypedArray(o: newObj))
1641 return scope.engine->throwTypeError();
1642 TypedArray *a = newObj->as<TypedArray>();
1643 Q_ASSERT(a);
1644 if (a->length() < static_cast<uint>(len))
1645 return scope.engine->throwTypeError();
1646
1647 for (int k = 0; k < len; ++k) {
1648 newObj->put(id: PropertyKey::fromArrayIndex(idx: k), v: items[k]);
1649 }
1650 return newObj->asReturnedValue();
1651}
1652
1653ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1654{
1655 Scope scope(f);
1656 ScopedObject itemsObject(scope, argv[0]);
1657 bool usingIterator = false;
1658
1659 ScopedFunctionObject mapfn(scope, Value::undefinedValue());
1660 Value *mapArguments = nullptr;
1661 if (argc > 1) {
1662 mapfn = ScopedFunctionObject(scope, argv[1]);
1663 if (!mapfn)
1664 return scope.engine->throwTypeError(message: QString::fromLatin1(str: "%1 is not a function").arg(a: argv[1].toQStringNoThrow()));
1665 mapArguments = scope.alloc(nValues: 2);
1666 }
1667
1668 // Iterator validity check goes after map function validity has been checked.
1669 if (itemsObject) {
1670 // If the object claims to support iterators, then let's try use them.
1671 ScopedValue it(scope, itemsObject->get(name: scope.engine->symbol_iterator()));
1672 CHECK_EXCEPTION();
1673 if (!it->isNullOrUndefined()) {
1674 ScopedFunctionObject itfunc(scope, it);
1675 if (!itfunc)
1676 return scope.engine->throwTypeError();
1677 usingIterator = true;
1678 }
1679 }
1680
1681 ScopedValue thisArg(scope);
1682 if (argc > 2)
1683 thisArg = argv[2];
1684
1685 const FunctionObject *C = thisObject->as<FunctionObject>();
1686
1687 if (usingIterator) {
1688 // Item iteration supported, so let's go ahead and try use that.
1689 CHECK_EXCEPTION();
1690
1691 qint64 iterableLength = 0;
1692 Value *nextValue = scope.alloc(nValues: 1);
1693 ScopedValue done(scope);
1694
1695 ScopedObject lengthIterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
1696 CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
1697 if (!lengthIterator) {
1698 return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
1699 }
1700
1701 forever {
1702 // Here we calculate the length of the iterable range.
1703 if (iterableLength > (static_cast<qint64>(1) << 53) - 1) {
1704 ScopedValue falsey(scope, Encode(false));
1705 ScopedValue error(scope, scope.engine->throwTypeError());
1706 return Runtime::IteratorClose::call(scope.engine, lengthIterator, falsey);
1707 }
1708 // Retrieve the next value. If the iteration ends, we're done here.
1709 done = Value::fromReturnedValue(val: Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue));
1710 if (scope.engine->hasException)
1711 return Runtime::IteratorClose::call(scope.engine, lengthIterator, Value::fromBoolean(b: false));
1712 if (done->toBoolean()) {
1713 break;
1714 }
1715 iterableLength++;
1716 }
1717
1718 // Constructor validity check goes after we have calculated the length, because that calculation can throw
1719 // errors that are not type errors and at least the tests expect those rather than type errors.
1720 if (!C || !C->isConstructor())
1721 return scope.engine->throwTypeError();
1722
1723 ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
1724 CHECK_EXCEPTION(); // symbol_iterator can throw.
1725 if (!iterator) {
1726 return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
1727 }
1728
1729 ScopedObject a(scope, Value::undefinedValue());
1730 ScopedValue ctorArgument(scope, Value::fromReturnedValue(val: QV4::Encode(int(iterableLength))));
1731 a = C->callAsConstructor(argv: ctorArgument, argc: 1);
1732 CHECK_EXCEPTION();
1733
1734 // We check exceptions above, and only after doing so, check the array's validity after construction.
1735 if (!::validateTypedArray(o: a) || (a->getLength() < iterableLength))
1736 return scope.engine->throwTypeError();
1737
1738
1739 // The loop below traverses the iterator, and puts elements into the created array.
1740 ScopedValue mappedValue(scope, Value::undefinedValue());
1741 for (qint64 k = 0; k < iterableLength; ++k) {
1742 done = Value::fromReturnedValue(val: Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
1743 if (scope.engine->hasException)
1744 return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(b: false));
1745
1746 if (mapfn) {
1747 mapArguments[0] = *nextValue;
1748 mapArguments[1] = Value::fromDouble(d: k);
1749 mappedValue = mapfn->call(thisObject: thisArg, argv: mapArguments, argc: 2);
1750 if (scope.engine->hasException)
1751 return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(b: false));
1752 } else {
1753 mappedValue = *nextValue;
1754 }
1755
1756 a->put(idx: k, v: mappedValue);
1757 if (scope.engine->hasException)
1758 return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(b: false));
1759 }
1760 return a.asReturnedValue();
1761 } else {
1762 // Array-like fallback. We request elements by index, and put them into the created array.
1763 ScopedObject arrayLike(scope, argv[0].toObject(e: scope.engine));
1764 if (!arrayLike)
1765 return scope.engine->throwTypeError(message: QString::fromLatin1(str: "Cannot convert %1 to object").arg(a: argv[0].toQStringNoThrow()));
1766
1767 int len = arrayLike->getLength();
1768 CHECK_EXCEPTION();
1769
1770 // Getting the length may throw, and must do so before we check the constructor validity.
1771 if (!C || !C->isConstructor())
1772 return scope.engine->throwTypeError();
1773
1774 ScopedObject a(scope, Value::undefinedValue());
1775 ScopedValue ctorArgument(scope, Value::fromReturnedValue(val: QV4::Encode(len)));
1776 a = C->callAsConstructor(argv: ctorArgument, argc: 1);
1777 CHECK_EXCEPTION();
1778
1779 // We check exceptions above, and only after doing so, check the array's validity after construction.
1780 if (!::validateTypedArray(o: a) || (a->getLength() < len))
1781 return scope.engine->throwTypeError();
1782
1783 ScopedValue mappedValue(scope, Value::undefinedValue());
1784 ScopedValue kValue(scope);
1785 for (int k = 0; k < len; ++k) {
1786 kValue = arrayLike->get(idx: k);
1787 CHECK_EXCEPTION();
1788
1789 if (mapfn) {
1790 mapArguments[0] = kValue;
1791 mapArguments[1] = Value::fromDouble(d: k);
1792 mappedValue = mapfn->call(thisObject: thisArg, argv: mapArguments, argc: 2);
1793 CHECK_EXCEPTION();
1794 } else {
1795 mappedValue = kValue;
1796 }
1797
1798 a->put(idx: k, v: mappedValue);
1799 CHECK_EXCEPTION();
1800 }
1801 return a.asReturnedValue();
1802 }
1803}
1804
1805void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
1806{
1807 Scope scope(engine);
1808 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: *this);
1809 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
1810 ScopedString s(scope, engine->newString(QStringLiteral("TypedArray")));
1811 ctor->defineReadonlyConfigurableProperty(name: engine->id_name(), value: s);
1812 s = scope.engine->newString(QStringLiteral("of"));
1813 ctor->defineDefaultProperty(name: s, code: IntrinsicTypedArrayCtor::method_of);
1814 s = scope.engine->newString(QStringLiteral("from"));
1815 ctor->defineDefaultProperty(name: s, code: IntrinsicTypedArrayCtor::method_from, argumentCount: 1);
1816 ctor->addSymbolSpecies();
1817
1818 defineAccessorProperty(QStringLiteral("buffer"), getter: method_get_buffer, setter: nullptr);
1819 defineAccessorProperty(QStringLiteral("byteLength"), getter: method_get_byteLength, setter: nullptr);
1820 defineAccessorProperty(QStringLiteral("byteOffset"), getter: method_get_byteOffset, setter: nullptr);
1821 defineAccessorProperty(QStringLiteral("length"), getter: method_get_length, setter: nullptr);
1822
1823 defineDefaultProperty(QStringLiteral("copyWithin"), code: method_copyWithin, argumentCount: 2);
1824 defineDefaultProperty(QStringLiteral("entries"), code: method_entries, argumentCount: 0);
1825 defineDefaultProperty(QStringLiteral("every"), code: method_every, argumentCount: 1);
1826 defineDefaultProperty(QStringLiteral("fill"), code: method_fill, argumentCount: 1);
1827 defineDefaultProperty(QStringLiteral("filter"), code: method_filter, argumentCount: 1);
1828 defineDefaultProperty(QStringLiteral("find"), code: method_find, argumentCount: 1);
1829 defineDefaultProperty(QStringLiteral("findIndex"), code: method_findIndex, argumentCount: 1);
1830 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
1831 defineDefaultProperty(QStringLiteral("includes"), code: method_includes, argumentCount: 1);
1832 defineDefaultProperty(QStringLiteral("indexOf"), code: method_indexOf, argumentCount: 1);
1833 defineDefaultProperty(QStringLiteral("join"), code: method_join, argumentCount: 1);
1834 defineDefaultProperty(QStringLiteral("keys"), code: method_keys, argumentCount: 0);
1835 defineDefaultProperty(QStringLiteral("lastIndexOf"), code: method_lastIndexOf, argumentCount: 1);
1836 defineDefaultProperty(QStringLiteral("map"), code: method_map, argumentCount: 1);
1837 defineDefaultProperty(QStringLiteral("reduce"), code: method_reduce, argumentCount: 1);
1838 defineDefaultProperty(QStringLiteral("reduceRight"), code: method_reduceRight, argumentCount: 1);
1839 defineDefaultProperty(QStringLiteral("reverse"), code: method_reverse, argumentCount: 0);
1840 defineDefaultProperty(QStringLiteral("some"), code: method_some, argumentCount: 1);
1841 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 1);
1842 defineDefaultProperty(QStringLiteral("slice"), code: method_slice, argumentCount: 2);
1843 defineDefaultProperty(QStringLiteral("subarray"), code: method_subarray, argumentCount: 2);
1844 defineDefaultProperty(name: engine->id_toLocaleString(), code: method_toLocaleString, argumentCount: 0);
1845 ScopedObject f(scope, engine->arrayPrototype()->get(name: engine->id_toString()));
1846 defineDefaultProperty(name: engine->id_toString(), value: f);
1847
1848 ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
1849 ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valuesString, code: method_values, argumentCount: 0));
1850 defineDefaultProperty(QStringLiteral("values"), value: values);
1851 defineDefaultProperty(name: engine->symbol_iterator(), value: values);
1852
1853 defineAccessorProperty(name: engine->symbol_toStringTag(), getter: method_get_toStringTag, setter: nullptr);
1854}
1855

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