From 0b2b6f96e9379717d45f02fb946ac090d68b8a4a Mon Sep 17 00:00:00 2001
From: Roberto Raggi <roberto.raggi@nokia.com>
Date: Tue, 22 Sep 2009 17:25:18 +0200
Subject: [PATCH] Initial work on the QML expression evaluator.

---
 src/plugins/duieditor/duidocument.cpp         |  13 +-
 src/plugins/duieditor/duidocument.h           |  40 +++----
 src/plugins/duieditor/duieditor.cpp           |  40 +++++--
 src/plugins/duieditor/duieditor.h             | 111 +++++++++---------
 .../duieditor/qmlexpressionundercursor.cpp    |  10 +-
 .../duieditor/qmlexpressionundercursor.h      |   3 +-
 src/plugins/duieditor/qmllookupcontext.cpp    |  16 +++
 src/plugins/duieditor/qmllookupcontext.h      |   4 +
 .../duieditor/resolveqmlexpression.cpp        |  58 ++++++++-
 src/plugins/duieditor/resolveqmlexpression.h  |  15 ++-
 10 files changed, 210 insertions(+), 100 deletions(-)

diff --git a/src/plugins/duieditor/duidocument.cpp b/src/plugins/duieditor/duidocument.cpp
index 3441406b207..0fb00d48c50 100644
--- a/src/plugins/duieditor/duidocument.cpp
+++ b/src/plugins/duieditor/duidocument.cpp
@@ -78,6 +78,11 @@ QList<DiagnosticMessage> DuiDocument::diagnosticMessages() const
     return _diagnosticMessages;
 }
 
