aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 {