aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArno Rehn <a.rehn@menlosystems.com>2019-01-14 15:31:57 +0100
committerArno Rehn <a.rehn@menlosystems.com>2019-01-31 08:19:11 +0000
commit38f097ba76fe5b26ccc15a5599e312336200e564 (patch)
tree45cca5f4d81fcce5b1f41c09330f7f17605f57ed
parentc80cbce7ac3b38a45a38b57795c7eb19e5177a17 (diff)
Correctly convert enums and QFlags from and to JSON
Previously, enums declared with Q_ENUM were stringified in the conversion to JSON and QFlags did not work at all. The conversion of QJsonValue(int) to enum worked fine, however. This patch implements some extra logic for detecting enums and QFlags, thus correctly converting to and from integers. [ChangeLog] Enum values and QFlags are now correctly converted to and from integers in the JS interface Fixes: QTBUG-72924 Change-Id: I23d4a1120b805201c8d450edbd990aad5ad258a2 Reviewed-by: Frederik Gladhorn <frederik.gladhorn@qt.io>
-rw-r--r--src/webchannel/qmetaobjectpublisher.cpp34
-rw-r--r--tests/auto/webchannel/tst_webchannel.cpp43
-rw-r--r--tests/auto/webchannel/tst_webchannel.h14
3 files changed, 91 insertions, 0 deletions
diff --git a/src/webchannel/qmetaobjectpublisher.cpp b/src/webchannel/qmetaobjectpublisher.cpp
index 9e08453..6ea3480 100644
--- a/src/webchannel/qmetaobjectpublisher.cpp
+++ b/src/webchannel/qmetaobjectpublisher.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Copyright (C) 2019 Menlo Systems GmbH, author Arno Rehn <a.rehn@menlosystems.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
@@ -56,6 +57,32 @@ QT_BEGIN_NAMESPACE
namespace {
+// FIXME: QFlags don't have the QMetaType::IsEnumeration flag set, although they have a QMetaEnum entry in the QMetaObject.
+// They only way to detect registered QFlags types is to find the named entry in the QMetaObject's enumerator list.
+// Ideally, this would be fixed in QMetaType.
+bool isQFlagsType(uint id)
+{
+ QMetaType type(id);
+
+ // Short-circuit to avoid more expensive operations
+ QMetaType::TypeFlags flags = type.flags();
+ if (flags.testFlag(QMetaType::PointerToQObject) || flags.testFlag(QMetaType::IsEnumeration)
+ || flags.testFlag(QMetaType::SharedPointerToQObject) || flags.testFlag(QMetaType::WeakPointerToQObject)
+ || flags.testFlag(QMetaType::TrackingPointerToQObject) || flags.testFlag(QMetaType::IsGadget))
+ {
+ return false;
+ }
+
+ const QMetaObject *mo = type.metaObject();
+ if (!mo) {
+ return false;
+ }
+
+ QByteArray name = QMetaType::typeName(id);
+ name = name.mid(name.lastIndexOf(":") + 1);
+ return mo->indexOfEnumerator(name.constData()) > -1;
+}
+
MessageType toType(const QJsonValue &value)
{
int i = value.toInt(-1);
@@ -487,6 +514,9 @@ QVariant QMetaObjectPublisher::toVariant(const QJsonValue &value, int targetType
if (unwrappedObject == Q_NULLPTR)
qWarning() << "Cannot not convert non-object argument" << value << "to QObject*.";
return QVariant::fromValue(unwrappedObject);
+ } else if (isQFlagsType(targetType)) {
+ int flagsValue = value.toInt();
+ return QVariant(targetType, reinterpret_cast<const void*>(&flagsValue));
}
// this converts QJsonObjects to QVariantMaps, which is not desired when
@@ -570,6 +600,10 @@ QJsonValue QMetaObjectPublisher::wrapResult(const QVariant &result, QWebChannelA
objectInfo[KEY_DATA] = classInfo;
return objectInfo;
+ } else if (QMetaType::typeFlags(result.userType()).testFlag(QMetaType::IsEnumeration)) {
+ return result.toInt();
+ } else if (isQFlagsType(result.userType())) {
+ return *reinterpret_cast<const int*>(result.constData());
#ifndef QT_NO_JSVALUE
} else if (result.canConvert<QJSValue>()) {
// Workaround for keeping QJSValues from QVariant.
diff --git a/tests/auto/webchannel/tst_webchannel.cpp b/tests/auto/webchannel/tst_webchannel.cpp
index b62a596..6764204 100644
--- a/tests/auto/webchannel/tst_webchannel.cpp
+++ b/tests/auto/webchannel/tst_webchannel.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Copyright (C) 2019 Menlo Systems GmbH, author Arno Rehn <a.rehn@menlosystems.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
@@ -341,8 +342,12 @@ void TestWebChannel::testInfoForObject()
QJsonObject fooEnum;
fooEnum["Asdf"] = TestObject::Asdf;
fooEnum["Bar"] = TestObject::Bar;
+ QJsonObject testFlags;
+ testFlags["FirstFlag"] = static_cast<int>(TestObject::FirstFlag);
+ testFlags["SecondFlag"] = static_cast<int>(TestObject::SecondFlag);
QJsonObject expected;
expected["Foo"] = fooEnum;
+ expected["TestFlags"] = testFlags;
QCOMPARE(info["enums"].toObject(), expected);
}
@@ -755,6 +760,44 @@ void TestWebChannel::testPassWrappedObjectBack()
QCOMPARE(registeredObj.mReturnedObject, &returnedObjProperty);
}
+void TestWebChannel::testWrapValues()
+{
+ QWebChannel channel;
+ channel.connectTo(m_dummyTransport);
+
+ {
+ QVariant variant = QVariant::fromValue(TestObject::Asdf);
+ QJsonValue value = channel.d_func()->publisher->wrapResult(variant, m_dummyTransport);
+ QVERIFY(value.isDouble());
+ QCOMPARE(value.toInt(), (int) TestObject::Asdf);
+ }
+ {
+ TestObject::TestFlags flags = TestObject::FirstFlag | TestObject::SecondFlag;
+ QVariant variant = QVariant::fromValue(flags);
+ QJsonValue value = channel.d_func()->publisher->wrapResult(variant, m_dummyTransport);
+ QVERIFY(value.isDouble());
+ QCOMPARE(value.toInt(), (int) flags);
+ }
+}
+
+void TestWebChannel::testJsonToVariant()
+{
+ QWebChannel channel;
+ channel.connectTo(m_dummyTransport);
+
+ {
+ QVariant variant = QVariant::fromValue(TestObject::Asdf);
+ QVariant convertedValue = channel.d_func()->publisher->toVariant(static_cast<int>(TestObject::Asdf), variant.userType());
+ QCOMPARE(convertedValue, variant);
+ }
+ {
+ TestObject::TestFlags flags = TestObject::FirstFlag | TestObject::SecondFlag;
+ QVariant variant = QVariant::fromValue(flags);
+ QVariant convertedValue = channel.d_func()->publisher->toVariant(static_cast<int>(flags), variant.userType());
+ QCOMPARE(convertedValue, variant);
+ }
+}
+
void TestWebChannel::testInfiniteRecursion()
{
QWebChannel channel;
diff --git a/tests/auto/webchannel/tst_webchannel.h b/tests/auto/webchannel/tst_webchannel.h
index 85846e7..386f314 100644
--- a/tests/auto/webchannel/tst_webchannel.h
+++ b/tests/auto/webchannel/tst_webchannel.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
+** Copyright (C) 2019 Menlo Systems GmbH, author Arno Rehn <a.rehn@menlosystems.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
@@ -92,6 +93,13 @@ public:
Asdf
};
+ enum TestFlag : quint16 {
+ FirstFlag = 0x1,
+ SecondFlag = 0x2
+ };
+ Q_DECLARE_FLAGS(TestFlags, TestFlag)
+ Q_FLAG(TestFlags)
+
Foo foo() const {return Bar;}
int asdf() const {return 42;}
QString bar() const {return QString();}
@@ -160,6 +168,8 @@ public:
QString mProp;
};
+Q_DECLARE_OPERATORS_FOR_FLAGS(TestObject::TestFlags)
+
class BenchObject : public QObject
{
Q_OBJECT
@@ -303,6 +313,8 @@ private slots:
void testWrapRegisteredObject();
void testRemoveUnusedTransports();
void testPassWrappedObjectBack();
+ void testWrapValues();
+ void testJsonToVariant();
void testInfiniteRecursion();
void testAsyncObject();
void testDeletionDuringMethodInvocation_data();
@@ -330,4 +342,6 @@ private:
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(TestObject::Foo)
+
#endif // TST_WEBCHANNEL_H