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