From 283a3d32cda68b0dcf7642d9ddbfb60dd88d9ccb Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Thu, 6 Jan 2011 10:01:45 +0100
Subject: [PATCH] Qml/C++: Fix performance problem with type extraction.

By moving the offending code into a background thread.

Reviewed-by: Erik Verbruggen
---
 src/libs/cplusplus/ModelManagerInterface.h   |  2 +-
 src/libs/qmljs/qmljsinterpreter.cpp          |  2 -
 src/libs/qmljs/qmljsinterpreter.h            |  1 -
 src/libs/qmljs/qmljslink.cpp                 |  8 ++++
 src/libs/qmljs/qmljsmodelmanagerinterface.h  |  4 ++
 src/plugins/cpptools/cppmodelmanager.cpp     | 41 +++++++++---------
 src/plugins/cpptools/cppmodelmanager.h       |  2 +-
 src/plugins/qmljstools/qmljsmodelmanager.cpp | 45 ++++++++++++++++++--
 src/plugins/qmljstools/qmljsmodelmanager.h   | 12 +++++-
 9 files changed, 87 insertions(+), 30 deletions(-)

diff --git a/src/libs/cplusplus/ModelManagerInterface.h b/src/libs/cplusplus/ModelManagerInterface.h
index 943d32000be..17724f55e94 100644
--- a/src/libs/cplusplus/ModelManagerInterface.h
+++ b/src/libs/cplusplus/ModelManagerInterface.h
@@ -147,7 +147,7 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro) = 0;
 
-    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects() const = 0;
+    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const = 0;
 
 Q_SIGNALS:
     void documentUpdated(CPlusPlus::Document::Ptr doc);
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index fb2fe5a589e..910b2f54398 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -1944,7 +1944,6 @@ const Value *Function::invoke(const Activation *activation) const
 ////////////////////////////////////////////////////////////////////////////////
 
 QList<FakeMetaObject::ConstPtr> CppQmlTypesLoader::builtinObjects;
-QList<FakeMetaObject::ConstPtr> CppQmlTypesLoader::cppObjects;
 
 QStringList CppQmlTypesLoader::load(const QFileInfoList &xmlFiles)
 {
@@ -2438,7 +2437,6 @@ Engine::Engine()
     initializePrototypes();
 
     _cppQmlTypes.load(this, CppQmlTypesLoader::builtinObjects);
-    _cppQmlTypes.load(this, CppQmlTypesLoader::cppObjects);
 
     // the 'Qt' object is dumped even though it is not exported
     // it contains useful information, in particular on enums - add the
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index f4f10b0fb41..f1e30c4bd1d 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -587,7 +587,6 @@ public:
     /** \return an empty list when successful, error messages otherwise. */
     static QStringList load(const QFileInfoList &xmlFiles);
     static QList<LanguageUtils::FakeMetaObject::ConstPtr> builtinObjects;
-    static QList<LanguageUtils::FakeMetaObject::ConstPtr> cppObjects;
 
     // parses the xml string and fills the newObjects map
     static QString parseQmlTypeXml(const QByteArray &xml,
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 0c4f85a1747..06a6e3438ec 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -120,6 +120,14 @@ Link::Link(Context *context, const Document::Ptr &doc, const Snapshot &snapshot,
     d->snapshot = snapshot;
     d->importPaths = importPaths;
 
+    // populate engine with types from C++
+    ModelManagerInterface *modelManager = ModelManagerInterface::instance();
+    if (modelManager) {
+        foreach (const QList<FakeMetaObject::ConstPtr> &cppTypes, modelManager->cppQmlTypes()) {
+            engine()->cppQmlTypes().load(engine(), cppTypes);
+        }
+    }
+
     linkImports();
 }
 
diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.h b/src/libs/qmljs/qmljsmodelmanagerinterface.h
index 7b579f52fe0..56a3022e6c0 100644
--- a/src/libs/qmljs/qmljsmodelmanagerinterface.h
+++ b/src/libs/qmljs/qmljsmodelmanagerinterface.h
@@ -110,6 +110,8 @@ public:
         Table _elements;
     };
 
+    typedef QHash<QString, QList<LanguageUtils::FakeMetaObject::ConstPtr> > CppQmlTypeHash;
+
 public:
     ModelManagerInterface(QObject *parent = 0);
     virtual ~ModelManagerInterface();
@@ -132,6 +134,8 @@ public:
 
     virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri) = 0;
 
