From 49c43aaab152ba4e885f53e2b38720c3704a135d Mon Sep 17 00:00:00 2001
From: Christian Kamm <christian.d.kamm@nokia.com>
Date: Wed, 3 Feb 2010 10:59:52 +0100
Subject: [PATCH] Get rid of Environment, introduce external ScopeChain in
 Link.

Done-with: Roberto
---
 src/libs/qmljs/qmljscheck.cpp                 | 22 +++---
 src/libs/qmljs/qmljscheck.h                   |  8 ++-
 src/libs/qmljs/qmljsinterpreter.cpp           | 51 +-------------
 src/libs/qmljs/qmljsinterpreter.h             | 22 +-----
 src/libs/qmljs/qmljslink.cpp                  | 70 +++++++++++--------
 src/libs/qmljs/qmljslink.h                    | 10 ++-
 src/plugins/qmljseditor/qmlcodecompletion.cpp | 47 +++++++------
 src/plugins/qmljseditor/qmlhoverhandler.cpp   |  6 +-
 8 files changed, 94 insertions(+), 142 deletions(-)

diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 7532f48de37..3d24f79054d 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -29,6 +29,7 @@
 
 #include "qmljscheck.h"
 #include "qmljsinterpreter.h"
+#include "qmljslink.h"
 #include "parser/qmljsparser_p.h"
 #include "parser/qmljsast_p.h"
 #include <QtCore/QDebug>
@@ -36,10 +37,10 @@
 using namespace QmlJS;
 using namespace QmlJS::Interpreter;
 
-Check::Check(Interpreter::Engine *engine, Interpreter::Context *context)
-    : _engine(engine),
-      _context(context),
-      _scope(engine->globalObject()),
+Check::Check(Link *link)
+    : _engine(link->engine()),
+      _link(link),
+      _scope(_engine->globalObject()),
       _result(0)
 {
 }
@@ -48,12 +49,9 @@ Check::~Check()
 {
 }
 
-const Interpreter::Value *Check::operator()(AST::Node *ast, const Interpreter::ObjectValue *scope)
+const Interpreter::Value *Check::operator()(AST::Node *ast)
 {
-    const Interpreter::ObjectValue *previousScope = switchScope(scope);
-    const Interpreter::Value *result = check(ast);
-    (void) switchScope(previousScope);
-    return result;
+    return check(ast);
 }
 
 const Interpreter::Value *Check::check(AST::Node *ast)
@@ -68,7 +66,7 @@ const Interpreter::Value *Check::check(AST::Node *ast)
     const Value *result = switchResult(previousResult);
 
     if (const Reference *ref = value_cast<const Reference *>(result))
-        result = ref->value(_context);
+        result = ref->value(_link->context());
 
     if (! result)
         result = _engine->undefinedValue();
@@ -167,7 +165,7 @@ bool Check::visit(AST::UiQualifiedId *ast)
     if (! ast->name)
          return false;
 
