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.cpp966
1 files changed, 903 insertions, 63 deletions
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index bdede94d46..456a577b52 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -43,6 +43,7 @@
#include "qv4string_p.h"
#include "qv4jscall_p.h"
#include "qv4symbol_p.h"
+#include "qv4runtime_p.h"
#include <cmath>
@@ -56,16 +57,31 @@ DEFINE_OBJECT_VTABLE(TypedArray);
Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)Heap::TypedArray::NTypes);
+static inline int toInt32(Value v)
+{
+ Q_ASSERT(v.isNumber());
+ if (v.isInteger())
+ return v.integerValue();
+ return Double::toInt32(v.doubleValue());
+}
+
+static inline double toDouble(Value v)
+{
+ Q_ASSERT(v.isNumber());
+ if (v.isInteger())
+ return v.integerValue();
+ return v.doubleValue();
+}
+
ReturnedValue Int8ArrayRead(const char *data, int index)
{
return Encode((int)(signed char)data[index]);
}
-void Int8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void Int8ArrayWrite(char *data, int index, const Value &value)
{
- signed char v = (signed char)value.toUInt32();
- if (e->hasException)
- return;
+ int n = toInt32(value);
+ signed char v = static_cast<signed char>(n);
data[index] = v;
}
@@ -74,23 +90,22 @@ ReturnedValue UInt8ArrayRead(const char *data, int index)
return Encode((int)(unsigned char)data[index]);
}
-void UInt8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void UInt8ArrayWrite(char *data, int index, const Value &value)
{
- unsigned char v = (unsigned char)value.toUInt32();
- if (e->hasException)
- return;
+ int n = toInt32(value);
+ unsigned char v = static_cast<unsigned char>(uint(n));
data[index] = v;
}
-void UInt8ClampedArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void UInt8ClampedArrayWrite(char *data, int index, const Value &value)
{
+ Q_ASSERT(value.isNumber());
if (value.isInteger()) {
data[index] = (char)(unsigned char)qBound(0, value.integerValue(), 255);
return;
}
- double d = value.toNumber();
- if (e->hasException)
- return;
+ Q_ASSERT(value.isDouble());
+ double d = value.doubleValue();
// ### is there a way to optimise this?
if (d <= 0 || std::isnan(d)) {
data[index] = 0;
@@ -122,11 +137,10 @@ ReturnedValue Int16ArrayRead(const char *data, int index)
return Encode((int)*(const short *)(data + index));
}
-void Int16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void Int16ArrayWrite(char *data, int index, const Value &value)
{
- short v = (short)value.toInt32();
- if (e->hasException)
- return;
+ int n = toInt32(value);
+ short v = static_cast<short>(n);
*(short *)(data + index) = v;
}
@@ -135,11 +149,10 @@ ReturnedValue UInt16ArrayRead(const char *data, int index)
return Encode((int)*(const unsigned short *)(data + index));
}
-void UInt16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void UInt16ArrayWrite(char *data, int index, const Value &value)
{
- unsigned short v = (unsigned short)value.toInt32();
- if (e->hasException)
- return;
+ int n = toInt32(value);
+ unsigned short v = static_cast<unsigned short>(n);
*(unsigned short *)(data + index) = v;
}
@@ -148,11 +161,9 @@ ReturnedValue Int32ArrayRead(const char *data, int index)
return Encode(*(const int *)(data + index));
}
-void Int32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void Int32ArrayWrite(char *data, int index, const Value &value)
{
- int v = (int)value.toInt32();
- if (e->hasException)
- return;
+ int v = toInt32(value);
*(int *)(data + index) = v;
}
@@ -161,11 +172,10 @@ ReturnedValue UInt32ArrayRead(const char *data, int index)
return Encode(*(const unsigned int *)(data + index));
}
-void UInt32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void UInt32ArrayWrite(char *data, int index, const Value &value)
{
- unsigned int v = (unsigned int)value.toUInt32();
- if (e->hasException)
- return;
+ int n = toInt32(value);
+ unsigned v = static_cast<unsigned>(n);
*(unsigned int *)(data + index) = v;
}
@@ -174,11 +184,9 @@ ReturnedValue Float32ArrayRead(const char *data, int index)
return Encode(*(const float *)(data + index));
}
-void Float32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void Float32ArrayWrite(char *data, int index, const Value &value)
{
- float v = value.toNumber();
- if (e->hasException)
- return;
+ float v = toDouble(value);
*(float *)(data + index) = v;
}
@@ -187,11 +195,9 @@ ReturnedValue Float64ArrayRead(const char *data, int index)
return Encode(*(const double *)(data + index));
}
-void Float64ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+void Float64ArrayWrite(char *data, int index, const Value &value)
{
- double v = value.toNumber();
- if (e->hasException)
- return;
+ double v = toDouble(value);
*(double *)(data + index) = v;
}
@@ -276,7 +282,7 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
for (uint i = 0; i < l; ++i) {
Primitive val;
val.setRawValue(read(src, i*srcElementSize));
- write(scope.engine, dest, i*destElementSize, val);
+ write(dest, i*destElementSize, val);
}
}
@@ -345,7 +351,10 @@ ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f,
ScopedValue val(scope);
while (idx < l) {
val = o->get(idx);
- array->d()->type->write(scope.engine, b, 0, val);
+ val = val->convertedToNumber();
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ array->d()->type->write(b, 0, val);
if (scope.engine->hasException)
return Encode::undefined();
++idx;
@@ -383,6 +392,8 @@ ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Val
uint index = id.asArrayIndex();
Scope scope(static_cast<const Object *>(m)->engine());
Scoped<TypedArray> a(scope, static_cast<const TypedArray *>(m));
+ if (a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
uint bytesPerElement = a->d()->type->bytesPerElement;
uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
@@ -408,13 +419,18 @@ 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())
+ return scope.engine->throwTypeError();
uint bytesPerElement = a->d()->type->bytesPerElement;
uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength())
return false;
- a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value);
+ Value v = Primitive::fromReturnedValue(value.convertedToNumber());
+ if (scope.hasException() || a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ a->d()->type->write(a->d()->buffer->data->data(), byteOffset, v);
return true;
}
@@ -450,6 +466,9 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const Function
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteLength);
}
@@ -460,6 +479,9 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const Function
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteOffset);
}
@@ -470,41 +492,723 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObje
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
}
+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())
+ return scope.engine->throwTypeError();
+
+ if (!argc)
+ return O->asReturnedValue();
+
+ qint64 len = static_cast<uint>(O->length());
+
+ qint64 to = static_cast<qint64>(argv[0].toInteger());
+ if (to < 0)
+ to = qMax(len + to, 0ll);
+ else
+ to = qMin(to, len);
+
+ 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);
+
+ qint64 count = qMin(end - from, len - to);
+
+ if (count <= 0)
+ return O->asReturnedValue();
+
+ if (O->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ 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);
+ }
+
+ return O->asReturnedValue();
+}
+
ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
Scope scope(b);
- Scoped<TypedArray> O(scope, thisObject);
- if (!O)
- THROW_TYPE_ERROR();
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
- Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
return ao->asReturnedValue();
}
-ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- Scoped<TypedArray> O(scope, thisObject);
- if (!O)
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ 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] : Primitive::undefinedValue());
+ 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;
+
+ bool ok = true;
+ for (uint k = 0; ok && k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ arguments[0] = v->d()->type->read(data, byteOffset + k * bytesPerElement);
+
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = v;
+ r = callback->call(that, arguments, 3);
+ 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->d()->buffer->isDetachedBuffer())
+ 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));
+ }
+
+ double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN();
+ Value value = Primitive::fromDouble(val);
+ if (scope.hasException() || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ char *data = v->d()->buffer->data->data();
+ uint bytesPerElement = v->d()->type->bytesPerElement;
+ uint byteOffset = v->d()->byteOffset;
+
+ 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.alloc(1);
+ arguments[0] = Encode(len);
+ Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1));
+ if (!a || a->d()->buffer->isDetachedBuffer() || 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->d()->buffer->isDetachedBuffer())
+ 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] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
+ Value *list = arguments;
+
+ uint to = 0;
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = instance->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ selected = callback->call(that, arguments, 3);
+ if (selected->toBoolean()) {
+ ++arguments;
+ scope.alloc(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->d()->buffer->isDetachedBuffer())
+ 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.alloc(3);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = v->get(k);
+ CHECK_EXCEPTION();
+
+ arguments[1] = Primitive::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->d()->buffer->isDetachedBuffer())
+ 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.alloc(3);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = v->get(k);
+ CHECK_EXCEPTION();
+
+ arguments[1] = Primitive::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->d()->buffer->isDetachedBuffer())
+ 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] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = v->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Primitive::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->d()->buffer->isDetachedBuffer())
+ 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->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+ if (!len)
+ return Encode(-1);
+
+ ScopedValue searchValue(scope, argc ? argv[0] : Primitive::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 *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ ScopedValue arg(scope, argc ? argv[0] : Primitive::undefinedValue());
+
+ QString r4;
+ if (arg->isUndefined())
+ r4 = QStringLiteral(",");
+ else
+ r4 = arg->toQString();
+
+ const quint32 r2 = len;
+
+ if (!r2)
+ return Encode(scope.engine->newString());
+
+ QString R;
+
+ //
+ // crazy!
+ //
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
+ ScopedValue r6(scope, v->get(name));
+ if (!r6->isNullOrUndefined())
+ R = r6->toQString();
+
+ ScopedValue r12(scope);
+ for (quint32 k = 1; k < r2; ++k) {
+ R += r4;
+
+ name = Primitive::fromDouble(k).toString(scope.engine);
+ r12 = v->get(name);
+ CHECK_EXCEPTION();
+
+ if (!r12->isNullOrUndefined())
+ R += r12->toQString();
+ }
+
+ return Encode(scope.engine->newString(R));
+}
+
+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())
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
return ao->asReturnedValue();
}
-ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- Scoped<TypedArray> O(scope, thisObject);
- if (!O)
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ 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 = Primitive::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->d()->buffer->isDetachedBuffer())
+ 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();
- Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ScopedValue v(scope);
+ ScopedValue mapped(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = instance->get(k);
+
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ mapped = callback->call(that, arguments, 3);
+ 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->d()->buffer->isDetachedBuffer())
+ 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.alloc(4);
+
+ while (k < len) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool kPresent;
+ v = instance->get(k, &kPresent);
+ if (kPresent) {
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Primitive::fromDouble(k);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
+ }
+ ++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->d()->buffer->isDetachedBuffer())
+ 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.alloc(4);
+
+ while (k > 0) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool kPresent;
+ v = instance->get(k - 1, &kPresent);
+ if (kPresent) {
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Primitive::fromDouble(k - 1);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
+ }
+ --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->d()->buffer->isDetachedBuffer())
+ 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->d()->buffer->isDetachedBuffer())
+ 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] : Primitive::undefinedValue());
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = instance->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Primitive::fromDouble(k);
+ arguments[2] = instance;
+ result = callback->call(that, arguments, 3);
+ 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->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
return ao->asReturnedValue();
}
@@ -550,7 +1254,12 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
ScopedValue val(scope);
while (idx < l) {
val = o->get(idx);
- a->d()->type->write(scope.engine, b, 0, val);
+ if (scope.hasException())
+ return Encode::undefined();
+ val = val->convertedToNumber();
+ if (scope.hasException() || buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ a->d()->type->write(b, 0, val);
if (scope.engine->hasException)
RETURN_UNDEFINED();
++idx;
@@ -591,7 +1300,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
for (uint i = 0; i < l; ++i) {
Primitive val;
val.setRawValue(read(src, i*srcElementSize));
- write(scope.engine, dest, i*elementSize, val);
+ write(dest, i*elementSize, val);
}
if (srcCopy)
@@ -600,6 +1309,53 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b,
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->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ double s = (argc ? argv[0] : Primitive::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->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ v = instance->get(i);
+ if (a->d()->buffer->isDetachedBuffer())
+ 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);
@@ -609,8 +1365,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject
return scope.engine->throwTypeError();
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
- if (!buffer || buffer->isDetachedBuffer())
- return scope.engine->throwTypeError();
+ Q_ASSERT(buffer);
int len = a->length();
double b = argc > 0 ? argv[0].toInteger() : 0;
@@ -630,7 +1385,7 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject
int newLen = end - begin;
- ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor()));
+ ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, scope.engine->typedArrayCtors + a->d()->arrayType));
if (!constructor)
return scope.engine->throwTypeError();
@@ -638,7 +1393,42 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject
arguments[0] = buffer;
arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
arguments[2] = Encode(newLen);
- return constructor->callAsConstructor(arguments, 3);
+ a = constructor->callAsConstructor(arguments, 3);
+ if (!a || a->d()->buffer->isDetachedBuffer())
+ 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->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+ const QString separator = QStringLiteral(",");
+
+ QString R;
+
+ ScopedValue v(scope);
+ ScopedString s(scope);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ 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);
+ 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)
@@ -650,20 +1440,51 @@ ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const Functio
return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
}
-ReturnedValue IntrinsicTypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+static bool validateTypedArray(const Object *o)
{
- return f->engine()->throwTypeError();
+ const TypedArray *a = o->as<TypedArray>();
+ if (!a)
+ return false;
+ if (a->d()->buffer->isDetachedBuffer())
+ return false;
+ return true;
}
-ReturnedValue IntrinsicTypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
- return f->engine()->throwTypeError();
+ 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 = Primitive::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();
}
void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
{
+ Scope scope(engine);
ctor->defineReadonlyProperty(engine->id_prototype(), *this);
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::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);
ctor->addSymbolSpecies();
defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
@@ -671,12 +1492,31 @@ void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedA
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("subarray"), method_subarray, 0);
+ 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);
- Scope scope(engine);
ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
defineDefaultProperty(QStringLiteral("values"), values);