Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qv4typedarray_p.h"
++ #include "qv4arrayiterator_p.h"
++ #include "qv4arraybuffer_p.h"
++ #include "qv4symbol_p.h"
++ #include "qv4runtime_p.h"
++ #include <QtCore/qatomic.h>
++
++ #include <cmath>
++
++ using namespace QV4;
++
++ DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor);
++ DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype);
++ DEFINE_OBJECT_VTABLE(TypedArrayCtor);
++ DEFINE_OBJECT_VTABLE(TypedArrayPrototype);
++ DEFINE_OBJECT_VTABLE(TypedArray);
++
++ Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)NTypedArrayTypes);
++
++ static inline int toInt32(Value v)
++ {
++ Q_ASSERT(v.isNumber());
++ if (v.isInteger())
++ return v.integerValue();
++ return QJSNumberCoercion::toInteger(v.doubleValue());
++ }
++
++ static inline double toDouble(Value v)
++ {
++ Q_ASSERT(v.isNumber());
++ if (v.isInteger())
++ return v.integerValue();
++ return v.doubleValue();
++ }
++
++ struct ClampedUInt8 {
++ quint8 c;
++ };
++
++ namespace {
++ template <typename T>
++ ReturnedValue typeToValue(T t) {
++ return Encode(t);
++ }
++
++ template <>
++ ReturnedValue typeToValue(ClampedUInt8 t) {
++ return Encode(t.c);
++ }
++
++ template <typename T>
++ T valueToType(Value value)
++ {
++ Q_ASSERT(value.isNumber());
++ int n = toInt32(value);
++ return static_cast<T>(n);
++ }
++
++ template <>
++ ClampedUInt8 valueToType(Value value)
++ {
++ Q_ASSERT(value.isNumber());
++ if (value.isInteger())
++ return { static_cast<quint8>(qBound(0, value.integerValue(), 255)) };
++ Q_ASSERT(value.isDouble());
++ double d = value.doubleValue();
++ // ### is there a way to optimise this?
++ if (d <= 0 || std::isnan(d))
++ return { 0 };
++ if (d >= 255)
++ return { 255 };
++ double f = std::floor(d);
++ if (f + 0.5 < d)
++ return { (quint8)(f + 1) };
++ if (d < f + 0.5)
++ return { (quint8)(f) };
++ if (int(f) % 2)
++ // odd number
++ return { (quint8)(f + 1) };
++ return { (quint8)(f) };
++ }
++
++ template <>
++ float valueToType(Value value)
++ {
++ Q_ASSERT(value.isNumber());
++ double d = toDouble(value);
++ return static_cast<float>(d);
++ }
++
++ template <>
++ double valueToType(Value value)
++ {
++ Q_ASSERT(value.isNumber());
++ return toDouble(value);
++ }
++
++ template <typename T>
++ ReturnedValue readValue(const char *data) {
++ return typeToValue(*reinterpret_cast<const T *>(data));
++ }
++ template <typename T>
++ void writeValue(char *data, Value value)
++ {
++ *reinterpret_cast<T *>(data) = valueToType<T>(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicAdd(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndAddOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicAnd(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndAndOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicExchange(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndStoreOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicOr(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndOrOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicSub(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndSubOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicXor(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ value = QAtomicOps<T>::fetchAndXorOrdered(*mem, value);
++ return typeToValue(value);
++ }
++
++ template <typename T>
++ ReturnedValue atomicCompareExchange(char *data, Value expected, Value v)
++ {
++ T value = valueToType<T>(v);
++ T exp = valueToType<T>(expected);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ T old;
++ QAtomicOps<T>::testAndSetOrdered(*mem, exp, value, &old);
++ return typeToValue(old);
++ }
++
++ template <typename T>
++ ReturnedValue atomicLoad(char *data)
++ {
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ T val = QAtomicOps<T>::loadRelaxed(*mem);
++ return typeToValue(val);
++ }
++
++ template <typename T>
++ ReturnedValue atomicStore(char *data, Value v)
++ {
++ T value = valueToType<T>(v);
++ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
++ QAtomicOps<T>::storeRelaxed(*mem, value);
++ return typeToValue(value);
++ }
++
++ } // namespace
++
++ template<typename T>
++ constexpr TypedArrayOperations TypedArrayOperations::create(const char *name)
++ {
++ return { sizeof(T),
++ name,
++ ::readValue<T>,
++ ::writeValue<T>,
++ { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr },
++ nullptr,
++ nullptr,
++ nullptr
++ };
++ }
++
++ template<typename T>
++ constexpr TypedArrayOperations TypedArrayOperations::createWithAtomics(const char *name)
++ {
++ return { sizeof(T),
++ name,
++ ::readValue<T>,
++ ::writeValue<T>,
++ { ::atomicAdd<T>, ::atomicAnd<T>, ::atomicExchange<T>, ::atomicOr<T>, ::atomicSub<T>, ::atomicXor<T> },
++ ::atomicCompareExchange<T>,
++ ::atomicLoad<T>,
++ ::atomicStore<T>
++ };
++ }
++
++ const TypedArrayOperations operations[NTypedArrayTypes] = {
++ #ifdef Q_ATOMIC_INT8_IS_SUPPORTED
++ TypedArrayOperations::createWithAtomics<qint8>("Int8Array"),
++ TypedArrayOperations::createWithAtomics<quint8>("Uint8Array"),
++ #else
++ TypedArrayOperations::create<qint8>("Int8Array"),
++ TypedArrayOperations::create<quint8>("Uint8Array"),
++ #endif
++ TypedArrayOperations::createWithAtomics<qint16>("Int16Array"),
++ TypedArrayOperations::createWithAtomics<quint16>("Uint16Array"),
++ TypedArrayOperations::createWithAtomics<qint32>("Int32Array"),
++ TypedArrayOperations::createWithAtomics<quint32>("Uint32Array"),
++ TypedArrayOperations::create<ClampedUInt8>("Uint8ClampedArray"),
++ TypedArrayOperations::create<float>("Float32Array"),
++ TypedArrayOperations::create<double>("Float64Array")
++ };
++
++
++ void Heap::TypedArrayCtor::init(QV4::ExecutionEngine *engine, TypedArray::Type t)
++ {
++ Heap::FunctionObject::init(engine, QLatin1String(operations[t].name));
++ type = t;
++ }
++
++ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
++ {
++ Scope scope(f->engine());
++ const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f);
++
++ auto updateProto = [=](Scope &scope, Scoped<TypedArray> &a) {
++ if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
++ const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
++ ScopedObject o(scope, nt->protoProperty());
++ if (o)
++ a->setPrototypeOf(o);
++ }
++ };
++
++ if (!argc || !argv[0].isObject()) {
++ // ECMA 6 22.2.1.1
++ const double l = argc ? argv[0].toInteger() : 0;
++ if (scope.hasException())
++ return Encode::undefined();
++ if (l < 0 || l > std::numeric_limits<int>::max())
++ return scope.engine->throwRangeError(QLatin1String("Index out of range."));
++
++ const double byteLength = l * operations[that->d()->type].bytesPerElement;
++
++ // TODO: This is an artificial restriction due to the fact that we store the byteLength in
++ // uint below. We should allow up to INT_MAX elements of any size.
++ if (byteLength > std::numeric_limits<uint>::max())
++ return scope.engine->throwRangeError(QLatin1String("Index out of range."));
++
++ Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(size_t(byteLength)));
++ if (scope.hasException())
++ return Encode::undefined();
++
++ Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
++ array->d()->buffer.set(scope.engine, buffer->d());
++ array->d()->byteLength = byteLength;
++ array->d()->byteOffset = 0;
++
++ updateProto(scope, array);
++ return array.asReturnedValue();
++ }
++ Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Value::undefinedValue());
++ if (!!typedArray) {
++ // ECMA 6 22.2.1.2
++ Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer);
++ if (!buffer || buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ uint srcElementSize = typedArray->bytesPerElement();
++ uint destElementSize = operations[that->d()->type].bytesPerElement;
++ uint byteLength = typedArray->byteLength();
++ uint destByteLength = byteLength*destElementSize/srcElementSize;
++
++ Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength));
++ if (scope.hasException())
++ return Encode::undefined();
++
++ Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
++ array->d()->buffer.set(scope.engine, newBuffer->d());
++ array->d()->byteLength = destByteLength;
++ array->d()->byteOffset = 0;
++
++ const char *src = buffer->constArrayData() + typedArray->byteOffset();
++ char *dest = newBuffer->arrayData();
++
++ // check if src and new type have the same size. In that case we can simply memcpy the data
++ if (srcElementSize == destElementSize) {
++ memcpy(dest, src, byteLength);
++ } else {
++ // not same size, we need to loop
++ uint l = typedArray->length();
++ TypedArrayOperations::Read read = typedArray->d()->type->read;
++ TypedArrayOperations::Write write =array->d()->type->write;
++ for (uint i = 0; i < l; ++i) {
++ Value val;
++ val.setRawValue(read(src + i*srcElementSize));
++ write(dest + i*destElementSize, val);
++ }
++ }
++
++ updateProto(scope, array);
++ return array.asReturnedValue();
++ }
++ Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
++ if (!!buffer) {
++ // ECMA 6 22.2.1.4
++
++ double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0;
++
++ if (buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint byteOffset = (uint)dbyteOffset;
++ uint elementSize = operations[that->d()->type].bytesPerElement;
++ if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->arrayDataLength())
++ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset"));
++
++ uint byteLength;
++ if (argc < 3 || argv[2].isUndefined()) {
++ byteLength = buffer->arrayDataLength() - byteOffset;
++ if (buffer->arrayDataLength() < byteOffset || byteLength % elementSize)
++ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
++ } else {
++ double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX);
++ if (scope.hasException())
++ return Encode::undefined();
++ if (buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ l *= elementSize;
++ if (buffer->arrayDataLength() - byteOffset < l)
++ return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
++ byteLength = (uint)l;
++ }
++
++ Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
++ array->d()->buffer.set(scope.engine, buffer->d());
++ array->d()->byteLength = byteLength;
++ array->d()->byteOffset = byteOffset;
++
++ updateProto(scope, array);
++ return array.asReturnedValue();
++ }
++
++ // ECMA 6 22.2.1.3
++
++ ScopedObject o(scope, argc ? argv[0] : Value::undefinedValue());
++ uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX);
++ if (scope.hasException())
++ return scope.engine->throwTypeError();
++
++ uint elementSize = operations[that->d()->type].bytesPerElement;
++ size_t bufferSize;
++ if (qMulOverflow(size_t(l), size_t(elementSize), &bufferSize))
++ return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length"));
++ Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize));
++ if (scope.hasException())
++ return Encode::undefined();
++
++ Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
++ array->d()->buffer.set(scope.engine, newBuffer->d());
++ array->d()->byteLength = l * elementSize;
++ array->d()->byteOffset = 0;
++
++ uint idx = 0;
++ char *b = newBuffer->arrayData();
++ ScopedValue val(scope);
++ while (idx < l) {
++ val = o->get(idx);
++ val = val->convertedToNumber();
++ if (scope.hasException())
++ return Encode::undefined();
++ array->d()->type->write(b, val);
++ if (scope.hasException())
++ return Encode::undefined();
++ ++idx;
++ b += elementSize;
++ }
++
++ updateProto(scope, array);
++ return array.asReturnedValue();
++ }
++
++ ReturnedValue TypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
++ {
++ return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid"));
++ }
++
++ void Heap::TypedArray::init(Type t)
++ {
++ Object::init();
++ type = operations + static_cast<int>(t);
++ arrayType = static_cast<int>(t);
++ }
++
++ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
++ {
++ Scope scope(e);
++ Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + static_cast<int>(t)));
++ return e->memoryManager->allocObject<TypedArray>(ic->d(), t);
++ }
++
++ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
++ {
++ const bool isArrayIndex = id.isArrayIndex();
++ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
++ return Object::virtualGet(m, id, receiver, hasProperty);
++
++ Scope scope(static_cast<const Object *>(m)->engine());
++ Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
++ if (a->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ if (!isArrayIndex || id.asArrayIndex() >= a->length()) {
++ if (hasProperty)
++ *hasProperty = false;
++ return Encode::undefined();
++ }
++
++ uint bytesPerElement = a->bytesPerElement();
++ uint byteOffset = a->byteOffset() + id.asArrayIndex() * bytesPerElement;
++ Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength());
++
++ if (hasProperty)
++ *hasProperty = true;
++ return a->d()->type->read(a->constArrayData() + byteOffset);
++ }
++
++ bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
++ {
++ const bool isArrayIndex = id.isArrayIndex();
++ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
++ return Object::virtualHasProperty(m, id);
++
++ const TypedArray *a = static_cast<const TypedArray *>(m);
++ if (a->hasDetachedArrayData()) {
++ a->engine()->throwTypeError();
++ return false;
++ }
++ return isArrayIndex && id.asArrayIndex() < a->length();
++ }
++
++ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
++ {
++ if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString())
++ return Object::virtualGetOwnProperty(m, id, p);
++
++ bool hasProperty = false;
++ ReturnedValue v = virtualGet(m, id, m, &hasProperty);
++ if (p)
++ p->value = v;
++ return hasProperty ? Attr_NotConfigurable : PropertyAttributes();
++ }
++
++ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
++ {
++ const bool isArrayIndex = id.isArrayIndex();
++ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
++ return Object::virtualPut(m, id, value, receiver);
++
++ ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
++ if (v4->hasException)
++ return false;
++
++ Scope scope(v4);
++ Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m));
++ if (a->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ if (!isArrayIndex)
++ return false;
++
++ const uint index = id.asArrayIndex();
++ if (index >= a->length())
++ return false;
++
++ uint bytesPerElement = a->bytesPerElement();
++ uint byteOffset = a->byteOffset() + index * bytesPerElement;
++ Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength());
++
++ Value v = Value::fromReturnedValue(value.convertedToNumber());
++ if (scope.hasException() || a->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ a->d()->type->write(a->arrayData() + byteOffset, v);
++ return true;
++ }
++
++ bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
++ {
++ if (!id.isArrayIndex()) {
++ return !id.isCanonicalNumericIndexString()
++ && Object::virtualDefineOwnProperty(m, id, p, attrs);
++ }
++
++ const uint index = id.asArrayIndex();
++ TypedArray *a = static_cast<TypedArray *>(m);
++ if (index >= a->length() || attrs.isAccessor())
++ return false;
++
++ if (attrs.hasConfigurable() && attrs.isConfigurable())
++ return false;
++ if (attrs.hasEnumerable() && !attrs.isEnumerable())
++ return false;
++ if (attrs.hasWritable() && !attrs.isWritable())
++ return false;
++ if (!p->value.isEmpty()) {
++ ExecutionEngine *engine = a->engine();
++
++ Value v = Value::fromReturnedValue(p->value.convertedToNumber());
++ if (engine->hasException || a->hasDetachedArrayData())
++ return engine->throwTypeError();
++ uint bytesPerElement = a->bytesPerElement();
++ uint byteOffset = a->byteOffset() + index * bytesPerElement;
++ Q_ASSERT(byteOffset + bytesPerElement <= a->arrayDataLength());
++ a->d()->type->write(a->arrayData() + byteOffset, v);
++ }
++ return true;
++ }
++
++ namespace {
++ struct TypedArrayOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
++ {
++ ~TypedArrayOwnPropertyKeyIterator() override = default;
++ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
++
++ };
++ } // namespace
++
++ PropertyKey TypedArrayOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
++ {
++ const TypedArray *a = static_cast<const TypedArray *>(o);
++ if (arrayIndex < a->length()) {
++ if (attrs)
++ *attrs = Attr_NotConfigurable;
++ PropertyKey id = PropertyKey::fromArrayIndex(arrayIndex);
++ if (pd) {
++ bool hasProperty = false;
++ pd->value = TypedArray::virtualGet(a, id, a, &hasProperty);
++ }
++ ++arrayIndex;
++ return id;
++ }
++
++ arrayIndex = UINT_MAX;
++ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
++ }
++
++ OwnPropertyKeyIterator *TypedArray::virtualOwnPropertyKeys(const Object *m, Value *target)
++ {
++ *target = *m;
++ return new TypedArrayOwnPropertyKeyIterator();
++ }
++
++ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
++ {
++ Scope scope(engine);
++ ScopedObject o(scope);
++
++ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(3));
++ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
++ ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Value::fromInt32(operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
++ ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor());
++
++ setPrototypeOf(engine->intrinsicTypedArrayPrototype());
++ defineDefaultProperty(engine->id_constructor(), (o = ctor));
++ defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Value::fromInt32(operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ const TypedArray *v = thisObject->as<TypedArray>();
++ if (!v)
++ return v4->throwTypeError();
++
++ return v->d()->buffer->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ const TypedArray *v = thisObject->as<TypedArray>();
++ if (!v)
++ return v4->throwTypeError();
++
++ if (v->hasDetachedArrayData())
++ return Encode(0);
++
++ return Encode(v->byteLength());
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ const TypedArray *v = thisObject->as<TypedArray>();
++ if (!v)
++ return v4->throwTypeError();
++
++ if (v->hasDetachedArrayData())
++ return Encode(0);
++
++ return Encode(v->byteOffset());
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ ExecutionEngine *v4 = b->engine();
++ const TypedArray *v = thisObject->as<TypedArray>();
++ if (!v)
++ return v4->throwTypeError();
++
++ if (v->hasDetachedArrayData())
++ return Encode(0);
++
++ return Encode(v->length());
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(f);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ if (!argc)
++ return instance->asReturnedValue();
++
++ const double len = instance->length();
++ Q_ASSERT(std::isfinite(len));
++
++ const double target = argv[0].toInteger();
++
++ const double start = (argc > 1)
++ ? argv[1].toInteger()
++ : 0;
++
++ const double end = (argc > 2 && !argv[2].isUndefined())
++ ? argv[2].toInteger()
++ : len;
++
++ const double fin = end < 0
++ ? std::max(len + end, 0.0)
++ : std::min(end, len);
++
++ const qsizetype from = start < 0
++ ? std::max(len + start, 0.0)
++ : std::min(start, len);
++
++ const qsizetype to = target < 0
++ ? std::max(len + target, 0.0)
++ : std::min(target, len);
++
++ const qsizetype count = std::min(fin - from, len - to);
++
++ if (count <= 0)
++ return instance->asReturnedValue();
++
++ if (from != to) {
++ int elementSize = instance->bytesPerElement();
++ char *data = instance->arrayData() + instance->byteOffset();
++ memmove(data + to * elementSize, data + from * elementSize, count * elementSize);
++ }
++
++ return instance->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
++ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
++ return ao->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++ ScopedValue r(scope);
++ Value *arguments = scope.constructUndefined(3);
++
++ const char *data = v->constArrayData();
++ uint bytesPerElement = v->bytesPerElement();
++ uint byteOffset = v->byteOffset();
++
++ bool ok = true;
++ for (uint k = 0; ok && k < len; ++k) {
++ if (v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ arguments[0] = v->d()->type->read(data + byteOffset + k * bytesPerElement);
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = v;
++ r = callback->call(that, arguments, 3);
++ CHECK_EXCEPTION();
++ ok = r->toBoolean();
++ }
++ return Encode(ok);
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++ double dlen = len;
++ double relativeStart = argc > 1 ? argv[1].toInteger() : 0.;
++ double relativeEnd = len;
++ if (argc > 2 && !argv[2].isUndefined())
++ relativeEnd = argv[2].toInteger();
++
++ uint k = 0;
++ uint fin = 0;
++
++ if (relativeStart < 0) {
++ k = static_cast<uint>(std::max(len+relativeStart, 0.));
++ } else {
++ k = static_cast<uint>(std::min(relativeStart, dlen));
++ }
++
++ if (relativeEnd < 0) {
++ fin = static_cast<uint>(std::max(len + relativeEnd, 0.));
++ } else {
++ fin = static_cast<uint>(std::min(relativeEnd, dlen));
++ }
++
++ if (scope.hasException() || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ char *data = v->arrayData();
++ uint bytesPerElement = v->bytesPerElement();
++ uint byteOffset = v->byteOffset();
++
++ Value value;
++ if (!argc)
++ value.setDouble(std::numeric_limits<double>::quiet_NaN());
++ else if (argv[0].isNumber())
++ value = argv[0];
++ else
++ value.setDouble(argv[0].toNumber());
++
++ while (k < fin) {
++ v->d()->type->write(data + byteOffset + k * bytesPerElement, value);
++ k++;
++ }
++
++ return v.asReturnedValue();
++ }
++
++ static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *instance, uint len)
++ {
++ const FunctionObject *constructor = instance->speciesConstructor(scope, scope.engine->typedArrayCtors + instance->d()->arrayType);
++ if (!constructor) {
++ scope.engine->throwTypeError();
++ return nullptr;
++ }
++
++ Value *arguments = scope.constructUndefined(1);
++ arguments[0] = Encode(len);
++ Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1));
++ if (!a || a->hasDetachedArrayData() || a->length() < len) {
++ scope.engine->throwTypeError();
++ return nullptr;
++ }
++ return a;
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue selected(scope);
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++ Value *arguments = scope.constructUndefined(3);
++ Value *list = arguments;
++
++ uint to = 0;
++ for (uint k = 0; k < len; ++k) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ bool exists;
++ arguments[0] = instance->get(k, &exists);
++ if (!exists)
++ continue;
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = instance;
++ selected = callback->call(that, arguments, 3);
++ CHECK_EXCEPTION();
++ if (selected->toBoolean()) {
++ ++arguments;
++ scope.constructUndefined(1);
++ ++to;
++ }
++ }
++
++ TypedArray *a = typedArraySpeciesCreate(scope, instance, to);
++ if (!a)
++ return Encode::undefined();
++
++ for (uint i = 0; i < to; ++i)
++ a->put(i, list[i]);
++
++ return a->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++
++ if (!argc || !argv[0].isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue result(scope);
++ Value *arguments = scope.constructUndefined(3);
++
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++
++ for (uint k = 0; k < len; ++k) {
++ if (v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ arguments[0] = v->get(k);
++ CHECK_EXCEPTION();
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = v;
++ result = callback->call(that, arguments, 3);
++
++ CHECK_EXCEPTION();
++ if (result->toBoolean())
++ return arguments[0].asReturnedValue();
++ }
++
++ RETURN_UNDEFINED();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++
++ if (!argc || !argv[0].isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue result(scope);
++ Value *arguments = scope.constructUndefined(3);
++
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++
++ for (uint k = 0; k < len; ++k) {
++ if (v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ arguments[0] = v->get(k);
++ CHECK_EXCEPTION();
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = v;
++ result = callback->call(that, arguments, 3);
++
++ CHECK_EXCEPTION();
++ if (result->toBoolean())
++ return Encode(k);
++ }
++
++ return Encode(-1);
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++ Value *arguments = scope.constructUndefined(3);
++
++ for (uint k = 0; k < len; ++k) {
++ if (v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ bool exists;
++ arguments[0] = v->get(k, &exists);
++ if (!exists)
++ continue;
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = v;
++ callback->call(that, arguments, 3);
++ }
++ RETURN_UNDEFINED();
++ }
++
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++ if (len == 0) {
++ return Encode(false);
++ }
++
++ double n = 0;
++ if (argc > 1 && !argv[1].isUndefined()) {
++ n = argv[1].toInteger();
++ }
++
++ double k = 0;
++ if (n >= 0) {
++ k = n;
++ } else {
++ k = len + n;
++ if (k < 0) {
++ k = 0;
++ }
++ }
++
++ while (k < len) {
++ ScopedValue val(scope, v->get(k));
++ if (val->sameValueZero(argv[0])) {
++ return Encode(true);
++ }
++ k++;
++ }
++
++ return Encode(false);
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = v->length();
++ if (!len)
++ return Encode(-1);
++
++ ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
++ uint fromIndex = 0;
++
++ if (argc >= 2) {
++ double f = argv[1].toInteger();
++ CHECK_EXCEPTION();
++ if (f >= len)
++ return Encode(-1);
++ if (f < 0)
++ f = qMax(len + f, 0.);
++ fromIndex = (uint) f;
++ }
++
++ if (v->isStringObject()) {
++ ScopedValue value(scope);
++ for (uint k = fromIndex; k < len; ++k) {
++ bool exists;
++ value = v->get(k, &exists);
++ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
++ return Encode(k);
++ }
++ return Encode(-1);
++ }
++
++ ScopedValue value(scope);
++
++ for (uint i = fromIndex; i < len; ++i) {
++ bool exists;
++ value = v->get(i, &exists);
++ CHECK_EXCEPTION();
++ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
++ return Encode(i);
++ }
++ return Encode(-1);
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_join(
++ const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(functionObject);
++ Scoped<TypedArray> typedArray(scope, thisObject);
++ if (!typedArray || typedArray->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ // We cannot optimize the resolution of the argument away if length is 0.
++ // It may have side effects.
++ ScopedValue argument(scope, argc ? argv[0] : Value::undefinedValue());
++ const QString separator = argument->isUndefined()
++ ? QStringLiteral(",")
++ : argument->toQString();
++
++ const quint32 length = typedArray->length();
++ if (!length)
++ return Encode(scope.engine->newString());
++
++ QString result;
++
++ ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
++ ScopedValue value(scope, typedArray->get(name));
++ if (!value->isNullOrUndefined())
++ result = value->toQString();
++
++ for (quint32 i = 1; i < length; ++i) {
++ result += separator;
++
++ name = Value::fromDouble(i).toString(scope.engine);
++ value = typedArray->get(name);
++ CHECK_EXCEPTION();
++
++ if (!value->isNullOrUndefined())
++ result += value->toQString();
++ }
++
++ return Encode(scope.engine->newString(result));
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
++ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
++ return ao->asReturnedValue();
++ }
++
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++ if (!len)
++ return Encode(-1);
++
++ ScopedValue searchValue(scope);
++ uint fromIndex = len;
++
++ if (argc >= 1)
++ searchValue = argv[0];
++ else
++ searchValue = Value::undefinedValue();
++
++ if (argc >= 2) {
++ double f = argv[1].toInteger();
++ CHECK_EXCEPTION();
++ if (f > 0)
++ f = qMin(f, (double)(len - 1));
++ else if (f < 0) {
++ f = len + f;
++ if (f < 0)
++ return Encode(-1);
++ }
++ fromIndex = (uint) f + 1;
++ }
++
++ ScopedValue value(scope);
++ for (uint k = fromIndex; k > 0;) {
++ --k;
++ bool exists;
++ value = instance->get(k, &exists);
++ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
++ return Encode(k);
++ }
++ return Encode(-1);
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ TypedArray *a = typedArraySpeciesCreate(scope, instance, len);
++ if (!a)
++ return Encode::undefined();
++
++ ScopedValue v(scope);
++ ScopedValue mapped(scope);
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++ Value *arguments = scope.constructUndefined(3);
++
++ for (uint k = 0; k < len; ++k) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ arguments[0] = instance->get(k);
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = instance;
++ mapped = callback->call(that, arguments, 3);
++ CHECK_EXCEPTION();
++ a->put(k, mapped);
++ }
++ return a->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ uint k = 0;
++ ScopedValue acc(scope);
++ ScopedValue v(scope);
++
++ if (argc > 1) {
++ acc = argv[1];
++ } else {
++ bool kPresent = false;
++ while (k < len && !kPresent) {
++ v = instance->get(k, &kPresent);
++ if (kPresent)
++ acc = v;
++ ++k;
++ }
++ if (!kPresent)
++ THROW_TYPE_ERROR();
++ }
++
++ Value *arguments = scope.constructUndefined(4);
++
++ while (k < len) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ bool kPresent;
++ v = instance->get(k, &kPresent);
++ if (kPresent) {
++ arguments[0] = acc;
++ arguments[1] = v;
++ arguments[2] = Value::fromDouble(k);
++ arguments[3] = instance;
++ acc = callback->call(nullptr, arguments, 4);
++ CHECK_EXCEPTION();
++ }
++ ++k;
++ }
++ return acc->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ if (len == 0) {
++ if (argc == 1)
++ THROW_TYPE_ERROR();
++ return argv[1].asReturnedValue();
++ }
++
++ uint k = len;
++ ScopedValue acc(scope);
++ ScopedValue v(scope);
++ if (argc > 1) {
++ acc = argv[1];
++ } else {
++ bool kPresent = false;
++ while (k > 0 && !kPresent) {
++ v = instance->get(k - 1, &kPresent);
++ if (kPresent)
++ acc = v;
++ --k;
++ }
++ if (!kPresent)
++ THROW_TYPE_ERROR();
++ }
++
++ Value *arguments = scope.constructUndefined(4);
++
++ while (k > 0) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ bool kPresent;
++ v = instance->get(k - 1, &kPresent);
++ if (kPresent) {
++ arguments[0] = acc;
++ arguments[1] = v;
++ arguments[2] = Value::fromDouble(k - 1);
++ arguments[3] = instance;
++ acc = callback->call(nullptr, arguments, 4);
++ CHECK_EXCEPTION();
++ }
++ --k;
++ }
++ return acc->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint length = instance->length();
++
++ int lo = 0, hi = length - 1;
++
++ ScopedValue lval(scope);
++ ScopedValue hval(scope);
++ for (; lo < hi; ++lo, --hi) {
++ bool loExists, hiExists;
++ lval = instance->get(lo, &loExists);
++ hval = instance->get(hi, &hiExists);
++ Q_ASSERT(hiExists && loExists);
++ bool ok;
++ ok = instance->put(lo, hval);
++ Q_ASSERT(ok);
++ ok = instance->put(hi, lval);
++ Q_ASSERT(ok);
++ }
++ return instance->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ if (!argc || !argv->isFunctionObject())
++ THROW_TYPE_ERROR();
++ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
++
++ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
++ ScopedValue result(scope);
++ Value *arguments = scope.constructUndefined(3);
++
++ for (uint k = 0; k < len; ++k) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ bool exists;
++ arguments[0] = instance->get(k, &exists);
++ if (!exists)
++ continue;
++
++ arguments[1] = Value::fromDouble(k);
++ arguments[2] = instance;
++ result = callback->call(that, arguments, 3);
++ CHECK_EXCEPTION();
++ if (result->toBoolean())
++ return Encode(true);
++ }
++ return Encode(false);
++ }
++
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> v(scope, thisObject);
++ if (!v || v->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
++ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
++ return ao->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> a(scope, *thisObject);
++ if (!a)
++ return scope.engine->throwTypeError();
++ Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
++
++ double doffset = argc >= 2 ? argv[1].toInteger() : 0;
++ if (scope.hasException())
++ RETURN_UNDEFINED();
++ if (!buffer || buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ if (doffset < 0 || doffset >= UINT_MAX)
++ RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
++ uint offset = (uint)doffset;
++ uint elementSize = a->bytesPerElement();
++
++ Scoped<TypedArray> srcTypedArray(scope, argv[0]);
++ if (!srcTypedArray) {
++ // src is a regular object
++ ScopedObject o(scope, argv[0].toObject(scope.engine));
++ if (scope.hasException() || !o)
++ return scope.engine->throwTypeError();
++
++ double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber();
++ uint l = (uint)len;
++ if (scope.hasException() || l != len)
++ return scope.engine->throwTypeError();
++
++ const uint aLength = a->length();
++ if (offset > aLength || l > aLength - offset)
++ RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
++
++ uint idx = 0;
++ if (buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ char *b = buffer->arrayData() + a->byteOffset() + offset*elementSize;
++ ScopedValue val(scope);
++ while (idx < l) {
++ val = o->get(idx);
++ if (scope.hasException())
++ return Encode::undefined();
++ val = val->convertedToNumber();
++ if (scope.hasException() || buffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ a->d()->type->write(b, val);
++ if (scope.hasException())
++ RETURN_UNDEFINED();
++ ++idx;
++ b += elementSize;
++ }
++ RETURN_UNDEFINED();
++ }
++
++ // src is a typed array
++ Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
++ if (!srcBuffer || srcBuffer->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint l = srcTypedArray->length();
++
++ const uint aLength = a->length();
++ if (offset > aLength || l > aLength - offset)
++ RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
++
++ char *dest = buffer->arrayData() + a->byteOffset() + offset*elementSize;
++ const char *src = srcBuffer->d()->constArrayData() + srcTypedArray->byteOffset();
++ if (srcTypedArray->d()->type == a->d()->type) {
++ // same type of typed arrays, use memmove (as srcbuffer and buffer could be the same)
++ memmove(dest, src, srcTypedArray->byteLength());
++ RETURN_UNDEFINED();
++ }
++
++ char *srcCopy = nullptr;
++ if (buffer->d() == srcBuffer->d()) {
++ // same buffer, need to take a temporary copy, to not run into problems
++ srcCopy = new char[srcTypedArray->byteLength()];
++ memcpy(srcCopy, src, srcTypedArray->byteLength());
++ src = srcCopy;
++ }
++
++ // typed arrays of different kind, need to manually loop
++ uint srcElementSize = srcTypedArray->bytesPerElement();
++ TypedArrayOperations::Read read = srcTypedArray->d()->type->read;
++ TypedArrayOperations::Write write = a->d()->type->write;
++ for (uint i = 0; i < l; ++i) {
++ Value val;
++ val.setRawValue(read(src + i*srcElementSize));
++ write(dest + i*elementSize, val);
++ }
++
++ if (srcCopy)
++ delete [] srcCopy;
++
++ RETURN_UNDEFINED();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++
++ double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
++ uint start;
++ if (s < 0)
++ start = (uint)qMax(len + s, 0.);
++ else if (s > len)
++ start = len;
++ else
++ start = (uint) s;
++ uint end = len;
++ if (argc > 1 && !argv[1].isUndefined()) {
++ double e = argv[1].toInteger();
++ if (e < 0)
++ end = (uint)qMax(len + e, 0.);
++ else if (e > len)
++ end = len;
++ else
++ end = (uint) e;
++ }
++ uint count = start > end ? 0 : end - start;
++
++ TypedArray *a = typedArraySpeciesCreate(scope, instance, count);
++ if (!a)
++ return Encode::undefined();
++
++ ScopedValue v(scope);
++ uint n = 0;
++ for (uint i = start; i < end; ++i) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ v = instance->get(i);
++ if (a->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ a->put(n, v);
++ ++n;
++ }
++ return a->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(builtin);
++ Scoped<TypedArray> a(scope, *thisObject);
++
++ if (!a)
++ return scope.engine->throwTypeError();
++
++ Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
++ Q_ASSERT(buffer);
++
++ int len = a->length();
++ double b = argc > 0 ? argv[0].toInteger() : 0;
++ if (b < 0)
++ b = len + b;
++ uint begin = (uint)qBound(0., b, (double)len);
++
++ double e = argc < 2 || argv[1].isUndefined() ? len : argv[1].toInteger();
++ if (e < 0)
++ e = len + e;
++ uint end = (uint)qBound(0., e, (double)len);
++ if (end < begin)
++ end = begin;
++
++ if (scope.hasException())
++ RETURN_UNDEFINED();
++
++ int newLen = end - begin;
++
++ ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, scope.engine->typedArrayCtors + a->d()->arrayType));
++ if (!constructor)
++ return scope.engine->throwTypeError();
++
++ Value *arguments = scope.constructUndefined(3);
++ arguments[0] = buffer;
++ arguments[1] = Encode(a->byteOffset() + begin * a->bytesPerElement());
++ arguments[2] = Encode(newLen);
++ a = constructor->callAsConstructor(arguments, 3);
++ if (!a || a->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ return a->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
++ {
++ Scope scope(b);
++ Scoped<TypedArray> instance(scope, thisObject);
++ if (!instance || instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++
++ uint len = instance->length();
++ const QString separator = QStringLiteral(",");
++
++ QString R;
++
++ ScopedValue v(scope);
++ ScopedString s(scope);
++
++ ScopedPropertyKey tolocaleString(scope, scope.engine->id_toLocaleString()->toPropertyKey());
++ Q_ASSERT(!scope.engine->hasException);
++
++ for (uint k = 0; k < len; ++k) {
++ if (instance->hasDetachedArrayData())
++ return scope.engine->throwTypeError();
++ if (k)
++ R += separator;
++
++ v = instance->get(k);
++ Q_ASSERT(!v->isNullOrUndefined()); // typed array cannot hold null or undefined
++
++ ScopedObject valueAsObject(scope, v->toObject(scope.engine));
++ Q_ASSERT(valueAsObject); // only null or undefined cannot be converted to object
++
++ ScopedFunctionObject function(scope, valueAsObject->get(tolocaleString));
++ if (!function)
++ return scope.engine->throwTypeError();
++
++ v = function->call(valueAsObject, nullptr, 0);
++ if (scope.hasException())
++ return Encode::undefined();
++
++ s = v->toString(scope.engine);
++ if (scope.hasException())
++ return Encode::undefined();
++
++ R += s->toQString();
++ }
++ return scope.engine->newString(R)->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
++ {
++ const TypedArray *a = thisObject->as<TypedArray>();
++ if (!a)
++ return Encode::undefined();
++
++ return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
++ }
++
++ static bool validateTypedArray(const Object *o)
++ {
++ const TypedArray *a = o->as<TypedArray>();
++ if (!a)
++ return false;
++ if (a->hasDetachedArrayData())
++ return false;
++ return true;
++ }
++
++ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(f);
++ int len = argc;
++ const Value *items = argv;
++ const FunctionObject *C = thisObject->as<FunctionObject>();
++ if (!C || !C->isConstructor())
++ return scope.engine->throwTypeError();
++
++ Value lenValue = Value::fromInt32(len);
++ ScopedObject newObj(scope, C->callAsConstructor(&lenValue, 1));
++ if (scope.hasException())
++ return Encode::undefined();
++ if (!::validateTypedArray(newObj))
++ return scope.engine->throwTypeError();
++ TypedArray *a = newObj->as<TypedArray>();
++ Q_ASSERT(a);
++ if (a->length() < static_cast<uint>(len))
++ return scope.engine->throwTypeError();
++
++ for (int k = 0; k < len; ++k) {
++ newObj->put(PropertyKey::fromArrayIndex(k), items[k]);
++ }
++ return newObj->asReturnedValue();
++ }
++
++ ReturnedValue IntrinsicTypedArrayCtor::method_from(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
++ {
++ Scope scope(f);
++ ScopedObject itemsObject(scope, argv[0]);
++ bool usingIterator = false;
++
++ ScopedFunctionObject mapfn(scope, Value::undefinedValue());
++ Value *mapArguments = nullptr;
++ if (argc > 1) {
++ mapfn = ScopedFunctionObject(scope, argv[1]);
++ if (!mapfn)
++ return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow()));
++ mapArguments = scope.constructUndefined(2);
++ }
++
++ // Iterator validity check goes after map function validity has been checked.
++ if (itemsObject) {
++ // If the object claims to support iterators, then let's try use them.
++ ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator()));
++ CHECK_EXCEPTION();
++ if (!it->isNullOrUndefined()) {
++ ScopedFunctionObject itfunc(scope, it);
++ if (!itfunc)
++ return scope.engine->throwTypeError();
++ usingIterator = true;
++ }
++ }
++
++ ScopedValue thisArg(scope);
++ if (argc > 2)
++ thisArg = argv[2];
++
++ const FunctionObject *C = thisObject->as<FunctionObject>();
++
++ if (usingIterator) {
++ // Item iteration supported, so let's go ahead and try use that.
++ CHECK_EXCEPTION();
++
++ qint64 iterableLength = 0;
++ Value *nextValue = scope.constructUndefined(1);
++ ScopedValue done(scope);
++
++ ScopedObject lengthIterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
++ CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
++ if (!lengthIterator) {
++ return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
++ }
++
++ forever {
++ // Here we calculate the length of the iterable range.
++ if (iterableLength > (static_cast<qint64>(1) << 53) - 1) {
++ ScopedValue error(scope, scope.engine->throwTypeError());
++ return Runtime::IteratorClose::call(scope.engine, lengthIterator);
++ }
++ // Retrieve the next value. If the iteration ends, we're done here.
++ done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue));
++ if (scope.hasException())
++ return Runtime::IteratorClose::call(scope.engine, lengthIterator);
++ if (done->toBoolean()) {
++ break;
++ }
++ iterableLength++;
++ }
++
++ // Constructor validity check goes after we have calculated the length, because that calculation can throw
++ // errors that are not type errors and at least the tests expect those rather than type errors.
++ if (!C || !C->isConstructor())
++ return scope.engine->throwTypeError();
++
++ ScopedObject iterator(scope, Runtime::GetIterator::call(scope.engine, itemsObject, true));
++ CHECK_EXCEPTION(); // symbol_iterator can throw.
++ if (!iterator) {
++ return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
++ }
++
++ ScopedObject a(scope, Value::undefinedValue());
++ ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(int(iterableLength))));
++ a = C->callAsConstructor(ctorArgument, 1);
++ CHECK_EXCEPTION();
++
++ // We check exceptions above, and only after doing so, check the array's validity after construction.
++ if (!::validateTypedArray(a) || (a->getLength() < iterableLength))
++ return scope.engine->throwTypeError();
++
++
++ // The loop below traverses the iterator, and puts elements into the created array.
++ ScopedValue mappedValue(scope, Value::undefinedValue());
++ for (qint64 k = 0; k < iterableLength; ++k) {
++ done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, iterator, nextValue));
++ if (scope.hasException())
++ return Runtime::IteratorClose::call(scope.engine, iterator);
++
++ if (mapfn) {
++ mapArguments[0] = *nextValue;
++ mapArguments[1] = Value::fromDouble(k);
++ mappedValue = mapfn->call(thisArg, mapArguments, 2);
++ if (scope.hasException())
++ return Runtime::IteratorClose::call(scope.engine, iterator);
++ } else {
++ mappedValue = *nextValue;
++ }
++
++ a->put(k, mappedValue);
++ if (scope.hasException())
++ return Runtime::IteratorClose::call(scope.engine, iterator);
++ }
++ return a.asReturnedValue();
++ } else {
++ // Array-like fallback. We request elements by index, and put them into the created array.
++ ScopedObject arrayLike(scope, argv[0].toObject(scope.engine));
++ if (!arrayLike)
++ return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow()));
++
++ qint64 len = arrayLike->getLength();
++ CHECK_EXCEPTION();
++
++ // Getting the length may throw, and must do so before we check the constructor validity.
++ if (!C || !C->isConstructor())
++ return scope.engine->throwTypeError();
++
++ ScopedObject a(scope, Value::undefinedValue());
++ ScopedValue ctorArgument(scope, Value::fromReturnedValue(QV4::Encode(double(len))));
++ a = C->callAsConstructor(ctorArgument, 1);
++ CHECK_EXCEPTION();
++
++ // We check exceptions above, and only after doing so, check the array's validity after construction.
++ if (!::validateTypedArray(a) || (a->getLength() < len))
++ return scope.engine->throwTypeError();
++
++ ScopedValue mappedValue(scope, Value::undefinedValue());
++ ScopedValue kValue(scope);
++ for (qint64 k = 0; k < len; ++k) {
++ kValue = arrayLike->get(k);
++ CHECK_EXCEPTION();
++
++ if (mapfn) {
++ mapArguments[0] = kValue;
++ mapArguments[1] = Value::fromDouble(k);
++ mappedValue = mapfn->call(thisArg, mapArguments, 2);
++ CHECK_EXCEPTION();
++ } else {
++ mappedValue = kValue;
++ }
++
++ a->put(k, mappedValue);
++ CHECK_EXCEPTION();
++ }
++ return a.asReturnedValue();
++ }
++ }
++
++ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
++ {
++ Scope scope(engine);
++ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
++ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
++ ScopedString s(scope, engine->newString(QStringLiteral("TypedArray")));
++ ctor->defineReadonlyConfigurableProperty(engine->id_name(), s);
++ s = scope.engine->newString(QStringLiteral("of"));
++ ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of);
++ s = scope.engine->newString(QStringLiteral("from"));
++ ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_from, 1);
++ ctor->addSymbolSpecies();
++
++ defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
++ defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
++ defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
++ defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
++
++ defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2);
++ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
++ defineDefaultProperty(QStringLiteral("every"), method_every, 1);
++ defineDefaultProperty(QStringLiteral("fill"), method_fill, 1);
++ defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
++ defineDefaultProperty(QStringLiteral("find"), method_find, 1);
++ defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
++ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
++ defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
++ defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
++ defineDefaultProperty(QStringLiteral("join"), method_join, 1);
++ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
++ defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
++ defineDefaultProperty(QStringLiteral("map"), method_map, 1);
++ defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
++ defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
++ defineDefaultProperty(QStringLiteral("reverse"), method_reverse, 0);
++ defineDefaultProperty(QStringLiteral("some"), method_some, 1);
++ defineDefaultProperty(QStringLiteral("set"), method_set, 1);
++ defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
++ defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 2);
++ defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
++ ScopedObject f(scope, engine->arrayPrototype()->get(engine->id_toString()));
++ defineDefaultProperty(engine->id_toString(), f);
++
++ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
++ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
++ defineDefaultProperty(QStringLiteral("values"), values);
++ defineDefaultProperty(engine->symbol_iterator(), values);
++
++ defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr);
++ }