diff options
author | Christian Kamm <christian.d.kamm@nokia.com> | 2011-07-12 14:55:27 +0200 |
---|---|---|
committer | Christian Kamm <christian.d.kamm@nokia.com> | 2011-08-08 12:05:03 +0200 |
commit | f87dc6198609abdb58b430cf272a4e6ca7144865 (patch) | |
tree | 738d7024efedab5254676139ca456631e334c78a | |
parent | ed1321a4f95d4ecbbd96794ec355cfc7984cfb2d (diff) | |
download | qt-creator-f87dc6198609abdb58b430cf272a4e6ca7144865.tar.gz |
QmlJS: Split Context and ScopeChain.
Context is created by Link and has information about imports
for all Documents in a Snapshot.
ScopeChain represents how lookup is done at a specific place in
a Document.
Change-Id: I874102d57bbaf1a497fa3f27633bed6ee75dcf10
Reviewed-on: http://codereview.qt.nokia.com/1694
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
22 files changed, 670 insertions, 415 deletions
diff --git a/src/libs/qmljs/qmljs-lib.pri b/src/libs/qmljs/qmljs-lib.pri index 47488948a0..7532a24eb2 100644 --- a/src/libs/qmljs/qmljs-lib.pri +++ b/src/libs/qmljs/qmljs-lib.pri @@ -30,7 +30,8 @@ HEADERS += \ $$PWD/qmljstypedescriptionreader.h \ $$PWD/qmljsscopeastpath.h \ $$PWD/qmljsvalueowner.h \ - $$PWD/qmljscontext.h + $$PWD/qmljscontext.h \ + $$PWD/qmljsscopechain.h SOURCES += \ $$PWD/qmljsbind.cpp \ @@ -52,7 +53,8 @@ SOURCES += \ $$PWD/qmljstypedescriptionreader.cpp \ $$PWD/qmljsscopeastpath.cpp \ $$PWD/qmljsvalueowner.cpp \ - $$PWD/qmljscontext.cpp + $$PWD/qmljscontext.cpp \ + $$PWD/qmljsscopechain.cpp RESOURCES += \ $$PWD/qmljs.qrc diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 1a4bafb350..149812bbbe 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -369,7 +369,7 @@ bool Bind::visit(VariableDeclaration *ast) if (! ast->name) return false; - ASTVariableReference *ref = new ASTVariableReference(ast, &_valueOwner); + ASTVariableReference *ref = new ASTVariableReference(ast, _doc, &_valueOwner); if (_currentObjectValue) _currentObjectValue->setMember(ast->name->asString(), ref); return true; diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 68849e1adc..8924bbaabb 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -365,17 +365,17 @@ private: } // end of anonymous namespace -Check::Check(Document::Ptr doc, const Context *linkedContextNoScope) +Check::Check(Document::Ptr doc, const Context *context) : _doc(doc) - , _context(*linkedContextNoScope) - , _scopeBuilder(&_context, doc) + , _context(*context) + , _scopeChain(doc, &_context) + , _scopeBuilder(&_scopeChain) , _options(WarnDangerousNonStrictEqualityChecks | WarnBlocks | WarnWith | WarnVoid | WarnCommaExpression | WarnExpressionStatement | WarnAssignInCondition | WarnUseBeforeDeclaration | WarnDuplicateDeclaration | WarnCaseWithoutFlowControlEnd | ErrCheckTypeErrors) , _lastValue(0) { - _scopeBuilder.initializeRootScope(); } Check::~Check() @@ -509,8 +509,7 @@ void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId, // suppress subsequent errors about scope object lookup by clearing // the scope object list // ### todo: better way? - _context.scopeChain().qmlScopeObjects.clear(); - _context.scopeChain().update(); + _scopeChain.setQmlScopeObjects(QList<const ObjectValue *>()); } Node::accept(initializer, this); @@ -565,7 +564,7 @@ bool Check::visit(UiScriptBinding *ast) if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) { ExpressionNode *expr = expStmt->expression; - Evaluate evaluator(&_context); + Evaluate evaluator(&_scopeChain); const Value *rhsValue = evaluator(expr); const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(), @@ -606,7 +605,7 @@ bool Check::visit(IdentifierExpression *ast) _lastValue = 0; if (ast->name) { - Evaluate evaluator(&_context); + Evaluate evaluator(&_scopeChain); _lastValue = evaluator.reference(ast); if (!_lastValue) error(ast->identifierToken, tr("unknown identifier")); @@ -683,7 +682,7 @@ bool Check::visit(BinaryExpression *ast) if (ast->op == QSOperator::Equal || ast->op == QSOperator::NotEqual) { bool warn = _options & WarnAllNonStrictEqualityChecks; if (!warn && _options & WarnDangerousNonStrictEqualityChecks) { - Evaluate eval(&_context); + Evaluate eval(&_scopeChain); const Value *lhs = eval(ast->left); const Value *rhs = eval(ast->right); warn = shouldAvoidNonStrictEqualityCheck(ast->left, rhs) @@ -850,7 +849,7 @@ bool Check::visit(DefaultClause *ast) /// ### Maybe put this into the context as a helper method. const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) { - QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects; + QList<const ObjectValue *> scopeObjects = _scopeChain.qmlScopeObjects(); if (scopeObjects.isEmpty()) return 0; @@ -869,7 +868,7 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) bool isAttachedProperty = false; if (! propertyName.isEmpty() && propertyName[0].isUpper()) { isAttachedProperty = true; - if (const ObjectValue *qmlTypes = _context.scopeChain().qmlTypes) + if (const ObjectValue *qmlTypes = _scopeChain.qmlTypes()) scopeObjects += qmlTypes; } diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 5fb2c8bcce..84a0977c56 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -36,6 +36,7 @@ #include <qmljs/qmljsdocument.h> #include <qmljs/qmljscontext.h> #include <qmljs/qmljsscopebuilder.h> +#include <qmljs/qmljsscopechain.h> #include <qmljs/parser/qmljsastvisitor_p.h> #include <QtCore/QCoreApplication> @@ -52,7 +53,8 @@ class QMLJS_EXPORT Check: protected AST::Visitor typedef QSet<QString> StringSet; public: - Check(Document::Ptr doc, const Interpreter::Context *linkedContextNoScope); + // prefer taking root scope chain? + Check(Document::Ptr doc, const Interpreter::Context *context); virtual ~Check(); QList<DiagnosticMessage> operator()(); @@ -127,6 +129,7 @@ private: Document::Ptr _doc; Interpreter::Context _context; + Interpreter::ScopeChain _scopeChain; ScopeBuilder _scopeBuilder; QList<DiagnosticMessage> _messages; diff --git a/src/libs/qmljs/qmljscontext.cpp b/src/libs/qmljs/qmljscontext.cpp index ab914cbfa9..2d659c0ccb 100644 --- a/src/libs/qmljs/qmljscontext.cpp +++ b/src/libs/qmljs/qmljscontext.cpp @@ -41,9 +41,7 @@ using namespace QmlJS::Interpreter; Context::Context(const QmlJS::Snapshot &snapshot, ValueOwner *valueOwner, const ImportsPerDocument &imports) : _snapshot(snapshot), _valueOwner(valueOwner), - _imports(imports), - _qmlScopeObjectIndex(-1), - _qmlScopeObjectSet(false) + _imports(imports) { } @@ -62,16 +60,6 @@ QmlJS::Snapshot Context::snapshot() const return _snapshot; } -const ScopeChain &Context::scopeChain() const -{ - return _scopeChain; -} - -ScopeChain &Context::scopeChain() -{ - return _scopeChain; -} - const Imports *Context::imports(const QmlJS::Document *doc) const { if (!doc) @@ -79,24 +67,6 @@ const Imports *Context::imports(const QmlJS::Document *doc) const return _imports.value(doc).data(); } -const Value *Context::lookup(const QString &name, const ObjectValue **foundInScope) const -{ - QList<const ObjectValue *> scopes = _scopeChain.all(); - for (int index = scopes.size() - 1; index != -1; --index) { - const ObjectValue *scope = scopes.at(index); - - if (const Value *member = scope->lookupMember(name, this)) { - if (foundInScope) - *foundInScope = scope; - return member; - } - } - - if (foundInScope) - *foundInScope = 0; - return _valueOwner->undefinedValue(); -} - const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId *qmlTypeName, UiQualifiedId *qmlTypeNameEnd) const { @@ -147,18 +117,8 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QString const Value *Context::lookupReference(const Value *value) const { - const Reference *reference = value_cast<const Reference *>(value); - if (!reference) - return value; - - if (_referenceStack.contains(reference)) - return 0; - - _referenceStack.append(reference); - const Value *v = reference->value(this); - _referenceStack.removeLast(); - - return v; + ReferenceContext refContext(this); + return refContext.lookupReference(value); } QString Context::defaultPropertyName(const ObjectValue *object) const @@ -176,3 +136,33 @@ QString Context::defaultPropertyName(const ObjectValue *object) const } return QString(); } + +ReferenceContext::ReferenceContext(const Context *context) + : m_context(context) +{} + +const Value *ReferenceContext::lookupReference(const Value *value) +{ + const Reference *reference = value_cast<const Reference *>(value); + if (!reference) + return value; + + if (m_references.contains(reference)) + return reference; // ### error + + m_references.append(reference); + const Value *v = reference->value(this); + m_references.removeLast(); + + return v; +} + +const Context *ReferenceContext::context() const +{ + return m_context; +} + +ReferenceContext::operator const Context *() const +{ + return m_context; +} diff --git a/src/libs/qmljs/qmljscontext.h b/src/libs/qmljs/qmljscontext.h index bd5c4cf03d..5d28c5872a 100644 --- a/src/libs/qmljs/qmljscontext.h +++ b/src/libs/qmljs/qmljscontext.h @@ -45,9 +45,12 @@ class Snapshot; namespace Interpreter { +// shared among threads, completely threadsafe +// currently also safe to copy, but will be deprecated class QMLJS_EXPORT Context { public: + typedef QSharedPointer<Context> Ptr; typedef QHash<const Document *, QSharedPointer<const Imports> > ImportsPerDocument; // Context takes ownership of valueOwner @@ -57,12 +60,8 @@ public: ValueOwner *valueOwner() const; Snapshot snapshot() const; - const ScopeChain &scopeChain() const; - ScopeChain &scopeChain(); - const Imports *imports(const Document *doc) const; - const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const; const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName, AST::UiQualifiedId *qmlTypeNameEnd = 0) const; const ObjectValue *lookupType(const Document *doc, const QStringList &qmlTypeName) const; @@ -71,19 +70,29 @@ public: QString defaultPropertyName(const ObjectValue *object) const; private: - typedef QHash<QString, const Value *> Properties; - Snapshot _snapshot; QSharedPointer<ValueOwner> _valueOwner; ImportsPerDocument _imports; - ScopeChain _scopeChain; - int _qmlScopeObjectIndex; - bool _qmlScopeObjectSet; +}; + +// for looking up references +class QMLJS_EXPORT ReferenceContext +{ +public: + // implicit conversion ok + ReferenceContext(const Context *context); + + const Value *lookupReference(const Value *value); + + const Context *context() const; + operator const Context *() const; - // for avoiding reference cycles during lookup - mutable QList<const Reference *> _referenceStack; +private: + const Context *m_context; + QList<const Reference *> m_references; }; + } // namespace Interpreter } // namespace QmlJS diff --git a/src/libs/qmljs/qmljsevaluate.cpp b/src/libs/qmljs/qmljsevaluate.cpp index 410d3d3a38..973144fbd5 100644 --- a/src/libs/qmljs/qmljsevaluate.cpp +++ b/src/libs/qmljs/qmljsevaluate.cpp @@ -33,15 +33,17 @@ #include "qmljsevaluate.h" #include "qmljscontext.h" #include "qmljsvalueowner.h" +#include "qmljsscopechain.h" #include "parser/qmljsast_p.h" #include <QtCore/QDebug> using namespace QmlJS; using namespace QmlJS::Interpreter; -Evaluate::Evaluate(const Context *context) - : _valueOwner(context->valueOwner()), - _context(context), +Evaluate::Evaluate(const ScopeChain *scopeChain) + : _valueOwner(scopeChain->context()->valueOwner()), + _context(scopeChain->context()), + _scopeChain(scopeChain), _scope(_valueOwner->globalObject()), _result(0) { @@ -165,7 +167,7 @@ bool Evaluate::visit(AST::UiQualifiedId *ast) if (! ast->name) return false; - const Value *value = _context->lookup(ast->name->asString()); + const Value *value = _scopeChain->lookup(ast->name->asString()); if (! ast->next) { _result = value; @@ -213,7 +215,7 @@ bool Evaluate::visit(AST::IdentifierExpression *ast) if (! ast->name) return false; - _result = _context->lookup(ast->name->asString()); + _result = _scopeChain->lookup(ast->name->asString()); return false; } diff --git a/src/libs/qmljs/qmljsevaluate.h b/src/libs/qmljs/qmljsevaluate.h index b7fc24fabe..d7316449bd 100644 --- a/src/libs/qmljs/qmljsevaluate.h +++ b/src/libs/qmljs/qmljsevaluate.h @@ -35,6 +35,7 @@ #include "parser/qmljsastvisitor_p.h" #include "qmljsdocument.h" +#include "qmljsscopechain.h" namespace QmlJS { @@ -49,7 +50,7 @@ namespace Interpreter { class QMLJS_EXPORT Evaluate: protected AST::Visitor { public: - Evaluate(const Interpreter::Context *context); + Evaluate(const Interpreter::ScopeChain *scopeChain); virtual ~Evaluate(); // same as value() @@ -165,6 +166,7 @@ private: QmlJS::Document::Ptr _doc; Interpreter::ValueOwner *_valueOwner; const Interpreter::Context *_context; + const Interpreter::ScopeChain *_scopeChain; const Interpreter::ObjectValue *_scope; const Interpreter::Value *_result; }; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 1002f96162..d44c6065fd 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -35,8 +35,9 @@ #include "qmljslink.h" #include "qmljsbind.h" #include "qmljsscopebuilder.h" -#include "qmljstypedescriptionreader.h" +#include "qmljsscopechain.h" #include "qmljsscopeastpath.h" +#include "qmljstypedescriptionreader.h" #include "qmljsvalueowner.h" #include "qmljscontext.h" #include "parser/qmljsast_p.h" @@ -688,82 +689,6 @@ void StringValue::accept(ValueVisitor *visitor) const visitor->visit(this); } - -ScopeChain::ScopeChain() - : globalScope(0) - , qmlTypes(0) - , jsImports(0) -{ -} - -ScopeChain::QmlComponentChain::QmlComponentChain() -{ -} - -ScopeChain::QmlComponentChain::~QmlComponentChain() -{ - qDeleteAll(instantiatingComponents); -} - -void ScopeChain::QmlComponentChain::clear() -{ - qDeleteAll(instantiatingComponents); - instantiatingComponents.clear(); - document = Document::Ptr(0); -} - -void ScopeChain::QmlComponentChain::collect(QList<const ObjectValue *> *list) const -{ - foreach (const QmlComponentChain *parent, instantiatingComponents) - parent->collect(list); - - if (!document) - return; - - if (ObjectValue *root = document->bind()->rootObjectValue()) - list->append(root); - if (ObjectValue *ids = document->bind()->idEnvironment()) - list->append(ids); -} - -void ScopeChain::update() -{ - _all.clear(); - - _all += globalScope; - - // the root scope in js files doesn't see instantiating components - if (jsScopes.count() != 1 || !qmlScopeObjects.isEmpty()) { - if (qmlComponentScope) { - foreach (const QmlComponentChain *parent, qmlComponentScope->instantiatingComponents) - parent->collect(&_all); - } - } - - ObjectValue *root = 0; - ObjectValue *ids = 0; - if (qmlComponentScope && qmlComponentScope->document) { - root = qmlComponentScope->document->bind()->rootObjectValue(); - ids = qmlComponentScope->document->bind()->idEnvironment(); - } - - if (root && !qmlScopeObjects.contains(root)) - _all += root; - _all += qmlScopeObjects; - if (ids) - _all += ids; - if (qmlTypes) - _all += qmlTypes; - if (jsImports) - _all += jsImports; - _all += jsScopes; -} - -QList<const ObjectValue *> ScopeChain::all() const -{ - return _all; -} - Reference::Reference(ValueOwner *valueOwner) : _valueOwner(valueOwner) { @@ -789,7 +714,7 @@ void Reference::accept(ValueVisitor *visitor) const visitor->visit(this); } -const Value *Reference::value(const Context *) const +const Value *Reference::value(const ReferenceContext *) const { return _valueOwner->undefinedValue(); } @@ -1771,8 +1696,10 @@ const QmlJS::Document *ASTObjectValue::document() const return _doc; } -ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, ValueOwner *valueOwner) - : Reference(valueOwner), _ast(ast) +ASTVariableReference::ASTVariableReference(VariableDeclaration *ast, const QmlJS::Document *doc, ValueOwner *valueOwner) + : Reference(valueOwner) + , _ast(ast) + , _doc(doc) { } @@ -1780,10 +1707,18 @@ ASTVariableReference::~ASTVariableReference() { } -const Value *ASTVariableReference::value(const Context *context) const +const Value *ASTVariableReference::value(const ReferenceContext *referenceContext) const { - Evaluate check(context); - return check(_ast->expression); + if (!_ast->expression) + return valueOwner()->undefinedValue(); + + Document::Ptr doc = _doc->ptr(); + ScopeChain scopeChain(doc, referenceContext->context()); + QmlJS::ScopeBuilder builder(&scopeChain); + builder.push(QmlJS::ScopeAstPath(doc)(_ast->expression->firstSourceLocation().begin())); + + QmlJS::Evaluate evaluator(&scopeChain); + return evaluator(_ast->expression); } ASTFunctionValue::ASTFunctionValue(FunctionExpression *ast, const QmlJS::Document *doc, ValueOwner *valueOwner) @@ -1859,9 +1794,9 @@ UiQualifiedId *QmlPrototypeReference::qmlTypeName() const return _qmlTypeName; } -const Value *QmlPrototypeReference::value(const Context *context) const +const Value *QmlPrototypeReference::value(const ReferenceContext *referenceContext) const { - return context->lookupType(_doc, _qmlTypeName); + return referenceContext->context()->lookupType(_doc, _qmlTypeName); } ASTPropertyReference::ASTPropertyReference(UiPublicMember *ast, const QmlJS::Document *doc, ValueOwner *valueOwner) @@ -1886,7 +1821,7 @@ bool ASTPropertyReference::getSourceLocation(QString *fileName, int *line, int * return true; } -const Value *ASTPropertyReference::value(const Context *context) const +const Value *ASTPropertyReference::value(const ReferenceContext *referenceContext) const { if (_ast->statement && (!_ast->memberType || _ast->memberType->asString() == QLatin1String("variant") @@ -1896,15 +1831,14 @@ const Value *ASTPropertyReference::value(const Context *context) const // ### Improve efficiency by caching the 'use chain' constructed in ScopeBuilder. QmlJS::Document::Ptr doc = _doc->ptr(); - Context localContext(*context); - QmlJS::ScopeBuilder builder(&localContext, doc); - builder.initializeRootScope(); + ScopeChain scopeChain(doc, referenceContext->context()); + QmlJS::ScopeBuilder builder(&scopeChain); int offset = _ast->statement->firstSourceLocation().begin(); builder.push(ScopeAstPath(doc)(offset)); - Evaluate check(&localContext); - return check(_ast->statement); + QmlJS::Evaluate evaluator(&scopeChain); + return evaluator(_ast->statement); } if (_ast->memberType) @@ -1934,7 +1868,7 @@ bool ASTSignalReference::getSourceLocation(QString *fileName, int *line, int *co return true; } -const Value *ASTSignalReference::value(const Context *) const +const Value *ASTSignalReference::value(const ReferenceContext *) const { return valueOwner()->undefinedValue(); } diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 85a49a5ee6..c88e7aaa79 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -75,6 +75,7 @@ class Imports; class TypeScope; class JSImportScope; class Context; +class ReferenceContext; typedef QList<const Value *> ValueList; @@ -283,40 +284,6 @@ public: virtual bool processGeneratedSlot(const QString &name, const Value *value); }; -class QMLJS_EXPORT ScopeChain -{ -public: - ScopeChain(); - - class QMLJS_EXPORT QmlComponentChain - { - Q_DISABLE_COPY(QmlComponentChain) - public: - QmlComponentChain(); - ~QmlComponentChain(); - - QList<const QmlComponentChain *> instantiatingComponents; - Document::Ptr document; - - void collect(QList<const ObjectValue *> *list) const; - void clear(); - }; - - const ObjectValue *globalScope; - QSharedPointer<const QmlComponentChain> qmlComponentScope; - QList<const ObjectValue *> qmlScopeObjects; - const TypeScope *qmlTypes; - const JSImportScope *jsImports; - QList<const ObjectValue *> jsScopes; - - // rebuilds the flat list of all scopes - void update(); - QList<const ObjectValue *> all() const; - -private: - QList<const ObjectValue *> _all; -}; - class QMLJS_EXPORT Reference: public Value { public: @@ -330,10 +297,10 @@ public: virtual void accept(ValueVisitor *) const; private: - virtual const Value *value(const Context *context) const; + virtual const Value *value(const ReferenceContext *referenceContext) const; ValueOwner *_valueOwner; - friend class Context; + friend class ReferenceContext; }; class QMLJS_EXPORT ColorValue: public Value @@ -724,7 +691,7 @@ public: AST::UiQualifiedId *qmlTypeName() const; private: - virtual const Value *value(const Context *context) const; + virtual const Value *value(const ReferenceContext *referenceContext) const; AST::UiQualifiedId *_qmlTypeName; const Document *_doc; @@ -733,13 +700,14 @@ private: class QMLJS_EXPORT ASTVariableReference: public Reference { AST::VariableDeclaration *_ast; + const Document *_doc; public: - ASTVariableReference(AST::VariableDeclaration *ast, ValueOwner *valueOwner); + ASTVariableReference(AST::VariableDeclaration *ast, const Document *doc, ValueOwner *valueOwner); virtual ~ASTVariableReference(); private: - virtual const Value *value(const Context *context) const; + virtual const Value *value(const ReferenceContext *referenceContext) const; }; class QMLJS_EXPORT ASTFunctionValue: public FunctionValue @@ -779,7 +747,7 @@ public: virtual bool getSourceLocation(QString *fileName, int *line, int *column) const; private: - virtual const Value *value(const Context *context) const; + virtual const Value *value(const ReferenceContext *referenceContext) const; }; class QMLJS_EXPORT ASTSignalReference: public Reference @@ -798,7 +766,7 @@ public: virtual bool getSourceLocation(QString *fileName, int *line, int *column) const; private: - virtual const Value *value(const Context *context) const; + virtual const Value *value(const ReferenceContext *referenceContext) const; }; class QMLJS_EXPORT ASTObjectValue: public ObjectValue diff --git a/src/libs/qmljs/qmljslookupcontext.cpp b/src/libs/qmljs/qmljslookupcontext.cpp index 04cc68938f..389a23fb86 100644 --- a/src/libs/qmljs/qmljslookupcontext.cpp +++ b/src/libs/qmljs/qmljslookupcontext.cpp @@ -36,6 +36,7 @@ #include "qmljsscopebuilder.h" #include "qmljsmodelmanagerinterface.h" #include "qmljsevaluate.h" +#include "qmljsscopechain.h" using namespace QmlJS; @@ -47,26 +48,26 @@ public: , context(Link(snapshot, ModelManagerInterface::instance()->importPaths(), ModelManagerInterface::instance()->builtins(doc))()) + , scopeChain(doc, &context) { - ScopeBuilder scopeBuilder(&context, doc); - scopeBuilder.initializeRootScope(); + ScopeBuilder scopeBuilder(&scopeChain); scopeBuilder.push(path); } LookupContextData(Document::Ptr doc, - const Interpreter::Context &contextWithoutScope, + const Interpreter::Context &context, const QList<AST::Node *> &path) : doc(doc) - , context(contextWithoutScope) + , context(context) + , scopeChain(doc, &context) { - ScopeBuilder scopeBuilder(&context, doc); - scopeBuilder.initializeRootScope(); + ScopeBuilder scopeBuilder(&scopeChain); scopeBuilder.push(path); } Document::Ptr doc; Interpreter::Context context; - // snapshot is in context + Interpreter::ScopeChain scopeChain; }; LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path) @@ -75,9 +76,9 @@ LookupContext::LookupContext(Document::Ptr doc, const Snapshot &snapshot, const } LookupContext::LookupContext(const Document::Ptr doc, - const Interpreter::Context &contextWithoutScope, + const Interpreter::Context &context, const QList<AST::Node *> &path) - : d(new LookupContextData(doc, contextWithoutScope, path)) + : d(new LookupContextData(doc, context, path)) { } @@ -92,16 +93,16 @@ LookupContext::Ptr LookupContext::create(Document::Ptr doc, const Snapshot &snap } LookupContext::Ptr LookupContext::create(const Document::Ptr doc, - const Interpreter::Context &linkedContextWithoutScope, + const Interpreter::Context &context, const QList<AST::Node *> &path) { - Ptr ptr(new LookupContext(doc, linkedContextWithoutScope, path)); + Ptr ptr(new LookupContext(doc, context, path)); return ptr; } const Interpreter::Value *LookupContext::evaluate(AST::Node *node) const { - Evaluate check(&d->context); + Evaluate check(&d->scopeChain); return check(node); } @@ -126,3 +127,8 @@ const Interpreter::Context *LookupContext::context() const { return &d->context; } + +const Interpreter::ScopeChain &LookupContext::scopeChain() const +{ + return d->scopeChain; +} diff --git a/src/libs/qmljs/qmljslookupcontext.h b/src/libs/qmljs/qmljslookupcontext.h index ce4199e092..f71ae80284 100644 --- a/src/libs/qmljs/qmljslookupcontext.h +++ b/src/libs/qmljs/qmljslookupcontext.h @@ -47,6 +47,7 @@ class LookupContextData; namespace Interpreter { class Value; class Context; +class ScopeChain; } class QMLJS_EXPORT LookupContext @@ -55,7 +56,7 @@ class QMLJS_EXPORT LookupContext LookupContext(const Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path); LookupContext(const Document::Ptr doc, - const Interpreter::Context &linkedContextWithoutScope, + const Interpreter::Context &context, const QList<AST::Node *> &path); public: @@ -67,7 +68,7 @@ public: static Ptr create(const Document::Ptr doc, const Snapshot &snapshot, const QList<AST::Node *> &path); static Ptr create(const Document::Ptr doc, - const Interpreter::Context &linkedContextWithoutScope, + const Interpreter::Context &context, const QList<AST::Node *> &path); const Interpreter::Value *evaluate(AST::Node *node) const; @@ -76,6 +77,7 @@ public: Snapshot snapshot() const; Interpreter::ValueOwner *valueOwner() const; const Interpreter::Context *context() const; + const Interpreter::ScopeChain &scopeChain() const; private: QScopedPointer<LookupContextData> d; diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index 10f18e6a80..90e97502df 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -35,15 +35,15 @@ #include "qmljsbind.h" #include "qmljscontext.h" #include "qmljsevaluate.h" +#include "qmljsscopechain.h" #include "parser/qmljsast_p.h" using namespace QmlJS; using namespace QmlJS::Interpreter; using namespace QmlJS::AST; -ScopeBuilder::ScopeBuilder(Context *context, Document::Ptr doc) - : _doc(doc) - , _context(context) +ScopeBuilder::ScopeBuilder(ScopeChain *scopeChain) + : _scopeChain(scopeChain) { } @@ -69,16 +69,17 @@ void ScopeBuilder::push(AST::Node *node) case Node::Kind_FunctionExpression: case Node::Kind_UiPublicMember: { - ObjectValue *scope = _doc->bind()->findAttachedJSScope(node); - if (scope) - _context->scopeChain().jsScopes += scope; + ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(node); + if (scope) { + QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes(); + jsScopes += scope; + _scopeChain->setJsScopes(jsScopes); + } break; } default: break; } - - _context->scopeChain().update(); } void ScopeBuilder::push(const QList<AST::Node *> &nodes) @@ -99,9 +100,14 @@ void ScopeBuilder::pop() case Node::Kind_FunctionExpression: case Node::Kind_UiPublicMember: { - ObjectValue *scope = _doc->bind()->findAttachedJSScope(toRemove); - if (scope) - _context->scopeChain().jsScopes.removeLast(); + ObjectValue *scope = _scopeChain->document()->bind()->findAttachedJSScope(toRemove); + if (scope) { + QList<const ObjectValue *> jsScopes = _scopeChain->jsScopes(); + if (!jsScopes.isEmpty()) { + jsScopes.removeLast(); + _scopeChain->setJsScopes(jsScopes); + } + } break; } default: @@ -112,103 +118,12 @@ void ScopeBuilder::pop() if (! _nodes.isEmpty() && (cast<UiObjectDefinition *>(toRemove) || cast<UiObjectBinding *>(toRemove))) setQmlScopeObject(_nodes.last()); - - _context->scopeChain().update(); -} - -void ScopeBuilder::initializeRootScope() -{ - ScopeChain &scopeChain = _context->scopeChain(); - if (scopeChain.qmlComponentScope - && scopeChain.qmlComponentScope->document == _doc) { - return; - } - - scopeChain = ScopeChain(); // reset - - Interpreter::ValueOwner *valueOwner = _context->valueOwner(); - - // ### TODO: This object ought to contain the global namespace additions by QML. - scopeChain.globalScope = valueOwner->globalObject(); - - if (! _doc) { - scopeChain.update(); - return; - } - - Bind *bind = _doc->bind(); - QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes; - const Snapshot &snapshot = _context->snapshot(); - - ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain; - scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain); - if (_doc->qmlProgram()) { - componentScopes.insert(_doc.data(), chain); - makeComponentChain(_doc, snapshot, chain, &componentScopes); - - if (const Imports *imports = _context->imports(_doc.data())) { - scopeChain.qmlTypes = imports->typeScope(); - scopeChain.jsImports = imports->jsImportScope(); - } - } else { - // add scope chains for all components that import this file - foreach (Document::Ptr otherDoc, snapshot) { - foreach (const ImportInfo &import, otherDoc->bind()->imports()) { - if (import.type() == ImportInfo::FileImport && _doc->fileName() == import.name()) { - ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain; - componentScopes.insert(otherDoc.data(), component); - chain->instantiatingComponents += component; - makeComponentChain(otherDoc, snapshot, component, &componentScopes); - } - } - } - - // ### TODO: Which type environment do scripts see? - - if (bind->rootObjectValue()) - scopeChain.jsScopes += bind->rootObjectValue(); - } - - scopeChain.update(); -} - -void ScopeBuilder::makeComponentChain( - Document::Ptr doc, - const Snapshot &snapshot, - ScopeChain::QmlComponentChain *target, - QHash<Document *, ScopeChain::QmlComponentChain *> *components) -{ - if (!doc->qmlProgram()) - return; - - Bind *bind = doc->bind(); - - // add scopes for all components instantiating this one - foreach (Document::Ptr otherDoc, snapshot) { - if (otherDoc == doc) - continue; - if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) { - if (components->contains(otherDoc.data())) { -// target->instantiatingComponents += components->value(otherDoc.data()); - } else { - ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain; - components->insert(otherDoc.data(), component); - target->instantiatingComponents += component; - - makeComponentChain(otherDoc, snapshot, component, components); - } - } - } - - // build this component scope - target->document = doc; } void ScopeBuilder::setQmlScopeObject(Node *node) { - ScopeChain &scopeChain = _context->scopeChain(); - - if (_doc->bind()->isGroupedPropertyBinding(node)) { + QList<const ObjectValue *> qmlScopeObjects; + if (_scopeChain->document()->bind()->isGroupedPropertyBinding(node)) { UiObjectDefinition *definition = cast<UiObjectDefinition *>(node); if (!definition) return; @@ -219,21 +134,21 @@ void ScopeBuilder::setQmlScopeObject(Node *node) if (!object) return; - scopeChain.qmlScopeObjects.clear(); - scopeChain.qmlScopeObjects += object; + qmlScopeObjects += object; + _scopeChain->setQmlScopeObjects(qmlScopeObjects); + return; } - const ObjectValue *scopeObject = _doc->bind()->findQmlObject(node); + const ObjectValue *scopeObject = _scopeChain->document()->bind()->findQmlObject(node); if (scopeObject) { - scopeChain.qmlScopeObjects.clear(); - scopeChain.qmlScopeObjects += scopeObject; + qmlScopeObjects += scopeObject; } else { return; // Probably syntax errors, where we're working with a "recovered" AST. } // check if the object has a Qt.ListElement or Qt.Connections ancestor // ### allow only signal bindings for Connections - PrototypeIterator iter(scopeObject, _context); + PrototypeIterator iter(scopeObject, _scopeChain->context()); iter.next(); while (iter.hasNext()) { const ObjectValue *prototype = iter.next(); @@ -242,15 +157,15 @@ void ScopeBuilder::setQmlScopeObject(Node *node) || qmlMetaObject->className() == QLatin1String("Connections") ) && (qmlMetaObject->packageName() == QLatin1String("Qt") || qmlMetaObject->packageName() == QLatin1String("QtQuick"))) { - scopeChain.qmlScopeObjects.clear(); + qmlScopeObjects.clear(); break; } } } // check if the object has a Qt.PropertyChanges ancestor - const ObjectValue *prototype = scopeObject->prototype(_context); - prototype = isPropertyChangesObject(_context, prototype); + const ObjectValue *prototype = scopeObject->prototype(_scopeChain->context()); + prototype = isPropertyChangesObject(_scopeChain->context(), prototype); // find the target script binding if (prototype) { UiObjectInitializer *initializer = 0; @@ -264,31 +179,33 @@ void ScopeBuilder::setQmlScopeObject(Node *node) if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name && scriptBinding->qualifiedId->name->asString() == QLatin1String("target") && ! scriptBinding->qualifiedId->next) { - Evaluate evaluator(_context); + Evaluate evaluator(_scopeChain); const Value *targetValue = evaluator(scriptBinding->statement); if (const ObjectValue *target = value_cast<const ObjectValue *>(targetValue)) { - scopeChain.qmlScopeObjects.prepend(target); + qmlScopeObjects.prepend(target); } else { - scopeChain.qmlScopeObjects.clear(); + qmlScopeObjects.clear(); } } } } } } + + _scopeChain->setQmlScopeObjects(qmlScopeObjects); } const Value *ScopeBuilder::scopeObjectLookup(AST::UiQualifiedId *id) { // do a name lookup on the scope objects const Value *result = 0; - foreach (const ObjectValue *scopeObject, _context->scopeChain().qmlScopeObjects) { + foreach (const ObjectValue *scopeObject, _scopeChain->qmlScopeObjects()) { const ObjectValue *object = scopeObject; for (UiQualifiedId *it = id; it; it = it->next) { if (!it->name) return 0; - result = object->lookupMember(it->name->asString(), _context); + result = object->lookupMember(it->name->asString(), _scopeChain->context()); if (!result) break; if (it->next) { diff --git a/src/libs/qmljs/qmljsscopebuilder.h b/src/libs/qmljs/qmljsscopebuilder.h index c565a55e12..34982da9a8 100644 --- a/src/libs/qmljs/qmljsscopebuilder.h +++ b/src/libs/qmljs/qmljsscopebuilder.h @@ -34,12 +34,19 @@ #define QMLJSSCOPEBUILDER_H #include <qmljs/qmljsdocument.h> -#include <qmljs/qmljsinterpreter.h> #include <QtCore/QList> namespace QmlJS { +namespace Interpreter { +class QmlComponentChain; +class Context; +class ObjectValue; +class Value; +class ScopeChain; +} + namespace AST { class Node; } @@ -47,11 +54,9 @@ namespace AST { class QMLJS_EXPORT ScopeBuilder { public: - ScopeBuilder(Interpreter::Context *context, Document::Ptr doc); + ScopeBuilder(Interpreter::ScopeChain *scopeChain); ~ScopeBuilder(); - void initializeRootScope(); - void push(AST::Node *node); void push(const QList<AST::Node *> &nodes); void pop(); @@ -59,15 +64,10 @@ public: static const Interpreter::ObjectValue *isPropertyChangesObject(const Interpreter::Context *context, const Interpreter::ObjectValue *object); private: - void makeComponentChain(Document::Ptr doc, const Snapshot &snapshot, - Interpreter::ScopeChain::QmlComponentChain *target, - QHash<Document *, Interpreter::ScopeChain::QmlComponentChain *> *components); - void setQmlScopeObject(AST::Node *node); const Interpreter::Value *scopeObjectLookup(AST::UiQualifiedId *id); - Document::Ptr _doc; - Interpreter::Context *_context; + Interpreter::ScopeChain *_scopeChain; QList<AST::Node *> _nodes; }; diff --git a/src/libs/qmljs/qmljsscopechain.cpp b/src/libs/qmljs/qmljsscopechain.cpp new file mode 100644 index 0000000000..2b5cda90f8 --- /dev/null +++ b/src/libs/qmljs/qmljsscopechain.cpp @@ -0,0 +1,291 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#include "qmljsscopechain.h" +#include "qmljsbind.h" + +using namespace QmlJS; +using namespace QmlJS::Interpreter; + +QmlComponentChain::QmlComponentChain(const Document::Ptr &document) + : m_document(document) +{ +} + +QmlComponentChain::~QmlComponentChain() +{ + qDeleteAll(m_instantiatingComponents); +} + +Document::Ptr QmlComponentChain::document() const +{ + return m_document; +} + +QList<const QmlComponentChain *> QmlComponentChain::instantiatingComponents() const +{ + return m_instantiatingComponents; +} + +void QmlComponentChain::addInstantiatingComponent(const QmlComponentChain *component) +{ + m_instantiatingComponents.append(component); +} + + +ScopeChain::ScopeChain(const Document::Ptr &document, const Context *context) + : m_document(document) + , m_context(context) + , m_globalScope(0) + , m_qmlTypes(0) + , m_jsImports(0) + , m_modified(false) +{ + initializeRootScope(); +} + +Document::Ptr ScopeChain::document() const +{ + return m_document; +} + +const Context *ScopeChain::context() const +{ + return m_context; +} + +const Value * ScopeChain::lookup(const QString &name, const ObjectValue **foundInScope) const +{ + QList<const ObjectValue *> scopes = all(); + for (int index = scopes.size() - 1; index != -1; --index) { + const ObjectValue *scope = scopes.at(index); + + if (const Value *member = scope->lookupMember(name, m_context)) { + if (foundInScope) + *foundInScope = scope; + return member; + } + } + + if (foundInScope) + *foundInScope = 0; + return m_context->valueOwner()->undefinedValue(); +} + +const ObjectValue *ScopeChain::globalScope() const +{ + return m_globalScope; +} + +void ScopeChain::setGlobalScope(const ObjectValue *globalScope) +{ + m_modified = true; + m_globalScope = globalScope; +} + +QSharedPointer<const QmlComponentChain> ScopeChain::qmlComponentChain() const +{ + return m_qmlComponentScope; +} + +void ScopeChain::setQmlComponentChain(const QSharedPointer<const QmlComponentChain> &qmlComponentChain) +{ + m_modified = true; + m_qmlComponentScope = qmlComponentChain; +} + +QList<const ObjectValue *> ScopeChain::qmlScopeObjects() const +{ + return m_qmlScopeObjects; +} + +void ScopeChain::setQmlScopeObjects(const QList<const ObjectValue *> &qmlScopeObjects) +{ + m_modified = true; + m_qmlScopeObjects = qmlScopeObjects; +} + +const TypeScope *ScopeChain::qmlTypes() const +{ + return m_qmlTypes; +} + +void ScopeChain::setQmlTypes(const TypeScope *qmlTypes) +{ + m_modified = true; + m_qmlTypes = qmlTypes; +} + +const JSImportScope *ScopeChain::jsImports() const +{ + return m_jsImports; +} + +void ScopeChain::setJsImports(const JSImportScope *jsImports) +{ + m_modified = true; + m_jsImports = jsImports; +} + +QList<const ObjectValue *> ScopeChain::jsScopes() const +{ + return m_jsScopes; +} + +void ScopeChain::setJsScopes(const QList<const ObjectValue *> &jsScopes) +{ + m_modified = true; + m_jsScopes = jsScopes; +} + +QList<const ObjectValue *> ScopeChain::all() const +{ + if (m_modified) + update(); + return m_all; +} + +static void collectScopes(const QmlComponentChain *chain, QList<const ObjectValue *> *target) +{ + foreach (const QmlComponentChain *parent, chain->instantiatingComponents()) + collectScopes(parent, target); + + if (!chain->document()) + return; + + if (ObjectValue *root = chain->document()->bind()->rootObjectValue()) + target->append(root); + if (ObjectValue *ids = chain->document()->bind()->idEnvironment()) + target->append(ids); +} + +void ScopeChain::update() const +{ + m_all.clear(); + + m_all += m_globalScope; + + // the root scope in js files doesn't see instantiating components + if (m_jsScopes.count() != 1 || !m_qmlScopeObjects.isEmpty()) { + if (m_qmlComponentScope) { + foreach (const QmlComponentChain *parent, m_qmlComponentScope->instantiatingComponents()) + collectScopes(parent, &m_all); + } + } + + ObjectValue *root = 0; + ObjectValue *ids = 0; + if (m_qmlComponentScope && m_qmlComponentScope->document()) { + const Bind *bind = m_qmlComponentScope->document()->bind(); + root = bind->rootObjectValue(); + ids = bind->idEnvironment(); + } + + if (root && !m_qmlScopeObjects.contains(root)) + m_all += root; + m_all += m_qmlScopeObjects; + if (ids) + m_all += ids; + if (m_qmlTypes) + m_all += m_qmlTypes; + if (m_jsImports) + m_all += m_jsImports; + m_all += m_jsScopes; +} + +void ScopeChain::initializeRootScope() +{ + ValueOwner *valueOwner = m_context->valueOwner(); + const Snapshot &snapshot = m_context->snapshot(); + Bind *bind = m_document->bind(); + + m_globalScope = valueOwner->globalObject(); + + QHash<Document *, QmlComponentChain *> componentScopes; + QmlComponentChain *chain = new QmlComponentChain(m_document); + m_qmlComponentScope = QSharedPointer<const QmlComponentChain>(chain); + + if (m_document->qmlProgram()) { + componentScopes.insert(m_document.data(), chain); + makeComponentChain(chain, snapshot, &componentScopes); + + if (const Imports *imports = m_context->imports(m_document.data())) { + m_qmlTypes = imports->typeScope(); + m_jsImports = imports->jsImportScope(); + } + } else { + // add scope chains for all components that import this file + foreach (Document::Ptr otherDoc, snapshot) { + foreach (const ImportInfo &import, otherDoc->bind()->imports()) { + if (import.type() == ImportInfo::FileImport && m_document->fileName() == import.name()) { + QmlComponentChain *component = new QmlComponentChain(otherDoc); + componentScopes.insert(otherDoc.data(), component); + chain->addInstantiatingComponent(component); + makeComponentChain(component, snapshot, &componentScopes); + } + } + } + + // ### TODO: Which type environment do scripts see? + + if (bind->rootObjectValue()) + m_jsScopes += bind->rootObjectValue(); + } +} + +void ScopeChain::makeComponentChain( + QmlComponentChain *target, + const Snapshot &snapshot, + QHash<Document *, QmlComponentChain *> *components) +{ + Document::Ptr doc = target->document(); + if (!doc->qmlProgram()) + return; + + const Bind *bind = doc->bind(); + + // add scopes for all components instantiating this one + foreach (Document::Ptr otherDoc, snapshot) { + if (otherDoc == doc) + continue; + if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), m_context)) { + if (!components->contains(otherDoc.data())) { + QmlComponentChain *component = new QmlComponentChain(otherDoc); + components->insert(otherDoc.data(), component); + target->addInstantiatingComponent(component); + + makeComponentChain(component, snapshot, components); + } + } + } +} + diff --git a/src/libs/qmljs/qmljsscopechain.h b/src/libs/qmljs/qmljsscopechain.h new file mode 100644 index 0000000000..b937cc6809 --- /dev/null +++ b/src/libs/qmljs/qmljsscopechain.h @@ -0,0 +1,127 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (info@qt.nokia.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef QMLJS_SCOPECHAIN_H +#define QMLJS_SCOPECHAIN_H + +#include "qmljs_global.h" +#include "qmljsdocument.h" +#include "qmljscontext.h" + +#include <QtCore/QList> +#include <QtCore/QSharedPointer> + +namespace QmlJS { +namespace Interpreter { + +class ObjectValue; +class TypeScope; +class JSImportScope; +class Context; +class Value; + +class QMLJS_EXPORT QmlComponentChain +{ + Q_DISABLE_COPY(QmlComponentChain) +public: + QmlComponentChain(const Document::Ptr &document); + ~QmlComponentChain(); + + Document::Ptr document() const; + QList<const QmlComponentChain *> instantiatingComponents() const; + + // takes ownership + void addInstantiatingComponent(const QmlComponentChain *component); + +private: + QList<const QmlComponentChain *> m_instantiatingComponents; + Document::Ptr m_document; +}; + +// scope chains are copyable +// constructing a new scope chain is currently too expensive: +// building the instantiating component chain needs to be sped up +class QMLJS_EXPORT ScopeChain +{ +public: + explicit ScopeChain(const Document::Ptr &document, const Context *context); + + Document::Ptr document() const; + const Context *context() const; + + const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const; + + const ObjectValue *globalScope() const; + void setGlobalScope(const ObjectValue *globalScope); + + QSharedPointer<const QmlComponentChain> qmlComponentChain() const; + void setQmlComponentChain(const QSharedPointer<const QmlComponentChain> &qmlComponentChain); + + QList<const ObjectValue *> qmlScopeObjects() const; + void setQmlScopeObjects(const QList<const ObjectValue *> &qmlScopeObjects); + + const TypeScope *qmlTypes() const; + void setQmlTypes(const TypeScope *qmlTypes); + + const JSImportScope *jsImports() const; + void setJsImports(const JSImportScope *jsImports); + + QList<const ObjectValue *> jsScopes() const; + void setJsScopes(const QList<const ObjectValue *> &jsScopes); + + QList<const ObjectValue *> all() const; + +private: + void update() const; + void initializeRootScope(); + void makeComponentChain(QmlComponentChain *target, const Snapshot &snapshot, + QHash<Document *, QmlComponentChain *> *components); + + + Document::Ptr m_document; + const Context *m_context; + + const ObjectValue *m_globalScope; + QSharedPointer<const QmlComponentChain> m_qmlComponentScope; + QList<const ObjectValue *> m_qmlScopeObjects; + const TypeScope *m_qmlTypes; + const JSImportScope *m_jsImports; + QList<const ObjectValue *> m_jsScopes; + + bool m_modified; + mutable QList<const ObjectValue *> m_all; +}; + +} // namespace Interpreter +} // namespace QmlJS + +#endif // QMLJS_SCOPECHAIN_H diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index 461745619f..5db49ed733 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -50,6 +50,7 @@ #include <qmljs/qmljscontext.h> #include <qmljs/qmljslink.h> #include <qmljs/qmljsscopebuilder.h> +#include <qmljs/qmljsscopechain.h> #include <qmljs/parser/qmljsast_p.h> #include <qmljs/qmljscheck.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -336,11 +337,10 @@ public: , m_link(snapshot, importPaths, QmlJS::ModelManagerInterface::instance()->builtins(doc)) , m_context(new Interpreter::Context(m_link(doc, &m_diagnosticLinkMessages))) - , m_scopeBuilder(m_context, doc) + , m_scopeChain(doc, m_context) + , m_scopeBuilder(&m_scopeChain) { m_lookupContext = LookupContext::create(doc, *m_context, QList<AST::Node*>()); - // cheaper than calling m_scopeBuilder.initializeRootScope() again - *m_context = *m_lookupContext->context(); } ~ReadingContext() @@ -407,7 +407,7 @@ public: /// ### Maybe put this into the context as a helper method. bool lookupProperty(const QString &prefix, const UiQualifiedId *id, const Interpreter::Value **property = 0, const Interpreter::ObjectValue **parentObject = 0, QString *name = 0) { - QList<const Interpreter::ObjectValue *> scopeObjects = m_context->scopeChain().qmlScopeObjects; + QList<const Interpreter::ObjectValue *> scopeObjects = m_scopeChain.qmlScopeObjects(); if (scopeObjects.isEmpty()) return false; @@ -433,7 +433,7 @@ public: bool isAttachedProperty = false; if (! propertyName.isEmpty() && propertyName[0].isUpper()) { isAttachedProperty = true; - if (const Interpreter::ObjectValue *qmlTypes = m_context->scopeChain().qmlTypes) + if (const Interpreter::ObjectValue *qmlTypes = m_scopeChain.qmlTypes()) scopeObjects += qmlTypes; } @@ -594,12 +594,12 @@ public: const Interpreter::ObjectValue *rhsValueObject = 0; QString rhsValueName; if (IdentifierExpression *idExp = cast<IdentifierExpression *>(eStmt->expression)) { - if (!m_context->scopeChain().qmlScopeObjects.isEmpty()) - rhsValueObject = m_context->scopeChain().qmlScopeObjects.last(); + if (!m_scopeChain.qmlScopeObjects().isEmpty()) + rhsValueObject = m_scopeChain.qmlScopeObjects().last(); if (idExp->name) rhsValueName = idExp->name->asString(); } else if (FieldMemberExpression *memberExp = cast<FieldMemberExpression *>(eStmt->expression)) { - Evaluate evaluate(m_context); + Evaluate evaluate(&m_scopeChain); const Interpreter::Value *result = evaluate(memberExp->base); rhsValueObject = result->asObjectValue(); @@ -634,6 +634,7 @@ private: QList<DiagnosticMessage> m_diagnosticLinkMessages; Interpreter::Context *m_context; LookupContext::Ptr m_lookupContext; + Interpreter::ScopeChain m_scopeChain; ScopeBuilder m_scopeBuilder; }; diff --git a/src/plugins/qmljseditor/qmljscompletionassist.cpp b/src/plugins/qmljseditor/qmljscompletionassist.cpp index 031447eb5f..a2079ccac5 100644 --- a/src/plugins/qmljseditor/qmljscompletionassist.cpp +++ b/src/plugins/qmljseditor/qmljscompletionassist.cpp @@ -48,6 +48,7 @@ #include <qmljs/parser/qmljsast_p.h> #include <qmljs/qmljsinterpreter.h> #include <qmljs/qmljscontext.h> +#include <qmljs/qmljsscopechain.h> #include <qmljs/qmljslookupcontext.h> #include <qmljs/qmljsscanner.h> #include <qmljs/qmljsbind.h> @@ -86,15 +87,15 @@ class EnumerateProperties: private Interpreter::MemberProcessor bool _globalCompletion; bool _enumerateGeneratedSlots; bool _enumerateSlots; - const Interpreter::Context *_context; + const Interpreter::ScopeChain *_scopeChain; const Interpreter::ObjectValue *_currentObject; public: - EnumerateProperties(const Interpreter::Context *context) + EnumerateProperties(const Interpreter::ScopeChain *scopeChain) : _globalCompletion(false), _enumerateGeneratedSlots(false), _enumerateSlots(true), - _context(context), + _scopeChain(scopeChain), _currentObject(0) { } @@ -131,7 +132,7 @@ public: _properties.clear(); _currentObject = 0; - foreach (const Interpreter::ObjectValue *scope, _context->scopeChain().all()) + foreach (const Interpreter::ObjectValue *scope, _scopeChain->all()) enumerateProperties(scope); return _properties; @@ -192,7 +193,7 @@ private: return; _processed.insert(object); - enumerateProperties(object->prototype(_context)); + enumerateProperties(object->prototype(_scopeChain->context())); object->processMembers(this); } @@ -422,6 +423,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface const QList<AST::Node *> path = semanticInfo.rangePath(m_interface->position()); LookupContext::Ptr lookupContext = semanticInfo.lookupContext(path); const Interpreter::Context *context = lookupContext->context(); + const Interpreter::ScopeChain &scopeChain = lookupContext->scopeChain(); // Search for the operator that triggered the completion. QChar completionOperator; @@ -521,7 +523,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface doJsKeywordCompletion = false; doQmlTypeCompletion = true; - EnumerateProperties enumerateProperties(context); + EnumerateProperties enumerateProperties(&scopeChain); enumerateProperties.setGlobalCompletion(true); enumerateProperties.setEnumerateGeneratedSlots(true); enumerateProperties.setEnumerateSlots(false); @@ -539,8 +541,8 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface contextFinder.isAfterOnInLhsOfBinding()); if (ScopeBuilder::isPropertyChangesObject(context, qmlScopeType) - && context->scopeChain().qmlScopeObjects.size() == 2) { - addCompletions(enumerateProperties(context->scopeChain().qmlScopeObjects.first()), + && scopeChain.qmlScopeObjects().size() == 2) { + addCompletions(enumerateProperties(scopeChain.qmlScopeObjects().first()), m_interface->symbolIcon(), SymbolOrder); } @@ -564,15 +566,15 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface doQmlTypeCompletion = true; if (doQmlTypeCompletion) { - if (const Interpreter::ObjectValue *qmlTypes = context->scopeChain().qmlTypes) { - EnumerateProperties enumerateProperties(context); + if (const Interpreter::ObjectValue *qmlTypes = scopeChain.qmlTypes()) { + EnumerateProperties enumerateProperties(&scopeChain); addCompletions(enumerateProperties(qmlTypes), m_interface->symbolIcon(), TypeOrder); } } if (doGlobalCompletion) { // It's a global completion. - EnumerateProperties enumerateProperties(context); + EnumerateProperties enumerateProperties(&scopeChain); enumerateProperties.setGlobalCompletion(true); addCompletions(enumerateProperties(), m_interface->symbolIcon(), SymbolOrder); } @@ -619,7 +621,7 @@ IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface //qDebug() << "type:" << interp->typeId(value); if (value && completionOperator == QLatin1Char('.')) { // member completion - EnumerateProperties enumerateProperties(context); + EnumerateProperties enumerateProperties(&scopeChain); if (contextFinder.isInLhsOfBinding() && qmlScopeType) { enumerateProperties.setEnumerateGeneratedSlots(true); addCompletionsPropertyLhs(enumerateProperties(value), diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index ebfdf17f0c..1e722884bb 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -1260,7 +1260,7 @@ TextEditor::BaseTextEditorWidget::Link QmlJSTextEditorWidget::findLinkAt(const Q } LookupContext::Ptr lookupContext = semanticInfo.lookupContext(semanticInfo.rangePath(cursorPosition)); - Evaluate evaluator(lookupContext->context()); + Evaluate evaluator(&lookupContext->scopeChain()); const Interpreter::Value *value = evaluator.reference(node); QString fileName; diff --git a/src/plugins/qmljseditor/qmljsfindreferences.cpp b/src/plugins/qmljseditor/qmljsfindreferences.cpp index 436af01a5f..1921ebc95d 100644 --- a/src/plugins/qmljseditor/qmljsfindreferences.cpp +++ b/src/plugins/qmljseditor/qmljsfindreferences.cpp @@ -80,10 +80,9 @@ public: FindUsages(Document::Ptr doc, Context *context) : _doc(doc) - , _context(context) - , _builder(context, doc) + , _scopeChain(doc, context) + , _builder(&_scopeChain) { - _builder.initializeRootScope(); } Result operator()(const QString &name, const ObjectValue *scope) @@ -106,7 +105,7 @@ protected: { if (node->name && node->name->asString() == _name - && _context->scopeChain().qmlScopeObjects.contains(_scope)) { + && _scopeChain.qmlScopeObjects().contains(_scope)) { _usages.append(node->identifierToken); } if (AST::cast<Block *>(node->statement)) { @@ -176,7 +175,7 @@ protected: return false; const ObjectValue *scope; - _context->lookup(_name, &scope); + _scopeChain.lookup(_name, &scope); if (!scope) return false; if (check(scope)) { @@ -188,14 +187,14 @@ protected: // so it might still be a use - we just found a different value in a different scope first // if scope is one of these, our match wasn't inside the instantiating components list - const ScopeChain &chain = _context->scopeChain(); - if (chain.jsScopes.contains(scope) - || chain.qmlScopeObjects.contains(scope) - || chain.qmlTypes == scope - || chain.globalScope == scope) + const ScopeChain &chain = _scopeChain; + if (chain.jsScopes().contains(scope) + || chain.qmlScopeObjects().contains(scope) + || chain.qmlTypes() == scope + || chain.globalScope() == scope) return false; - if (contains(chain.qmlComponentScope.data())) + if (contains(chain.qmlComponentChain().data())) _usages.append(node->identifierToken); return false; @@ -206,7 +205,7 @@ protected: if (!node->name || node->name->asString() != _name) return true; - Evaluate evaluate(_context); + Evaluate evaluate(&_scopeChain); const Value *lhsValue = evaluate(node->base); if (!lhsValue) return true; @@ -245,19 +244,19 @@ protected: } private: - bool contains(const ScopeChain::QmlComponentChain *chain) + bool contains(const QmlComponentChain *chain) { - if (!chain || !chain->document) + if (!chain || !chain->document()) return false; - if (chain->document->bind()->idEnvironment()->lookupMember(_name, _context)) - return chain->document->bind()->idEnvironment() == _scope; - const ObjectValue *root = chain->document->bind()->rootObjectValue(); - if (root->lookupMember(_name, _context)) { + if (chain->document()->bind()->idEnvironment()->lookupMember(_name, _scopeChain.context())) + return chain->document()->bind()->idEnvironment() == _scope; + const ObjectValue *root = chain->document()->bind()->rootObjectValue(); + if (root->lookupMember(_name, _scopeChain.context())) { return check(root); } - foreach (const ScopeChain::QmlComponentChain *parent, chain->instantiatingComponents) { + foreach (const QmlComponentChain *parent, chain->instantiatingComponents()) { if (contains(parent)) return true; } @@ -269,13 +268,13 @@ private: if (!s) return false; const ObjectValue *definingObject; - s->lookupMember(_name, _context, &definingObject); + s->lookupMember(_name, _scopeChain.context(), &definingObject); return definingObject == _scope; } bool checkQmlScope() { - foreach (const ObjectValue *s, _context->scopeChain().qmlScopeObjects) { + foreach (const ObjectValue *s, _scopeChain.qmlScopeObjects()) { if (check(s)) return true; } @@ -285,14 +284,14 @@ private: bool checkLookup() { const ObjectValue *scope = 0; - _context->lookup(_name, &scope); + _scopeChain.lookup(_name, &scope); return check(scope); } Result _usages; Document::Ptr _doc; - Context *_context; + ScopeChain _scopeChain; ScopeBuilder _builder; QString _name; @@ -307,9 +306,9 @@ public: FindTypeUsages(Document::Ptr doc, Context *context) : _doc(doc) , _context(context) - , _builder(context, doc) + , _scopeChain(doc, context) + , _builder(&_scopeChain) { - _builder.initializeRootScope(); } Result operator()(const QString &name, const ObjectValue *typeValue) @@ -380,7 +379,7 @@ protected: return false; const ObjectValue *scope; - const Value *objV = _context->lookup(_name, &scope); + const Value *objV = _scopeChain.lookup(_name, &scope); if (objV == _typeValue) _usages.append(node->identifierToken); return false; @@ -390,7 +389,7 @@ protected: { if (!node->name || node->name->asString() != _name) return true; - Evaluate evaluate(_context); + Evaluate evaluate(&_scopeChain); const Value *lhsValue = evaluate(node->base); if (!lhsValue) return true; @@ -452,6 +451,7 @@ private: Document::Ptr _doc; Context *_context; + ScopeChain _scopeChain; ScopeBuilder _builder; QString _name; @@ -466,8 +466,8 @@ public: TypeKind }; - FindTargetExpression(Document::Ptr doc, Context *context) - : _doc(doc), _context(context) + FindTargetExpression(Document::Ptr doc, const ScopeChain *scopeChain) + : _doc(doc), _scopeChain(scopeChain) { } @@ -488,7 +488,7 @@ public: const ObjectValue *scope() { if (!_scope) - _context->lookup(_name, &_scope); + _scopeChain->lookup(_name, &_scope); return _scope; } @@ -524,7 +524,7 @@ protected: _name = node->name->asString(); if ((!_name.isEmpty()) && _name.at(0).isUpper()) { // a possible type - _targetValue = _context->lookup(_name, &_scope); + _targetValue = _scopeChain->lookup(_name, &_scope); if (value_cast<const ObjectValue*>(_targetValue)) _typeKind = TypeKind; } @@ -539,14 +539,14 @@ protected: _name = node->name->asString(); if ((!_name.isEmpty()) && _name.at(0).isUpper()) { // a possible type - Evaluate evaluate(_context); + Evaluate evaluate(_scopeChain); const Value *lhsValue = evaluate(node->base); if (!lhsValue) return true; const ObjectValue *lhsObj = lhsValue->asObjectValue(); if (lhsObj) { _scope = lhsObj; - _targetValue = lhsObj->lookupMember(_name, _context); + _targetValue = lhsObj->lookupMember(_name, _scopeChain->context()); _typeKind = TypeKind; } } @@ -593,7 +593,7 @@ protected: if (containsOffset(node->typeToken)){ if (node->memberType){ _name = node->memberType->asString(); - _targetValue = _context->lookupType(_doc.data(), QStringList(_name)); + _targetValue = _scopeChain->context()->lookupType(_doc.data(), QStringList(_name)); _scope = 0; _typeKind = TypeKind; } @@ -654,7 +654,7 @@ private: { for (UiQualifiedId *att = id; att; att = att->next) { if (att->name && containsOffset(att->identifierToken)) { - _targetValue = _context->lookupType(_doc.data(), id, att->next); + _targetValue = _scopeChain->context()->lookupType(_doc.data(), id, att->next); _scope = 0; _name = att->name->asString(); _typeKind = TypeKind; @@ -666,7 +666,7 @@ private: void setScope(Node *node) { - Evaluate evaluate(_context); + Evaluate evaluate(_scopeChain); const Value *v = evaluate(node); if (v) _scope = v->asObjectValue(); @@ -677,7 +677,7 @@ private: const Value *_targetValue; Node *_objectNode; Document::Ptr _doc; - Context *_context; + const ScopeChain *_scopeChain; quint32 _offset; Kind _typeKind; }; @@ -823,12 +823,12 @@ static void find_helper(QFutureInterface<FindReferences::Usage> &future, Link link(snapshot, modelManager->importPaths(), modelManager->builtins(doc)); Context context = link(); - ScopeBuilder builder(&context, doc); - builder.initializeRootScope(); + ScopeChain scopeChain(doc, &context); + ScopeBuilder builder(&scopeChain); ScopeAstPath astPath(doc); builder.push(astPath(offset)); - FindTargetExpression findTarget(doc, &context); + FindTargetExpression findTarget(doc, &scopeChain); findTarget(offset); const QString &name = findTarget.name(); if (name.isEmpty()) diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index ee7a17deb1..57f1921f4b 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -186,7 +186,7 @@ bool HoverHandler::matchColorItem(const LookupContext::Ptr &lookupContext, } else if (const AST::UiPublicMember *publicMember = AST::cast<const AST::UiPublicMember *>(member)) { if (publicMember->name && posIsInSource(pos, publicMember->statement)) { - value = lookupContext->context()->lookup(publicMember->name->asString()); + value = lookupContext->scopeChain().lookup(publicMember->name->asString()); if (const Interpreter::Reference *ref = value->asReference()) value = lookupContext->context()->lookupReference(ref); color = textAt(qmlDocument, @@ -306,7 +306,7 @@ static const Interpreter::ObjectValue *isMember(const LookupContext::Ptr &lookup if (!identExp->name) return 0; *name = identExp->name->asString(); - lookupContext->context()->lookup(*name, &owningObject); + lookupContext->scopeChain().lookup(*name, &owningObject); } else if (AST::FieldMemberExpression *fme = AST::cast<AST::FieldMemberExpression *>(node)) { if (!fme->base || !fme->name) return 0; @@ -321,7 +321,7 @@ static const Interpreter::ObjectValue *isMember(const LookupContext::Ptr &lookup if (!qid->name) return 0; *name = qid->name->asString(); - const Interpreter::Value *value = lookupContext->context()->lookup(*name, &owningObject); + const Interpreter::Value *value = lookupContext->scopeChain().lookup(*name, &owningObject); for (AST::UiQualifiedId *it = qid->next; it; it = it->next) { if (!value) return 0; diff --git a/src/plugins/qmljseditor/quicktoolbar.cpp b/src/plugins/qmljseditor/quicktoolbar.cpp index 4d86052feb..9c4fe1a372 100644 --- a/src/plugins/qmljseditor/quicktoolbar.cpp +++ b/src/plugins/qmljseditor/quicktoolbar.cpp @@ -78,7 +78,7 @@ static inline const Interpreter::ObjectValue * getPropertyChangesTarget(Node *no if (scriptBinding->qualifiedId && scriptBinding->qualifiedId->name->asString() == QLatin1String("target") && ! scriptBinding->qualifiedId->next) { - Evaluate evaluator(lookupContext->context()); + Evaluate evaluator(&lookupContext->scopeChain()); const Interpreter::Value *targetValue = evaluator(scriptBinding->statement); if (const Interpreter::ObjectValue *targetObject = Interpreter::value_cast<const Interpreter::ObjectValue *>(targetValue)) { return targetObject; |