aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4typedarray.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4typedarray.cpp')
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp626
1 files changed, 382 insertions, 244 deletions
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index faf7934c06..9c752f43bb 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -1,47 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#include "qv4typedarray_p.h"
#include "qv4arrayiterator_p.h"
#include "qv4arraybuffer_p.h"
-#include "qv4string_p.h"
-#include "qv4jscall_p.h"
#include "qv4symbol_p.h"
#include "qv4runtime_p.h"
#include <QtCore/qatomic.h>
@@ -63,7 +25,7 @@ static inline int toInt32(Value v)
Q_ASSERT(v.isNumber());
if (v.isInteger())
return v.integerValue();
- return Double::toInt32(v.doubleValue());
+ return QJSNumberCoercion::toInteger(v.doubleValue());
}
static inline double toDouble(Value v)
@@ -214,7 +176,7 @@ template <typename T>
ReturnedValue atomicLoad(char *data)
{
typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
- T val = QAtomicOps<T>::load(*mem);
+ T val = QAtomicOps<T>::loadRelaxed(*mem);
return typeToValue(val);
}
@@ -223,7 +185,7 @@ 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>::store(*mem, value);
+ QAtomicOps<T>::storeRelaxed(*mem, value);
return typeToValue(value);
}
@@ -274,9 +236,9 @@ const TypedArrayOperations operations[NTypedArrayTypes] = {
};
-void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t)
+void Heap::TypedArrayCtor::init(QV4::ExecutionEngine *engine, TypedArray::Type t)
{
- Heap::FunctionObject::init(scope, QLatin1String(operations[t].name));
+ Heap::FunctionObject::init(engine, QLatin1String(operations[t].name));
type = t;
}
@@ -296,18 +258,21 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
if (!argc || !argv[0].isObject()) {
// ECMA 6 22.2.1.1
- qint64 l = argc ? argv[0].toIndex() : 0;
- if (scope.engine->hasException)
+ const double l = argc ? argv[0].toInteger() : 0;
+ if (scope.hasException())
return Encode::undefined();
- // ### lift UINT_MAX restriction
- if (l < 0 || l > UINT_MAX)
+ 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."));
- uint len = (uint)l;
- if (l != len)
- scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
- uint byteLength = len * operations[that->d()->type].bytesPerElement;
- Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength));
- if (scope.engine->hasException)
+
+ 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));
@@ -322,15 +287,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
if (!!typedArray) {
// ECMA 6 22.2.1.2
Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer);
- if (!buffer || buffer->isDetachedBuffer())
+ if (!buffer || buffer->hasDetachedArrayData())
return scope.engine->throwTypeError();
- uint srcElementSize = typedArray->d()->type->bytesPerElement;
+ uint srcElementSize = typedArray->bytesPerElement();
uint destElementSize = operations[that->d()->type].bytesPerElement;
- uint byteLength = typedArray->d()->byteLength;
+ uint byteLength = typedArray->byteLength();
uint destByteLength = byteLength*destElementSize/srcElementSize;
Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength));
- if (scope.engine->hasException)
+ if (scope.hasException())
return Encode::undefined();
Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
@@ -338,8 +303,8 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
array->d()->byteLength = destByteLength;
array->d()->byteOffset = 0;
- const char *src = buffer->d()->data->data() + typedArray->d()->byteOffset;
- char *dest = newBuffer->d()->data->data();
+ 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) {
@@ -365,27 +330,27 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0;
- if (buffer->isDetachedBuffer())
+ 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->byteLength())
+ 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->byteLength() - byteOffset;
- if (buffer->byteLength() < byteOffset || byteLength % elementSize)
+ 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.engine->hasException)
+ if (scope.hasException())
return Encode::undefined();
- if (buffer->isDetachedBuffer())
+ if (buffer->hasDetachedArrayData())
return scope.engine->throwTypeError();
l *= elementSize;
- if (buffer->byteLength() - byteOffset < l)
+ if (buffer->arrayDataLength() - byteOffset < l)
return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
byteLength = (uint)l;
}
@@ -403,15 +368,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
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.engine->hasException)
+ if (scope.hasException())
return scope.engine->throwTypeError();
uint elementSize = operations[that->d()->type].bytesPerElement;
size_t bufferSize;
- if (mul_overflow(size_t(l), size_t(elementSize), &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.engine->hasException)
+ if (scope.hasException())
return Encode::undefined();
Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type));
@@ -420,15 +385,15 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
array->d()->byteOffset = 0;
uint idx = 0;
- char *b = newBuffer->d()->data->data();
+ char *b = newBuffer->arrayData();
ScopedValue val(scope);
while (idx < l) {
val = o->get(idx);
val = val->convertedToNumber();
- if (scope.engine->hasException)
+ if (scope.hasException())
return Encode::undefined();
array->d()->type->write(b, val);
- if (scope.engine->hasException)
+ if (scope.hasException())
return Encode::undefined();
++idx;
b += elementSize;
@@ -459,54 +424,48 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type
ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualGet(m, id, receiver, hasProperty);
- // fall through, with index == UINT_MAX it'll do the right thing.
Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
- if (a->d()->buffer->isDetachedBuffer())
+ if (a->hasDetachedArrayData())
return scope.engine->throwTypeError();
- if (index >= a->length()) {
+ if (!isArrayIndex || id.asArrayIndex() >= a->length()) {
if (hasProperty)
*hasProperty = false;
return Encode::undefined();
}
- uint bytesPerElement = a->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
- Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
+ 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->d()->buffer->data->data() + byteOffset);
+ return a->d()->type->read(a->constArrayData() + byteOffset);
}
bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualHasProperty(m, id);
- // fall through, with index == UINT_MAX it'll do the right thing.
const TypedArray *a = static_cast<const TypedArray *>(m);
- if (a->d()->buffer->isDetachedBuffer()) {
+ if (a->hasDetachedArrayData()) {
a->engine()->throwTypeError();
return false;
}
- if (index >= a->length())
- return false;
- return true;
+ return isArrayIndex && id.asArrayIndex() < a->length();
}
PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ if (!id.isArrayIndex() && !id.isCanonicalNumericIndexString())
return Object::virtualGetOwnProperty(m, id, p);
- // fall through, with index == UINT_MAX it'll do the right thing.
bool hasProperty = false;
ReturnedValue v = virtualGet(m, id, m, &hasProperty);
@@ -517,10 +476,9 @@ PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyK
bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
+ const bool isArrayIndex = id.isArrayIndex();
+ if (!isArrayIndex && !id.isCanonicalNumericIndexString())
return Object::virtualPut(m, id, value, receiver);
- // fall through, with index == UINT_MAX it'll do the right thing.
ExecutionEngine *v4 = static_cast<Object *>(m)->engine();
if (v4->hasException)
@@ -528,30 +486,35 @@ bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Valu
Scope scope(v4);
Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m));
- if (a->d()->buffer->isDetachedBuffer())
+ 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->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
- Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
+ 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->d()->buffer->isDetachedBuffer())
+ if (scope.hasException() || a->hasDetachedArrayData())
return scope.engine->throwTypeError();
- a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
+ a->d()->type->write(a->arrayData() + byteOffset, v);
return true;
}
bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
{
- uint index = id.asArrayIndex();
- if (index == UINT_MAX && !id.isCanonicalNumericIndexString())
- return Object::virtualDefineOwnProperty(m, id, p, attrs);
- // fall through, with index == UINT_MAX it'll do the right thing.
+ 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;
@@ -566,12 +529,12 @@ bool TypedArray::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Prop
ExecutionEngine *engine = a->engine();
Value v = Value::fromReturnedValue(p->value.convertedToNumber());
- if (engine->hasException || a->d()->buffer->isDetachedBuffer())
+ if (engine->hasException || a->hasDetachedArrayData())
return engine->throwTypeError();
- uint bytesPerElement = a->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
- Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
- a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
+ 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;
}
@@ -640,10 +603,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const Function
if (!v)
return v4->throwTypeError();
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return Encode(0);
- return Encode(v->d()->byteLength);
+ return Encode(v->byteLength());
}
ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -653,10 +616,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const Function
if (!v)
return v4->throwTypeError();
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return Encode(0);
- return Encode(v->d()->byteOffset);
+ return Encode(v->byteOffset());
}
ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -666,67 +629,66 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObje
if (!v)
return v4->throwTypeError();
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return Encode(0);
- return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
+ return Encode(v->length());
}
ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(f);
- Scoped<TypedArray> O(scope, thisObject);
- if (!O || O->d()->buffer->isDetachedBuffer())
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
if (!argc)
- return O->asReturnedValue();
+ return instance->asReturnedValue();
- qint64 len = static_cast<uint>(O->length());
+ const double len = instance->length();
+ Q_ASSERT(std::isfinite(len));
- qint64 to = static_cast<qint64>(argv[0].toInteger());
- if (to < 0)
- to = qMax(len + to, 0ll);
- else
- to = qMin(to, len);
+ const double target = argv[0].toInteger();
- qint64 from = (argc > 1) ? static_cast<qint64>(argv[1].toInteger()) : 0ll;
- if (from < 0)
- from = qMax(len + from, 0ll);
- else
- from = qMin(from, len);
-
- double fend = argv[2].toInteger();
- if (fend > len)
- fend = len;
- qint64 end = (argc > 2 && !argv[2].isUndefined()) ? static_cast<qint64>(fend) : len;
- if (end < 0)
- end = qMax(len + end, 0ll);
- else
- end = qMin(end, len);
+ const double start = (argc > 1)
+ ? argv[1].toInteger()
+ : 0;
- qint64 count = qMin(end - from, len - to);
+ const double end = (argc > 2 && !argv[2].isUndefined())
+ ? argv[2].toInteger()
+ : len;
- if (count <= 0)
- return O->asReturnedValue();
+ const double fin = end < 0
+ ? std::max(len + end, 0.0)
+ : std::min(end, len);
- if (O->d()->buffer->isDetachedBuffer())
- return scope.engine->throwTypeError();
+ 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 = O->d()->type->bytesPerElement;
- char *data = O->d()->buffer->data->data() + O->d()->byteOffset;
- memmove(data + to*elementSize, data + from*elementSize, count*elementSize);
+ int elementSize = instance->bytesPerElement();
+ char *data = instance->arrayData() + instance->byteOffset();
+ memmove(data + to * elementSize, data + from * elementSize, count * elementSize);
}
- return O->asReturnedValue();
+ 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->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
@@ -738,7 +700,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -751,13 +713,13 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b
ScopedValue r(scope);
Value *arguments = scope.alloc(3);
- const char *data = v->d()->buffer->data->data();
- uint bytesPerElement = v->d()->type->bytesPerElement;
- uint byteOffset = v->d()->byteOffset;
+ 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->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return scope.engine->throwTypeError();
arguments[0] = v->d()->type->read(data + byteOffset + k * bytesPerElement);
@@ -765,6 +727,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b
arguments[1] = Value::fromDouble(k);
arguments[2] = v;
r = callback->call(that, arguments, 3);
+ CHECK_EXCEPTION();
ok = r->toBoolean();
}
return Encode(ok);
@@ -774,7 +737,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b,
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -799,14 +762,20 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b,
fin = static_cast<uint>(std::min(relativeEnd, dlen));
}
- double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN();
- Value value = Value::fromDouble(val);
- if (scope.hasException() || v->d()->buffer->isDetachedBuffer())
+ if (scope.hasException() || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
- char *data = v->d()->buffer->data->data();
- uint bytesPerElement = v->d()->type->bytesPerElement;
- uint byteOffset = v->d()->byteOffset;
+ 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);
@@ -827,7 +796,7 @@ static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *insta
Value *arguments = scope.alloc(1);
arguments[0] = Encode(len);
Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1));
- if (!a || a->d()->buffer->isDetachedBuffer() || a->length() < len) {
+ if (!a || a->hasDetachedArrayData() || a->length() < len) {
scope.engine->throwTypeError();
return nullptr;
}
@@ -838,7 +807,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -854,7 +823,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *
uint to = 0;
for (uint k = 0; k < len; ++k) {
- if (instance->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
bool exists;
arguments[0] = instance->get(k, &exists);
@@ -864,6 +833,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *
arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
selected = callback->call(that, arguments, 3);
+ CHECK_EXCEPTION();
if (selected->toBoolean()) {
++arguments;
scope.alloc(1);
@@ -885,7 +855,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b,
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -900,7 +870,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b,
ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
for (uint k = 0; k < len; ++k) {
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return scope.engine->throwTypeError();
arguments[0] = v->get(k);
CHECK_EXCEPTION();
@@ -921,7 +891,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObjec
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -936,7 +906,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObjec
ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
for (uint k = 0; k < len; ++k) {
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return scope.engine->throwTypeError();
arguments[0] = v->get(k);
CHECK_EXCEPTION();
@@ -957,7 +927,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -970,7 +940,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
- if (v->d()->buffer->isDetachedBuffer())
+ if (v->hasDetachedArrayData())
return scope.engine->throwTypeError();
bool exists;
arguments[0] = v->get(k, &exists);
@@ -989,7 +959,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -1027,7 +997,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = v->length();
@@ -1070,58 +1040,51 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject
return Encode(-1);
}
-ReturnedValue IntrinsicTypedArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_join(
+ const FunctionObject *functionObject, const Value *thisObject, const Value *argv, int argc)
{
- Scope scope(b);
- Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ Scope scope(functionObject);
+ Scoped<TypedArray> typedArray(scope, thisObject);
+ if (!typedArray || typedArray->hasDetachedArrayData())
return scope.engine->throwTypeError();
- uint len = v->length();
-
- ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue());
+ // 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();
- QString r4;
- if (arg->isUndefined())
- r4 = QStringLiteral(",");
- else
- r4 = arg->toQString();
-
- const quint32 r2 = len;
-
- if (!r2)
+ const quint32 length = typedArray->length();
+ if (!length)
return Encode(scope.engine->newString());
- QString R;
+ QString result;
- //
- // crazy!
- //
ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
- ScopedValue r6(scope, v->get(name));
- if (!r6->isNullOrUndefined())
- R = r6->toQString();
+ ScopedValue value(scope, typedArray->get(name));
+ if (!value->isNullOrUndefined())
+ result = value->toQString();
- ScopedValue r12(scope);
- for (quint32 k = 1; k < r2; ++k) {
- R += r4;
+ for (quint32 i = 1; i < length; ++i) {
+ result += separator;
- name = Value::fromDouble(k).toString(scope.engine);
- r12 = v->get(name);
+ name = Value::fromDouble(i).toString(scope.engine);
+ value = typedArray->get(name);
CHECK_EXCEPTION();
- if (!r12->isNullOrUndefined())
- R += r12->toQString();
+ if (!value->isNullOrUndefined())
+ result += value->toQString();
}
- return Encode(scope.engine->newString(R));
+ 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->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
@@ -1134,7 +1097,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObj
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1177,7 +1140,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b,
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1196,13 +1159,14 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b,
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
- if (instance->d()->buffer->isDetachedBuffer())
+ 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();
@@ -1212,7 +1176,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1242,7 +1206,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *
Value *arguments = scope.alloc(4);
while (k < len) {
- if (instance->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
bool kPresent;
v = instance->get(k, &kPresent);
@@ -1252,6 +1216,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *
arguments[2] = Value::fromDouble(k);
arguments[3] = instance;
acc = callback->call(nullptr, arguments, 4);
+ CHECK_EXCEPTION();
}
++k;
}
@@ -1262,7 +1227,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1297,7 +1262,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj
Value *arguments = scope.alloc(4);
while (k > 0) {
- if (instance->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
bool kPresent;
v = instance->get(k - 1, &kPresent);
@@ -1307,6 +1272,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObj
arguments[2] = Value::fromDouble(k - 1);
arguments[3] = instance;
acc = callback->call(nullptr, arguments, 4);
+ CHECK_EXCEPTION();
}
--k;
}
@@ -1317,7 +1283,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint length = instance->length();
@@ -1344,7 +1310,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b,
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1358,7 +1324,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b,
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
- if (instance->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
bool exists;
arguments[0] = instance->get(k, &exists);
@@ -1368,6 +1334,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b,
arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
result = callback->call(that, arguments, 3);
+ CHECK_EXCEPTION();
if (result->toBoolean())
return Encode(true);
}
@@ -1379,7 +1346,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *
{
Scope scope(b);
Scoped<TypedArray> v(scope, thisObject);
- if (!v || v->d()->buffer->isDetachedBuffer())
+ if (!v || v->hasDetachedArrayData())
return scope.engine->throwTypeError();
Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
@@ -1396,45 +1363,46 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
double doffset = argc >= 2 ? argv[1].toInteger() : 0;
- if (scope.engine->hasException)
+ if (scope.hasException())
RETURN_UNDEFINED();
- if (!buffer || buffer->isDetachedBuffer())
+ 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->d()->type->bytesPerElement;
+ 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.engine->hasException || !o)
+ 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.engine->hasException || l != len)
+ if (scope.hasException() || l != len)
return scope.engine->throwTypeError();
- if (offset + l > a->length())
+ 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->isDetachedBuffer())
+ if (buffer->hasDetachedArrayData())
return scope.engine->throwTypeError();
- char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
+ 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->isDetachedBuffer())
+ if (scope.hasException() || buffer->hasDetachedArrayData())
return scope.engine->throwTypeError();
a->d()->type->write(b, val);
- if (scope.engine->hasException)
+ if (scope.hasException())
RETURN_UNDEFINED();
++idx;
b += elementSize;
@@ -1444,31 +1412,33 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
// src is a typed array
Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
- if (!srcBuffer || srcBuffer->isDetachedBuffer())
+ if (!srcBuffer || srcBuffer->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint l = srcTypedArray->length();
- if (offset + l > a->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->d()->data->data() + a->d()->byteOffset + offset*elementSize;
- const char *src = srcBuffer->d()->data->data() + srcTypedArray->d()->byteOffset;
+ 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->d()->byteLength);
+ 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->d()->byteLength];
- memcpy(srcCopy, src, srcTypedArray->d()->byteLength);
+ srcCopy = new char[srcTypedArray->byteLength()];
+ memcpy(srcCopy, src, srcTypedArray->byteLength());
src = srcCopy;
}
// typed arrays of different kind, need to manually loop
- uint srcElementSize = srcTypedArray->d()->type->bytesPerElement;
+ 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) {
@@ -1487,7 +1457,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1519,10 +1489,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b
ScopedValue v(scope);
uint n = 0;
for (uint i = start; i < end; ++i) {
- if (instance->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
v = instance->get(i);
- if (a->d()->buffer->isDetachedBuffer())
+ if (a->hasDetachedArrayData())
return scope.engine->throwTypeError();
a->put(n, v);
++n;
@@ -1554,7 +1524,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject
if (end < begin)
end = begin;
- if (scope.engine->hasException)
+ if (scope.hasException())
RETURN_UNDEFINED();
int newLen = end - begin;
@@ -1565,10 +1535,10 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject
Value *arguments = scope.alloc(3);
arguments[0] = buffer;
- arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
+ arguments[1] = Encode(a->byteOffset() + begin * a->bytesPerElement());
arguments[2] = Encode(newLen);
a = constructor->callAsConstructor(arguments, 3);
- if (!a || a->d()->buffer->isDetachedBuffer())
+ if (!a || a->hasDetachedArrayData())
return scope.engine->throwTypeError();
return a->asReturnedValue();
}
@@ -1577,7 +1547,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function
{
Scope scope(b);
Scoped<TypedArray> instance(scope, thisObject);
- if (!instance || instance->d()->buffer->isDetachedBuffer())
+ if (!instance || instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
uint len = instance->length();
@@ -1588,14 +1558,29 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const Function
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->d()->buffer->isDetachedBuffer())
+ if (instance->hasDetachedArrayData())
return scope.engine->throwTypeError();
if (k)
R += separator;
v = instance->get(k);
- v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
+ 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();
@@ -1619,7 +1604,7 @@ static bool validateTypedArray(const Object *o)
const TypedArray *a = o->as<TypedArray>();
if (!a)
return false;
- if (a->d()->buffer->isDetachedBuffer())
+ if (a->hasDetachedArrayData())
return false;
return true;
}
@@ -1650,6 +1635,157 @@ ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const
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.alloc(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.alloc(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()));
+
+ int 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(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 (int 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);
@@ -1659,6 +1795,8 @@ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedA
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);