aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/qmljs/qmljsplugindumper.cpp
diff options
context:
space:
mode:
authorPhilip Van Hoof <philip@codeminded.be>2020-04-17 11:40:42 +0200
committerPhilip Van Hoof <philip@codeminded.be>2020-04-27 16:28:09 +0000
commitba171172e4f569c2e4be7bce13b581606f44345a (patch)
treee55afb4fc58e58d53a3c8536ae3d22e4ca0f9df1 /src/libs/qmljs/qmljsplugindumper.cpp
parentb1352fb044f2604930481fb8d10c89c0034697d6 (diff)
qmljs: Make the QMLJsPluginDumper asynchronous to avoid eventloop hangs
Change-Id: I3f6e6acaaf3781d86a0fa5fb100219f92b70f0b5 Fixes: QTCREATORBUG-20243 Task-number: QTCREATORBUG-18533 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Fawzi Mohamed <fawzi.mohamed@qt.io>
Diffstat (limited to 'src/libs/qmljs/qmljsplugindumper.cpp')
-rw-r--r--src/libs/qmljs/qmljsplugindumper.cpp260
1 files changed, 164 insertions, 96 deletions
diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp
index 7ba665dc94..c603610b2a 100644
--- a/src/libs/qmljs/qmljsplugindumper.cpp
+++ b/src/libs/qmljs/qmljsplugindumper.cpp
@@ -34,6 +34,7 @@
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
+#include <utils/runextensions.h>
#include <QDir>
#include <QDirIterator>
@@ -283,35 +284,60 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode)
if (!privatePlugin)
ModelManagerInterface::writeWarning(qmldumpErrorMessage(libraryPath, errorMessages));
libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpFailedMessage(libraryPath, errorMessages));
- }
- const QByteArray output = process->readAllStandardOutput();
- QString error;
- QString warning;
- CppQmlTypesLoader::BuiltinObjects objectsList;
- QList<ModuleApiInfo> moduleApis;
- QStringList dependencies;
- CppQmlTypesLoader::parseQmlTypeDescriptions(output, &objectsList, &moduleApis, &dependencies,
- &error, &warning,
- QLatin1String("<dump of ") + libraryPath + QLatin1Char('>'));
- if (exitCode == 0) {
- if (!error.isEmpty()) {
- libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
- qmldumpErrorMessage(libraryPath, error));
- if (!privatePlugin)
- printParseWarnings(libraryPath, libraryInfo.pluginTypeInfoError());
- } else {
- libraryInfo.setMetaObjects(objectsList.values());
- libraryInfo.setModuleApis(moduleApis);
- libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
- }
-
- if (!warning.isEmpty())
- printParseWarnings(libraryPath, warning);
+ const QByteArray output = process->readAllStandardOutput();
+
+ class CppQmlTypesInfo {
+ public:
+ QString error;
+ QString warning;
+ CppQmlTypesLoader::BuiltinObjects objectsList;
+ QList<ModuleApiInfo> moduleApis;
+ QStringList dependencies;
+ };
+
+ auto watcher = QSharedPointer<QFutureWatcher<CppQmlTypesInfo>>(new QFutureWatcher<CppQmlTypesInfo>());
+
+ connect(watcher.data(), &QFutureWatcher<CppQmlTypesInfo>::finished, this,
+ [this, watcher, libraryInfo, privatePlugin, libraryPath] {
+ CppQmlTypesInfo infos = watcher->result();
+
+ LibraryInfo libInfo = libraryInfo;
+
+ if (!infos.error.isEmpty()) {
+ libInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError,
+ qmldumpErrorMessage(libraryPath, infos.error));
+ if (!privatePlugin)
+ printParseWarnings(libraryPath, libInfo.pluginTypeInfoError());
+ } else {
+ libInfo.setMetaObjects(infos.objectsList.values());
+ libInfo.setModuleApis(infos.moduleApis);
+ libInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
+ }
+
+ if (!infos.warning.isEmpty())
+ printParseWarnings(libraryPath, infos.warning);
+
+ libInfo.updateFingerprint();
+
+ m_modelManager->updateLibraryInfo(libraryPath, libInfo);
+ });
+
+ auto future = Utils::runAsync([output, libraryPath](QFutureInterface<CppQmlTypesInfo> &future)
+ {
+ CppQmlTypesInfo infos;
+ CppQmlTypesLoader::parseQmlTypeDescriptions(output, &infos.objectsList, &infos.moduleApis, &infos.dependencies,
+ &infos.error, &infos.warning,
+ QLatin1String("<dump of ") + libraryPath + QLatin1Char('>'));
+ future.reportFinished(&infos);
+ });
+
+ watcher->setFuture(future);
+ } else {
+ libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone);
+ libraryInfo.updateFingerprint();
+ m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
}
- libraryInfo.updateFingerprint();
-
- m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
}
void PluginDumper::qmlPluginTypeDumpError(QProcess::ProcessError)
@@ -344,38 +370,42 @@ void PluginDumper::pluginChanged(const QString &pluginLibrary)
dump(plugin);
}
-void PluginDumper::loadQmlTypeDescription(const QStringList &paths,
- QStringList &errors,
- QStringList &warnings,
- QList<FakeMetaObject::ConstPtr> &objects,
- QList<ModuleApiInfo> *moduleApi,
- QStringList *dependencies) const {
- for (const QString &p: paths) {
- Utils::FileReader reader;
- if (!reader.fetch(p, QFile::Text)) {
- errors += reader.errorString();
- continue;
- }
- QString error;
- QString warning;
- CppQmlTypesLoader::BuiltinObjects objs;
- QList<ModuleApiInfo> apis;
- QStringList deps;
- CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &objs, &apis, &deps,
- &error, &warning, p);
- if (!error.isEmpty()) {
- errors += tr("Failed to parse \"%1\".\nError: %2").arg(p, error);
- } else {
- objects += objs.values();
- if (moduleApi)
- *moduleApi += apis;
- if (!deps.isEmpty())
- *dependencies += deps;
+QFuture<PluginDumper::QmlTypeDescription> PluginDumper::loadQmlTypeDescription(const QStringList &paths) const {
+ auto future = Utils::runAsync([=](QFutureInterface<PluginDumper::QmlTypeDescription> &future)
+ {
+ PluginDumper::QmlTypeDescription result;
+
+ for (const QString &p: paths) {
+ Utils::FileReader reader;
+ if (!reader.fetch(p, QFile::Text)) {
+ result.errors += reader.errorString();
+ continue;
+ }
+ QString error;
+ QString warning;
+ CppQmlTypesLoader::BuiltinObjects objs;
+ QList<ModuleApiInfo> apis;
+ QStringList deps;
+ CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &objs, &apis, &deps,
+ &error, &warning, p);
+ if (!error.isEmpty()) {
+ result.errors += tr("Failed to parse \"%1\".\nError: %2").arg(p, error);
+ } else {
+ result.objects += objs.values();
+ result.moduleApis += apis;
+ if (!deps.isEmpty())
+ result.dependencies += deps;
+ }
+ if (!warning.isEmpty())
+ result.warnings += warning;
}
- if (!warning.isEmpty())
- warnings += warning;
- }
+
+ future.reportFinished(&result);
+ });
+
+ return future;
}
+
/*!
* \brief Build the path of an existing qmltypes file from a module name.
* \param name
@@ -421,16 +451,14 @@ QString PluginDumper::buildQmltypesPath(const QString &name) const
* Recursively load type descriptions of dependencies, collecting results
* in \a objects.
*/
-void PluginDumper::loadDependencies(const QStringList &dependencies,
- QStringList &errors,
- QStringList &warnings,
- QList<FakeMetaObject::ConstPtr> &objects,
- QSet<QString> *visited) const
+QFuture<PluginDumper::DependencyInfo> PluginDumper::loadDependencies(const QStringList &dependencies,
+ QSharedPointer<QSet<QString>> visited) const
{
- if (dependencies.isEmpty())
- return;
+ auto iface = QSharedPointer<QFutureInterface<PluginDumper::DependencyInfo>>(new QFutureInterface<PluginDumper::DependencyInfo>);
- QScopedPointer<QSet<QString>> visitedPtr(visited ? visited : new QSet<QString>());
+ if (visited.isNull()) {
+ visited = QSharedPointer<QSet<QString>>(new QSet<QString>());
+ }
QStringList dependenciesPaths;
QString path;
@@ -438,44 +466,84 @@ void PluginDumper::loadDependencies(const QStringList &dependencies,
path = buildQmltypesPath(name);
if (!path.isNull())
dependenciesPaths << path;
- visitedPtr->insert(name);
+ visited->insert(name);
}
- QStringList newDependencies;
- loadQmlTypeDescription(dependenciesPaths, errors, warnings, objects, nullptr, &newDependencies);
- newDependencies = Utils::toList(Utils::toSet(newDependencies) - *visitedPtr);
- if (!newDependencies.isEmpty())
- loadDependencies(newDependencies, errors, warnings, objects, visitedPtr.take());
+
+ auto typesWatcher = QSharedPointer<QFutureWatcher<PluginDumper::QmlTypeDescription>>(new QFutureWatcher<PluginDumper::QmlTypeDescription>());
+ connect(typesWatcher.data(), &QFutureWatcher<PluginDumper::QmlTypeDescription>::finished, this, [this, iface, visited, typesWatcher] {
+ QStringList newDependencies = typesWatcher->result().dependencies;
+ newDependencies = Utils::toList(Utils::toSet(newDependencies) - *visited.data());
+
+ if (!newDependencies.isEmpty()) {
+ auto loadWatcher = QSharedPointer<QFutureWatcher<PluginDumper::DependencyInfo>>(new QFutureWatcher<PluginDumper::DependencyInfo>());
+ connect(loadWatcher.data(), &QFutureWatcher<PluginDumper::DependencyInfo>::finished, this, [iface, newDependencies, visited, typesWatcher, loadWatcher] {
+ PluginDumper::DependencyInfo result = loadWatcher->result();
+
+ result.errors += typesWatcher->result().errors;
+ result.objects += typesWatcher->result().objects;
+ result.warnings+= typesWatcher->result().warnings;
+
+ iface->reportFinished(&result);
+ });
+
+ loadWatcher->setFuture(loadDependencies(newDependencies, visited));
+ } else {
+ PluginDumper::DependencyInfo result;
+ result.errors += typesWatcher->result().errors;
+ result.objects += typesWatcher->result().objects;
+ result.warnings+= typesWatcher->result().warnings;
+ iface->reportFinished(&result);
+ }
+ });
+ typesWatcher->setFuture(loadQmlTypeDescription(dependenciesPaths));
+
+ return iface->future();
}
void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths,
const QString &libraryPath,
QmlJS::LibraryInfo libraryInfo)
{
- QStringList errors;
- QStringList warnings;
- QList<FakeMetaObject::ConstPtr> objects;
- QList<ModuleApiInfo> moduleApis;
- QStringList dependencies;
-
- loadQmlTypeDescription(qmltypesFilePaths, errors, warnings, objects, &moduleApis, &dependencies);
- loadDependencies(dependencies, errors, warnings, objects);
-
- libraryInfo.setMetaObjects(objects);
- libraryInfo.setModuleApis(moduleApis);
- libraryInfo.setDependencies(dependencies);
- if (errors.isEmpty()) {
- libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
- } else {
- printParseWarnings(libraryPath, errors.join(QLatin1Char('\n')));
- errors.prepend(tr("Errors while reading typeinfo files:"));
- libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1Char('\n')));
- }
-
- if (!warnings.isEmpty())
- printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
-
- libraryInfo.updateFingerprint();
- m_modelManager->updateLibraryInfo(libraryPath, libraryInfo);
+ auto typesWatcher = QSharedPointer<QFutureWatcher<PluginDumper::QmlTypeDescription>>(new QFutureWatcher<PluginDumper::QmlTypeDescription>());
+ connect(typesWatcher.data(), &QFutureWatcher<PluginDumper::QmlTypeDescription>::finished, this, [this, typesWatcher, libraryPath, libraryInfo] {
+
+ auto loadWatcher = QSharedPointer<QFutureWatcher<PluginDumper::DependencyInfo>>(new QFutureWatcher<PluginDumper::DependencyInfo>());
+ connect(loadWatcher.data(), &QFutureWatcher<PluginDumper::DependencyInfo>::finished, this, [this, typesWatcher, loadWatcher, libraryPath, libraryInfo] {
+
+ QStringList deps = typesWatcher->result().dependencies;
+ QStringList errors = typesWatcher->result().errors;
+ QStringList warnings = typesWatcher->result().errors;
+ QList<FakeMetaObject::ConstPtr> objects = typesWatcher->result().objects;
+
+ errors += loadWatcher->result().errors;
+ warnings += loadWatcher->result().warnings;
+ objects += loadWatcher->result().objects;
+
+ QmlJS::LibraryInfo libInfo = libraryInfo;
+
+ libInfo.setMetaObjects(objects);
+ libInfo.setModuleApis(typesWatcher->result().moduleApis);
+ libInfo.setDependencies(typesWatcher->result().dependencies);
+
+ if (errors.isEmpty()) {
+ libInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone);
+ } else {
+ printParseWarnings(libraryPath, errors.join(QLatin1Char('\n')));
+ errors.prepend(tr("Errors while reading typeinfo files:"));
+ libInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileError, errors.join(QLatin1Char('\n')));
+ }
+
+ if (!warnings.isEmpty())
+ printParseWarnings(libraryPath, warnings.join(QLatin1String("\n")));
+
+ libInfo.updateFingerprint();
+ m_modelManager->updateLibraryInfo(libraryPath, libInfo);
+ });
+ if (!typesWatcher->result().dependencies.isEmpty()) {
+ loadWatcher->setFuture(loadDependencies(typesWatcher->result().dependencies, QSharedPointer<QSet<QString>>()));
+ }
+ });
+ typesWatcher->setFuture(loadQmlTypeDescription(qmltypesFilePaths));
}
void PluginDumper::runQmlDump(const QmlJS::ModelManagerInterface::ProjectInfo &info,