From 1cde99fbafe035cbd88237d8e0a3b52b780f68af Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 3 Jul 2018 17:21:31 +0200 Subject: Make Array.prototype.concat comply better with the spec There are still some failures in the test cases, but at least less than before. Change-Id: I5bad4ddb1e9d6fe120e981f806a6d986fd43b64d Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4arrayobject.cpp | 29 +++++++++++++++++++---------- src/qml/jsruntime/qv4object.cpp | 27 +++++++++++++++++++++++++++ src/qml/jsruntime/qv4object_p.h | 3 +++ 3 files changed, 49 insertions(+), 10 deletions(-) (limited to 'src/qml/jsruntime') diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 05f6b7dfec..4dbb61298e 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -364,22 +364,31 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value ScopedArrayObject result(scope, scope.engine->newArrayObject()); - if (thisObject->isArrayObject()) { - result->copyArrayData(thisObject); - } else { - result->arraySet(0, thisObject); - } - ScopedArrayObject elt(scope); ScopedObject eltAsObj(scope); ScopedValue entry(scope); - for (int i = 0, ei = argc; i < ei; ++i) { - eltAsObj = argv[i]; - elt = argv[i]; + for (int i = -1; i < argc; ++i) { + const Value *v = i == -1 ? thisObject.getPointer() : argv + i; + eltAsObj = *v; + elt = *v; if (elt) { uint n = elt->getLength(); uint newLen = ArrayData::append(result, elt, n); result->setArrayLengthUnchecked(newLen); + } else if (eltAsObj && eltAsObj->isConcatSpreadable()) { + const uint startIndex = result->getLength(); + const uint len = eltAsObj->getLength(); + if (scope.engine->hasException) + return Encode::undefined(); + + for (uint i = 0; i < len; ++i) { + bool hasProperty = false; + entry = eltAsObj->get(i, &hasProperty); + if (hasProperty) { + if (!result->put(startIndex + i, entry)) + return scope.engine->throwTypeError(); + } + } } else if (eltAsObj && eltAsObj->isListType()) { const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { @@ -388,7 +397,7 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value result->put(startIndex + i, entry); } } else { - result->arraySet(result->getLength(), argv[i]); + result->arraySet(result->getLength(), *v); } } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index e4d670d4f3..718bfbe5ec 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -51,6 +51,7 @@ #include "qv4identifiertable_p.h" #include "qv4jscall_p.h" #include "qv4symbol_p.h" +#include "qv4proxy_p.h" #include @@ -930,6 +931,32 @@ void Object::initSparseArray() ArrayData::realloc(this, Heap::ArrayData::Sparse, 0, false); } +bool Object::isConcatSpreadable() const +{ + Scope scope(this); + ScopedValue spreadable(scope, get(scope.engine->symbol_isConcatSpreadable())); + if (!spreadable->isUndefined()) + return spreadable->toBoolean(); + return isArray(); +} + +bool Object::isArray() const +{ + if (isArrayObject()) + return true; + if (vtable() == ProxyObject::staticVTable()) { + const ProxyObject *p = static_cast(this); + Scope scope(this); + if (!p->d()->handler) { + scope.engine->throwTypeError(); + return false; + } + ScopedObject o(scope, p->d()->target); + return o->isArray(); + } + return false; +} + DEFINE_OBJECT_VTABLE(ArrayObject); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index dea080f98a..a9ad926289 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -359,6 +359,9 @@ public: ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } + bool isConcatSpreadable() const; + bool isArray() const; + protected: static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); -- cgit v1.2.3