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