From 2cfe1bb09c11432ca5033f9589243e9e62fe9488 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Fri, 27 Oct 2017 12:54:57 +0200 Subject: Add a means to unregister custom qml types In cases where Qt is used in a plugin it is possible that a plugin will be unloaded while Qt itself is still loaded and as a result there is a chance that there will be conflicting types registered. Therefore, to ensure that plugins correctly clean up after themselves cleanly, we need to add a means to unregister qml types. This is intended to only be used when the user knows what they are doing. Task-number: QTBUG-56521 Task-number: QTBUG-56532 Change-Id: Ie396e522385004e6e9f3841e04f8072ff29cb15b Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlmetatype.cpp | 21 +++ src/qml/qml/qqmlmetatype_p.h | 2 + .../data/testUnregisterCustomSingletonType.qml | 8 + .../qqmlmetatype/data/testUnregisterCustomType.qml | 8 + tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp | 193 +++++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml create mode 100644 tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ac670bdabb..3a0d5c3daf 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -2238,6 +2238,27 @@ QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVe return data->propertyCache(type, minorVersion); } +void qmlUnregisterType(int typeIndex) +{ + QMutexLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + { + const QQmlTypePrivate *d = data->types.value(typeIndex).priv(); + if (d) { + removeQQmlTypePrivate(data->idToType, d); + removeQQmlTypePrivate(data->nameToType, d); + removeQQmlTypePrivate(data->urlToType, d); + removeQQmlTypePrivate(data->urlToNonFileImportType, d); + removeQQmlTypePrivate(data->metaObjectToType, d); + for (QQmlMetaTypeData::TypeModules::Iterator module = data->uriToModule.begin(); module != data->uriToModule.end(); ++module) { + QQmlTypeModulePrivate *modulePrivate = (*module)->priv(); + modulePrivate->remove(d); + } + data->types[typeIndex] = QQmlType(); + } + } +} + void QQmlMetaType::freeUnusedTypesAndCaches() { QMutexLocker lock(metaTypeDataLock()); diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 4bfdf52de1..f5b9beb905 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -75,6 +75,8 @@ class QQmlCompiledData; namespace QV4 { struct String; } +void Q_QML_PRIVATE_EXPORT qmlUnregisterType(int type); + class Q_QML_PRIVATE_EXPORT QQmlMetaType { public: diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml new file mode 100644 index 0000000000..85b8f5ac8b --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomSingletonType.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 +import mytypes 1.0 + +Item { + id: root + property string text: StaticProvider.singletonGetString() +} + diff --git a/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml new file mode 100644 index 0000000000..f6ee4e9b77 --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/testUnregisterCustomType.qml @@ -0,0 +1,8 @@ +import QtQuick 2.7 +import mytypes 1.0 + +Item { + id: root + Controller { id: controller; objectName: "controller" } +} + diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index 798e3fd386..58361b4b12 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -60,6 +60,8 @@ private slots: void isList(); void defaultObject(); + void unregisterCustomType(); + void unregisterCustomSingletonType(); }; class TestType : public QObject @@ -330,6 +332,197 @@ void tst_qqmlmetatype::externalEnums() } +class Controller1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString string MEMBER m_string) + Q_PROPERTY(Controller1Enum enumVal MEMBER m_enumVal) +public: + enum Controller1Enum { + ENUM_VALUE_1 = 1, + ENUM_VALUE_2 = 2 + }; + Q_ENUMS(Controller1Enum) + + Controller1(QObject *parent = nullptr) : QObject(parent), m_string("Controller #1"), + m_enumVal(ENUM_VALUE_1) + {} +private: + QString m_string; + Controller1Enum m_enumVal; +}; + +class Controller2 : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString string MEMBER m_string) + Q_PROPERTY(Controller2Enum enumVal MEMBER m_enumVal) +public: + enum Controller2Enum { + ENUM_VALUE_1 = 111, + ENUM_VALUE_2 = 222 + }; + Q_ENUMS(Controller2Enum) + + Controller2(QObject *parent = nullptr) : QObject(parent), m_string("Controller #2"), + m_enumVal(ENUM_VALUE_1) + {} +private: + QString m_string; + Controller2Enum m_enumVal; +}; + +void tst_qqmlmetatype::unregisterCustomType() +{ + int controllerId = 0; + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild("controller"); + QVERIFY(qobject_cast(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 1); + } + qmlUnregisterType(controllerId); + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild("controller"); + QVERIFY(qobject_cast(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #2")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 111); + } + qmlUnregisterType(controllerId); + { + QQmlEngine engine; + QQmlType type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(!type.isValid()); + controllerId = qmlRegisterType("mytypes", 1, 0, "Controller"); + type = QQmlMetaType::qmlType(QString("Controller"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(!type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj); + QObject *controller = obj->findChild("controller"); + QVERIFY(qobject_cast(controller)); + QVariant stringVal = controller->property("string"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("Controller #1")); + QVariant enumVal = controller->property("enumVal"); + QCOMPARE(enumVal.type(), QVariant::Int); + QCOMPARE(enumVal.toInt(), 1); + } +} + +class StaticProvider1 : public QObject +{ + Q_OBJECT +public: + StaticProvider1(QObject *parent = nullptr) : QObject(parent) {} + Q_INVOKABLE QString singletonGetString() { return "StaticProvider #1"; } +}; + +static QObject* createStaticProvider1(QQmlEngine *, QJSEngine *) +{ + return new StaticProvider1; +} + +class StaticProvider2 : public QObject +{ + Q_OBJECT +public: + StaticProvider2(QObject *parent = nullptr) : QObject(parent) {} + Q_INVOKABLE QString singletonGetString() { return "StaticProvider #2"; } +}; + +static QObject* createStaticProvider2(QQmlEngine *, QJSEngine *) +{ + return new StaticProvider2; +} + +void tst_qqmlmetatype::unregisterCustomSingletonType() +{ + int staticProviderId = 0; + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider1); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); + } + qmlUnregisterType(staticProviderId); + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider2); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #2")); + } + qmlUnregisterType(staticProviderId); + { + QQmlEngine engine; + staticProviderId = qmlRegisterSingletonType("mytypes", 1, 0, "StaticProvider", createStaticProvider1); + QQmlType type = QQmlMetaType::qmlType(QString("StaticProvider"), QString("mytypes"), 1, 0); + QVERIFY(type.isValid()); + QVERIFY(!type.isInterface()); + QVERIFY(type.isSingleton()); + QVERIFY(!type.isComposite()); + QQmlComponent c(&engine, testFileUrl("testUnregisterCustomSingletonType.qml")); + QScopedPointer obj(c.create()); + QVERIFY(obj.data()); + QVariant stringVal = obj->property("text"); + QCOMPARE(stringVal.type(), QVariant::String); + QCOMPARE(stringVal.toString(), QStringLiteral("StaticProvider #1")); + } +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" -- cgit v1.2.3