From d7fb215e2e207312c8dd4d0fabd6bb0861b0b669 Mon Sep 17 00:00:00 2001
From: Olivier Goffart <olivier.goffart@nokia.com>
Date: Mon, 30 Aug 2010 12:05:26 +0200
Subject: [PATCH] qml debugger: Use a hash table to store the debugId

This should speed up the matching between the AST nodes and the debugIds
---
 .../qmljsinspector/qmljsclientproxy.cpp       | 40 +++++++++
 src/plugins/qmljsinspector/qmljsclientproxy.h |  7 ++
 .../qmljsinspector/qmljslivetextpreview.cpp   | 82 ++++++-------------
 3 files changed, 71 insertions(+), 58 deletions(-)

diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.cpp b/src/plugins/qmljsinspector/qmljsclientproxy.cpp
index fd0100bd65f..fb9393d71d8 100644
--- a/src/plugins/qmljsinspector/qmljsclientproxy.cpp
+++ b/src/plugins/qmljsinspector/qmljsclientproxy.cpp
@@ -30,6 +30,7 @@
 #include "qmljsclientproxy.h"
 #include "qmljsprivateapi.h"
 #include "qmljsdesigndebugclient.h"
+#include "qmljsinspector.h"
 
 #include <debugger/debuggerplugin.h>
 #include <debugger/debuggerrunner.h>
@@ -37,10 +38,12 @@
 #include <debugger/qml/qmladapter.h>
 #include <extensionsystem/pluginmanager.h>
 #include <utils/qtcassert.h>
+#include <projectexplorer/project.h>
 
 #include <QUrl>
 #include <QAbstractSocket>
 #include <QDebug>
+#include <QFileInfo>
 
 using namespace QmlJSInspector::Internal;
 
@@ -328,6 +331,11 @@ void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state)
     delete query;
 
     if (m_objectTreeQuery.isEmpty()) {
+        int old_count = m_debugIdHash.count();
+        m_debugIdHash.clear();
+        m_debugIdHash.reserve(old_count + 1);
+        foreach(const QDeclarativeDebugObjectReference &it, m_rootObjects)
+            buildDebugIdHashRecursive(it);
         emit objectTreeUpdated();
 
         if (isDesignClientConnected()) {
@@ -339,6 +347,38 @@ void ClientProxy::objectTreeFetched(QDeclarativeDebugQuery::State state)
     }
 }
 
