diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index 5a24ef84e5e0811886e06288b474d7322b7200f9..d1f0847c994fdc97a19777761ec6bc26d6d47528 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -29,6 +29,7 @@ #include "parser/qmljsast_p.h" #include "qmljsbind.h" +#include "qmljscheck.h" #include "qmljsdocument.h" #include <QtCore/QDir> @@ -54,11 +55,12 @@ using namespace QmlJS::Interpreter; It allows AST to code model lookup through findQmlObject() and findFunctionScope(). */ -Bind::Bind(Document *doc) +Bind::Bind(Document *doc, QList<DiagnosticMessage> *messages) : _doc(doc), _currentObjectValue(0), _idEnvironment(0), - _rootObjectValue(0) + _rootObjectValue(0), + _diagnosticMessages(messages) { if (_doc) accept(_doc->ast()); @@ -68,7 +70,7 @@ Bind::~Bind() { } -QList<Bind::ImportInfo> Bind::imports() const +QList<ImportInfo> Bind::imports() const { return _imports; } @@ -185,32 +187,44 @@ bool Bind::visit(AST::Program *) bool Bind::visit(UiImport *ast) { - ImportInfo info; - info.ast = ast; + ComponentVersion version; + ImportInfo::Type type = ImportInfo::InvalidImport; + QString name; if (ast->versionToken.isValid()) { const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length); const int dotIdx = versionString.indexOf(QLatin1Char('.')); if (dotIdx != -1) { - info.version = ComponentVersion(versionString.left(dotIdx).toInt(), - versionString.mid(dotIdx + 1).toInt()); + version = ComponentVersion(versionString.left(dotIdx).toInt(), + versionString.mid(dotIdx + 1).toInt()); + } else { + _diagnosticMessages->append( + errorMessage(ast->versionToken, tr("expected two numbers separated by a dot"))); } } if (ast->importUri) { - info.type = ImportInfo::LibraryImport; - info.name = toString(ast->importUri, QLatin1Char('/')); + type = ImportInfo::LibraryImport; + name = toString(ast->importUri, QDir::separator()); + + if (!version.isValid()) { + _diagnosticMessages->append( + errorMessage(ast, tr("package import requires a version number"))); + } } else if (ast->fileName) { - const QFileInfo importFileInfo(_doc->path() + QLatin1Char('/') + ast->fileName->asString()); - info.name = importFileInfo.absoluteFilePath(); + const QFileInfo importFileInfo(_doc->path() + QDir::separator() + ast->fileName->asString()); + name = importFileInfo.absoluteFilePath(); if (importFileInfo.isFile()) - info.type = ImportInfo::FileImport; + type = ImportInfo::FileImport; else if (importFileInfo.isDir()) - info.type = ImportInfo::DirectoryImport; - else - info.type = ImportInfo::InvalidFileImport; + type = ImportInfo::DirectoryImport; + else { + _diagnosticMessages->append( + errorMessage(ast, tr("file or directory not found"))); + type = ImportInfo::UnknownFileImport; + } } - _imports += info; + _imports += ImportInfo(type, name, version, ast); return false; } diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index ddbfa5b1df3697c87febeadf46b8457f0aae3ef1..b5e779616b6b1d51ba834c68e4c823be648b2349 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -37,6 +37,7 @@ #include <QtCore/QHash> #include <QtCore/QStringList> #include <QtCore/QSharedPointer> +#include <QtCore/QCoreApplication> namespace QmlJS { @@ -46,28 +47,13 @@ class Document; class QMLJS_EXPORT Bind: protected AST::Visitor { Q_DISABLE_COPY(Bind) + Q_DECLARE_TR_FUNCTIONS(QmlJS::Bind) public: - Bind(Document *doc); + Bind(Document *doc, QList<DiagnosticMessage> *messages); virtual ~Bind(); - struct ImportInfo { - enum Type { - LibraryImport, - FileImport, - DirectoryImport, - InvalidFileImport // refers a file/directoy that wasn't found - }; - - Type type; - // LibraryImport: uri with '/' separator - // Other: absoluteFilePath - QString name; - ComponentVersion version; - AST::UiImport *ast; - }; - - QList<ImportInfo> imports() const; + QList<Interpreter::ImportInfo> imports() const; Interpreter::ObjectValue *idEnvironment() const; Interpreter::ObjectValue *rootObjectValue() const; @@ -119,7 +105,9 @@ private: QHash<AST::FunctionDeclaration *, Interpreter::ObjectValue *> _functionScopes; QStringList _includedScripts; - QList<ImportInfo> _imports; + QList<Interpreter::ImportInfo> _imports; + + QList<DiagnosticMessage> *_diagnosticMessages; }; } // end of namespace Qml diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 96d8d5e87cbb9264c9b5b97690a959ef426d2283..bcab73dcb8e1faae6238375b0a5117da65f5b64b 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -62,6 +62,20 @@ QColor QmlJS::toQColor(const QString &qmlColorString) return color; } +SourceLocation QmlJS::locationFromRange(const SourceLocation &start, + const SourceLocation &end) +{ + return SourceLocation(start.offset, + end.end() - start.begin(), + start.startLine, + start.startColumn); +} + +DiagnosticMessage QmlJS::errorMessage(const AST::SourceLocation &loc, const QString &message) +{ + return DiagnosticMessage(DiagnosticMessage::Error, loc, message); +} + namespace { class AssignmentCheck : public ValueVisitor @@ -371,12 +385,3 @@ void Check::warning(const AST::SourceLocation &loc, const QString &message) { _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message)); } - -SourceLocation Check::locationFromRange(const SourceLocation &start, - const SourceLocation &end) -{ - return SourceLocation(start.offset, - end.end() - start.begin(), - start.startLine, - start.startColumn); -} diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h index 3015987dc73e4087dbe9ce055a3a25d01cd0edef..049b197e5f9b53a239b0990752bd7c57f4055e29 100644 --- a/src/libs/qmljs/qmljscheck.h +++ b/src/libs/qmljs/qmljscheck.h @@ -64,8 +64,6 @@ private: void warning(const AST::SourceLocation &loc, const QString &message); void error(const AST::SourceLocation &loc, const QString &message); - static AST::SourceLocation locationFromRange(const AST::SourceLocation &start, - const AST::SourceLocation &end); Document::Ptr _doc; Snapshot _snapshot; @@ -80,6 +78,20 @@ private: QMLJS_EXPORT QColor toQColor(const QString &qmlColorString); +QMLJS_EXPORT AST::SourceLocation locationFromRange(const AST::SourceLocation &start, + const AST::SourceLocation &end); +QMLJS_EXPORT DiagnosticMessage errorMessage(const AST::SourceLocation &loc, + const QString &message); + +template <class T> +DiagnosticMessage errorMessage(const T *node, const QString &message) +{ + return DiagnosticMessage(DiagnosticMessage::Error, + locationFromRange(node->firstSourceLocation(), + node->lastSourceLocation()), + message); +} + } // namespace QmlJS #endif // QMLJSCHECK_H diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index c1d9d7f3cbe0546217e9476a785ade807e48eee5..75379474f2efa2f758dc8a74033f3b164b94b0e0 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -236,7 +236,7 @@ bool Document::parse_helper(int startToken) _ast = parser.rootNode(); _diagnosticMessages = parser.diagnosticMessages(); - _bind = new Bind(this); + _bind = new Bind(this, &_diagnosticMessages); return _parsedCorrectly; } diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index ddf0ed993bceb3ff59731672c4563b2f92f042c1..ff32bb517bfb84a02316562d44afc67e5cffb3e1 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -32,7 +32,6 @@ #include "qmljslink.h" #include "qmljsbind.h" #include "qmljsscopebuilder.h" -#include "qmljscomponentversion.h" #include "parser/qmljsast_p.h" #include <QtCore/QFile> @@ -1448,14 +1447,14 @@ ScopeChain &Context::scopeChain() return _scopeChain; } -const ObjectValue *Context::typeEnvironment(const QmlJS::Document *doc) const +const TypeEnvironment *Context::typeEnvironment(const QmlJS::Document *doc) const { if (!doc) return 0; return _typeEnvironments.value(doc->fileName(), 0); } -void Context::setTypeEnvironment(const QmlJS::Document *doc, const ObjectValue *typeEnvironment) +void Context::setTypeEnvironment(const QmlJS::Document *doc, const TypeEnvironment *typeEnvironment) { if (!doc) return; @@ -3097,3 +3096,134 @@ const Value *ASTSignalReference::value(const Context *) const { return engine()->undefinedValue(); } + +ImportInfo::ImportInfo() + : _type(InvalidImport) + , _ast(0) +{ +} + +ImportInfo::ImportInfo(Type type, const QString &name, + QmlJS::ComponentVersion version, UiImport *ast) + : _type(type) + , _name(name) + , _version(version) + , _ast(ast) +{ +} + +bool ImportInfo::isValid() const +{ + return _type != InvalidImport; +} + +ImportInfo::Type ImportInfo::type() const +{ + return _type; +} + +QString ImportInfo::name() const +{ + return _name; +} + +QString ImportInfo::id() const +{ + if (_ast && _ast->importId) + return _ast->importId->asString(); + return QString(); +} + +QmlJS::ComponentVersion ImportInfo::version() const +{ + return _version; +} + +UiImport *ImportInfo::ast() const +{ + return _ast; +} + +TypeEnvironment::TypeEnvironment(Engine *engine) + : ObjectValue(engine) +{ +} + +const Value *TypeEnvironment::lookupMember(const QString &name, const Context *context, bool) const +{ + QHashIterator<const ObjectValue *, ImportInfo> it(_imports); + while (it.hasNext()) { + it.next(); + const ObjectValue *import = it.key(); + const ImportInfo &info = it.value(); + + if (!info.id().isEmpty()) { + if (info.id() == name) + return import; + continue; + } + + if (info.type() == ImportInfo::FileImport) { + if (import->className() == name) + return import; + } else { + if (const Value *v = import->property(name, context)) + return v; + } + } + return 0; +} + +void TypeEnvironment::processMembers(MemberProcessor *processor) const +{ + QHashIterator<const ObjectValue *, ImportInfo> it(_imports); + while (it.hasNext()) { + it.next(); + const ObjectValue *import = it.key(); + const ImportInfo &info = it.value(); + + if (!info.id().isEmpty()) { + processor->processProperty(info.id(), import); + } else { + if (info.type() == ImportInfo::FileImport) + processor->processProperty(import->className(), import); + else + import->processMembers(processor); + } + } +} + +void TypeEnvironment::addImport(const ObjectValue *import, const ImportInfo &info) +{ + _imports.insert(import, info); +} + +ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *context) const +{ + QString firstId = name; + int dotIdx = firstId.indexOf(QLatin1Char('.')); + if (dotIdx != -1) + firstId = firstId.left(dotIdx); + + QHashIterator<const ObjectValue *, ImportInfo> it(_imports); + while (it.hasNext()) { + it.next(); + const ObjectValue *import = it.key(); + const ImportInfo &info = it.value(); + + if (!info.id().isEmpty()) { + if (info.id() == firstId) + return info; + continue; + } + + if (info.type() == ImportInfo::FileImport) { + if (import->className() == firstId) + return info; + } else { + if (import->property(firstId, context)) + return info; + } + } + return ImportInfo(); +} diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 208ed034e00ad0cd0574feffc0f39cf8506e1dd3..f6c7086fb181ee3f3d5a7ce5db8177da40a370bf 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -63,6 +63,7 @@ class FunctionValue; class Reference; class ColorValue; class AnchorLineValue; +class TypeEnvironment; typedef QList<const Value *> ValueList; @@ -259,7 +260,7 @@ public: const ObjectValue *globalScope; QSharedPointer<const QmlComponentChain> qmlComponentScope; QList<const ObjectValue *> qmlScopeObjects; - const ObjectValue *qmlTypes; + const TypeEnvironment *qmlTypes; QList<const ObjectValue *> jsScopes; // rebuilds the flat list of all scopes @@ -280,8 +281,8 @@ public: const ScopeChain &scopeChain() const; ScopeChain &scopeChain(); - const ObjectValue *typeEnvironment(const Document *doc) const; - void setTypeEnvironment(const Document *doc, const ObjectValue *typeEnvironment); + const TypeEnvironment *typeEnvironment(const Document *doc) const; + void setTypeEnvironment(const Document *doc, const TypeEnvironment *typeEnvironment); const Value *lookup(const QString &name) const; const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName) const; @@ -298,7 +299,7 @@ private: QSharedPointer<Engine> _engine; QHash<const ObjectValue *, Properties> _properties; - QHash<QString, const ObjectValue *> _typeEnvironments; + QHash<QString, const TypeEnvironment *> _typeEnvironments; ScopeChain _scopeChain; int _qmlScopeObjectIndex; bool _qmlScopeObjectSet; @@ -846,6 +847,57 @@ public: QString defaultPropertyName() const; }; +class QMLJS_EXPORT ImportInfo +{ +public: + enum Type { + InvalidImport, + ImplicitDirectoryImport, + LibraryImport, + FileImport, + DirectoryImport, + UnknownFileImport // refers a file/directoy that wasn't found + }; + + ImportInfo(); + ImportInfo(Type type, const QString &name, + ComponentVersion version = ComponentVersion(), + AST::UiImport *ast = 0); + + bool isValid() const; + Type type() const; + + // LibraryImport: uri with '/' separator + // Other: absoluteFilePath + QString name() const; + + // null if the import has no 'as', otherwise the target id + QString id() const; + + ComponentVersion version() const; + AST::UiImport *ast() const; + +private: + Type _type; + QString _name; + ComponentVersion _version; + AST::UiImport *_ast; +}; + +class QMLJS_EXPORT TypeEnvironment: public ObjectValue +{ + QHash<const ObjectValue *, ImportInfo> _imports; + +public: + TypeEnvironment(Engine *engine); + + virtual const Value *lookupMember(const QString &name, const Context *context, bool examinePrototypes) const; + virtual void processMembers(MemberProcessor *processor) const; + + void addImport(const ObjectValue *import, const ImportInfo &info); + ImportInfo importInfo(const QString &name, const Context *context) const; +}; + } } // end of namespace QmlJS::Interpreter #endif // QMLJS_INTERPRETER_H diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 583a884f78fa356655de2e4fddfe520554d48f53..a8a971308dde728bccdd3b443765ccfc302af339 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -32,6 +32,7 @@ #include "parser/qmljsast_p.h" #include "qmljsdocument.h" #include "qmljsbind.h" +#include "qmljscheck.h" #include "qmljsscopebuilder.h" #include "qmljsmodelmanagerinterface.h" @@ -43,6 +44,52 @@ using namespace QmlJS; using namespace QmlJS::Interpreter; using namespace QmlJS::AST; +namespace { +class ImportCacheKey +{ +public: + explicit ImportCacheKey(const Interpreter::ImportInfo &info) + : type(info.type()) + , name(info.name()) + , majorVersion(info.version().majorVersion()) + , minorVersion(info.version().minorVersion()) + {} + + int type; + QString name; + int majorVersion; + int minorVersion; +}; + +uint qHash(const ImportCacheKey &info) +{ + return ::qHash(info.type) ^ ::qHash(info.name) ^ + ::qHash(info.majorVersion) ^ ::qHash(info.minorVersion); +} + +bool operator==(const ImportCacheKey &i1, const ImportCacheKey &i2) +{ + return i1.type == i2.type + && i1.name == i2.name + && i1.majorVersion == i2.majorVersion + && i1.minorVersion == i2.minorVersion; +} +} + + +class QmlJS::LinkPrivate +{ +public: + Document::Ptr doc; + Snapshot snapshot; + Interpreter::Context *context; + QStringList importPaths; + + QHash<ImportCacheKey, Interpreter::ObjectValue *> importCache; + + QList<DiagnosticMessage> diagnosticMessages; +}; + /*! \class QmlJS::Link \brief Initializes the Context for a Document. @@ -58,11 +105,14 @@ using namespace QmlJS::AST; Link::Link(Context *context, const Document::Ptr &doc, const Snapshot &snapshot, const QStringList &importPaths) - : _doc(doc) - , _snapshot(snapshot) - , _context(context) - , _importPaths(importPaths) + : d_ptr(new LinkPrivate) { + Q_D(Link); + d->context = context; + d->doc = doc; + d->snapshot = snapshot; + d->importPaths = importPaths; + linkImports(); initializeScopeChain(); } @@ -73,42 +123,46 @@ Link::~Link() Interpreter::Engine *Link::engine() { - return _context->engine(); + Q_D(Link); + return d->context->engine(); } QList<DiagnosticMessage> Link::diagnosticMessages() const { - return _diagnosticMessages; + Q_D(const Link); + return d->diagnosticMessages; } void Link::initializeScopeChain() { - ScopeChain &scopeChain = _context->scopeChain(); + Q_D(Link); + + ScopeChain &scopeChain = d->context->scopeChain(); // ### TODO: This object ought to contain the global namespace additions by QML. scopeChain.globalScope = engine()->globalObject(); - if (! _doc) { + if (! d->doc) { scopeChain.update(); return; } - Bind *bind = _doc->bind(); + Bind *bind = d->doc->bind(); QHash<Document *, ScopeChain::QmlComponentChain *> componentScopes; ScopeChain::QmlComponentChain *chain = new ScopeChain::QmlComponentChain; scopeChain.qmlComponentScope = QSharedPointer<const ScopeChain::QmlComponentChain>(chain); - if (_doc->qmlProgram()) { - componentScopes.insert(_doc.data(), chain); - makeComponentChain(_doc, chain, &componentScopes); + if (d->doc->qmlProgram()) { + componentScopes.insert(d->doc.data(), chain); + makeComponentChain(d->doc, chain, &componentScopes); - if (const ObjectValue *typeEnvironment = _context->typeEnvironment(_doc.data())) + if (const TypeEnvironment *typeEnvironment = d->context->typeEnvironment(d->doc.data())) scopeChain.qmlTypes = typeEnvironment; } else { // add scope chains for all components that import this file - foreach (Document::Ptr otherDoc, _snapshot) { - foreach (const Bind::ImportInfo &fileImport, otherDoc->bind()->fileImports()) { - if (_doc->fileName() == fileImport.name) { + foreach (Document::Ptr otherDoc, d->snapshot) { + foreach (const ImportInfo &import, otherDoc->bind()->imports()) { + if (import.type() == ImportInfo::FileImport && d->doc->fileName() == import.name()) { ScopeChain::QmlComponentChain *component = new ScopeChain::QmlComponentChain; componentScopes.insert(otherDoc.data(), component); chain->instantiatingComponents += component; @@ -131,16 +185,18 @@ void Link::makeComponentChain( ScopeChain::QmlComponentChain *target, QHash<Document *, ScopeChain::QmlComponentChain *> *components) { + Q_D(Link); + if (!doc->qmlProgram()) return; Bind *bind = doc->bind(); // add scopes for all components instantiating this one - foreach (Document::Ptr otherDoc, _snapshot) { + foreach (Document::Ptr otherDoc, d->snapshot) { if (otherDoc == doc) continue; - if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), _context)) { + if (otherDoc->bind()->usesQmlPrototype(bind->rootObjectValue(), d->context)) { if (components->contains(otherDoc.data())) { // target->instantiatingComponents += components->value(otherDoc.data()); } else { @@ -159,41 +215,62 @@ void Link::makeComponentChain( void Link::linkImports() { - foreach (Document::Ptr doc, _snapshot) { - ObjectValue *typeEnv = engine()->newObject(/*prototype =*/0); // ### FIXME + Q_D(Link); - // Populate the _typeEnvironment with imports. - populateImportedTypes(typeEnv, doc); + // do it on d->doc first, to make sure import errors are shown + TypeEnvironment *typeEnv = new TypeEnvironment(engine()); + populateImportedTypes(typeEnv, d->doc); + d->context->setTypeEnvironment(d->doc.data(), typeEnv); - _context->setTypeEnvironment(doc.data(), typeEnv); + foreach (Document::Ptr doc, d->snapshot) { + if (doc == d->doc) + continue; + + TypeEnvironment *typeEnv = new TypeEnvironment(engine()); + populateImportedTypes(typeEnv, doc); + d->context->setTypeEnvironment(doc.data(), typeEnv); } } -void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc) +void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) { + Q_D(Link); + if (! (doc->qmlProgram() && doc->qmlProgram()->imports)) return; // implicit imports: // qml files in the same directory are available without explicit imports - foreach (Document::Ptr otherDoc, _snapshot.documentsInDirectory(doc->path())) { - if (otherDoc == doc || !otherDoc->bind()->rootObjectValue()) - continue; - - typeEnv->setProperty(otherDoc->componentName(), - otherDoc->bind()->rootObjectValue()); + ImportInfo implcitImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path()); + ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitImportInfo)); + if (!directoryImport) { + directoryImport = importFile(doc, implcitImportInfo); + if (directoryImport) + d->importCache.insert(ImportCacheKey(implcitImportInfo), directoryImport); } - - // explicit imports, whether directories or files - for (UiImportList *it = doc->qmlProgram()->imports; it; it = it->next) { - if (! it->import) - continue; - - if (it->import->fileName) { - importFile(typeEnv, doc, it->import); - } else if (it->import->importUri) { - importNonFile(typeEnv, doc, it->import); + if (directoryImport) + typeEnv->addImport(directoryImport, implcitImportInfo); + + // explicit imports, whether directories, files or libraries + foreach (const ImportInfo &info, doc->bind()->imports()) { + ObjectValue *import = d->importCache.value(ImportCacheKey(info)); + if (!import) { + switch (info.type()) { + case ImportInfo::FileImport: + case ImportInfo::DirectoryImport: + import = importFile(doc, info); + break; + case ImportInfo::LibraryImport: + import = importNonFile(doc, info); + break; + default: + break; + } + if (import) + d->importCache.insert(ImportCacheKey(info), import); } + if (import) + typeEnv->addImport(import, info); } } @@ -205,61 +282,29 @@ void Link::populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Pt import "http://www.ovi.com/" as Ovi */ -void Link::importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, - AST::UiImport *import) +ObjectValue *Link::importFile(Document::Ptr, const ImportInfo &importInfo) { - Q_UNUSED(doc) - - if (!import->fileName) - return; - - QString path = doc->path(); - path += QLatin1Char('/'); - path += import->fileName->asString(); - path = QDir::cleanPath(path); + Q_D(Link); - ObjectValue *importNamespace = typeEnv; - - // directory import - QList<Document::Ptr> documentsInDirectory = _snapshot.documentsInDirectory(path); - if (! documentsInDirectory.isEmpty()) { - if (import->importId) { - importNamespace = engine()->newObject(/*prototype =*/0); - typeEnv->setProperty(import->importId->asString(), importNamespace); - } + ObjectValue *import = 0; + const QString &path = importInfo.name(); + if (importInfo.type() == ImportInfo::DirectoryImport + || importInfo.type() == ImportInfo::ImplicitDirectoryImport) { + import = new ObjectValue(engine()); + const QList<Document::Ptr> &documentsInDirectory = d->snapshot.documentsInDirectory(path); foreach (Document::Ptr importedDoc, documentsInDirectory) { if (importedDoc->bind()->rootObjectValue()) { const QString targetName = importedDoc->componentName(); - importNamespace->setProperty(targetName, importedDoc->bind()->rootObjectValue()); + import->setProperty(targetName, importedDoc->bind()->rootObjectValue()); } } + } else if (importInfo.type() == ImportInfo::FileImport) { + Document::Ptr importedDoc = d->snapshot.document(path); + import = importedDoc->bind()->rootObjectValue(); } - // file import - else if (Document::Ptr importedDoc = _snapshot.document(path)) { - if (importedDoc->bind()->rootObjectValue()) { - QString targetName; - if (import->importId) { - targetName = import->importId->asString(); - } else { - targetName = importedDoc->componentName(); - } - importNamespace->setProperty(targetName, importedDoc->bind()->rootObjectValue()); - } - } else { - error(doc, import->fileNameToken, - tr("could not find file or directory")); - } -} - -static SourceLocation locationFromRange(const SourceLocation &start, - const SourceLocation &end) -{ - return SourceLocation(start.offset, - end.end() - start.begin(), - start.startLine, - start.startColumn); + return import; } /* @@ -267,52 +312,24 @@ static SourceLocation locationFromRange(const SourceLocation &start, import Qt 4.6 as Xxx (import com.nokia.qt is the same as the ones above) */ -void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, AST::UiImport *import) +ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo) { - if (! import->importUri) - return; - - ObjectValue *namespaceObject = 0; - - if (import->importId) { // with namespace we insert an object in the type env. to hold the imported types - namespaceObject = engine()->newObject(/*prototype */ 0); - typeEnv->setProperty(import->importId->asString(), namespaceObject); - - } else { // without namespace we insert all types directly into the type env. - namespaceObject = typeEnv; - } + Q_D(Link); - const QString packageName = Bind::toString(import->importUri, '.'); - ComponentVersion version; - - if (import->versionToken.isValid()) { - const QString versionString = doc->source().mid(import->versionToken.offset, import->versionToken.length); - const int dotIdx = versionString.indexOf(QLatin1Char('.')); - if (dotIdx == -1) { - error(doc, import->versionToken, - tr("expected two numbers separated by a dot")); - return; - } else { - const int majorVersion = versionString.left(dotIdx).toInt(); - const int minorVersion = versionString.mid(dotIdx + 1).toInt(); - version = ComponentVersion(majorVersion, minorVersion); - } - } else { - error(doc, locationFromRange(import->firstSourceLocation(), import->lastSourceLocation()), - tr("package import requires a version number")); - return; - } + ObjectValue *import = new ObjectValue(engine()); + const QString packageName = Bind::toString(importInfo.ast()->importUri, '.'); + const ComponentVersion version = importInfo.version(); bool importFound = false; // check the filesystem - const QString packagePath = Bind::toString(import->importUri, QDir::separator()); - foreach (const QString &importPath, _importPaths) { + const QString &packagePath = importInfo.name(); + foreach (const QString &importPath, d->importPaths) { QString libraryPath = importPath; libraryPath += QDir::separator(); libraryPath += packagePath; - const LibraryInfo libraryInfo = _snapshot.libraryInfo(libraryPath); + const LibraryInfo libraryInfo = d->snapshot.libraryInfo(libraryPath); if (!libraryInfo.isValid()) continue; @@ -322,7 +339,7 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A if (libraryInfo.metaObjects().isEmpty()) { ModelManagerInterface *modelManager = ModelManagerInterface::instance(); if (modelManager) - modelManager->loadPluginTypes(libraryPath, importPath, Bind::toString(import->importUri, QLatin1Char('.'))); + modelManager->loadPluginTypes(libraryPath, importPath, packageName); } else { engine()->cppQmlTypes().load(engine(), libraryInfo.metaObjects()); } @@ -333,14 +350,16 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A if (importedTypes.contains(component.typeName)) continue; - ComponentVersion componentVersion(component.majorVersion, component.minorVersion); + ComponentVersion componentVersion(component.majorVersion, + component.minorVersion); if (version < componentVersion) continue; importedTypes.insert(component.typeName); - if (Document::Ptr importedDoc = _snapshot.document(libraryPath + QDir::separator() + component.fileName)) { - if (importedDoc->bind()->rootObjectValue()) - namespaceObject->setProperty(component.typeName, importedDoc->bind()->rootObjectValue()); + if (Document::Ptr importedDoc = d->snapshot.document( + libraryPath + QDir::separator() + component.fileName)) { + if (ObjectValue *v = importedDoc->bind()->rootObjectValue()) + import->setProperty(component.typeName, v); } } @@ -350,15 +369,19 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A // if there are cpp-based types for this package, use them too if (engine()->cppQmlTypes().hasPackage(packageName)) { importFound = true; - foreach (QmlObjectValue *object, engine()->cppQmlTypes().typesForImport(packageName, version)) { - namespaceObject->setProperty(object->className(), object); + foreach (QmlObjectValue *object, + engine()->cppQmlTypes().typesForImport(packageName, version)) { + import->setProperty(object->className(), object); } } if (!importFound) { - error(doc, locationFromRange(import->firstSourceLocation(), import->lastSourceLocation()), + error(doc, locationFromRange(importInfo.ast()->firstSourceLocation(), + importInfo.ast()->lastSourceLocation()), tr("package not found")); } + + return import; } UiQualifiedId *Link::qualifiedTypeNameId(Node *node) @@ -373,6 +396,8 @@ UiQualifiedId *Link::qualifiedTypeNameId(Node *node) void Link::error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message) { - if (doc->fileName() == _doc->fileName()) - _diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message)); + Q_D(Link); + + if (doc->fileName() == d->doc->fileName()) + d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message)); } diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 54e85446aae6a414f313f0be71ba1198847af2f0..d81fcc509a61c8c3fa51e9cc1a7f9fb48ba4ea1a 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -42,12 +42,14 @@ namespace QmlJS { class NameId; +class LinkPrivate; /* Helper for building a context. */ class QMLJS_EXPORT Link { + Q_DECLARE_PRIVATE(Link) Q_DECLARE_TR_FUNCTIONS(QmlJS::Link) public: @@ -73,22 +75,15 @@ private: void linkImports(); void initializeScopeChain(); - void populateImportedTypes(Interpreter::ObjectValue *typeEnv, Document::Ptr doc); - void importFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, - AST::UiImport *import); - void importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, - AST::UiImport *import); + void populateImportedTypes(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc); + Interpreter::ObjectValue *importFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo); + Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo); void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace); void error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message); private: - Document::Ptr _doc; - Snapshot _snapshot; - Interpreter::Context *_context; - const QStringList _importPaths; - - QList<DiagnosticMessage> _diagnosticMessages; + QScopedPointer<LinkPrivate> d_ptr; }; } // namespace QmlJS diff --git a/src/plugins/qmljseditor/qmljseditor.cpp b/src/plugins/qmljseditor/qmljseditor.cpp index ddfd9cfa5ac9e257f9d7b675a426c158988b77d7..de0f29823b4decc7523428be3c0739e6953dac6a 100644 --- a/src/plugins/qmljseditor/qmljseditor.cpp +++ b/src/plugins/qmljseditor/qmljseditor.cpp @@ -579,9 +579,9 @@ AST::Node *SemanticInfo::nodeUnderCursor(int pos) const const unsigned cursorPosition = pos; - foreach (const Bind::ImportInfo &import, document->bind()->imports()) { - if (importContainsCursor(import.ast, cursorPosition)) - return import.ast; + foreach (const Interpreter::ImportInfo &import, document->bind()->imports()) { + if (importContainsCursor(import.ast(), cursorPosition)) + return import.ast(); } CollectASTNodes nodes; @@ -1381,9 +1381,9 @@ TextEditor::BaseTextEditor::Link QmlJSTextEditor::findLinkAt(const QTextCursor & if (AST::UiImport *importAst = cast<AST::UiImport *>(node)) { // if it's a file import, link to the file - foreach (const Bind::ImportInfo &import, semanticInfo.document->bind()->imports()) { - if (import.ast == importAst && import.type == Bind::ImportInfo::FileImport) { - BaseTextEditor::Link link(import.name); + foreach (const Interpreter::ImportInfo &import, semanticInfo.document->bind()->imports()) { + if (import.ast() == importAst && import.type() == Interpreter::ImportInfo::FileImport) { + BaseTextEditor::Link link(import.name()); link.begin = importAst->firstSourceLocation().begin(); link.end = importAst->lastSourceLocation().end(); return link; diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.cpp b/src/plugins/qmljseditor/qmljsmodelmanager.cpp index ae8e9752416d5e170e1487365e380db79f14f1e1..2dd2b256c2bc2a2d27abb4a84c03078e54842365 100644 --- a/src/plugins/qmljseditor/qmljsmodelmanager.cpp +++ b/src/plugins/qmljseditor/qmljsmodelmanager.cpp @@ -276,15 +276,16 @@ static void findNewFileImports(const Document::Ptr &doc, const Snapshot &snapsho QStringList *importedFiles, QSet<QString> *scannedPaths) { // scan files and directories that are explicitly imported - foreach (const Bind::ImportInfo &import, doc->bind()->imports()) { - if (import.type == Bind::ImportInfo::FileImport) { - if (! snapshot.document(import.name)) - *importedFiles += import.name; - } else if (import.type == Bind::ImportInfo::DirectoryImport) { - if (snapshot.documentsInDirectory(import.name).isEmpty()) { - if (! scannedPaths->contains(import.name)) { - *importedFiles += qmlFilesInDirectory(import.name); - scannedPaths->insert(import.name); + foreach (const Interpreter::ImportInfo &import, doc->bind()->imports()) { + const QString &importName = import.name(); + if (import.type() == Interpreter::ImportInfo::FileImport) { + if (! snapshot.document(importName)) + *importedFiles += importName; + } else if (import.type() == Interpreter::ImportInfo::DirectoryImport) { + if (snapshot.documentsInDirectory(importName).isEmpty()) { + if (! scannedPaths->contains(importName)) { + *importedFiles += qmlFilesInDirectory(importName); + scannedPaths->insert(importName); } } } @@ -297,12 +298,12 @@ static void findNewLibraryImports(const Document::Ptr &doc, const Snapshot &snap { // scan library imports const QStringList importPaths = modelManager->importPaths(); - foreach (const Bind::ImportInfo &import, doc->bind()->imports()) { - if (import.type != Bind::ImportInfo::LibraryImport) + foreach (const Interpreter::ImportInfo &import, doc->bind()->imports()) { + if (import.type() != Interpreter::ImportInfo::LibraryImport) continue; foreach (const QString &importPath, importPaths) { QDir dir(importPath); - dir.cd(import.name); + dir.cd(import.name()); const QString targetPath = dir.absolutePath(); // if we know there is a library, done