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 while (k < len) {
991 ScopedValue val(scope, v->get(k));
992 if (val->sameValueZero(argv[0])) {
993 return Encode(true);
994 }
995 k++;
996 }
997
998 return Encode(false);
999}
1000
1001ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1002{
1003 Scope scope(b);
1004 Scoped<TypedArray> v(scope, thisObject);
1005 if (!v || v->hasDetachedArrayData())
1006 return scope.engine->throwTypeError();
1007
1008 uint len = v->length();
1009 if (!len)
1010 return Encode(-1);
1011
1012 ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
1013 uint fromIndex = 0;
1014
1015 if (argc >= 2) {
1016 double f = argv[1].toInteger();
1018 if (f >= len)
1019 return Encode(-1);
1020 if (f < 0)
1021 f = qMax(len + f, 0.);
1022 fromIndex = (uint) f;
1023 }
1024
1025 if (v->isStringObject()) {
1026 ScopedValue value(scope);
1027 for (uint k = fromIndex; k < len; ++k) {
1028 bool exists;
1029 value = v->get(k, &exists);
1030 if (exists && RuntimeHelpers::strictEqual(value, searchValue))
1031 return Encode(k);
1032 }
1033 return Encode(-1);
1034 }
1035
1036 ScopedValue value(scope);
1037
1038 for (uint i = fromIndex; i < len; ++i) {
1039 bool exists;
1040 value = v->get(i, &exists);
1042 if (exists && RuntimeHelpers::strictEqual(value, searchValue))
1043 return Encode(i);
1044 }
1045 return Encode(-1);
1046}
1047
1048ReturnedValue IntrinsicTypedArrayPrototype::method_join(
1049 const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc)
1050{
1051 Scope scope(functionObject);
1052 Scoped<TypedArray> typedArray(scope, thisObject);
1053 if (!typedArray || typedArray->hasDetachedArrayData())
1054 return scope.engine->throwTypeError();
1055
1056 // We cannot optimize the resolution of the argument away if length is 0.
1057 // It may have side effects.
1058 ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue());
1059 const QString separator = argument->isUndefined()
1060 ? QStringLiteral(",")
1061 : argument->toQString();
1062
1063 const quint32 length = typedArray->length();
1064 if (!length)
1065 return Encode(scope.engine->newString());
1066
1067 QString result;
1068
1069 ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
1070 ScopedValue value(scope, typedArray->get(name));
1071 if (!value->isNullOrUndefined())
1072 result = value->toQString();
1073
1074 for (quint32 i = 1; i < length; ++i) {
1075 result += separator;
1076
1077 name = Value::fromDouble(i).toString(scope.engine);
1078 value = typedArray->get(name);
1080
1081 if (!value->isNullOrUndefined())
1082 result += value->toQString();
1083 }
1084
1085 return Encode(scope.engine->newString(result));
1086}
1087
1088ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
1089{
1090 Scope scope(b);
1091 Scoped<TypedArray> v(scope, thisObject);
1092 if (!v || v->hasDetachedArrayData())
1093 return scope.engine->throwTypeError();
1094
1095 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
1096 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
1097 return ao->asReturnedValue();
1098}
1099
1100
1101ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1102{
1103 Scope scope(b);
1104 Scoped<TypedArray> instance(scope, thisObject);
1105 if (!instance || instance->hasDetachedArrayData())
1106 return scope.engine->throwTypeError();
1107
1108 uint len = instance->length();
1109 if (!len)
1110 return Encode(-1);
1111
1112 ScopedValue searchValue(scope);
1113 uint fromIndex = len;
1114
1115 if (argc >= 1)
1116 searchValue = argv[0];
1117 else
1118 searchValue = Value::undefinedValue();
1119
1120 if (argc >= 2) {
1121 double f = argv[1].toInteger();
1123 if (f > 0)
1124 f = qMin(f, (double)(len - 1));
1125 else if (f < 0) {
1126 f = len + f;
1127 if (f < 0)
1128 return Encode(-1);
1129 }
1130 fromIndex = (uint) f + 1;
1131 }
1132
1133 ScopedValue value(scope);
1134 for (uint k = fromIndex; k > 0;) {
1135 --k;
1136 bool exists;
1137 value = instance->get(k, &exists);
1138 if (exists && RuntimeHelpers::strictEqual(value, searchValue))
1139 return Encode(k);
1140 }
1141 return Encode(-1);
1142}
1143
1144ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1145{
1146 Scope scope(b);
1147 Scoped<TypedArray> instance(scope, thisObject);
1148 if (!instance || instance->hasDetachedArrayData())
1149 return scope.engine->throwTypeError();
1150
1151 uint len = instance->length();
1152
1153 if (!argc || !argv->isFunctionObject())
1155 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1156
1157 TypedArray *a = typedArraySpeciesCreate(scope, instance, len);
1158 if (!a)
1159 return Encode::undefined();
1160
1161 ScopedValue v(scope);
1162 ScopedValue mapped(scope);
1163 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1164 Value *arguments = scope.constructUndefined(3);
1165
1166 for (uint k = 0; k < len; ++k) {
1167 if (instance->hasDetachedArrayData())
1168 return scope.engine->throwTypeError();
1169 arguments[0] = instance->get(k);
1170
1171 arguments[1] = Value::fromDouble(k);
1172 arguments[2] = instance;
1173 mapped = callback->call(that, arguments, 3);
1175 a->put(k, mapped);
1176 }
1177 return a->asReturnedValue();
1178}
1179
1180ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1181{
1182 Scope scope(b);
1183 Scoped<TypedArray> instance(scope, thisObject);
1184 if (!instance || instance->hasDetachedArrayData())
1185 return scope.engine->throwTypeError();
1186
1187 uint len = instance->length();
1188
1189 if (!argc || !argv->isFunctionObject())
1191 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1192
1193 uint k = 0;
1194 ScopedValue acc(scope);
1195 ScopedValue v(scope);
1196
1197 if (argc > 1) {
1198 acc = argv[1];
1199 } else {
1200 bool kPresent = false;
1201 while (k < len && !kPresent) {
1202 v = instance->get(k, &kPresent);
1203 if (kPresent)
1204 acc = v;
1205 ++k;
1206 }
1207 if (!kPresent)
1209 }
1210
1211 Value *arguments = scope.constructUndefined(4);
1212
1213 while (k < len) {
1214 if (instance->hasDetachedArrayData())
1215 return scope.engine->throwTypeError();
1216 bool kPresent;
1217 v = instance->get(k, &kPresent);
1218 if (kPresent) {
1219 arguments[0] = acc;
1220 arguments[1] = v;
1221 arguments[2] = Value::fromDouble(k);
1222 arguments[3] = instance;
1223 acc = callback->call(nullptr, arguments, 4);
1225 }
1226 ++k;
1227 }
1228 return acc->asReturnedValue();
1229}
1230
1231ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1232{
1233 Scope scope(b);
1234 Scoped<TypedArray> instance(scope, thisObject);
1235 if (!instance || instance->hasDetachedArrayData())
1236 return scope.engine->throwTypeError();
1237
1238 uint len = instance->length();
1239
1240 if (!argc || !argv->isFunctionObject())
1242 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1243
1244 if (len == 0) {
1245 if (argc == 1)
1247 return argv[1].asReturnedValue();
1248 }
1249
1250 uint k = len;
1251 ScopedValue acc(scope);
1252 ScopedValue v(scope);
1253 if (argc > 1) {
1254 acc = argv[1];
1255 } else {
1256 bool kPresent = false;
1257 while (k > 0 && !kPresent) {
1258 v = instance->get(k - 1, &kPresent);
1259 if (kPresent)
1260 acc = v;
1261 --k;
1262 }
1263 if (!kPresent)
1265 }
1266
1267 Value *arguments = scope.constructUndefined(4);
1268
1269 while (k > 0) {
1270 if (instance->hasDetachedArrayData())
1271 return scope.engine->throwTypeError();
1272 bool kPresent;
1273 v = instance->get(k - 1, &kPresent);
1274 if (kPresent) {
1275 arguments[0] = acc;
1276 arguments[1] = v;
1277 arguments[2] = Value::fromDouble(k - 1);
1278 arguments[3] = instance;
1279 acc = callback->call(nullptr, arguments, 4);
1281 }
1282 --k;
1283 }
1284 return acc->asReturnedValue();
1285}
1286
1287ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
1288{
1289 Scope scope(b);
1290 Scoped<TypedArray> instance(scope, thisObject);
1291 if (!instance || instance->hasDetachedArrayData())
1292 return scope.engine->throwTypeError();
1293
1294 uint length = instance->length();
1295
1296 int lo = 0, hi = length - 1;
1297
1298 ScopedValue lval(scope);
1299 ScopedValue hval(scope);
1300 for (; lo < hi; ++lo, --hi) {
1301 bool loExists, hiExists;
1302 lval = instance->get(lo, &loExists);
1303 hval = instance->get(hi, &hiExists);
1304 Q_ASSERT(hiExists && loExists);
1305 bool ok;
1306 ok = instance->put(lo, hval);
1307 Q_ASSERT(ok);
1308 ok = instance->put(hi, lval);
1309 Q_ASSERT(ok);
1310 }
1311 return instance->asReturnedValue();
1312}
1313
1314ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1315{
1316 Scope scope(b);
1317 Scoped<TypedArray> instance(scope, thisObject);
1318 if (!instance || instance->hasDetachedArrayData())
1319 return scope.engine->throwTypeError();
1320
1321 uint len = instance->length();
1322
1323 if (!argc || !argv->isFunctionObject())
1325 const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
1326
1327 ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
1328 ScopedValue result(scope);
1329 Value *arguments = scope.constructUndefined(3);
1330
1331 for (uint k = 0; k < len; ++k) {
1332 if (instance->hasDetachedArrayData())
1333 return scope.engine->throwTypeError();
1334 bool exists;
1335 arguments[0] = instance->get(k, &exists);
1336 if (!exists)
1337 continue;
1338
1339 arguments[1] = Value::fromDouble(k);
1340 arguments[2] = instance;
1341 result = callback->call(that, arguments, 3);
1343 if (result->toBoolean())
1344 return Encode(true);
1345 }
1346 return Encode(false);
1347}
1348
1349
1350ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
1351{
1352 Scope scope(b);
1353 Scoped<TypedArray> v(scope, thisObject);
1354 if (!v || v->hasDetachedArrayData())
1355 return scope.engine->throwTypeError();
1356
1357 Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
1358 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
1359 return ao->asReturnedValue();
1360}
1361
1362ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1363{
1364 Scope scope(b);
1365 Scoped<TypedArray> a(scope, *thisObject);
1366 if (!a)
1367 return scope.engine->throwTypeError();
1368 Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
1369
1370 double doffset = argc >= 2 ? argv[1].toInteger() : 0;
1371 if (scope.hasException())
1373 if (!buffer || buffer->hasDetachedArrayData())
1374 return scope.engine->throwTypeError();
1375
1376 if (doffset < 0 || doffset >= UINT_MAX)
1377 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1378 uint offset = (uint)doffset;
1379 uint elementSize = a->bytesPerElement();
1380
1381 Scoped<TypedArray> srcTypedArray(scope, argv[0]);
1382 if (!srcTypedArray) {
1383 // src is a regular object
1384 ScopedObject o(scope, argv[0].toObject(scope.engine));
1385 if (scope.hasException() || !o)
1386 return scope.engine->throwTypeError();
1387
1388 double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber();
1389 uint l = (uint)len;
1390 if (scope.hasException() || l != len)
1391 return scope.engine->throwTypeError();
1392
1393 const uint aLength = a->length();
1394 if (offset > aLength || l > aLength - offset)
1395 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1396
1397 uint idx = 0;
1398 if (buffer->hasDetachedArrayData())
1399 return scope.engine->throwTypeError();
1400 char *b = buffer->arrayData() + a->byteOffset() + offset*elementSize;
1401 ScopedValue val(scope);
1402 while (idx < l) {
1403 val = o->get(idx);
1404 if (scope.hasException())
1405 return Encode::undefined();
1406 val = val->convertedToNumber();
1407 if (scope.hasException() || buffer->hasDetachedArrayData())
1408 return scope.engine->throwTypeError();
1409 a->d()->type->write(b, val);
1410 if (scope.hasException())
1412 ++idx;
1413 b += elementSize;
1414 }
1416 }
1417
1418 // src is a typed array
1419 Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
1420 if (!srcBuffer || srcBuffer->hasDetachedArrayData())
1421 return scope.engine->throwTypeError();
1422
1423 uint l = srcTypedArray->length();
1424
1425 const uint aLength = a->length();
1426 if (offset > aLength || l > aLength - offset)
1427 RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
1428
1429 char *dest = buffer->arrayData() + a->byteOffset() + offset*elementSize;
1430 const char *src = srcBuffer->d()->constArrayData() + srcTypedArray->byteOffset();
1431 if (srcTypedArray->d()->type == a->d()->type) {
1432 // same type of typed arrays, use memmove (as srcbuffer and buffer could be the same)
1433 memmove(dest, src, srcTypedArray->byteLength());
1435 }
1436
1437 char *srcCopy = nullptr;
1438 if (buffer->d() == srcBuffer->d()) {
1439 // same buffer, need to take a temporary copy, to not run into problems
1440 srcCopy = new char[srcTypedArray->byteLength()];
1441 memcpy(srcCopy, src, srcTypedArray->byteLength());
1442 src = srcCopy;
1443 }
1444
1445 // typed arrays of different kind, need to manually loop
1446 uint srcElementSize = srcTypedArray->bytesPerElement();
1447 TypedArrayOperations::Read read = srcTypedArray->d()->type->read;
1448 TypedArrayOperations::Write write = a->d()->type->write;
1449 for (uint i = 0; i < l; ++i) {
1450 Value val;
1451 val.setRawValue(read(src + i*srcElementSize));
1452 write(dest + i*elementSize, val);
1453 }
1454
1455 if (srcCopy)
1456 delete [] srcCopy;
1457
1459}
1460
1461ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
1462{
1463 Scope scope(b);
1464 Scoped<TypedArray> instance(scope, thisObject);
1465 if (!instance || instance->hasDetachedArrayData())
1466 return scope.engine->throwTypeError();
1467
1468 uint len = instance->length();
1469
1470 double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
1471 uint start;
1472 if (s < 0)
1473 start = (uint)qMax(len + s, 0.);
1474 else if (s > len)
1475 start = len;
1476 else
1477 start = (uint) s;
1478 uint end = len;
1479 if (argc > 1 && !argv[1].isUndefined()) {
1480 double e = argv[1].toInteger();
1481 if (e < 0)
1482 end = (uint)qMax(len + e, 0.);
1483 else if (e > len)
1484 end = len;
1485 else
1486 end = (uint) e;
1487 }
1488 uint count = start > end ? 0 : end - start;
1489
1490 TypedArray *a = typedArraySpeciesCreate(scope, instance, count);
1491 if (!a)
1492 return Encode::undefined();
1493
1494 ScopedValue v(scope);
1495 uint n = 0;
1496 for (uint i = start; i < end; ++i) {
1497 if (instance->hasDetachedArrayData())
1498 return scope.engine->throwTypeError();
1499 v = instance->get(i);
1500 if (a->hasDetachedArrayData())
1501 return scope.engine->throwTypeError();
1502 a->put(n, v);
1503 ++n;
1504 }
1505 return a->asReturnedValue();
1506}
1507
1508ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
1509{
1510 Scope scope(builtin);
1511 Scoped<TypedArray> a(scope, *thisObject);
1512
1513 if (!a)
1514 return scope.engine->throwTypeError();
1515
1516 Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
1517 Q_ASSERT(buffer);
1518
1519 int len = a->length();
1520 double b = argc > 0 ? argv[0].toInteger() : 0;
1521 if (b < 0)
1522 b = len + b;
1523 uint begin = (uint)qBound(0., b, (double)len);
1524
1525 double e = argc < 2 || argv[1].isUndefined() ? len : argv[1].toInteger();
1526 if (e < 0)
1527 e = len + e;
1528 uint end = (uint)qBound(0., e, (double)len);
1529 if (end < begin)
1530 end = begin;
1531
1532 if (scope.hasException())
1534
1535 int newLen = end - begin;
1536
1537 ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, scope.engine->typedArrayCtors + a->d()->arrayType));
1538 if (!constructor)
1539 return scope.engine->throwTypeError();
1540
1541 Value *arguments = scope.constructUndefined(3);
1542 arguments[0] = buffer;
1543 arguments[1] = Encode(a->byteOffset() + begin * a->bytesPerElement());
1544 arguments[2] = Encode(newLen);
1545 a = constructor->callAsConstructor(arguments, 3);
1546 if (!a || a->hasDetachedArrayData())
1547 return scope.engine->throwTypeError();
1548 return a->asReturnedValue();
1549}
1550
1551ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
1552{
1553 Scope scope(b);
1554 Scoped<TypedArray> instance(scope, thisObject);
1555 if (!instance || instance->hasDetachedArrayData())
1556 return scope.engine->throwTypeError();
1557
1558 uint len = instance->length();
1559 const QString separator = QStringLiteral(",");
1560
1561 QString R;
1562
1563 ScopedValue v(scope);
1564 ScopedString s(scope);
1565
1566 ScopedPropertyKey tolocaleString(scope, scope.engine->id_toLocaleString()->toPropertyKey());
1567 Q_ASSERT(!scope.engine->hasException);
1568
1569 for (uint k = 0; k < len; ++k) {
1570 if (instance->hasDetachedArrayData())
1571 return scope.engine->throwTypeError();
1572 if (k)
1573 R += separator;
1574
1575 v = instance->get(k);
1576 Q_ASSERT(!v->isNullOrUndefined()); // typed array cannot hold null or undefined
1577
1578 ScopedObject valueAsObject(scope, v->toObject(scope.engine));
1579 Q_ASSERT(valueAsObject); // only null or undefined cannot be converted to object
1580
1581 ScopedFunctionObject function(scope, valueAsObject->get(tolocaleString));
1582 if (!function)
1583 return scope.engine->throwTypeError();
1584
1585 v = function->call(valueAsObject, nullptr, 0);
1586 if (scope.hasException())
1587 return Encode::undefined();
1588
1589 s = v->toString(scope.engine);
1590 if (scope.hasException())
1591 return Encode::undefined();
1592
1593 R += s->toQString();
1594 }
1595 return scope.engine->newString(R)->asReturnedValue();
1596}
1597
1598ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
1599{
1600 const TypedArray *a = thisObject->as<TypedArray>();
1601 if (!a)
1602 return Encode::undefined();
1603
1604 return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
1605}
1606
1607static bool validateTypedArray(const Object *o)
1608{
1609 const TypedArray *a = o->as<TypedArray>();
1610 if (!a)
1611 return false;
1612 if (a->hasDetachedArrayData())
1613 return false;
1614 return true;
1615}
1616
1617ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1618{
1619 Scope scope(f);
1620 int len = argc;
1621 const Value *items = argv;
1622 const FunctionObject *C = thisObject->as<FunctionObject>();
1623 if (!C || !C->isConstructor())
1624 return scope.engine->throwTypeError();
1625
1626 Value lenValue = Value::fromInt32(len);
1627 ScopedObject newObj(scope, C->callAsConstructor(&lenValue, 1));
1628 if (scope.hasException())
1629 return Encode::undefined();
1630 if (!::validateTypedArray(newObj))
1631 return scope.engine->throwTypeError();
1632 TypedArray *a = newObj->as<TypedArray>();
1633 Q_ASSERT(a);
1634 if (a->length() < static_cast<uint>(len))
1635 return scope.engine->throwTypeError();
1636
1637 for (int k = 0; k < len; ++k) {
1638 newObj->put(PropertyKey::fromArrayIndex(k), items[k]);
1639 }
1640 return newObj->asReturnedValue();
1641}
1642
1643ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
1644{
1645 Scope scope(f);
1646 ScopedObject itemsObject(scope, argv[0]);
1647 bool usingIterator = false;
1648
1649 ScopedFunctionObject mapfn(scope, Value::undefinedValue());
1650 Value *mapArguments = nullptr;
1651 if (argc > 1) {
1652 mapfn = ScopedFunctionObject(scope, argv[1]);
1653 if (!mapfn)
1654 return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow()));
1655 mapArguments = scope.constructUndefined(2);
1656 }
1657
1658 // Iterator validity check goes after map function validity has been checked.
1659 if (itemsObject) {
1660 // If the object claims to support iterators, then let's try use them.
1661 ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator()));
1663 if (!it->isNullOrUndefined()) {
1664 ScopedFunctionObject itfunc(scope, it);
1665 if (!itfunc)
1666 return scope.engine->throwTypeError();
1667 usingIterator = true;
1668 }
1669 }
1670
1671 ScopedValue thisArg(scope);
1672 if (argc > 2)
1673 thisArg = argv[2];
1674
1675 const FunctionObject *C = thisObject->as<FunctionObject>();
1676
1677 if (usingIterator) {
1678 // Item iteration supported, so let's go ahead and try use that.
1680
1681 qint64 iterableLength = 0;
1682 Value *nextValue = scope.constructUndefined(1);
1683 ScopedValue done(scope);
1684
1685 ScopedObject lengthIterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
1686 CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
1687 if (!lengthIterator) {
1688 return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
1689 }
1690
1691 forever {
1692 // Here we calculate the length of the iterable range.
1693 if (iterableLength > (static_cast<qint64>(1) << 53) - 1) {
1694 ScopedValue error(scope, scope.engine->throwTypeError());
1695 return Runtime::IteratorClose::call(scope.engine, lengthIterator);
1696 }
1697 // Retrieve the next value. If the iteration ends, we're done here.
1698 done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue));
1699 if (scope.hasException())
1700 return Runtime::IteratorClose::call(scope.engine, lengthIterator);
1701 if (done->toBoolean()) {
1702 break;
1703 }
1704 iterableLength++;
1705 }
1706
1707 // Constructor validity check goes after we have calculated the length, because that calculation can throw
1708 // errors that are not type errors and at least the tests expect those rather than type errors.
1709 if (!C || !C->isConstructor())
1710 return scope.engine->throwTypeError();
1711
1712 ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
1713 CHECK_EXCEPTION(); // symbol_iterator can throw.
1714 if (!iterator) {
1715 return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
1716 }
1717
1718 ScopedObject a(scope, Value::undefinedValue());
1719 ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(int(iterableLength))));
1720 a = C->callAsConstructor(ctorArgument, 1);
1722
1723 // We check exceptions above, and only after doing so, check the array's validity after construction.
1724 if (!::validateTypedArray(a) || (a->getLength() < iterableLength))
1725 return scope.engine->throwTypeError();
1726
1727
1728 // The loop below traverses the iterator, and puts elements into the created array.
1729 ScopedValue mappedValue(scope, Value::undefinedValue());
1730 for (qint64 k = 0; k < iterableLength; ++k) {
1731 done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
1732 if (scope.hasException())
1733 return Runtime::IteratorClose::call(scope.engine, iterator);
1734
1735 if (mapfn) {
1736 mapArguments[0] = *nextValue;
1737 mapArguments[1] = Value::fromDouble(k);
1738 mappedValue = mapfn->call(thisArg, mapArguments, 2);
1739 if (scope.hasException())
1740 return Runtime::IteratorClose::call(scope.engine, iterator);
1741 } else {
1742 mappedValue = *nextValue;
1743 }
1744
1745 a->put(k, mappedValue);
1746 if (scope.hasException())
1747 return Runtime::IteratorClose::call(scope.engine, iterator);
1748 }
1749 return a.asReturnedValue();
1750 } else {
1751 // Array-like fallback. We request elements by index, and put them into the created array.
1752 ScopedObject arrayLike(scope, argv[0].toObject(scope.engine));
1753 if (!arrayLike)
1754 return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow()));
1755
1756 int len = arrayLike->getLength();
1758
1759 // Getting the length may throw, and must do so before we check the constructor validity.
1760 if (!C || !C->isConstructor())
1761 return scope.engine->throwTypeError();
1762
1763 ScopedObject a(scope, Value::undefinedValue());
1764 ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(len)));
1765 a = C->callAsConstructor(ctorArgument, 1);
1767
1768 // We check exceptions above, and only after doing so, check the array's validity after construction.
1769 if (!::validateTypedArray(a) || (a->getLength() < len))
1770 return scope.engine->throwTypeError();
1771
1772 ScopedValue mappedValue(scope, Value::undefinedValue());
1773 ScopedValue kValue(scope);
1774 for (int k = 0; k < len; ++k) {
1775 kValue = arrayLike->get(k);
1777
1778 if (mapfn) {
1779 mapArguments[0] = kValue;
1780 mapArguments[1] = Value::fromDouble(k);
1781 mappedValue = mapfn->call(thisArg, mapArguments, 2);
1783 } else {
1784 mappedValue = kValue;
1785 }
1786
1787 a->put(k, mappedValue);
1789 }
1790 return a.asReturnedValue();
1791 }
1792}
1793
1794void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
1795{
1796 Scope scope(engine);
1797 ctor->defineReadonlyProperty(engine->id_prototype(), *this);
1798 ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
1799 ScopedString s(scope, engine->newString(QStringLiteral("TypedArray")));
1800 ctor->defineReadonlyConfigurableProperty(engine->id_name(), s);
1801 s = scope.engine->newString(QStringLiteral("of"));
1802 ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of);
1803 s = scope.engine->newString(QStringLiteral("from"));
1804 ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_from, 1);
1805 ctor->addSymbolSpecies();
1806
1807 defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
1808 defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
1809 defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
1810 defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
1811
1812 defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2);
1813 defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
1814 defineDefaultProperty(QStringLiteral("every"), method_every, 1);
1815 defineDefaultProperty(QStringLiteral("fill"), method_fill, 1);
1816 defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
1817 defineDefaultProperty(QStringLiteral("find"), method_find, 1);
1818 defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
1819 defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
1820 defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
1821 defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
1822 defineDefaultProperty(QStringLiteral("join"), method_join, 1);
1823 defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
1824 defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
1825 defineDefaultProperty(QStringLiteral("map"), method_map, 1);
1826 defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
1827 defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
1828 defineDefaultProperty(QStringLiteral("reverse"), method_reverse, 0);
1829 defineDefaultProperty(QStringLiteral("some"), method_some, 1);
1830 defineDefaultProperty(QStringLiteral("set"), method_set, 1);
1831 defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
1832 defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 2);
1833 defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
1834 ScopedObject f(scope, engine->arrayPrototype()->get(engine->id_toString()));
1835 defineDefaultProperty(engine->id_toString(), f);
1836
1837 ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
1838 ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
1839 defineDefaultProperty(QStringLiteral("values"), values);
1840 defineDefaultProperty(engine->symbol_iterator(), values);
1841
1842 defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr);
1843}
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)