+void ClientProxy::buildDebugIdHashRecursive(const QDeclarativeDebugObjectReference& ref)
+{
+    QString filename = ref.source().url().toLocalFile();
+    int lineNum = ref.source().lineNumber();
+    int colNum = ref.source().columnNumber();
+    int rev = 0;
+    static QRegExp rx("^(.*)_(\\d+):(\\d+)$");
+    if (rx.exactMatch(filename)) {
+        filename = rx.cap(1);
+        rev = rx.cap(2).toInt();
+        lineNum += rx.cap(3).toInt() - 1;
+    }
+
+    //convert the filename to a canonical filename in case of febug build.
+    bool isShadowBuild = InspectorUi::instance()->isShadowBuildProject();
+    if (isShadowBuild && rev == 0) {
+        QString shadowBuildDir = InspectorUi::instance()->debugProjectBuildDirectory();
+
+        //QFileInfo objectFileInfo(filename);
+        if (filename.startsWith(shadowBuildDir)) {
+            ProjectExplorer::Project *debugProject = InspectorUi::instance()->debugProject();
+            filename = debugProject->projectDirectory() + filename.mid(shadowBuildDir.length());
+        }
+    }
+
+    m_debugIdHash[qMakePair<QString, int>(filename, rev)][qMakePair<int, int>(lineNum, colNum)].append(ref.debugId());
+
+    foreach(const QDeclarativeDebugObjectReference &it, ref.children())
+        buildDebugIdHashRecursive(it);
+}
+
+
 void ClientProxy::reloadQmlViewer()
 {
     if (isDesignClientConnected())
diff --git a/src/plugins/qmljsinspector/qmljsclientproxy.h b/src/plugins/qmljsinspector/qmljsclientproxy.h
index a6e3b1cec63..0642f2ee18d 100644
--- a/src/plugins/qmljsinspector/qmljsclientproxy.h
+++ b/src/plugins/qmljsinspector/qmljsclientproxy.h
@@ -42,6 +42,10 @@ class QmlAdapter;
 }
 
 namespace QmlJSInspector {
+
+//map <filename, editorRevision> -> <lineNumber, columnNumber> -> debugIds
+typedef QHash<QPair<QString, int>, QHash<QPair<int, int>, QList<int> > > DebugIdHash;
+
 namespace Internal {
 
 class InspectorPlugin;
@@ -67,6 +71,7 @@ public:
     QList<QDeclarativeDebugObjectReference> objectReferences(const QUrl &url = QUrl()) const;
     QDeclarativeDebugObjectReference objectReferenceForId(int debugId) const;
     QList<QDeclarativeDebugObjectReference> rootObjectReference() const;
+    DebugIdHash debugIdHash() const { return m_debugIdHash; };
 
     bool isConnected() const;
 
@@ -137,6 +142,7 @@ private:
 
 private:
     Q_DISABLE_COPY(ClientProxy);
+    void buildDebugIdHashRecursive(const QDeclarativeDebugObjectReference &ref);
 
     Debugger::Internal::QmlAdapter *m_adapter;
     QDeclarativeEngineDebug *m_client;
@@ -149,6 +155,7 @@ private:
     QList<QDeclarativeDebugObjectReference> m_rootObjects;
     QList<QDeclarativeDebugEngineReference> m_engines;
     QTimer m_requestObjectsTimer;
+    DebugIdHash m_debugIdHash;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
index d4ab06db305..e6645115653 100644
--- a/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
+++ b/src/plugins/qmljsinspector/qmljslivetextpreview.cpp
@@ -71,16 +71,15 @@ class MapObjectWithDebugReference : public Visitor
         virtual bool visit(UiObjectDefinition *ast) ;
         virtual bool visit(UiObjectBinding *ast) ;
 
-        QList<QDeclarativeDebugObjectReference> root;
+        QHash<QPair<int, int>, DebugIdList> ids;
         QString filename;
         QHash<UiObjectMember *, DebugIdList> result;
-        QSet<QmlJS::AST::UiObjectMember *> lookupObjects;
-        Document::Ptr doc;
+        QSet<UiObjectMember *> lookupObjects;
+
     private:
-        bool filenamesMatch(const QString &objectFileName, const QString &buildFilename) const;
+        void process(UiObjectMember *ast);
     private:
         int activated;
-        void processRecursive(const QDeclarativeDebugObjectReference &object, UiObjectMember *ast);
 };
 
 bool MapObjectWithDebugReference::visit(UiObjectDefinition* ast)
@@ -99,66 +98,25 @@ bool MapObjectWithDebugReference::visit(UiObjectBinding* ast)
 
 void MapObjectWithDebugReference::endVisit(UiObjectDefinition* ast)
 {
-    if (lookupObjects.isEmpty() || activated) {
-        foreach(const QDeclarativeDebugObjectReference& it, root)
-            processRecursive(it, ast);
-    }
-
+    process(ast);
     if (lookupObjects.contains(ast))
         activated--;
 }
 
 void MapObjectWithDebugReference::endVisit(UiObjectBinding* ast)
 {
-    if (lookupObjects.isEmpty() || activated) {
-        foreach(const QDeclarativeDebugObjectReference& it, root)
-            processRecursive(it, ast);
-    }
-
+    process(ast);
     if (lookupObjects.contains(ast))
         activated--;
 }
 
-bool MapObjectWithDebugReference::filenamesMatch(const QString &objectFileName, const QString &buildFilename) const
+void MapObjectWithDebugReference::process(UiObjectMember* ast)
 {
-    bool isShadowBuild = InspectorUi::instance()->isShadowBuildProject();
-    ProjectExplorer::Project *debugProject = InspectorUi::instance()->debugProject();
-
-    if (!isShadowBuild) {
-        return (objectFileName == buildFilename);
-    } else {
-        QString projectDir = debugProject->projectDirectory();
-        QString shadowBuildDir = InspectorUi::instance()->debugProjectBuildDirectory();
-
-        QFileInfo objectFileInfo(objectFileName);
-        QFileInfo buildFileInfo(buildFilename);
-        QString objectRelativePath = objectFileInfo.absoluteFilePath().mid(shadowBuildDir.length());
-        QString buildRelativePath = buildFileInfo.absoluteFilePath().mid(projectDir.length());
-
-        return (objectRelativePath == buildRelativePath);
-    }
-}
-
-void MapObjectWithDebugReference::processRecursive(const QDeclarativeDebugObjectReference& object, UiObjectMember* ast)
-{
-    // If this is too slow, it can be speed up by indexing
-    // the QDeclarativeDebugObjectReference by filename/loc in a fist pass
-
-    SourceLocation loc = ast->firstSourceLocation();
-    if (object.source().columnNumber() == int(loc.startColumn)) {
-        QString objectFileName = object.source().url().toLocalFile();
-        if (!doc && object.source().lineNumber() == int(loc.startLine) && filenamesMatch(objectFileName, filename)) {
-            result[ast] += object.debugId();
-        } else if (doc && objectFileName.startsWith(filename + QLatin1Char('_') + QString::number(doc->editorRevision()) + QLatin1Char(':'))) {
-            bool ok;
-            int line = objectFileName.mid(objectFileName.lastIndexOf(':') + 1).toInt(&ok);
-            if (ok && int(loc.startLine) == line + object.source().lineNumber() - 1)
-                result[ast] += object.debugId();
-        }
-    }
-
-    foreach (const QDeclarativeDebugObjectReference &it, object.children()) {
-        processRecursive(it, ast);
+    if (lookupObjects.isEmpty() || activated) {
+        SourceLocation loc = ast->firstSourceLocation();
+        QHash<QPair<int, int>, DebugIdList>::const_iterator it = ids.constFind(qMakePair<int, int>(loc.startLine, loc.startColumn));
+        if (it != ids.constEnd())
+            result[ast].append(*it);
     }
 }
 
@@ -314,10 +272,13 @@ void QmlJSLiveTextPreview::updateDebugIds()
     if (!clientProxy)
         return;
 
-    { // Map all the object that comes from the document as it has been loaded by the server.
+    DebugIdHash::const_iterator it = clientProxy->debugIdHash().constFind(qMakePair<QString, int>(m_initialDoc->fileName(), 0));
+    if (it != clientProxy->debugIdHash().constEnd()) {
+        // Map all the object that comes from the document as it has been loaded by the server.
         const QmlJS::Document::Ptr &doc = m_initialDoc;
+
         MapObjectWithDebugReference visitor;
-        visitor.root = clientProxy->rootObjectReference();
+        visitor.ids = (*it);
         visitor.filename = doc->fileName();
         doc->qmlProgram()->accept(&visitor);
 
@@ -347,11 +308,16 @@ void QmlJSLiveTextPreview::updateDebugIds()
         it != m_createdObjects.constEnd(); ++it) {
 
         const QmlJS::Document::Ptr &doc = it.key();
+
+        DebugIdHash::const_iterator id_it = clientProxy->debugIdHash().constFind(
+            qMakePair<QString, int>(doc->fileName(), doc->editorRevision()));
+        if (id_it == clientProxy->debugIdHash().constEnd())
+            continue;
+
         MapObjectWithDebugReference visitor;
-        visitor.root = clientProxy->rootObjectReference();
+        visitor.ids = *id_it;
         visitor.filename = doc->fileName();
         visitor.lookupObjects = it.value();
-        visitor.doc = doc;
         doc->qmlProgram()->accept(&visitor);
 
         Delta::DebugIdMap debugIds = visitor.result;
-- 
GitLab