+    virtual CppQmlTypeHash cppQmlTypes() const = 0;
+
 signals:
     void documentUpdated(QmlJS::Document::Ptr doc);
     void documentChangedOnDisk(QmlJS::Document::Ptr doc);
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index 263e0c1a81a..4860027f693 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -291,8 +291,7 @@ public:
     void operator()()
     {
         _doc->check(_mode);
-        // temporarily disabled because it's too expensive
-        //_doc->findExposedQmlTypes();
+        _doc->findExposedQmlTypes();
         _doc->releaseSource();
         _doc->releaseTranslationUnit();
 
@@ -1548,32 +1547,34 @@ static void populate(LanguageUtils::FakeMetaObject::Ptr fmo, Class *klass,
     }
 }
 
-QList<LanguageUtils::FakeMetaObject::ConstPtr> CppModelManager::exportedQmlObjects() const
+QList<LanguageUtils::FakeMetaObject::ConstPtr> CppModelManager::exportedQmlObjects(const Document::Ptr &doc) const
 {
     using namespace LanguageUtils;
     QList<FakeMetaObject::ConstPtr> exportedObjects;
     QHash<Class *, FakeMetaObject::Ptr> classes;
 
+    const QList<CPlusPlus::Document::ExportedQmlType> exported = doc->exportedQmlTypes();
+    if (exported.isEmpty())
+        return exportedObjects;
+
+    TypeOfExpression typeOf;
     const Snapshot currentSnapshot = snapshot();
-    foreach (Document::Ptr doc, currentSnapshot) {
-        TypeOfExpression typeOf;
-        typeOf.init(doc, currentSnapshot);
-        foreach (const Document::ExportedQmlType &exportedType, doc->exportedQmlTypes()) {
-            FakeMetaObject::Ptr fmo(new FakeMetaObject);
-            fmo->addExport(exportedType.typeName, exportedType.packageName,
-                           ComponentVersion(exportedType.majorVersion, exportedType.minorVersion));
-            exportedObjects += fmo;
-
-            Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
-            if (!klass)
-                continue;
+    typeOf.init(doc, currentSnapshot);
+    foreach (const Document::ExportedQmlType &exportedType, exported) {
+        FakeMetaObject::Ptr fmo(new FakeMetaObject);
+        fmo->addExport(exportedType.typeName, exportedType.packageName,
+                       ComponentVersion(exportedType.majorVersion, exportedType.minorVersion));
+        exportedObjects += fmo;
+
+        Class *klass = lookupClass(exportedType.typeExpression, exportedType.scope, typeOf);
+        if (!klass)
+            continue;
 
-            // add the no-package export, so the cpp name can be used in properties
-            Overview overview;
-            fmo->addExport(overview(klass->name()), QString(), ComponentVersion());
+        // add the no-package export, so the cpp name can be used in properties
+        Overview overview;
+        fmo->addExport(overview(klass->name()), QString(), ComponentVersion());
 
-            populate(fmo, klass, &classes, typeOf);
-        }
+        populate(fmo, klass, &classes, typeOf);
     }
 
     return exportedObjects;
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index 262a30d99f7..1b79e67bb87 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -131,7 +131,7 @@ public:
 
     virtual void findMacroUsages(const CPlusPlus::Macro &macro);
 
-    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects() const;
+    virtual QList<LanguageUtils::FakeMetaObject::ConstPtr> exportedQmlObjects(const CPlusPlus::Document::Ptr &doc) const;
 
     void setHeaderSuffixes(const QStringList &suffixes)
     { m_headerSuffixes = suffixes; }
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp
index 377d2540e5e..6f0463470d4 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.cpp
+++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp
@@ -40,6 +40,9 @@
 #include <coreplugin/progressmanager/progressmanager.h>
 #include <coreplugin/mimedatabase.h>
 #include <cplusplus/ModelManagerInterface.h>
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/TypeOfExpression.h>
+#include <cplusplus/Overview.h>
 #include <qmljs/qmljsinterpreter.h>
 #include <qmljs/qmljsbind.h>
 #include <qmljs/parser/qmldirparser_p.h>
@@ -77,7 +80,7 @@ ModelManager::ModelManager(QObject *parent):
     m_updateCppQmlTypesTimer = new QTimer(this);
     m_updateCppQmlTypesTimer->setInterval(1000);
     m_updateCppQmlTypesTimer->setSingleShot(true);
-    connect(m_updateCppQmlTypesTimer, SIGNAL(timeout()), SLOT(updateCppQmlTypes()));
+    connect(m_updateCppQmlTypesTimer, SIGNAL(timeout()), SLOT(startCppQmlTypeUpdate()));
 
     qRegisterMetaType<QmlJS::Document::Ptr>("QmlJS::Document::Ptr");
     qRegisterMetaType<QmlJS::LibraryInfo>("QmlJS::LibraryInfo");
@@ -94,7 +97,7 @@ void ModelManager::delayedInitialization()
             CPlusPlus::CppModelManagerInterface::instance();
     if (cppModelManager) {
         connect(cppModelManager, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
-                m_updateCppQmlTypesTimer, SLOT(start()));
+                this, SLOT(queueCppQmlTypeUpdate(CPlusPlus::Document::Ptr)));
     }
 }
 