-    const Value *value = _scope->lookup(ast->name->asString());
+    const Value *value = _link->lookup(ast->name->asString());
     if (! ast->next) {
         _result = value;
 
@@ -215,7 +213,7 @@ bool Check::visit(AST::IdentifierExpression *ast)
     if (! ast->name)
         return false;
 
-    _result = _scope->lookup(ast->name->asString());
+    _result = _link->lookup(ast->name->asString());
     return false;
 }
 
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index b37375ea7b1..95fef91b3ff 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -35,6 +35,8 @@
 
 namespace QmlJS {
 
+class Link;
+
 namespace Interpreter {
     class Engine;
     class Context;
@@ -46,10 +48,10 @@ namespace Interpreter {
 class QMLJS_EXPORT Check: protected AST::Visitor
 {
 public:
-    Check(Interpreter::Engine *engine, Interpreter::Context *context);
+    Check(Link *link);
     virtual ~Check();
 
-    const Interpreter::Value *operator()(AST::Node *ast, const Interpreter::ObjectValue *scope);
+    const Interpreter::Value *operator()(AST::Node *ast);
 
 protected:
     void accept(AST::Node *node);
@@ -156,7 +158,7 @@ protected:
 private:
     QmlJS::Document::Ptr _doc;
     Interpreter::Engine *_engine;
-    Interpreter::Context *_context;
+    Link *_link;
     const Interpreter::ObjectValue *_scope;
     const Interpreter::Value *_result;
 };
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index b248fd2f94d..6d293ac5e37 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -622,39 +622,6 @@ const Reference *Value::asReference() const
     return 0;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Environment
-////////////////////////////////////////////////////////////////////////////////
-Environment::Environment()
-{
-}
-
-Environment::~Environment()
-{
-}
-
-const Environment *Environment::parent() const
-{
-    return 0;
-}
-
-const Value *Environment::lookup(const QString &name) const
-{
-    if (const Value *member = lookupMember(name))
-        return member;
-
-    else if (const Environment *p = parent())
-        return p->lookup(name);
-
-    else
-        return 0;
-}
-
-const Value *Environment::lookupMember(const QString &) const
-{
-    return 0;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Values
 ////////////////////////////////////////////////////////////////////////////////
@@ -788,8 +755,7 @@ bool MemberProcessor::processGeneratedSlot(const QString &, const Value *)
 
 ObjectValue::ObjectValue(Engine *engine)
     : _engine(engine),
-      _prototype(0),
-      _scope(0)
+      _prototype(0)
 {
     engine->registerObject(this);
 }
@@ -818,16 +784,6 @@ const ObjectValue *ObjectValue::prototype() const
     return _prototype;
 }
 
-const ObjectValue *ObjectValue::scope() const
-{
-    return _scope;
-}
-
-void ObjectValue::setScope(const ObjectValue *scope)
-{
-    _scope = scope;
-}
-
 void ObjectValue::setProperty(const QString &name, const Value *value)
 {
     _members[name] = value;
@@ -893,11 +849,6 @@ void ObjectValue::processMembers(MemberProcessor *processor) const
     }
 }
 
-const Environment *ObjectValue::parent() const
-{
-    return _scope;
-}
-
 const Value *ObjectValue::lookupMember(const QString &name) const
 {
     if (const Value *m = _members.value(name))
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 623d56821c9..f6cc9dc5201 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -150,20 +150,6 @@ template <> Q_INLINE_TEMPLATE const Reference *value_cast(const Value *v)
     else   return 0;
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Execution environment
-////////////////////////////////////////////////////////////////////////////////
-class QMLJS_EXPORT Environment
-{
-public:
-    Environment();
-    virtual ~Environment();
-
-    virtual const Environment *parent() const;
-    virtual const Value *lookup(const QString &name) const;
-    virtual const Value *lookupMember(const QString &name) const;
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 // Value nodes
 ////////////////////////////////////////////////////////////////////////////////
@@ -250,7 +236,7 @@ public:
     virtual void accept(ValueVisitor *) const;
 };
 
-class QMLJS_EXPORT ObjectValue: public Value, public Environment
+class QMLJS_EXPORT ObjectValue: public Value
 {
 public:
     ObjectValue(Engine *engine);
@@ -264,17 +250,12 @@ public:
     const ObjectValue *prototype() const;
     void setPrototype(const ObjectValue *prototype);
 
-    const ObjectValue *scope() const;
-    void setScope(const ObjectValue *scope);
-
     virtual void processMembers(MemberProcessor *processor) const;
 
     virtual const Value *property(const QString &name) const;
     virtual void setProperty(const QString &name, const Value *value);
     virtual void removeProperty(const QString &name);
 
-    // Environment interface
-    virtual const Environment *parent() const;
     virtual const Value *lookupMember(const QString &name) const;
 
     // Value interface
@@ -287,7 +268,6 @@ private:
 private:
     Engine *_engine;
     const ObjectValue *_prototype;
-    const ObjectValue *_scope;
     QHash<QString, const Value *> _members;
     QString _className;
 };
diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp
index 55871970563..5af7edf8308 100644
--- a/src/libs/qmljs/qmljslink.cpp
+++ b/src/libs/qmljs/qmljslink.cpp
@@ -24,69 +24,81 @@ Link::Link(Document::Ptr currentDoc, const Snapshot &snapshot, Interpreter::Engi
 
 Link::~Link()
 {
-    // unset all prototypes and scopes
+    // unset all prototypes
     foreach (Document::Ptr doc, _docs) {
         BindPtr bind = doc->bind();
 
         if (doc->qmlProgram()) {
-            bind->_idEnvironment->setScope(0);
-            bind->_functionEnvironment->setScope(0);
-
             foreach (ObjectValue *object, bind->_qmlObjects) {
                 object->setPrototype(0);
-                object->setScope(0);
             }
-        } else if (doc->jsProgram()) {
-            bind->_rootObjectValue->setScope(0);
         }
     }
 }
 
-static ObjectValue *pushScope(ObjectValue *next, ObjectValue *onto)
+Context *Link::context()
 {
-    onto->setScope(next);
-    return next;
+    return &_context;
 }
 
-Context *Link::context()
+Link::ScopeChain Link::scopeChain() const
 {
-    return &_context;
+    return _scopeChain;
 }
 
-ObjectValue *Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
+Interpreter::Engine *Link::engine()
 {
-    BindPtr bind = doc->bind();
+    return _interp;
+}
 
-    ObjectValue *scopeObject = 0;
-    if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject))
-        scopeObject = bind->_qmlObjects.value(definition);
+void Link::scopeChainAt(Document::Ptr doc, Node *currentObject)
+{
+    _scopeChain.clear();
+
+    if (! doc) {
+        _scopeChain.append(_interp->globalObject());
+        return;
+    }
 
-    if (!scopeObject)
-        return bind->_interp.globalObject();
+    BindPtr bind = doc->bind();
 
     // Build the scope chain.
-    ObjectValue *scopeStart = _typeEnvironments.value(doc.data());
-    ObjectValue *scope = scopeStart;
-    scope = pushScope(bind->_idEnvironment, scope);
-    scope = pushScope(bind->_functionEnvironment, scope);
+    _scopeChain.append(_typeEnvironments.value(doc.data()));
+    _scopeChain.append(bind->_idEnvironment);
+    _scopeChain.append(bind->_functionEnvironment);
 
     foreach (const QString &scriptFile, doc->bind()->includedScripts()) {
         if (Document::Ptr scriptDoc = _snapshot.document(scriptFile)) {
             if (scriptDoc->jsProgram()) {
-                scope = pushScope(scriptDoc->bind()->_rootObjectValue, scope);
+                _scopeChain.append(scriptDoc->bind()->_rootObjectValue);
             }
         }
     }
 
-    scope = pushScope(scopeObject, scope);
-    if (scopeObject != bind->_rootObjectValue)
-        scope = pushScope(bind->_rootObjectValue, scope);
 
-    scope = pushScope(bind->_interp.globalObject(), scope);
+    if (UiObjectDefinition *definition = cast<UiObjectDefinition *>(currentObject)) {
+        ObjectValue *scopeObject = bind->_qmlObjects.value(definition);
+        _scopeChain.append(scopeObject);
+
+        // ### FIXME: should add the root regardless
+        if (scopeObject != bind->_rootObjectValue)
+            _scopeChain.append(bind->_rootObjectValue);
+    }
+
+    _scopeChain.append(bind->_interp.globalObject());
 
     // May want to link to instantiating components from here.
+}
+
+const Value *Link::lookup(const QString &name) const
+{
+    foreach (const ObjectValue *scope, _scopeChain) {
+        if (const Value *member = scope->lookupMember(name)) {
+            return member;
+        }
+    }
 
-    return scopeStart;
+    return _interp->undefinedValue();
 }
 
 void Link::linkImports()
diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h
index 1233412848f..8b622140ed0 100644
--- a/src/libs/qmljs/qmljslink.h
+++ b/src/libs/qmljs/qmljslink.h
@@ -18,15 +18,22 @@ namespace QmlJS {
 */
 class QMLJS_EXPORT Link
 {
+public:
+    typedef QList<const Interpreter::ObjectValue *> ScopeChain;
+
 public:
     // Link all documents in snapshot reachable from doc.
     Link(Document::Ptr doc, const Snapshot &snapshot, Interpreter::Engine *interp);
     ~Link();
 
     Interpreter::Context *context();
+    ScopeChain scopeChain() const;
+    Interpreter::Engine *engine();
 
     // Get the scope chain for the currentObject inside doc.
-    Interpreter::ObjectValue *scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
+    void scopeChainAt(Document::Ptr doc, AST::Node *currentObject);
+
+    const Interpreter::Value *lookup(const QString &name) const;
 
 private:
     static QList<Document::Ptr> reachableDocuments(Document::Ptr startDoc, const Snapshot &snapshot);
@@ -48,6 +55,7 @@ private:
     Interpreter::Context _context;
     QList<Document::Ptr> _docs;
     QHash<Document *, Interpreter::ObjectValue *> _typeEnvironments;
+    ScopeChain _scopeChain;
 };
 
 } // namespace QmlJS
diff --git a/src/plugins/qmljseditor/qmlcodecompletion.cpp b/src/plugins/qmljseditor/qmlcodecompletion.cpp
index 75e7eca331d..277db9d9b3a 100644
--- a/src/plugins/qmljseditor/qmlcodecompletion.cpp
+++ b/src/plugins/qmljseditor/qmlcodecompletion.cpp
@@ -129,10 +129,12 @@ class EnumerateProperties: private Interpreter::MemberProcessor
     QSet<const Interpreter::ObjectValue *> _processed;
     QHash<QString, const Interpreter::Value *> _properties;
     bool _globalCompletion;
+    Link *_link;
 
 public:
-    EnumerateProperties()
-        : _globalCompletion(false)
+    EnumerateProperties(Link *link)
+        : _globalCompletion(false),
+          _link(link)
     {
     }
 
@@ -141,12 +143,16 @@ public:
         _globalCompletion = globalCompletion;
     }
 
-    QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value,
-                                                          bool lookAtScope = false)
+    QHash<QString, const Interpreter::Value *> operator ()(bool lookAtScope = false)
     {
         _processed.clear();
         _properties.clear();
-        enumerateProperties(value, lookAtScope);
+        if (!lookAtScope) {
+            enumerateProperties(_link->scopeChain().first());
+        } else {
+            foreach (const Interpreter::ObjectValue *scope, _link->scopeChain())
+                enumerateProperties(scope);
+        }
         return _properties;
     }
 