+QString DuiDocument::source() const
+{
+    return _source;
+}
+
 void DuiDocument::setSource(const QString &source)
 {
     _source = source;
@@ -129,9 +134,7 @@ DuiDocument::PtrList Snapshot::importedDocuments(const DuiDocument::Ptr &doc, co
 
     const QString docPath = doc->path() + '/' + importPath;
 
-    for (Iterator i = iterator(); i.hasNext();) {
-        DuiDocument::Ptr candidate = i.next().value();
-
+    foreach (DuiDocument::Ptr candidate, *this) {
         if (candidate == doc)
             continue;
 
@@ -148,9 +151,7 @@ QMap<QString, DuiDocument::Ptr> Snapshot::componentsDefinedByImportedDocuments(c
 
     const QString docPath = doc->path() + '/' + importPath;
 
-    for (Iterator i = iterator(); i.hasNext();) {
-        DuiDocument::Ptr candidate = i.next().value();
-
+    foreach (DuiDocument::Ptr candidate, *this) {
         if (candidate == doc)
             continue;
 
diff --git a/src/plugins/duieditor/duidocument.h b/src/plugins/duieditor/duidocument.h
index 3295956012a..86c88d1b622 100644
--- a/src/plugins/duieditor/duidocument.h
+++ b/src/plugins/duieditor/duidocument.h
@@ -45,23 +45,25 @@ namespace DuiEditor {
 class DUIEDITOR_EXPORT DuiDocument
 {
 public:
-	typedef QSharedPointer<DuiDocument> Ptr;
+    typedef QSharedPointer<DuiDocument> Ptr;
     typedef QList<DuiDocument::Ptr> PtrList;
     typedef QMap<QString, QPair<QmlJS::AST::SourceLocation, QmlJS::AST::Node*> > IdTable;
 
 protected:
-	DuiDocument(const QString &fileName);
+    DuiDocument(const QString &fileName);
 
 public:
-	~DuiDocument();
+    ~DuiDocument();
 
-	static DuiDocument::Ptr create(const QString &fileName);
+    static DuiDocument::Ptr create(const QString &fileName);
 
-	QmlJS::AST::UiProgram *program() const;
-	QList<QmlJS::DiagnosticMessage> diagnosticMessages() const;
+    QmlJS::AST::UiProgram *program() const;
+    QList<QmlJS::DiagnosticMessage> diagnosticMessages() const;
 
-	void setSource(const QString &source);
-	bool parse();
+    QString source() const;
+    void setSource(const QString &source);
+
+    bool parse();
 
     bool isParsedCorrectly() const
     { return _parsedCorrectly; }
@@ -73,30 +75,26 @@ public:
     QString componentName() const { return _componentName; }
 
 private:
-	QmlJS::Engine *_engine;
-	QmlJS::NodePool *_pool;
-	QmlJS::AST::UiProgram *_program;
-	QList<QmlJS::DiagnosticMessage> _diagnosticMessages;
-	QString _fileName;
+    QmlJS::Engine *_engine;
+    QmlJS::NodePool *_pool;
+    QmlJS::AST::UiProgram *_program;
+    QList<QmlJS::DiagnosticMessage> _diagnosticMessages;
+    QString _fileName;
     QString _path;
     QString _componentName;
-	QString _source;
+    QString _source;
     bool _parsedCorrectly;
     IdTable _ids;
 };
 
-class DUIEDITOR_EXPORT Snapshot: protected QMap<QString, DuiDocument::Ptr>
+class DUIEDITOR_EXPORT Snapshot: public QMap<QString, DuiDocument::Ptr>
 {
 public:
-	Snapshot();
-	~Snapshot();
+    Snapshot();
+    ~Snapshot();
 
     void insert(const DuiDocument::Ptr &document);
 
-    typedef QMapIterator<QString, DuiDocument::Ptr> Iterator;
-    Iterator iterator() const
-    { return Iterator(*this); }
-
     DuiDocument::Ptr document(const QString &fileName) const
     { return value(fileName); }
 
diff --git a/src/plugins/duieditor/duieditor.cpp b/src/plugins/duieditor/duieditor.cpp
index 3add88f8de7..a2e12b86716 100644
--- a/src/plugins/duieditor/duieditor.cpp
+++ b/src/plugins/duieditor/duieditor.cpp
@@ -37,7 +37,9 @@
 #include "qmljsastvisitor_p.h"
 #include "qmljsast_p.h"
 #include "qmljsengine_p.h"
-
+#include "qmlexpressionundercursor.h"
+#include "qmllookupcontext.h"
+#include "resolveqmlexpression.h"
 #include "rewriter_p.h"
 
 #include "idcollector.h"
@@ -714,16 +716,32 @@ TextEditor::BaseTextEditor::Link ScriptEditor::findLinkAt(const QTextCursor &cur
     if (!doc)
         return link;
 
-    NavigationTokenFinder finder;
-    finder(doc, cursor.position(), snapshot);
-    if (finder.targetFound()) {
-        link.fileName = finder.fileName();
-        link.pos = finder.linkPosition();
-        link.length = finder.linkLength();
-
-        if (resolveTarget) {
-            link.line = finder.targetLine();
-            link.column = finder.targetColumn() - 1;
+//    NavigationTokenFinder finder;
+//    finder(doc, cursor.position(), snapshot);
+//    if (finder.targetFound()) {
+//        link.fileName = finder.fileName();
+//        link.pos = finder.linkPosition();
+//        link.length = finder.linkLength();
+//
+//        if (resolveTarget) {
+//            link.line = finder.targetLine();
+//            link.column = finder.targetColumn() - 1;
+//        }
+//    }
+
+    QmlExpressionUnderCursor expressionUnderCursor;
+    expressionUnderCursor(cursor, doc->program());
+
+    QmlLookupContext context(expressionUnderCursor.expressionScopes(),
+                             expressionUnderCursor.expressionNode(),
+                             doc, snapshot);
+
+    ResolveQmlExpression resolve(context);
+    if (QmlLookupContext::Symbol *symbol = resolve(expressionUnderCursor.expressionNode())) {
+        if (UiObjectMember *member = static_cast<UiObjectMember *>(symbol)) { // ### FIXME: don't use static_cast<>
+            const int begin = member->firstSourceLocation().begin();
+            const int end = member->lastSourceLocation().end();
+            qDebug() << doc->source().mid(begin, end - begin);
         }
     }
 
diff --git a/src/plugins/duieditor/duieditor.h b/src/plugins/duieditor/duieditor.h
index 9e73fae908c..3149bc482bc 100644
--- a/src/plugins/duieditor/duieditor.h
+++ b/src/plugins/duieditor/duieditor.h
@@ -42,7 +42,7 @@ class QTimer;
 QT_END_NAMESPACE
 
 namespace Core {
-	class ICore;
+class ICore;
 }
 
 namespace DuiEditor {
@@ -52,103 +52,102 @@ class DuiModelManagerInterface;
 namespace Internal {
 
 class DuiHighlighter;
-
 class ScriptEditor;
 
 class ScriptEditorEditable : public TextEditor::BaseTextEditorEditable
 {
-	Q_OBJECT
+    Q_OBJECT
 
 public:
-	ScriptEditorEditable(ScriptEditor *, const QList<int> &);
-	QList<int> context() const;
+    ScriptEditorEditable(ScriptEditor *, const QList<int> &);
+    QList<int> context() const;
 
-	bool duplicateSupported() const { return true; }
-	Core::IEditor *duplicate(QWidget *parent);
-	const char *kind() const;
-        bool isTemporary() const { return false; }
+    bool duplicateSupported() const { return true; }
+    Core::IEditor *duplicate(QWidget *parent);
+    const char *kind() const;
+    bool isTemporary() const { return false; }
 
 private:
-	QList<int> m_context;
+    QList<int> m_context;
 };
 
 
 struct Declaration
 {
-	QString text;
-	int startLine;
-	int startColumn;
-	int endLine;
-	int endColumn;
-
-	Declaration()
-		: startLine(0),
-		  startColumn(0),
-		  endLine(0),
-		  endColumn(0)
-	{ }
+    QString text;
+    int startLine;
+    int startColumn;
+    int endLine;
+    int endColumn;
+
+    Declaration()
+        : startLine(0),
+        startColumn(0),
+        endLine(0),
+        endColumn(0)
+    { }
 };
 
 class ScriptEditor : public TextEditor::BaseTextEditor
 {
-	Q_OBJECT
+    Q_OBJECT
 
 public:
-	typedef QList<int> Context;
+    typedef QList<int> Context;
 
-	ScriptEditor(const Context &context,
-				 QWidget *parent = 0);
-	~ScriptEditor();
+    ScriptEditor(const Context &context,
+                 QWidget *parent = 0);
+    ~ScriptEditor();
 
-	QList<Declaration> declarations() const;
-	QStringList words() const;
-	QStringList keywords() const;
+    QList<Declaration> declarations() const;
+    QStringList words() const;
+    QStringList keywords() const;
 
-	QList<QmlJS::DiagnosticMessage> diagnosticMessages() const
-	{ return m_diagnosticMessages; }
+    QList<QmlJS::DiagnosticMessage> diagnosticMessages() const
+    { return m_diagnosticMessages; }
 
-	virtual void unCommentSelection();
+    virtual void unCommentSelection();
 
     DuiDocument::Ptr duiDocument() const { return m_document; }
 
 public slots:
-	virtual void setFontSettings(const TextEditor::FontSettings &);
+    virtual void setFontSettings(const TextEditor::FontSettings &);
 
 private slots:
     void onDocumentUpdated(DuiDocument::Ptr doc);
 
-	void updateDocument();
-	void updateDocumentNow();
-	void jumpToMethod(int index);
-	void updateMethodBoxIndex();
-	void updateMethodBoxToolTip();
-	void updateFileName();
+    void updateDocument();
+    void updateDocumentNow();
+    void jumpToMethod(int index);
+    void updateMethodBoxIndex();
+    void updateMethodBoxToolTip();
+    void updateFileName();
 
-	// refactoring ops
-	void renameIdUnderCursor();
+    // refactoring ops
+    void renameIdUnderCursor();
 
 protected:
-	void contextMenuEvent(QContextMenuEvent *e);
-	TextEditor::BaseTextEditorEditable *createEditableInterface();
-	void createToolBar(ScriptEditorEditable *editable);
+    void contextMenuEvent(QContextMenuEvent *e);
+    TextEditor::BaseTextEditorEditable *createEditableInterface();
+    void createToolBar(ScriptEditorEditable *editable);
     TextEditor::BaseTextEditor::Link findLinkAt(const QTextCursor &cursor, bool resolveTarget = true);
     virtual void reformat(QTextDocument *doc, QTextBlock block);
 
 private:
-	virtual bool isElectricCharacter(const QChar &ch) const;
-	virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
+    virtual bool isElectricCharacter(const QChar &ch) const;
+    virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
 
-	QString wordUnderCursor() const;
+    QString wordUnderCursor() const;
 
-	const Context m_context;
+    const Context m_context;
 
-	QTimer *m_updateDocumentTimer;
-	QComboBox *m_methodCombo;
-	QList<Declaration> m_declarations;
-	QStringList m_words;
-	QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap
-	QList<QmlJS::DiagnosticMessage> m_diagnosticMessages;
-	DuiDocument::Ptr m_document;
+    QTimer *m_updateDocumentTimer;
+    QComboBox *m_methodCombo;
+    QList<Declaration> m_declarations;
+    QStringList m_words;
+    QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap
+    QList<QmlJS::DiagnosticMessage> m_diagnosticMessages;
+    DuiDocument::Ptr m_document;
     DuiModelManagerInterface *m_modelManager;
 };
 
diff --git a/src/plugins/duieditor/qmlexpressionundercursor.cpp b/src/plugins/duieditor/qmlexpressionundercursor.cpp
index 7b0e0980c5e..18df851ff07 100644
--- a/src/plugins/duieditor/qmlexpressionundercursor.cpp
+++ b/src/plugins/duieditor/qmlexpressionundercursor.cpp
@@ -9,12 +9,20 @@ using namespace QmlJS;
 using namespace QmlJS::AST;
 
 QmlExpressionUnderCursor::QmlExpressionUnderCursor()
+    : _expressionNode(0),
+      _pos(0)
 {
 }
 
-void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor)
+void QmlExpressionUnderCursor::operator()(const QTextCursor &cursor,
+                                          QmlJS::AST::UiProgram *program)
 {
     _pos = cursor.position();
+    _expressionNode = 0;
+    _scopes.clear();
+
+    if (program)
+        program->accept(this);
 }
 
 bool QmlExpressionUnderCursor::visit(QmlJS::AST::Block *ast)
diff --git a/src/plugins/duieditor/qmlexpressionundercursor.h b/src/plugins/duieditor/qmlexpressionundercursor.h
index 3280218f93b..37dab184623 100644
--- a/src/plugins/duieditor/qmlexpressionundercursor.h
+++ b/src/plugins/duieditor/qmlexpressionundercursor.h
@@ -14,7 +14,7 @@ class QmlExpressionUnderCursor: protected QmlJS::AST::Visitor
 public:
     QmlExpressionUnderCursor();
 
-    void operator()(const QTextCursor &cursor);
+    void operator()(const QTextCursor &cursor, QmlJS::AST::UiProgram *program);
 
     QStack<QmlJS::AST::Node *> expressionScopes() const
     { return _expressionScopes; }
@@ -36,7 +36,6 @@ private:
     QStack<QmlJS::AST::Node *> _scopes;
     QStack<QmlJS::AST::Node *> _expressionScopes;
     QmlJS::AST::Node *_expressionNode;
-
     quint32 _pos;
 };
 
diff --git a/src/plugins/duieditor/qmllookupcontext.cpp b/src/plugins/duieditor/qmllookupcontext.cpp
index af9d1961437..e967c342655 100644
--- a/src/plugins/duieditor/qmllookupcontext.cpp
+++ b/src/plugins/duieditor/qmllookupcontext.cpp
@@ -20,3 +20,19 @@ QmlLookupContext::QmlLookupContext(const QStack<QmlJS::AST::Node *> &scopes,
         _snapshot(snapshot)
 {
 }
+
+QmlLookupContext::Symbol *QmlLookupContext::resolve(const QString &name) const
+{
+    // ### TODO: look at property definitions
+
+    // look at the ids.
+    foreach (DuiDocument::Ptr doc, _snapshot) {
+        const DuiDocument::IdTable ids = doc->ids();
+        const QPair<SourceLocation, Node *> use = ids.value(name);
+
+        if (Node *node = use.second)
+            return node;
+    }
+
+    return 0;
+}
diff --git a/src/plugins/duieditor/qmllookupcontext.h b/src/plugins/duieditor/qmllookupcontext.h
index 0ca732bb617..44f50c2eb01 100644
--- a/src/plugins/duieditor/qmllookupcontext.h
+++ b/src/plugins/duieditor/qmllookupcontext.h
@@ -17,6 +17,10 @@ public:
                      const DuiDocument::Ptr &doc,
                      const Snapshot &snapshot);
 
+    typedef QmlJS::AST::Node Symbol; // ### FIXME: this needs to be a class.
+
+    Symbol *resolve(const QString &name) const;
+
 private:
     QStack<QmlJS::AST::Node *> _scopes;
     QmlJS::AST::Node *_expressionNode;
diff --git a/src/plugins/duieditor/resolveqmlexpression.cpp b/src/plugins/duieditor/resolveqmlexpression.cpp
index 895ffbc55de..38311ac30a3 100644
--- a/src/plugins/duieditor/resolveqmlexpression.cpp
+++ b/src/plugins/duieditor/resolveqmlexpression.cpp
@@ -1,6 +1,5 @@
 #include "qmljsast_p.h"
 #include "qmljsengine_p.h"
-
 #include "resolveqmlexpression.h"
 
 using namespace DuiEditor;
@@ -8,6 +7,61 @@ using namespace DuiEditor::Internal;
 using namespace QmlJS;
 using namespace QmlJS::AST;
 
-ResolveQmlExpression::ResolveQmlExpression()
+ResolveQmlExpression::ResolveQmlExpression(const QmlLookupContext &context)
+    : _context(context), _value(0)
+{
+}
+
+QmlLookupContext::Symbol *ResolveQmlExpression::typeOf(Node *node)
+{
+    QmlLookupContext::Symbol *previousValue = switchValue(0);
+    if (node)
+        node->accept(this);
+    return switchValue(previousValue);
+}
+
+
+QmlLookupContext::Symbol *ResolveQmlExpression::switchValue(QmlLookupContext::Symbol *value)
+{
+    QmlLookupContext::Symbol *previousValue = _value;
+    _value = value;
+    return previousValue;
+}
+
+bool ResolveQmlExpression::visit(IdentifierExpression *ast)
 {
+    const QString name = ast->name->asString();
+    _value = _context.resolve(name);
+    return false;
+}
+
+bool ResolveQmlExpression::visit(FieldMemberExpression *ast)
+{
+    const QString memberName = ast->name->asString();
+
+    if (QmlLookupContext::Symbol *base = typeOf(ast->base)) {
+        UiObjectMemberList *members = 0;
+
+        if (UiObjectBinding *uiObjectBinding = cast<UiObjectBinding *>(base)) {
+            if (uiObjectBinding->initializer)
+                members = uiObjectBinding->initializer->members;
+        } else if (UiObjectDefinition *uiObjectDefinition = cast<UiObjectDefinition *>(base)) {
+            if (uiObjectDefinition->initializer)
+                members = uiObjectDefinition->initializer->members;
+        }
+
+        for (UiObjectMemberList *it = members; it; it = it->next) {
+            UiObjectMember *member = it->member;
+
+            if (UiPublicMember *publicMember = cast<UiPublicMember *>(member)) {
+                if (publicMember->name->asString() == memberName) {
+                    _value = publicMember;
+                    break; // we're done.
+                }
+            }
+        }
+    }
+
+    return false;
 }
+
diff --git a/src/plugins/duieditor/resolveqmlexpression.h b/src/plugins/duieditor/resolveqmlexpression.h
index 61377f313f0..8cbea1ec29a 100644
--- a/src/plugins/duieditor/resolveqmlexpression.h
+++ b/src/plugins/duieditor/resolveqmlexpression.h
@@ -2,6 +2,7 @@
 #define RESOLVEQMLEXPRESSION_H
 
 #include "qmljsastvisitor_p.h"
+#include "qmllookupcontext.h"
 
 namespace DuiEditor {
 namespace Internal {
@@ -9,11 +10,23 @@ namespace Internal {
 class ResolveQmlExpression: protected QmlJS::AST::Visitor
 {
 public:
-    ResolveQmlExpression();
+    ResolveQmlExpression(const QmlLookupContext &context);
+
+    QmlLookupContext::Symbol *operator()(QmlJS::AST::Node *node)
+    { return typeOf(node); }
 
 protected:
+    using QmlJS::AST::Visitor::visit;
+
+    QmlLookupContext::Symbol *typeOf(QmlJS::AST::Node *node);
+    QmlLookupContext::Symbol *switchValue(QmlLookupContext::Symbol *symbol);
+
+    virtual bool visit(QmlJS::AST::IdentifierExpression *ast);
+    virtual bool visit(QmlJS::AST::FieldMemberExpression *ast);
 
 private:
+    QmlLookupContext _context;
+    QmlLookupContext::Symbol *_value;
 };
 
 } // namespace Internal
-- 
GitLab