@@ -555,12 +558,46 @@ void ModelManager::loadPluginTypes(const QString &libraryPath, const QString &im
     m_pluginDumper->loadPluginTypes(libraryPath, importPath, importUri);
 }
 
-void ModelManager::updateCppQmlTypes()
+void ModelManager::queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc)
+{
+    m_queuedCppDocuments.insert(doc->fileName());
+    m_updateCppQmlTypesTimer->start();
+}
+
+void ModelManager::startCppQmlTypeUpdate()
 {
     CPlusPlus::CppModelManagerInterface *cppModelManager =
             CPlusPlus::CppModelManagerInterface::instance();
     if (!cppModelManager)
         return;
 
-    Interpreter::CppQmlTypesLoader::cppObjects = cppModelManager->exportedQmlObjects();
+    QtConcurrent::run(&ModelManager::updateCppQmlTypes,
+                      this, cppModelManager, m_queuedCppDocuments);
+    m_queuedCppDocuments.clear();
+}
+
+void ModelManager::updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files)
+{
+    CppQmlTypeHash newCppTypes = qmlModelManager->cppQmlTypes();
+    CPlusPlus::Snapshot snapshot = cppModelManager->snapshot();
+
+    foreach (const QString &fileName, files) {
+        CPlusPlus::Document::Ptr doc = snapshot.document(fileName);
+        QList<LanguageUtils::FakeMetaObject::ConstPtr> exported;
+        if (doc)
+            exported = cppModelManager->exportedQmlObjects(doc);
+        if (!exported.isEmpty())
+            newCppTypes[fileName] = exported;
+        else
+            newCppTypes.remove(fileName);
+    }
+
+    QMutexLocker locker(&qmlModelManager->m_cppTypesMutex);
+    qmlModelManager->m_cppTypes = newCppTypes;
+}
+
+ModelManagerInterface::CppQmlTypeHash ModelManager::cppQmlTypes() const
+{
+    QMutexLocker locker(&m_cppTypesMutex);
+    return m_cppTypes;
 }
diff --git a/src/plugins/qmljstools/qmljsmodelmanager.h b/src/plugins/qmljstools/qmljsmodelmanager.h
index 1a0b4c2833d..b4a98cabcea 100644
--- a/src/plugins/qmljstools/qmljsmodelmanager.h
+++ b/src/plugins/qmljstools/qmljsmodelmanager.h
@@ -38,6 +38,8 @@
 
 #include <qmljs/qmljsmodelmanagerinterface.h>
 #include <qmljs/qmljsdocument.h>
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/ModelManagerInterface.h>
 
 #include <QFuture>
 #include <QFutureSynchronizer>
@@ -85,6 +87,8 @@ public:
 
     virtual void loadPluginTypes(const QString &libraryPath, const QString &importPath, const QString &importUri);
 
+    virtual CppQmlTypeHash cppQmlTypes() const;
+
 Q_SIGNALS:
     void projectPathChanged(const QString &projectPath);
 
@@ -104,10 +108,12 @@ protected:
     void updateImportPaths();
 
 private slots:
-    void updateCppQmlTypes();
+    void queueCppQmlTypeUpdate(const CPlusPlus::Document::Ptr &doc);
+    void startCppQmlTypeUpdate();
 
 private:
     static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType);
+    static void updateCppQmlTypes(ModelManager *qmlModelManager, CPlusPlus::CppModelManagerInterface *cppModelManager, QSet<QString> files);
 
     mutable QMutex m_mutex;
     Core::ICore *m_core;
@@ -116,7 +122,11 @@ private:
     QStringList m_defaultImportPaths;
 
     QFutureSynchronizer<void> m_synchronizer;
+
     QTimer *m_updateCppQmlTypesTimer;
+    QSet<QString> m_queuedCppDocuments;
+    CppQmlTypeHash m_cppTypes;
+    mutable QMutex m_cppTypesMutex;
 
     // project integration
     QMap<ProjectExplorer::Project *, ProjectInfo> m_projects;
-- 
GitLab