@@ -183,25 +189,22 @@ private:
         return true;
     }
 
-    void enumerateProperties(const Interpreter::Value *value, bool lookAtScope)
+    void enumerateProperties(const Interpreter::Value *value)
     {
         if (! value)
             return;
         else if (const Interpreter::ObjectValue *object = value->asObjectValue()) {
-            enumerateProperties(object, lookAtScope);
+            enumerateProperties(object);
         }
     }
 
-    void enumerateProperties(const Interpreter::ObjectValue *object, bool lookAtScope)
+    void enumerateProperties(const Interpreter::ObjectValue *object)
     {
         if (! object || _processed.contains(object))
             return;
 
         _processed.insert(object);
-        enumerateProperties(object->prototype(), /* lookAtScope = */ false);
-
-        if (lookAtScope)
-            enumerateProperties(object->scope(), /* lookAtScope = */ true);
+        enumerateProperties(object->prototype());
 
         object->processMembers(this);
     }
@@ -631,13 +634,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     Interpreter::Engine interp;
 
     // Set up the current scope chain.
-    Interpreter::ObjectValue *scope = interp.globalObject();
     Link link(document, snapshot, &interp);
 
-    if (document) {
-        AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
-        scope = link.scopeChainAt(document, declaringMember);
-    }
+    AST::Node *declaringMember = semanticInfo.declaringMember(editor->position());
+    link.scopeChainAt(document, declaringMember);
+    Link::ScopeChain scope = link.scopeChain();
 
     // Search for the operator that triggered the completion.
     QChar completionOperator;
