From 0496475831972387fe74733fec6de68c4fcb2c45 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 4 Apr 2018 12:09:12 +0200 Subject: The length of array like objects can in some cases be 2^53 -1 in ES7 Add a Value::getLength(), that converts a Value to a length bound between 0 and 2^53-1 as per ES7 spec. Use the extended range in Array.prototype.splice and map to fix hanging test cases. Change-Id: If9280d501423cfc10a60abd4e8aa30521d2a7bca Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4argumentsobject.cpp | 6 ++---- src/qml/jsruntime/qv4argumentsobject_p.h | 2 +- src/qml/jsruntime/qv4arrayobject.cpp | 32 +++++++++++++++++++++++--------- src/qml/jsruntime/qv4object.cpp | 10 ++++------ src/qml/jsruntime/qv4object_p.h | 8 ++++---- src/qml/jsruntime/qv4value_p.h | 13 +++++++++++++ 6 files changed, 47 insertions(+), 24 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index de00ac8984..d3ca573a2e 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -242,10 +242,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const return Encode::undefined(); } -uint ArgumentsObject::getLength(const Managed *m) +qint64 ArgumentsObject::getLength(const Managed *m) { const ArgumentsObject *a = static_cast(m); - if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength(); } diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 9264f938e2..01e2c10090 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -149,7 +149,7 @@ struct ArgumentsObject: Object { static bool putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index bd019d3bcb..37c386d781 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -548,19 +548,31 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); - - ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); + qint64 len = instance->getLength(); double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger(); - uint start; + qint64 start; if (rs < 0) - start = (uint) qMax(0., len + rs); + start = static_cast(qMax(0., len + rs)); else - start = (uint) qMin(rs, (double)len); + start = static_cast(qMin(rs, static_cast(len))); + + qint64 deleteCount = 0; + qint64 itemCount = 0; + if (argc == 1) { + deleteCount = len - start; + } else if (argc > 1){ + itemCount = argc - 2; + double dc = argv[1].toInteger(); + deleteCount = static_cast(qMin(qMax(dc, 0.), double(len - start))); + } - uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start)); + if (len + itemCount - deleteCount > /*(static_cast(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwTypeError(); + if (deleteCount > /*(static_cast(1) << 53) - 1*/ UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); newArray->arrayReserve(deleteCount); ScopedValue v(scope); for (uint i = 0; i < deleteCount; ++i) { @@ -572,7 +584,6 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = argc < 2 ? 0 : argc - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { @@ -873,12 +884,15 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t if (!instance) RETURN_UNDEFINED(); - uint len = instance->getLength(); + qint64 len = instance->getLength(); if (!argc || !argv->isFunctionObject()) THROW_TYPE_ERROR(); const FunctionObject *callback = static_cast(argv); + if (len > UINT_MAX - 1) + return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range.")); + ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); a->setArrayLengthUnchecked(len); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 170ad9f556..d7cad2231e 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -1034,11 +1034,11 @@ void Object::copyArrayData(Object *other) setArrayLengthUnchecked(other->getLength()); } -uint Object::getLength(const Managed *m) +qint64 Object::getLength(const Managed *m) { Scope scope(static_cast(m)->engine()); ScopedValue v(scope, static_cast(const_cast(m))->get(scope.engine->id_length())); - return v->toUInt32(); + return v->toLength(); } // 'var' is 'V' in 15.3.5.3. @@ -1136,12 +1136,10 @@ void Heap::ArrayObject::init(const QStringList &list) a->setArrayLengthUnchecked(len); } -uint ArrayObject::getLength(const Managed *m) +qint64 ArrayObject::getLength(const Managed *m) { const ArrayObject *a = static_cast(m); - if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger()) - return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue(); - return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue()); + return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength(); } QStringList ArrayObject::toQStringList() const diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 79ffef47ba..7b1046d6ee 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -174,7 +174,7 @@ struct ObjectVTable PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, String *name); bool (*deleteIndexedProperty)(Managed *m, uint index); - uint (*getLength)(const Managed *m); + qint64 (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); }; @@ -406,7 +406,7 @@ public: { return vtable()->deleteIndexedProperty(this, index); } void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } - uint getLength() const { return vtable()->getLength(this); } + quint64 getLength() const { return vtable()->getLength(this); } ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } @@ -422,7 +422,7 @@ protected: static bool deleteProperty(Managed *m, String *name); static bool deleteIndexedProperty(Managed *m, uint index); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); private: @@ -504,7 +504,7 @@ struct ArrayObject: Object { void init(ExecutionEngine *engine); using Object::getLength; - static uint getLength(const Managed *m); + static qint64 getLength(const Managed *m); QStringList toQStringList() const; }; diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 42c0370138..03c9eda0f2 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -389,6 +389,7 @@ public: int toUInt16() const; inline int toInt32() const; inline unsigned int toUInt32() const; + qint64 toLength() const; bool toBoolean() const { if (integerCompatible()) @@ -789,6 +790,18 @@ inline unsigned int Value::toUInt32() const return static_cast(toInt32()); } +inline qint64 Value::toLength() const +{ + if (Q_LIKELY(integerCompatible())) + return int_32(); + double i = Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()); + if (i <= 0) + return 0; + if (i > (static_cast(1) << 53) - 1) + return (static_cast(1) << 53) - 1; + return static_cast(i); +} + inline double Value::toInteger() const { if (integerCompatible()) -- cgit v1.2.3