summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2021-09-27 18:27:15 -0700
committerIvan Solovev <ivan.solovev@qt.io>2021-12-17 17:54:05 +0100
commit46dc8e453ae1d0c1eb749cfebe686995f3a6cfd0 (patch)
treed7893f8e2ce0a0b1d1d8cb5dffd2aebe1c8de48c
parentaf36675afd4300387390ceaba35a81994751cb75 (diff)
QVariant: use a typedef name when saving user types to QDataStream
Due to the way Qt 5 and 6 registered type names, they end up producing different type names for the same content for a typedef. For example, because Q_DECLARE_METATYPE can't manage a comma (it's a macro), users are forced to write something like: using MyTypeMap = QMap<QString, MyType> Q_DECLARE_METATYPE(MyTypeMap) Qt 5's Q_DECLARE_METATYPE's argument "MyTypeMap" was the only name we knew about the type, so that's what got saved in the stream. However, Qt 6 QtPrivate::typenameHelper is much more clever and obtains the name from the compiler itself, so it "sees through" the typedef and registers "QMap<QString,MyType>" as the official type name. If another library/plugin has a different typedef name for the same type (e.g., StringTypeMap), it's indeterminate which type gets saved and will even change from run to run (depends on the QHash order). [ChangeLog][QtCore][QDataStream] If QDataStream is used with a QDataStream::Version < Qt_6_0 to serialize a user type that was registered via a typedef with the metatype system, the typedef's name is used in the stream instead of the non-typedef name. This restores compatibility with Qt 5, allowing existing content to read the same QDataStreams; reading from older Qt 6 versions should not be affected. (Note: if more than one typedef name is registered, it's indetermine which name gets used) Fixes: QTBUG-96916 Pick-to: 6.3 6.2 Change-Id: I2bbf422288924c198645fffd16a8d811aa58201e Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r--src/corelib/kernel/qmetatype.cpp40
-rw-r--r--src/corelib/kernel/qmetatype_p.h1
-rw-r--r--src/corelib/kernel/qvariant.cpp14
-rw-r--r--tests/auto/corelib/serialization/qdatastream/CMakeLists.txt2
-rw-r--r--tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp53
-rw-r--r--tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp44
-rw-r--r--tests/auto/corelib/serialization/qdatastream/typedef.q5bin0 -> 28 bytes
7 files changed, 148 insertions, 6 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 9494ff84f9..476234d7c5 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -1,6 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -179,6 +181,42 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
} // namespace
+// used by QVariant::save(): returns the name used in the Q_DECLARE_METATYPE
+// macro (one of them, indetermine which one)
+const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d)
+{
+ const char *name = nullptr;
+ QMetaTypeCustomRegistry *r = customTypeRegistry;
+ if (!r)
+ return name;
+
+ QByteArrayView officialName(type_d->name);
+ QReadLocker l(&r->lock);
+ auto it = r->aliases.constBegin();
+ auto end = r->aliases.constEnd();
+ for ( ; it != end; ++it) {
+ if (it.value() != type_d)
+ continue;
+ if (it.key() == officialName)
+ continue; // skip the official name
+ name = it.key().constData();
+ break;
+ }
+
+#ifndef QT_NO_DEBUG
+ QByteArrayList otherNames;
+ for ( ; it != end; ++it) {
+ if (it.value() == type_d)
+ otherNames << it.key();
+ }
+ if (!otherNames.isEmpty())
+ qWarning("QMetaType: type %s has more than one typedef alias: %s, %s",
+ type_d->name, name, otherNames.join(", ").constData());
+#endif
+
+ return name;
+}
+
/*!
\macro Q_DECLARE_OPAQUE_POINTER(PointerType)
\relates QMetaType
diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h
index 16270bc374..2b533608be 100644
--- a/src/corelib/kernel/qmetatype_p.h
+++ b/src/corelib/kernel/qmetatype_p.h
@@ -233,6 +233,7 @@ static const QT_PREPEND_NAMESPACE(QtPrivate::QMetaTypeInterface) *getInterfaceFr
case QMetaType::MetaTypeName: \
return QtMetaTypePrivate::getInterfaceFromType<RealName>();
+const char *typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d);
} //namespace QtMetaTypePrivate
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index b34da6ca18..b942ac50c7 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2021 Intel Corporation.
** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
** Contact: https://www.qt.io/licensing/
**
@@ -1314,13 +1314,17 @@ void QVariant::save(QDataStream &s) const
}
}
const char *typeName = nullptr;
- if (saveAsUserType)
- typeName = d.type().name();
+ if (saveAsUserType) {
+ if (s.version() < QDataStream::Qt_6_0)
+ typeName = QtMetaTypePrivate::typedefNameForType(d.type().d_ptr);
+ if (!typeName)
+ typeName = d.type().name();
+ }
s << typeId;
if (s.version() >= QDataStream::Qt_4_2)
s << qint8(d.is_null);
if (typeName)
- s << d.type().name();
+ s << typeName;
if (!isValid()) {
if (s.version() < QDataStream::Qt_5_0)
diff --git a/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt b/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt
index 136ff4ee19..b29088c712 100644
--- a/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt
+++ b/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt
@@ -6,6 +6,7 @@
# Collect test data
list(APPEND test_data "datastream.q42")
+list(APPEND test_data "typedef.q5")
qt_internal_add_test(tst_qdatastream
SOURCES
@@ -22,6 +23,7 @@ if(ANDROID OR INTEGRITY)
# Resources:
set(testdata_resource_files
"datastream.q42"
+ "typedef.q5"
)
qt_internal_add_resource(tst_qdatastream "testdata"
diff --git a/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp b/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp
new file mode 100644
index 0000000000..3a10e40576
--- /dev/null
+++ b/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QDataStream>
+#include <QPair>
+#include <QFile>
+#include <QVariant>
+#include <QDebug>
+
+using CustomPair = QPair<int, int>;
+QDataStream &operator<<(QDataStream &ds, CustomPair pd)
+{ return ds << pd.first << pd.second; }
+QDataStream &operator>>(QDataStream &ds, CustomPair &pd)
+{ return ds >> pd.first >> pd.second; }
+Q_DECLARE_METATYPE(CustomPair)
+
+
+int main() {
+ qRegisterMetaTypeStreamOperators<CustomPair>();
+ QFile out("typedef.q5");
+ out.open(QIODevice::ReadWrite);
+ QDataStream stream(&out);
+ stream.setVersion(QDataStream::Qt_5_15);
+ CustomPair p {42, 100};
+ qDebug() << p.first << p.second;
+ stream << QVariant::fromValue(p);
+}
diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp
index 8fbe8d745e..511d8f957f 100644
--- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp
+++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp
@@ -221,6 +221,8 @@ private slots:
void nestedTransactionsResult_data();
void nestedTransactionsResult();
+ void typedefQt5Compat();
+
private:
void writebool(QDataStream *s);
void writeQBitArray(QDataStream *s);
@@ -3872,6 +3874,48 @@ void tst_QDataStream::nestedTransactionsResult()
QCOMPARE(int(stream.status()), expectedStatus);
}
+using CustomPair = QPair<int, int>;
+QDataStream &operator<<(QDataStream &ds, CustomPair pd)
+{ return ds << pd.first << pd.second; }
+QDataStream &operator>>(QDataStream &ds, CustomPair &pd)
+{ return ds >> pd.first >> pd.second; }
+
+
+void tst_QDataStream::typedefQt5Compat()
+{
+ qRegisterMetaType<CustomPair>("CustomPair");
+ QByteArray qt5Data;
+ {
+ // we can read the qt5 version
+ QFile in(QFINDTESTDATA("typedef.q5"));
+ QVERIFY(in.open(QIODevice::ReadOnly));
+ qt5Data = in.readAll();
+ QVERIFY(in.seek(0));
+ QDataStream stream(&in);
+ stream.setVersion(QDataStream::Qt_5_15);
+ QVariant var;
+ stream >> var;
+ QCOMPARE(stream.status(), QDataStream::Ok);
+ CustomPair p = var.value<CustomPair>();
+ QCOMPARE(p.first, 42);
+ QCOMPARE(p.second, 100);
+ }
+ {
+ // writing in Qt 6 results in the same file
+ QTemporaryDir dir;
+ QVERIFY(dir.isValid());
+ QFile file(dir.filePath(u"typedef.q6"_qs));
+ file.open(QIODevice::WriteOnly);
+ QDataStream stream(&file);
+ stream.setVersion(QDataStream::Qt_5_15);
+ CustomPair p {42, 100};
+ stream << QVariant::fromValue(p);
+ file.close();
+ file.open(QIODevice::ReadOnly);
+ QCOMPARE(file.readAll(), qt5Data);
+ }
+}
+
QTEST_MAIN(tst_QDataStream)
#include "tst_qdatastream.moc"
diff --git a/tests/auto/corelib/serialization/qdatastream/typedef.q5 b/tests/auto/corelib/serialization/qdatastream/typedef.q5
new file mode 100644
index 0000000000..c6b5e8a4df
--- /dev/null
+++ b/tests/auto/corelib/serialization/qdatastream/typedef.q5
Binary files differ