aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4engine.cpp89
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp64
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.h2
3 files changed, 155 insertions, 0 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index db3ca97780..885f06a3e3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -43,6 +43,7 @@
#include <private/qqmljsdiagnosticmessage_p.h>
#include <QtCore/QTextStream>
+#include <QtCore/private/qvariant_p.h>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
@@ -118,6 +119,7 @@
#endif
#include <private/qv4sqlerrors_p.h>
#include <qqmlfile.h>
+#include <qmetatype.h>
#if USE(PTHREADS)
# include <pthread.h>
@@ -185,6 +187,91 @@ static void restoreJSValue(QDataStream &stream, void *data)
}
}
+struct JSArrayIterator {
+ QJSValue const* data;
+ quint32 index;
+};
+
+namespace {
+void createNewIteratorIfNonExisting(void **iterator) {
+ if (*iterator == nullptr)
+ *iterator = new JSArrayIterator;
+}
+}
+
+static QtMetaTypePrivate::QSequentialIterableImpl jsvalueToSequence (const QJSValue& value) {
+ using namespace QtMetaTypePrivate;
+
+ QSequentialIterableImpl iterator {};
+ if (!value.isArray()) {
+ // set up some functions so that non-array QSequentialIterables do not crash
+ // but instead appear as an empty sequence
+ iterator._size = [](const void *) {return 0;};
+ iterator._moveToBegin = [](const void *, void **) {};
+ iterator._moveToEnd = [](const void *, void **) {};
+ iterator._advance = [](void **, int) {};
+ iterator._equalIter = [](void * const *, void * const *){return true; /*all iterators are nullptr*/};
+ iterator._destroyIter = [](void **){};
+ return iterator;
+ }
+
+ iterator._iterable = &value;
+ iterator._iterator = nullptr;
+ iterator._metaType_id = qMetaTypeId<QVariant>();
+ iterator._metaType_flags = QVariantConstructionFlags::ShouldDeleteVariantData;
+ iterator._iteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability;
+ iterator._size = [](const void *p) -> int {
+ return static_cast<QJSValue const *>(p)->property(QString::fromLatin1("length")).toInt();
+ };
+ /* Lifetime management notes:
+ * _at and _get return a pointer to a JSValue allocated via QMetaType::create
+ * Because we set QVariantConstructionFlags::ShouldDeleteVariantData, QSequentialIterable::at
+ * and QSequentialIterable::operator*() will free that memory
+ */
+
+ iterator._at = [](const void *iterable, int index) -> void const * {
+ auto const value = static_cast<QJSValue const *>(iterable)->property(quint32(index)).toVariant();
+ return QMetaType::create(qMetaTypeId<QVariant>(), &value);
+ };
+ iterator._moveToBegin = [](const void *iterable, void **iterator) {
+ createNewIteratorIfNonExisting(iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(*iterator);
+ jsArrayIterator->index = 0;
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ };
+ iterator._moveToEnd = [](const void *iterable, void **iterator) {
+ createNewIteratorIfNonExisting(iterator);
+ auto jsArrayIterator = static_cast<JSArrayIterator *>(*iterator);
+ auto length = static_cast<QJSValue const *>(iterable)->property(QString::fromLatin1("length")).toInt();
+ jsArrayIterator->data = reinterpret_cast<QJSValue const*>(iterable);
+ jsArrayIterator->index = quint32(length);
+ };
+ iterator._advance = [](void **iterator, int advanceBy) {
+ static_cast<JSArrayIterator *>(*iterator)->index += quint32(advanceBy);
+ };
+ iterator._get = []( void * const *iterator, int metaTypeId, uint flags) -> VariantData {
+ auto const * const arrayIterator = static_cast<const JSArrayIterator *>(*iterator);
+ QJSValue const * const jsArray = arrayIterator->data;
+ auto const value = jsArray->property(arrayIterator->index).toVariant();
+ Q_ASSERT(flags & QVariantConstructionFlags::ShouldDeleteVariantData);
+ return {metaTypeId, QMetaType::create(qMetaTypeId<QVariant>(), &value), flags};
+ };
+ iterator._destroyIter = [](void **iterator) {
+ delete static_cast<JSArrayIterator *>(*iterator);
+ };
+ iterator._equalIter = [](void * const *p, void * const *other) {
+ auto this_ = static_cast<const JSArrayIterator *>(*p);
+ auto that_ = static_cast<const JSArrayIterator *>(*other);
+ return this_->index == that_->index && this_->data == that_->data;
+ };
+ iterator._copyIter = [](void **iterator, void * const * otherIterator) {
+ auto *otherIter = (static_cast<JSArrayIterator const *>(*otherIterator));
+ static_cast<JSArrayIterator *>(*iterator)->index = otherIter->index;
+ static_cast<JSArrayIterator *>(*iterator)->data = otherIter->data;
+ };
+ return iterator;
+}
+
ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
@@ -708,6 +795,8 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
QMetaType::registerConverter<QJSValue, QVariantList>(convertJSValueToVariantType<QVariantList>);
if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QStringList>())
QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>);
+ if (!QMetaType::hasRegisteredConverterFunction<QJSValue, QtMetaTypePrivate::QSequentialIterableImpl>())
+ QMetaType::registerConverter<QJSValue, QtMetaTypePrivate::QSequentialIterableImpl>(jsvalueToSequence);
QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue);
QV4::QObjectWrapper::initializeBindings(this);
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index 37d0ea4dea..95f554776f 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -2708,6 +2708,70 @@ void tst_QJSValue::nestedObjectToVariant()
QCOMPARE(o.toVariant(), expected);
}
+static int instanceCount = 0;
+
+struct MyType
+{
+ MyType(int n = 0, const char *t=nullptr): number(n), text(t)
+ {
+ ++instanceCount;
+ }
+ MyType(const MyType &other)
+ : number(other.number), text(other.text)
+ {
+ ++instanceCount;
+ }
+ ~MyType()
+ {
+ --instanceCount;
+ }
+ int number;
+ const char *text;
+};
+
+Q_DECLARE_METATYPE(MyType)
+Q_DECLARE_METATYPE(MyType*)
+
+void tst_QJSValue::jsvalueArrayToSequenceType()
+{
+ QCOMPARE(instanceCount, 0);
+ {
+ QJSEngine eng {};
+ auto testObject = eng.newObject();
+ testObject.setProperty("test", 42);
+ testObject.setProperty("mytypeobject", eng.toScriptValue(QVariant::fromValue(MyType {42, "hello"})));
+ auto array = eng.newArray(4);
+ array.setProperty(0, QLatin1String("Hello World"));
+ array.setProperty(1, 42);
+ array.setProperty(2, QJSValue(QJSValue::UndefinedValue));
+ array.setProperty(3, testObject);
+ auto asVariant = QVariant::fromValue(array);
+ QVERIFY(asVariant.canConvert<QVariantList>());
+ auto asIterable = asVariant.value<QSequentialIterable>();
+ for (auto it = asIterable.begin(); it != asIterable.end(); ++it) {
+ Q_UNUSED(*it)
+ }
+ int i = 0;
+ for (QVariant myVariant: asIterable) {
+ QCOMPARE(myVariant.isValid(), i != 2);
+ ++i;
+ }
+ QVERIFY(asIterable.at(2).value<QVariant>().isNull());
+ QCOMPARE(asIterable.at(3).value<QVariantMap>().find("mytypeobject")->value<MyType>().number, 42);
+ QCOMPARE(asIterable.at(0).value<QVariant>().toString(), QLatin1String("Hello World"));
+ auto it1 = asIterable.begin();
+ auto it2 = asIterable.begin();
+ QCOMPARE((*it1).value<QVariant>().toString(), (*it2).value<QVariant>().toString());
+ QCOMPARE((*it1).value<QVariant>().toString(), QLatin1String("Hello World"));
+ ++it2;
+ QCOMPARE((*it1).value<QVariant>().toString(), QLatin1String("Hello World"));
+ QCOMPARE((*it2).value<QVariant>().toInt(), 42);
+ }
+ // tests need to be done after engine has been destroyed, else it will hold a reference until
+ // the gc decides to collect it
+ QCOMPARE(instanceCount, 0);
+}
+
void tst_QJSValue::deleteFromDifferentThread()
{
#if !QT_CONFIG(thread)
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.h b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
index f704169d43..d85b9a0552 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.h
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.h
@@ -142,6 +142,8 @@ private slots:
void nestedObjectToVariant_data();
void nestedObjectToVariant();
+ void jsvalueArrayToSequenceType();
+
void deleteFromDifferentThread();
private: