aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2020-11-06 15:13:27 +0100
committerUlf Hermann <ulf.hermann@qt.io>2020-11-11 11:37:49 +0100
commitf0908255c9921371d886eff0b8ce245929b50d88 (patch)
treeb246bca2fcdae9ee906acc6e2b54a874b36734ea
parent40c0cbda771e9888999d8b78179e9600de4e7795 (diff)
QtQml: Integrate sequences with registration macros
You get to write QML_SEQUENTIAL_CONTAINER(value_type) now, and qmltyperegistrar will generate a sensible registration call from that. A registration might look like this: struct MyStringListForeign { Q_GADGET QML_ANONYMOUS QML_SEQUENTIAL_CONTAINER(QString) QML_FOREIGN(MyStringList) QML_ADDED_IN_VERSION(3, 1) }; It's unfortunate that we need to use a metaobject to transfer all of this information, but there is no other sensible way. Transform the containers defined in qv4sequenceobject.cpp to use the new style, and move them out of the builtins, into QtQml. Recognize that only one of them was ever tested, and add tests for the rest. Task-number: QTBUG-82443 Change-Id: I3a30f9e27266bb575eea26c5daf5dad1ec461cc5 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/qml/jsruntime/qv4engine.cpp2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp33
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h46
-rw-r--r--src/qml/qml/qqml.cpp43
-rw-r--r--src/qml/qml/qqml.h33
-rw-r--r--src/qml/qml/qqmlmetatype.cpp6
-rw-r--r--src/qml/qml/qqmlprivate.h46
-rw-r--r--src/qmlcompiler/qqmljsscope.cpp1
-rw-r--r--src/qmlcompiler/qqmljsscope_p.h10
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp6
-rw-r--r--src/qmltyperegistrar/metatypesjsonprocessor.cpp9
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.cpp29
-rw-r--r--src/qmltyperegistrar/qmltypesclassdescription.h5
-rw-r--r--src/qmltyperegistrar/qmltypescreator.cpp3
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp70
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp22
-rw-r--r--tests/auto/qml/qqmlitemmodels/data/itemselection.qml1
17 files changed, 297 insertions, 68 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index fe34207ddc..b09f874ef3 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -709,8 +709,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
static_cast<VariantPrototype *>(variantPrototype())->init();
#if QT_CONFIG(qml_sequence_object)
- static const bool registered = QV4::SequencePrototype::registerDefaultTypes();
- Q_UNUSED(registered);
sequencePrototype()->cast<SequencePrototype>()->init();
#endif
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 5c7f8289f2..73fa2385fd 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -37,7 +37,6 @@
**
****************************************************************************/
-#include <QtQml/qqml.h>
#include <QtCore/qsequentialiterable.h>
#include "qv4sequenceobject_p.h"
@@ -51,10 +50,6 @@
#include "qv4objectiterator_p.h"
#include <private/qqmlmetatype_p.h>
#include <private/qqmltype_p_p.h>
-#if QT_CONFIG(qml_itemmodel)
-#include <private/qqmlmodelindexvaluetype_p.h>
-#include <QtCore/qabstractitemmodel.h>
-#endif
#include <algorithm>
@@ -581,34 +576,6 @@ namespace QV4 {
DEFINE_OBJECT_VTABLE(QV4Sequence);
}
-
-template<typename SequenceType>
-void registerSequenceType()
-{
- qRegisterMetaType<SequenceType>();
- qmlRegisterAnonymousSequentialContainer<SequenceType>("QML", 1);
-}
-
-bool SequencePrototype::registerDefaultTypes()
-{
- registerSequenceType<std::vector<int>>();
- registerSequenceType<std::vector<qreal>>();
- registerSequenceType<std::vector<bool>>();
- registerSequenceType<QList<int>>();
- registerSequenceType<QList<qreal>>();
- registerSequenceType<QList<bool>>();
- registerSequenceType<QStringList>();
- registerSequenceType<std::vector<QString>>();
- registerSequenceType<QList<QUrl>>();
- registerSequenceType<std::vector<QUrl>>();
-#if QT_CONFIG(qml_itemmodel)
- registerSequenceType<QModelIndexList>();
- registerSequenceType<std::vector<QModelIndex>>();
- registerSequenceType<QItemSelection>();
-#endif
- return true;
-}
-
void SequencePrototype::init()
{
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index 6229a4b5ec..9143430872 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -53,12 +53,18 @@
#include <QtCore/qglobal.h>
#include <QtCore/qvariant.h>
+#include <QtQml/qqml.h>
#include "qv4value_p.h"
#include "qv4object_p.h"
#include "qv4context_p.h"
#include "qv4string_p.h"
+#if QT_CONFIG(qml_itemmodel)
+#include <private/qqmlmodelindexvaluetype_p.h>
+#include <QtCore/qabstractitemmodel.h>
+#endif
+
QT_REQUIRE_CONFIG(qml_sequence_object);
QT_BEGIN_NAMESPACE
@@ -68,7 +74,6 @@ namespace QV4 {
struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
{
V4_PROTOTYPE(arrayPrototype)
- static bool registerDefaultTypes();
void init();
static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -84,6 +89,45 @@ struct Q_QML_PRIVATE_EXPORT SequencePrototype : public QV4::Object
}
+#define QT_DECLARE_SEQUENTIAL_CONTAINER(LOCAL, FOREIGN, VALUE) \
+ struct LOCAL \
+ { \
+ Q_GADGET \
+ QML_ANONYMOUS \
+ QML_SEQUENTIAL_CONTAINER(VALUE) \
+ QML_FOREIGN(FOREIGN) \
+ QML_ADDED_IN_VERSION(2, 0) \
+ }
+
+// We use the original QT_COORD_TYPE name because that will match up with relevant other
+// types in plugins.qmltypes (if you use either float or double, that is; otherwise you're
+// on your own).
+#ifdef QT_COORD_TYPE
+QT_DECLARE_SEQUENTIAL_CONTAINER(QStdRealVectorForeign, std::vector<qreal>, QT_COORD_TYPE);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, QT_COORD_TYPE);
+#else
+QT_DECLARE_SEQUENTIAL_CONTAINER(QRealStdVectorForeign, std::vector<qreal>, double);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QRealListForeign, QList<qreal>, double);
+#endif
+
+QT_DECLARE_SEQUENTIAL_CONTAINER(QIntStdVectorForeign, std::vector<int>, int);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolStdVectorForeign, std::vector<bool>, bool);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QStringStdVectorForeign, std::vector<QString>, QString);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlStdVectorForeign, std::vector<QUrl>, QUrl);
+
+QT_DECLARE_SEQUENTIAL_CONTAINER(QIntListForeign, QList<int>, int);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QBoolListForeign, QList<bool>, bool);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QStringListForeign, QStringList, QString);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QUrlListForeign, QList<QUrl>, QUrl);
+
+#if QT_CONFIG(qml_itemmodel)
+QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexListForeign, QModelIndexList, QModelIndex);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QModelIndexStdVectorForeign, std::vector<QModelIndex>, QModelIndex);
+QT_DECLARE_SEQUENTIAL_CONTAINER(QItemSelectionForeign, QItemSelection, QItemSelectionRange);
+#endif
+
+#undef QT_DECLARE_SEQUENTIAL_CONTAINER
+
QT_END_NAMESPACE
#endif // QV4SEQUENCEWRAPPER_P_H
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 20608a60e4..40e629284b 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -421,6 +421,48 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data)
}
break;
}
+ case SequentialContainerAndRevisionsRegistration: {
+ const RegisterSequentialContainerAndRevisions &type
+ = *reinterpret_cast<RegisterSequentialContainerAndRevisions *>(data);
+ const char *elementName = classElementName(type.classInfoMetaObject);
+ RegisterSequentialContainer revisionRegistration = {
+ 0,
+ type.uri,
+ type.version,
+ elementName,
+ type.typeId,
+ type.metaSequence,
+ QTypeRevision()
+ };
+
+ const QTypeRevision added = revisionClassInfo(
+ type.classInfoMetaObject, "QML.AddedInVersion",
+ QTypeRevision::fromMinorVersion(0));
+ const QTypeRevision removed = revisionClassInfo(
+ type.classInfoMetaObject, "QML.RemovedInVersion");
+
+ QVector<QTypeRevision> revisions = { added };
+ uniqueRevisions(&revisions, type.version, added);
+
+ for (QTypeRevision revision : qAsConst(revisions)) {
+ if (revision < added)
+ continue;
+ if (revision.hasMajorVersion() && revision.majorVersion() > type.version.majorVersion())
+ break;
+
+ // When removed, we still add revisions, but anonymous ones
+ if (removed.isValid() && !(revision < removed))
+ revisionRegistration.typeName = nullptr;
+ else
+ revisionRegistration.typeName = elementName;
+
+ assignVersions(&revisionRegistration, revision, type.version);
+ const int id = qmlregister(SequentialContainerRegistration, &revisionRegistration);
+ if (type.qmlTypeIds)
+ type.qmlTypeIds->append(id);
+ }
+ break;
+ }
case TypeRegistration:
dtype = QQmlMetaType::registerType(*reinterpret_cast<RegisterType *>(data));
break;
@@ -472,6 +514,7 @@ void QQmlPrivate::qmlunregister(RegistrationType type, quintptr data)
break;
case TypeAndRevisionsRegistration:
case SingletonAndRevisionsRegistration:
+ case SequentialContainerAndRevisionsRegistration:
// Currently unnecessary. We'd need a special data structure to hold
// URI + majorVersion and then we'd iterate the minor versions, look up the
// associated QQmlType objects by uri/elementName/major/minor and qmlunregister
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 75f7ae5f06..7f0c81c514 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -94,6 +94,14 @@
template<typename T, typename... Args> \
friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *);
+#define QML_SEQUENTIAL_CONTAINER(VALUE_TYPE) \
+ Q_CLASSINFO("QML.Sequence", #VALUE_TYPE) \
+ using QmlSequenceValueType = VALUE_TYPE; \
+ enum class QmlIsSequence {yes = true}; \
+ template<typename, typename> friend struct QML_PRIVATE_NAMESPACE::QmlSequence; \
+ template<typename T, typename... Args> \
+ friend void QML_REGISTER_TYPES_AND_REVISIONS(const char *uri, int versionMajor, QList<int> *);
+
#define QML_ADDED_IN_MINOR_VERSION(VERSION) \
Q_CLASSINFO("QML.AddedInVersion", Q_REVISION(VERSION))
@@ -792,17 +800,18 @@ inline int qmlRegisterAnonymousSequentialContainer(const char *uri, int versionM
QTypeRevision::fromMajorVersion(versionMajor),
nullptr,
QMetaType::fromType<Container>(),
- QMetaSequence::fromContainer<Container>()
+ QMetaSequence::fromContainer<Container>(),
+ QTypeRevision::zero()
};
return QQmlPrivate::qmlregister(QQmlPrivate::SequentialContainerRegistration, &type);
}
-template<class T, class Resolved, class Extended, bool Singleton, bool Interface>
+template<class T, class Resolved, class Extended, bool Singleton, bool Interface, bool Sequence>
struct QmlTypeAndRevisionsRegistration;
template<class T, class Resolved, class Extended>
-struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false> {
+struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false, false> {
static void registerTypeAndRevisions(const char *uri, int versionMajor, QList<int> *qmlTypeIds,
const QMetaObject *extension)
{
@@ -812,8 +821,19 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, false, false> {
}
};
+template<class T, class Resolved>
+struct QmlTypeAndRevisionsRegistration<T, Resolved, void, false, false, true> {
+ static void registerTypeAndRevisions(const char *uri, int versionMajor, QList<int> *qmlTypeIds,
+ const QMetaObject *)
+ {
+ QQmlPrivate::qmlRegisterSequenceAndRevisions<Resolved>(
+ uri, versionMajor, QQmlPrivate::StaticMetaObject<T>::staticMetaObject(),
+ qmlTypeIds);
+ }
+};
+
template<class T, class Resolved, class Extended>
-struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, true, false> {
+struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, true, false, false> {
static void registerTypeAndRevisions(const char *uri, int versionMajor, QList<int> *qmlTypeIds,
const QMetaObject *extension)
{
@@ -824,7 +844,7 @@ struct QmlTypeAndRevisionsRegistration<T, Resolved, Extended, true, false> {
};
template<class T, class Resolved>
-struct QmlTypeAndRevisionsRegistration<T, Resolved, void, false, true> {
+struct QmlTypeAndRevisionsRegistration<T, Resolved, void, false, true, false> {
static void registerTypeAndRevisions(const char *uri, int versionMajor, QList<int> *qmlTypeIds,
const QMetaObject *)
{
@@ -845,7 +865,8 @@ void qmlRegisterTypesAndRevisions(const char *uri, int versionMajor, QList<int>
T, typename QQmlPrivate::QmlResolved<T>::Type,
typename QQmlPrivate::QmlExtended<T>::Type,
QQmlPrivate::QmlSingleton<T>::Value,
- QQmlPrivate::QmlInterface<T>::Value>
+ QQmlPrivate::QmlInterface<T>::Value,
+ QQmlPrivate::QmlSequence<T>::Value>
::registerTypeAndRevisions(uri, versionMajor, qmlTypeIds,
QQmlPrivate::QmlExtendedNamespace<T>::metaObject());
qmlRegisterTypesAndRevisions<Args...>(uri, versionMajor, qmlTypeIds);
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index e59ebc97eb..9dea173127 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -667,7 +667,8 @@ QQmlType QQmlMetaType::registerSequentialContainer(
QQmlMetaTypeDataPtr data;
- if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, QString(),
+ const QString typeName = QString::fromUtf8(container.typeName);
+ if (!checkRegistration(QQmlType::SequentialContainerType, data, container.uri, typeName,
container.version, {})) {
return QQmlType();
}
@@ -675,8 +676,9 @@ QQmlType QQmlMetaType::registerSequentialContainer(
QQmlTypePrivate *priv = new QQmlTypePrivate(QQmlType::SequentialContainerType);
data->registerType(priv);
- priv->setName(QString::fromUtf8(container.uri), QString());
+ priv->setName(QString::fromUtf8(container.uri), typeName);
priv->version = container.version;
+ priv->revision = container.revision;
priv->typeId = container.typeId;
*priv->extraData.ld = container.metaSequence;
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 19072f0751..feee3f8686 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -574,6 +574,19 @@ namespace QQmlPrivate
const char *typeName;
QMetaType typeId;
QMetaSequence metaSequence;
+ QTypeRevision revision;
+ };
+
+ struct RegisterSequentialContainerAndRevisions {
+ int structVersion;
+ const char *uri;
+ QTypeRevision version;
+
+ const QMetaObject *classInfoMetaObject;
+ QMetaType typeId;
+ QMetaSequence metaSequence;
+
+ QVector<int> *qmlTypeIds;
};
struct AOTCompiledFunction {
@@ -605,6 +618,7 @@ namespace QQmlPrivate
TypeAndRevisionsRegistration = 7,
SingletonAndRevisionsRegistration = 8,
SequentialContainerRegistration = 9,
+ SequentialContainerAndRevisionsRegistration = 10,
};
int Q_QML_EXPORT qmlregister(RegistrationType, void *);
@@ -717,6 +731,20 @@ namespace QQmlPrivate
};
template<class T, class = std::void_t<>>
+ struct QmlSequence
+ {
+ static constexpr bool Value = false;
+ };
+
+ template<class T>
+ struct QmlSequence<T, std::void_t<typename T::QmlIsSequence>>
+ {
+ Q_STATIC_ASSERT((std::is_same_v<typename T::QmlSequenceValueType,
+ typename QmlResolved<T>::Type::value_type>));
+ static constexpr bool Value = bool(T::QmlIsSequence::yes);
+ };
+
+ template<class T, class = std::void_t<>>
struct QmlInterface
{
static constexpr bool Value = false;
@@ -823,6 +851,24 @@ namespace QQmlPrivate
qmlregister(TypeAndRevisionsRegistration, &type);
}
+ template<typename T>
+ void qmlRegisterSequenceAndRevisions(const char *uri, int versionMajor,
+ const QMetaObject *classInfoMetaObject,
+ QVector<int> *qmlTypeIds)
+ {
+ RegisterSequentialContainerAndRevisions type = {
+ 0,
+ uri,
+ QTypeRevision::fromMajorVersion(versionMajor),
+ classInfoMetaObject,
+ QMetaType::fromType<T>(),
+ QMetaSequence::fromContainer<T>(),
+ qmlTypeIds
+ };
+
+ qmlregister(SequentialContainerAndRevisionsRegistration, &type);
+ }
+
template<>
void Q_QML_EXPORT qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
const char *uri, int versionMajor, const QMetaObject *classInfoMetaObject,
diff --git a/src/qmlcompiler/qqmljsscope.cpp b/src/qmlcompiler/qqmljsscope.cpp
index 0787e1062e..ed029208dc 100644
--- a/src/qmlcompiler/qqmljsscope.cpp
+++ b/src/qmlcompiler/qqmljsscope.cpp
@@ -152,6 +152,7 @@ void QQmlJSScope::resolveTypes(const QHash<QString, QQmlJSScope::ConstPtr> &cont
m_baseType = findType(m_baseTypeName);
m_attachedType = findType(m_attachedTypeName);
+ m_valueType = findType(m_valueTypeName);
for (auto it = m_properties.begin(), end = m_properties.end(); it != end; ++it)
it->setType(findType(it->typeName()));
diff --git a/src/qmlcompiler/qqmljsscope_p.h b/src/qmlcompiler/qqmljsscope_p.h
index 99645a8071..24e401fa0a 100644
--- a/src/qmlcompiler/qqmljsscope_p.h
+++ b/src/qmlcompiler/qqmljsscope_p.h
@@ -101,7 +101,8 @@ public:
enum class AccessSemantics {
Reference,
Value,
- None
+ None,
+ Sequence
};
enum Flag {
@@ -200,6 +201,10 @@ public:
void setAttachedTypeName(const QString &name) { m_attachedTypeName = name; }
QQmlJSScope::ConstPtr attachedType() const { return m_attachedType; }
+ QString valueTypeName() const { return m_valueTypeName; }
+ void setValueTypeName(const QString &name) { m_valueTypeName = name; }
+ QQmlJSScope::ConstPtr valueType() const { return m_valueType; }
+
bool isSingleton() const { return m_flags & Singleton; }
bool isCreatable() const { return m_flags & Creatable; }
bool isComposite() const { return m_flags & Composite; }
@@ -258,6 +263,9 @@ private:
QString m_attachedTypeName;
QQmlJSScope::WeakConstPtr m_attachedType;
+ QString m_valueTypeName;
+ QQmlJSScope::WeakConstPtr m_valueType;
+
Flags m_flags;
AccessSemantics m_semantics = AccessSemantics::Reference;
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 215a82b4bc..fd875dd715 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -224,6 +224,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
readMetaObjectRevisions(script, scope);
} else if (name == QLatin1String("attachedType")) {
scope->setAttachedTypeName(readStringBinding(script));
+ } else if (name == QLatin1String("valueType")) {
+ scope->setValueTypeName(readStringBinding(script));
} else if (name == QLatin1String("isSingleton")) {
scope->setIsSingleton(readBoolBinding(script));
} else if (name == QLatin1String("isCreatable")) {
@@ -238,6 +240,8 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Value);
} else if (semantics == QLatin1String("none")) {
scope->setAccessSemantics(QQmlJSScope::AccessSemantics::None);
+ } else if (semantics == QLatin1String("sequence")) {
+ scope->setAccessSemantics(QQmlJSScope::AccessSemantics::Sequence);
} else {
addWarning(script->firstSourceLocation(),
tr("Unknown access semantics \"%1\".").arg(semantics));
@@ -245,7 +249,7 @@ void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
} else {
addWarning(script->firstSourceLocation(),
tr("Expected only name, prototype, defaultProperty, attachedType, "
- "exports, isSingleton, isCreatable, isComposite and "
+ "valueType, exports, isSingleton, isCreatable, isComposite and "
"exportMetaObjectRevisions script bindings, not \"%1\".").arg(name));
}
} else {
diff --git a/src/qmltyperegistrar/metatypesjsonprocessor.cpp b/src/qmltyperegistrar/metatypesjsonprocessor.cpp
index f71a94c67d..e4b9c32b8b 100644
--- a/src/qmltyperegistrar/metatypesjsonprocessor.cpp
+++ b/src/qmltyperegistrar/metatypesjsonprocessor.cpp
@@ -153,6 +153,7 @@ QVector<QJsonObject> MetaTypesJsonProcessor::foreignRelatedTypes() const
const QLatin1String qmlNamePrefix("QML.");
const QLatin1String qmlForeignName("QML.Foreign");
const QLatin1String qmlAttachedName("QML.Attached");
+ const QLatin1String qmlSequenceName("QML.Sequence");
const QLatin1String valueKey("value");
const QLatin1String superClassesKey("superClasses");
const QLatin1String accessKey("access");
@@ -214,9 +215,10 @@ QVector<QJsonObject> MetaTypesJsonProcessor::foreignRelatedTypes() const
const auto classInfos = classDef.value(classInfosKey).toArray();
for (const QJsonValue classInfo : classInfos) {
const QJsonObject obj = classInfo.toObject();
- if (obj.value(nameKey).toString() == qmlAttachedName) {
+ const QString objNameValue = obj.value(nameKey).toString();
+ if (objNameValue == qmlAttachedName || objNameValue == qmlSequenceName) {
addType(obj.value(valueKey).toString());
- } else if (obj.value(nameKey).toString() == qmlForeignName) {
+ } else if (objNameValue == qmlForeignName) {
const QString foreignClassName = obj.value(valueKey).toString();
if (const QJsonObject *other = QmlTypesClassDescription::findType(
m_foreignTypes, foreignClassName)) {
@@ -230,7 +232,8 @@ QVector<QJsonObject> MetaTypesJsonProcessor::foreignRelatedTypes() const
const auto otherClassInfos = other->value(classInfosKey).toArray();
for (const QJsonValue otherClassInfo : otherClassInfos) {
const QJsonObject obj = otherClassInfo.toObject();
- if (obj.value(nameKey).toString() == qmlAttachedName) {
+ const QString objNameValue = obj.value(nameKey).toString();
+ if (objNameValue == qmlAttachedName || objNameValue == qmlSequenceName) {
addType(obj.value(valueKey).toString());
break;
}
diff --git a/src/qmltyperegistrar/qmltypesclassdescription.cpp b/src/qmltyperegistrar/qmltypesclassdescription.cpp
index 20f5d4c46a..94cfd2c2a4 100644
--- a/src/qmltyperegistrar/qmltypesclassdescription.cpp
+++ b/src/qmltyperegistrar/qmltypesclassdescription.cpp
@@ -99,7 +99,11 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef,
} else if (name == QLatin1String("QML.Creatable")) {
isCreatable = (value != QLatin1String("false"));
} else if (name == QLatin1String("QML.Attached")) {
- collectAttached(value, types, foreign, defaultRevision);
+ attachedType = value;
+ collectRelated(value, types, foreign, defaultRevision);
+ } else if (name == QLatin1String("QML.Sequence")) {
+ sequenceValueType = value;
+ collectRelated(value, types, foreign, defaultRevision);
} else if (name == QLatin1String("QML.Singleton")) {
if (value == QLatin1String("true"))
isSingleton = true;
@@ -112,10 +116,15 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef,
const QJsonObject obj = classInfo.toObject();
const QString foreignName = obj[QLatin1String("name")].toString();
const QString foreignValue = obj[QLatin1String("value")].toString();
- if (defaultProp.isEmpty() && foreignName == QLatin1String("DefaultProperty"))
+ if (defaultProp.isEmpty() && foreignName == QLatin1String("DefaultProperty")) {
defaultProp = foreignValue;
- else if (foreignName == QLatin1String("QML.Attached"))
- collectAttached(foreignValue, types, foreign, defaultRevision);
+ } else if (foreignName == QLatin1String("QML.Attached")) {
+ attachedType = foreignValue;
+ collectRelated(foreignValue, types, foreign, defaultRevision);
+ } else if (foreignName == QLatin1String("QML.Sequence")) {
+ sequenceValueType = foreignValue;
+ collectRelated(foreignValue, types, foreign, defaultRevision);
+ }
}
} else {
// The foreign type does not have a meta object: We only override the name.
@@ -176,7 +185,10 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef,
if (className.isEmpty() && mode == TopLevel)
className = classDef->value(QLatin1String("qualifiedClassName")).toString();
- if (classDef->value(QLatin1String("object")).toBool()) {
+ if (!sequenceValueType.isEmpty()) {
+ isCreatable = false;
+ accessSemantics = QLatin1String("sequence");
+ } else if (classDef->value(QLatin1String("object")).toBool()) {
accessSemantics = QLatin1String("reference");
} else {
isCreatable = false;
@@ -186,14 +198,13 @@ void QmlTypesClassDescription::collect(const QJsonObject *classDef,
}
}
-void QmlTypesClassDescription::collectAttached(const QString &attached,
+void QmlTypesClassDescription::collectRelated(const QString &related,
const QVector<QJsonObject> &types,
const QVector<QJsonObject> &foreign,
QTypeRevision defaultRevision)
{
- attachedType = attached;
- if (const QJsonObject *other = findType(types, attachedType))
+ if (const QJsonObject *other = findType(types, related))
collect(other, types, foreign, AttachedType, defaultRevision);
- else if (const QJsonObject *other = findType(foreign, attachedType))
+ else if (const QJsonObject *other = findType(foreign, related))
collect(other, types, foreign, AttachedType, defaultRevision);
}
diff --git a/src/qmltyperegistrar/qmltypesclassdescription.h b/src/qmltyperegistrar/qmltypesclassdescription.h
index 17235c58eb..7e496159ff 100644
--- a/src/qmltyperegistrar/qmltypesclassdescription.h
+++ b/src/qmltyperegistrar/qmltypesclassdescription.h
@@ -44,6 +44,7 @@ struct QmlTypesClassDescription
QString defaultProp;
QString superClass;
QString attachedType;
+ QString sequenceValueType;
QString accessSemantics;
QList<QTypeRevision> revisions;
QTypeRevision addedInRevision;
@@ -61,8 +62,8 @@ struct QmlTypesClassDescription
void collect(const QJsonObject *classDef, const QVector<QJsonObject> &types,
const QVector<QJsonObject> &foreign, CollectMode mode,
QTypeRevision defaultRevision);
- void collectAttached(const QString &attached, const QVector<QJsonObject> &types,
- const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision);
+ void collectRelated(const QString &related, const QVector<QJsonObject> &types,
+ const QVector<QJsonObject> &foreign, QTypeRevision defaultRevision);
static const QJsonObject *findType(const QVector<QJsonObject> &types, const QString &name);
};
diff --git a/src/qmltyperegistrar/qmltypescreator.cpp b/src/qmltyperegistrar/qmltypescreator.cpp
index 50980685e9..9e1e757b69 100644
--- a/src/qmltyperegistrar/qmltypescreator.cpp
+++ b/src/qmltyperegistrar/qmltypescreator.cpp
@@ -58,6 +58,9 @@ void QmlTypesCreator::writeClassProperties(const QmlTypesClassDescription &colle
if (!collector.superClass.isEmpty())
m_qml.writeScriptBinding(QLatin1String("prototype"), enquote(collector.superClass));
+ if (!collector.sequenceValueType.isEmpty())
+ m_qml.writeScriptBinding(QLatin1String("valueType"), enquote(collector.sequenceValueType));
+
if (collector.elementName.isEmpty())
return;
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index d98a5ec51a..4f2e6f1866 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -41,6 +41,8 @@
#include <private/qv4alloca_p.h>
#include <private/qjsvalue_p.h>
#include <QScopeGuard>
+#include <QUrl>
+#include <QModelIndex>
#ifdef Q_CC_MSVC
#define NO_INLINE __declspec(noinline)
@@ -66,8 +68,10 @@ private slots:
void newArray();
void newArray_HooliganTask218092();
void newArray_HooliganTask233836();
- void toScriptValue_data();
- void toScriptValue();
+ void toScriptValueBuiltin_data();
+ void toScriptValueBuiltin();
+ void toScriptValueQtQml_data();
+ void toScriptValueQtQml();
void toScriptValuenotroundtripped_data();
void toScriptValuenotroundtripped();
void newVariant();
@@ -512,7 +516,7 @@ void tst_QJSEngine::newArray_HooliganTask233836()
}
}
-void tst_QJSEngine::toScriptValue_data()
+void tst_QJSEngine::toScriptValueBuiltin_data()
{
QTest::addColumn<QVariant>("input");
@@ -544,8 +548,6 @@ void tst_QJSEngine::toScriptValue_data()
vm.clear(); vm.insert("point1", QPointF(42.24, 24.42)); vm.insert("point2", QPointF(42.24, 24.42));
QTest::newRow("qvariantmap_point") << QVariant(vm);
QTest::newRow("qvariant") << QVariant(QVariant(42));
- QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>() << 1 << 2 << 3 << 4);
- QTest::newRow("QVector<int>") << QVariant::fromValue(QVector<int>() << 1 << 2 << 3 << 4);
QTest::newRow("QList<QString>") << QVariant::fromValue(QVector<QString>() << "1" << "2" << "3" << "4");
QTest::newRow("QStringList") << QVariant::fromValue(QStringList() << "1" << "2" << "3" << "4");
QTest::newRow("QMap<QString, QString>") << QVariant::fromValue(QMap<QString, QString>{{ "1", "2" }, { "3", "4" }});
@@ -554,7 +556,7 @@ void tst_QJSEngine::toScriptValue_data()
QTest::newRow("QHash<QString, QPointF>") << QVariant::fromValue(QHash<QString, QPointF>{{ "1", { 42.24, 24.42 } }, { "3", { 24.42, 42.24 } }});
}
-void tst_QJSEngine::toScriptValue()
+void tst_QJSEngine::toScriptValueBuiltin()
{
QFETCH(QVariant, input);
@@ -570,6 +572,62 @@ void tst_QJSEngine::toScriptValue()
QCOMPARE(input, output);
}
+void tst_QJSEngine::toScriptValueQtQml_data()
+{
+ QTest::addColumn<QVariant>("input");
+
+ QTest::newRow("std::vector<qreal>") << QVariant::fromValue(std::vector<qreal>{.1, .2, .3, .4});
+ QTest::newRow("QList<qreal>") << QVariant::fromValue(QList<qreal>{.1, .2, .3, .4});
+
+ QTest::newRow("std::vector<int>") << QVariant::fromValue(std::vector<int>{1, 2, 3, 4});
+ QTest::newRow("std::vector<bool>") << QVariant::fromValue(std::vector<bool>{true, false, true, false});
+ QTest::newRow("std::vector<QString>") << QVariant::fromValue(std::vector<QString>{"a", "b", "c", "d"});
+ QTest::newRow("std::vector<QUrl>") << QVariant::fromValue(std::vector<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
+
+ QTest::newRow("QList<int>") << QVariant::fromValue(QList<int>{1, 2, 3, 4});
+ QTest::newRow("QList<bool>") << QVariant::fromValue(QList<bool>{true, false, true, false});
+ QTest::newRow("QStringList") << QVariant::fromValue(QStringList{"a", "b", "c", "d"});
+ QTest::newRow("QList<QUrl>") << QVariant::fromValue(QList<QUrl>{QUrl("htt://a.com"), QUrl("file:///tmp/b/"), QUrl("c.foo"), QUrl("/some/d")});
+
+ static const QStandardItemModel model(4, 4);
+ QTest::newRow("QModelIndexList") << QVariant::fromValue(QModelIndexList{ model.index(1, 2), model.index(2, 3), model.index(3, 1), model.index(3, 2)});
+ QTest::newRow("std::vector<QModelIndex>") << QVariant::fromValue(std::vector<QModelIndex>{ model.index(1, 2), model.index(2, 3), model.index(3, 1), model.index(3, 2)});
+
+ // QVariant wants to implicitly convert this to a QList<QItemSelectionRange>. Prevent that by
+ // keeping the instance on the stack, and explicitly instantiating the template below.
+ QItemSelection selection;
+
+ // It doesn't have an initializer list ctor ...
+ selection << QItemSelectionRange(model.index(1, 2), model.index(2, 3))
+ << QItemSelectionRange(model.index(3, 1), model.index(3, 2))
+ << QItemSelectionRange(model.index(2, 2), model.index(3, 3))
+ << QItemSelectionRange(model.index(1, 1), model.index(2, 2));
+
+ QTest::newRow("QItemSelection") << QVariant::fromValue<QItemSelection>(selection);
+}
+
+void tst_QJSEngine::toScriptValueQtQml()
+{
+ QFETCH(QVariant, input);
+
+ // Import QtQml, to enable the sequential containers defined there.
+ QQmlEngine engine;
+ QQmlComponent c(&engine);
+ c.setData("import QtQml\nQtObject{}", QUrl());
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+
+ QJSValue outputJS = engine.toScriptValue(input);
+ QVariant output = engine.fromScriptValue<QVariant>(outputJS);
+
+ if (input.metaType().id() == QMetaType::QChar) {
+ if (!input.convert(QMetaType(QMetaType::QString)))
+ QFAIL("cannot convert to the original value");
+ } else if (!output.convert(input.metaType()) && input.isValid())
+ QFAIL("cannot convert to the original value");
+ QCOMPARE(input, output);
+}
+
void tst_QJSEngine::toScriptValuenotroundtripped_data()
{
QTest::addColumn<QVariant>("input");
diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
index cdf04ceb00..1771d8ccde 100644
--- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
+++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp
@@ -33,6 +33,8 @@
#include <QtWidgets/QPushButton>
#include <QtCore/qthread.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
#include <memory>
@@ -2250,8 +2252,8 @@ void tst_QJSValue::strictlyEquals()
{
QJSValue var1 = eng.toScriptValue(QVariant(QStringList() << "a"));
QJSValue var2 = eng.toScriptValue(QVariant(QStringList() << "a"));
- QVERIFY(!var1.isArray());
- QVERIFY(!var2.isArray());
+ QVERIFY(var1.isArray());
+ QVERIFY(var2.isArray());
QVERIFY(!var1.strictlyEquals(var2));
}
{
@@ -2269,6 +2271,22 @@ void tst_QJSValue::strictlyEquals()
QJSValue var2 = eng.toScriptValue(QVariant(QPoint(3, 4)));
QVERIFY(!var1.strictlyEquals(var2));
}
+
+ {
+ // Import QtQml to trigger the registration of QStringList, which makes it a sequence
+ // type, rather than a generic JS array.
+ QQmlEngine qmlEngine;
+ QQmlComponent c(&qmlEngine);
+ c.setData("import QtQml\nQtObject {}", QUrl());
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(!obj.isNull());
+
+ QJSValue var1 = qmlEngine.toScriptValue(QVariant(QStringList() << "a"));
+ QJSValue var2 = qmlEngine.toScriptValue(QVariant(QStringList() << "a"));
+ QVERIFY(!var1.isArray());
+ QVERIFY(!var2.isArray());
+ QVERIFY(!var1.strictlyEquals(var2));
+ }
}
Q_DECLARE_METATYPE(int*)
diff --git a/tests/auto/qml/qqmlitemmodels/data/itemselection.qml b/tests/auto/qml/qqmlitemmodels/data/itemselection.qml
index c2da71627a..c2b3884573 100644
--- a/tests/auto/qml/qqmlitemmodels/data/itemselection.qml
+++ b/tests/auto/qml/qqmlitemmodels/data/itemselection.qml
@@ -1,3 +1,4 @@
+import QtQml
import Test 1.0
ItemModelsTest {