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