aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4sequenceobject.cpp
diff options
context:
space:
mode:
authorLuca Di Sera <luca.disera@qt.io>2024-05-23 10:57:49 +0200
committerLuca Di Sera <luca.disera@qt.io>2024-06-06 20:16:11 +0200
commit6537b2b23b22d8ac776d1d97f436bb6d9e8e20da (patch)
tree5744b917b4ca3378e56cebba47d698a656fc0f01 /src/qml/jsruntime/qv4sequenceobject.cpp
parentdc109e959345150bbcf8385100ed22baca018a9b (diff)
Rework the sort implementation for Sequences
When QML reads a property with a C++ provenance it sometimes apply certain transformations to work with the property in a JS environment. For example, certain containers, such as `QJsonArray` or `QVariantList`, are converted to a `Sequence`, an array-like object that knows how to modify the container and takes care of reflecting mutations back to the property. `Sequence` provides a specialized implementation for the built-in sort method. Generally, the default sort implementation for an array in JS converts the elements to a string and compares the stringified representation. In the case of `Sequence`, the sort implementation will treats the elements as `QVariant`s and use `QVariant::toString` to perform this part of the sorting algorithm. Due to the way `QVariant::toString` works, this can fail for certain elements. For example, `QVariant::toString` is unaware of how to produce a string from a `QJsonValue`, the type of the elements that compose a `QJsonArray`, thus failing to correctly sort a container with such elements. Other than the `Sequence` implementation, the JS runtime provides, as per specification, a sort method for the Array prototype. Contrary to other methods that are implemented for the prototype, the `sort` method is implemented so that it can only work on values that have a populated `ArrayData`, an optimized storage for certain array and array-like objects. As `Sequences` do not use an `ArrayData` storage for their elements, the method is unable to work on a `Sequence`. To broaden the ability of the sort method implementation for `Sequence` to work more generically, the default sort implementation for the Array prototype sort method was modified to work more generically on objects that do not present an `ArrayData` storage, with an implementation based on the latest draft of the JS specification. The specialized `Sequence` implementation was removed, in favor of `Sequence` delegating to the Array prototype implementation which would now support working with `Sequence`s. While this should be generally slower than the specialized implementation, foregoing some performance, it should allow a more generic foundation for the sort method for `Sequences` or other elements that act like an array but do not use the specialized `ArrayData` representation. Some specialization could later be reapplied to `Sequence` to improve the performances of the implementation. Previously, the Array prototype implementation would directly delegate to `ArrayData::sort`, the sort implementation for the specialized `ArrayData` storage. This was modified to dispatch to an implementation based on generic methods when no `ArrayData` is populated on the object of the sort. The code related to the specialized `Sequence` implementation for sort was removed and the sequence prototype was modified to not present a specialized `sort` property, so as to fallback on the Array prototype one. The `ArrayData::sort` implementation was slightly modified. `ArrayData::sort` would perform a check on the presence of a defined callback for the sorting and throw a type error if the non-undefined element is not callable, as per specification. This check was moved to the Array prototype implementation, to be shared between the specialized `ArrayData::sort` implementation and the generic implementation. As per the spec, the check should be performed as soon as the method is entered and before the receiver of the method is converted to an object. With the check moved to the start of the Array prototype sort implementation this ordering of operations is now fulfilled. The compliance test that checks for this behavior, `comparefn-nonfunction-call-throws`, that was previously failing, will now pass and was thus removed from the list of expected failures for the `ecmascript` tests. A `QEXPECT_FAIL` related to testing the default sort of a `QJsonArray` property was removed from `tst_qqmllanguage`, as the sort is now expected to work correctly. Fixes: QTBUG-125400 Change-Id: I158a9a160b8bdde2b8a06bb349a76469fc25c5a1 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4sequenceobject.cpp')
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp90
1 files changed, 0 insertions, 90 deletions
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index cc05e030c5..8de85a86bf 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -42,25 +42,6 @@ static ReturnedValue doGetIndexed(const Sequence *s, qsizetype index) {
return v->asReturnedValue();
}
-template<typename Compare>
-void sortSequence(Sequence *sequence, const Compare &compare)
-{
- /* non-const */ Heap::Sequence *p = sequence->d();
-
- QSequentialIterable iterable(p->metaSequence(), p->listType(), p->storagePointer());
- if (iterable.canRandomAccessIterate()) {
- std::sort(QSequentialIterable::RandomAccessIterator(iterable.mutableBegin()),
- QSequentialIterable::RandomAccessIterator(iterable.mutableEnd()),
- compare);
- } else if (iterable.canReverseIterate()) {
- std::sort(QSequentialIterable::BidirectionalIterator(iterable.mutableBegin()),
- QSequentialIterable::BidirectionalIterator(iterable.mutableEnd()),
- compare);
- } else {
- qWarning() << "Container has no suitable iterator for sorting";
- }
-}
-
// helper function to generate valid warnings if errors occur during sequence operations.
static void generateWarning(QV4::ExecutionEngine *v4, const QString& description)
{
@@ -108,40 +89,6 @@ struct SequenceOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
}
};
-struct SequenceCompareFunctor
-{
- SequenceCompareFunctor(QV4::ExecutionEngine *v4, const QV4::Value &compareFn)
- : m_v4(v4), m_compareFn(&compareFn)
- {}
-
- bool operator()(const QVariant &lhs, const QVariant &rhs)
- {
- QV4::Scope scope(m_v4);
- ScopedFunctionObject compare(scope, m_compareFn);
- if (!compare)
- return m_v4->throwTypeError();
- Value *argv = scope.alloc(2);
- argv[0] = m_v4->fromVariant(lhs);
- argv[1] = m_v4->fromVariant(rhs);
- QV4::ScopedValue result(scope, compare->call(m_v4->globalObject, argv, 2));
- if (scope.hasException())
- return false;
- return result->toNumber() < 0;
- }
-
-private:
- QV4::ExecutionEngine *m_v4;
- const QV4::Value *m_compareFn;
-};
-
-struct SequenceDefaultCompareFunctor
-{
- bool operator()(const QVariant &lhs, const QVariant &rhs)
- {
- return lhs.toString() < rhs.toString();
- }
-};
-
void Heap::Sequence::initTypes(QMetaType listType, QMetaSequence metaSequence)
{
m_listType = listType.iface();
@@ -440,24 +387,6 @@ bool Sequence::containerIsEqualTo(Managed *other)
return false;
}
-bool Sequence::sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
-{
- if (d()->isReadOnly())
- return false;
- if (d()->isReference() && !loadReference())
- return false;
-
- if (argc == 1 && argv[0].as<FunctionObject>())
- sortSequence(this, SequenceCompareFunctor(f->engine(), argv[0]));
- else
- sortSequence(this, SequenceDefaultCompareFunctor());
-
- if (d()->object())
- storeReference();
-
- return true;
-}
-
void *Sequence::getRawContainerPtr() const
{ return d()->storagePointer(); }
@@ -635,7 +564,6 @@ static QV4::ReturnedValue method_set_length(const FunctionObject *f, const Value
void SequencePrototype::init()
{
- defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
defineDefaultProperty(engine()->id_valueOf(), method_valueOf, 0);
defineAccessorProperty(QStringLiteral("length"), method_get_length, method_set_length);
defineDefaultProperty(QStringLiteral("shift"), method_shift, 0);
@@ -646,24 +574,6 @@ ReturnedValue SequencePrototype::method_valueOf(const FunctionObject *f, const V
return Encode(thisObject->toString(f->engine()));
}
-ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
-{
- Scope scope(b);
- QV4::ScopedObject o(scope, thisObject);
- if (!o || !o->isV4SequenceType())
- THROW_TYPE_ERROR();
-
- if (argc >= 2)
- return o.asReturnedValue();
-
- if (auto *s = o->as<Sequence>()) {
- if (!s->sort(b, thisObject, argv, argc))
- THROW_TYPE_ERROR();
- }
-
- return o.asReturnedValue();
-}
-
ReturnedValue SequencePrototype::method_shift(
const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{