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

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