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

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