@@ -647,9 +648,9 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
     if (completionOperator.isSpace() || completionOperator.isNull() || isDelimiter(completionOperator) ||
             (completionOperator == QLatin1Char('(') && m_startPosition != editor->position())) {
         // It's a global completion.
-        EnumerateProperties enumerateProperties;
+        EnumerateProperties enumerateProperties(&link);
         enumerateProperties.setGlobalCompletion(true);
-        QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(scope, /* lookAtScope = */ true));
+        QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(/* lookAtScope = */ true));
         while (it.hasNext()) {
             it.next();
 
@@ -670,15 +671,15 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
         //qDebug() << "expression:" << expression;
 
         if (expression  != 0) {
-            Check evaluate(&interp, link.context());
+            Check evaluate(&link);
 
             // Evaluate the expression under cursor.
-            const Interpreter::Value *value = interp.convertToObject(evaluate(expression , scope));
+            const Interpreter::Value *value = interp.convertToObject(evaluate(expression));
             //qDebug() << "type:" << interp.typeId(value);
 
             if (value && completionOperator == QLatin1Char('.')) { // member completion
-                EnumerateProperties enumerateProperties;
-                QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
+                EnumerateProperties enumerateProperties(&link);
+                QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties());
                 while (it.hasNext()) {
                     it.next();
 
diff --git a/src/plugins/qmljseditor/qmlhoverhandler.cpp b/src/plugins/qmljseditor/qmlhoverhandler.cpp
index 2ee275643c6..167a7c887d0 100644
--- a/src/plugins/qmljseditor/qmlhoverhandler.cpp
+++ b/src/plugins/qmljseditor/qmlhoverhandler.cpp
@@ -173,10 +173,10 @@ void QmlHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, in
 
             Interpreter::Engine interp;
             Link link(qmlDocument, snapshot, &interp);
-            const Interpreter::ObjectValue *scope = link.scopeChainAt(qmlDocument, declaringMember);
+            link.scopeChainAt(qmlDocument, declaringMember);
 
-            Check check(&interp, link.context());
-            const Interpreter::Value *value = check(node, scope);
+            Check check(&link);
+            const Interpreter::Value *value = check(node);
 
             QStringList baseClasses;
             m_toolTip = prettyPrint(value, &interp, &baseClasses);
-- 
GitLab