diff options
Diffstat (limited to 'src/qml/jsruntime/qv4typedarray.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4typedarray.cpp | 209 |
1 files changed, 178 insertions, 31 deletions
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 6a1b003b25..d03b67aa27 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -1070,51 +1070,44 @@ 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->d()->buffer->isDetachedBuffer()) 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) @@ -1657,6 +1650,158 @@ 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 falsey(scope, Encode(false)); + ScopedValue error(scope, scope.engine->throwTypeError()); + return Runtime::IteratorClose::call(scope.engine, lengthIterator, falsey); + } + // Retrieve the next value. If the iteration ends, we're done here. + done = Value::fromReturnedValue(Runtime::IteratorNext::call(scope.engine, lengthIterator, nextValue)); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, lengthIterator, Value::fromBoolean(false)); + 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.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + + if (mapfn) { + mapArguments[0] = *nextValue; + mapArguments[1] = Value::fromDouble(k); + mappedValue = mapfn->call(thisArg, mapArguments, 2); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + } else { + mappedValue = *nextValue; + } + + a->put(k, mappedValue); + if (scope.engine->hasException) + return Runtime::IteratorClose::call(scope.engine, iterator, Value::fromBoolean(false)); + } + 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); @@ -1666,6 +1811,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); |