From c763c5bbe6d0d3cf033fdae48eae068962e95a5f Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Fri, 16 Aug 2019 16:26:45 +0200 Subject: qmlplugindump: Handle types reexported by QtQuick Now that our own types are not registered with qmlRegisterType() anymore, we can use QQmlType::module to decide whether a type should be included in the qmltypes file. We include types with no module, to support existing user code which still calls qmlRegisterType(). Change-Id: Iee0a132bf5b3ddcdfc222ab4b2d37a1f05b1851d Reviewed-by: Ulf Hermann --- tools/qmlplugindump/main.cpp | 133 +++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 61 deletions(-) (limited to 'tools/qmlplugindump') diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 464f3e8a6b..f0ed1f8ebe 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -100,21 +100,45 @@ static QString enquote(const QString &string) .replace(QLatin1Char('"'),QLatin1String("\\\""))); } -void collectReachableMetaObjects(const QMetaObject *meta, QSet *metas, bool extended = false) +struct QmlVersionInfo { + QString pluginImportUri; + int majorVersion; + int minorVersion; +}; + +static bool matchingImportUri(const QQmlType &ty, const QmlVersionInfo& versionInfo) { + return (versionInfo.pluginImportUri == ty.module() + && (ty.majorVersion() == versionInfo.majorVersion || ty.majorVersion() == -1)) + || ty.module().isEmpty(); +} + +void collectReachableMetaObjects(const QMetaObject *meta, QSet *metas, const QmlVersionInfo &info, bool extended = false, bool alreadyChangedModule = false) +{ + auto ty = QQmlMetaType::qmlType(meta); if (! meta || metas->contains(meta)) return; - // dynamic meta objects can break things badly - // but extended types are usually fine - const QMetaObjectPrivate *mop = reinterpret_cast(meta->d.data); - if (extended || !(mop->flags & DynamicMetaObject)) - metas->insert(meta); + if (matchingImportUri(ty, info)) { + if (!alreadyChangedModule) { + // dynamic meta objects can break things badly + // but extended types are usually fine + const QMetaObjectPrivate *mop = reinterpret_cast(meta->d.data); + if (extended || !(mop->flags & DynamicMetaObject)) + metas->insert(meta); + } else if (!ty.module().isEmpty()) { // empty module (e.g. from an attached property) would cause a (false) match; do not warn about them + qWarning() << "Circular module dependency cannot be expressed in plugin.qmltypes file" + << "Object was:" << meta->className() + << ty.module() << info.pluginImportUri; + } + } else if (!ty.module().isEmpty()) { + alreadyChangedModule = true; + } - collectReachableMetaObjects(meta->superClass(), metas); + collectReachableMetaObjects(meta->superClass(), metas, info, /*extended=*/ false, alreadyChangedModule); } -void collectReachableMetaObjects(QObject *object, QSet *metas) +void collectReachableMetaObjects(QObject *object, QSet *metas, const QmlVersionInfo &info) { if (! object) return; @@ -122,7 +146,7 @@ void collectReachableMetaObjects(QObject *object, QSet *met const QMetaObject *meta = object->metaObject(); if (verbose) std::cerr << "Processing object " << qPrintable( meta->className() ) << std::endl; - collectReachableMetaObjects(meta, metas); + collectReachableMetaObjects(meta, metas, info); for (int index = 0; index < meta->propertyCount(); ++index) { QMetaProperty prop = meta->property(index); @@ -135,17 +159,18 @@ void collectReachableMetaObjects(QObject *object, QSet *met // accessing a member of oo is going to cause a segmentation fault QObject *oo = QQmlMetaType::toQObject(prop.read(object)); if (oo && !metas->contains(oo->metaObject())) - collectReachableMetaObjects(oo, metas); + collectReachableMetaObjects(oo, metas, info); currentProperty.clear(); } } } -void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet *metas) +void collectReachableMetaObjects(QQmlEnginePrivate *engine, const QQmlType &ty, QSet *metas, const QmlVersionInfo& info) { - collectReachableMetaObjects(ty.baseMetaObject(), metas, ty.isExtendedType()); - if (ty.attachedPropertiesType(engine)) - collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas); + collectReachableMetaObjects(ty.baseMetaObject(), metas, info, ty.isExtendedType()); + if (ty.attachedPropertiesType(engine) && matchingImportUri(ty, info)) { + collectReachableMetaObjects(ty.attachedPropertiesType(engine), metas, info); + } } /* We want to add the MetaObject for 'Qt' to the list, this is a @@ -205,13 +230,13 @@ QByteArray convertToId(const QMetaObject *mo) // Collect all metaobjects for types registered with qmlRegisterType() without parameters void collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate *engine, QSet& metas, - QMap> &compositeTypes) { + QMap> &compositeTypes, const QmlVersionInfo &info) { const auto qmlAllTypes = QQmlMetaType::qmlAllTypes(); for (const QQmlType &ty : qmlAllTypes) { if (!metas.contains(ty.baseMetaObject())) { if (!ty.isComposite()) { - collectReachableMetaObjects(engine, ty, &metas); - } else { + collectReachableMetaObjects(engine, ty, &metas, info); + } else if (matchingImportUri(ty, info)) { compositeTypes[ty.elementName()].insert(ty); } } @@ -222,20 +247,24 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, QSet &noncreatables, QSet &singletons, QMap> &compositeTypes, - const QList &skip = QList()) + const QmlVersionInfo &info, + const QList &skip = QList() + ) { QSet metas; metas.insert(FriendlyQObject::qtMeta()); const auto qmlTypes = QQmlMetaType::qmlTypes(); for (const QQmlType &ty : qmlTypes) { + if (!matchingImportUri(ty,info)) + continue; if (!ty.isCreatable()) noncreatables.insert(ty.baseMetaObject()); if (ty.isSingleton()) singletons.insert(ty.baseMetaObject()); if (!ty.isComposite()) { qmlTypesByCppName[ty.baseMetaObject()->className()].insert(ty); - collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas); + collectReachableMetaObjects(QQmlEnginePrivate::get(engine), ty, &metas, info); } else { compositeTypes[ty.elementName()].insert(ty); } @@ -245,6 +274,8 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, // find even more QMetaObjects by instantiating QML types and running // over the instances for (const QQmlType &ty : qmlTypes) { + if (!matchingImportUri(ty, info)) + continue; if (skip.contains(ty)) continue; if (ty.isExtendedType()) @@ -274,7 +305,7 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, if (verbose) std::cerr << "Trying to get singleton for " << qPrintable(tyName) << " (" << qPrintable( siinfo->typeName ) << ")" << std::endl; - collectReachableMetaObjects(object, &metas); + collectReachableMetaObjects(object, &metas, info); object = QQmlEnginePrivate::get(engine)->singletonInstance(ty); } else { inObjectInstantiation.clear(); @@ -293,7 +324,7 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, if (verbose) std::cerr << "Got " << qPrintable( tyName ) << " (" << qPrintable( QString::fromUtf8(ty.typeName()) ) << ")" << std::endl; - collectReachableMetaObjects(object, &metas); + collectReachableMetaObjects(object, &metas, info); object->deleteLater(); } else { std::cerr << "Could not create " << qPrintable(tyName) << std::endl; @@ -301,7 +332,7 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, } } - collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes); + collectReachableMetaObjectsWithoutQmlName(QQmlEnginePrivate::get(engine), metas, compositeTypes, info); return metas; } @@ -1186,11 +1217,7 @@ int main(int argc, char *argv[]) QSet uncreatableMetas; QSet singletonMetas; QMap> defaultCompositeTypes; - QSet defaultReachable = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes); - QList defaultTypes = QQmlMetaType::qmlTypes(); - // add some otherwise unreachable QMetaObjects - defaultReachable.insert(&QQuickMouseEvent::staticMetaObject); // QQuickKeyEvent, QQuickPinchEvent, QQuickDropEvent are not exported QSet defaultReachableNames; @@ -1200,35 +1227,25 @@ int main(int argc, char *argv[]) // composite types we want to dump information of QMap> compositeTypes; + int majorVersion = qtQmlMajorVersion, minorVersion = qtQmlMinorVersion; if (action == Builtins) { - for (const QMetaObject *m : qAsConst(defaultReachable)) { - if (m->className() == QLatin1String("Qt")) { - metas.insert(m); - break; - } - } - } else if (pluginImportUri == QLatin1String("QtQml")) { - bool ok = false; - const uint major = pluginImportVersion.splitRef('.').at(0).toUInt(&ok, 10); - if (!ok) { - std::cerr << "Malformed version string \""<< qPrintable(pluginImportVersion) << "\"." - << std::endl; - return EXIT_INVALIDARGUMENTS; - } - if (major != qtQmlMajorVersion) { - std::cerr << "Unsupported version \"" << qPrintable(pluginImportVersion) - << "\": Major version number must be \"" << qtQmlMajorVersion << "\"." - << std::endl; - return EXIT_INVALIDARGUMENTS; - } - metas = defaultReachable; - for (const QMetaObject *m : qAsConst(defaultReachable)) { - if (m->className() == QLatin1String("Qt")) { - metas.remove(m); - break; - } - } + QSet builtins = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, defaultCompositeTypes, {QLatin1String("Qt"), majorVersion, minorVersion}); + Q_ASSERT(builtins.size() == 1); + metas.insert(*builtins.begin()); } else { + auto versionSplitted = pluginImportVersion.split("."); + bool ok = versionSplitted.size() == 2; + if (!ok) + qCritical("Invalid version number"); + else { + majorVersion = versionSplitted.at(0).toInt(&ok); + if (!ok) + qCritical("Invalid major version"); + minorVersion = versionSplitted.at(1).toInt(&ok); + if (!ok) + qCritical("Invalid minor version"); + } + QList defaultTypes = QQmlMetaType::qmlTypes(); // find a valid QtQuick import QByteArray importCode; QQmlType qtObjectType = QQmlMetaType::qmlType(&QObject::staticMetaObject); @@ -1273,8 +1290,7 @@ int main(int argc, char *argv[]) } } - QSet candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, defaultTypes); - candidates.subtract(defaultReachable); + QSet candidates = collectReachableMetaObjects(&engine, uncreatableMetas, singletonMetas, compositeTypes, {pluginImportUri, majorVersion, minorVersion}, defaultTypes); for (QString iter: compositeTypes.keys()) { if (defaultCompositeTypes.contains(iter)) { @@ -1284,13 +1300,8 @@ int main(int argc, char *argv[]) } } - // Also eliminate meta objects with the same classname. - // This is required because extended objects seem not to share - // a single meta object instance. - for (const QMetaObject *mo : qAsConst(defaultReachable)) - defaultReachableNames.insert(QByteArray(mo->className())); for (const QMetaObject *mo : qAsConst(candidates)) { - if (!defaultReachableNames.contains(mo->className())) + if (mo->className() != QLatin1String("Qt")) metas.insert(mo); } } -- cgit v1.2.3