diff options
author | Christian Kamm <christian.d.kamm@nokia.com> | 2011-09-27 15:12:22 +0200 |
---|---|---|
committer | Christian Kamm <christian.d.kamm@nokia.com> | 2011-09-28 08:52:56 +0200 |
commit | 82cbd0a9740010021853d1c954d788131861fb04 (patch) | |
tree | 939dffe4b442fbe54f072c74e38309893da0ee35 | |
parent | faa792f3cdf7141004ea83cfc1406fc678520b58 (diff) | |
download | qt-creator-82cbd0a9740010021853d1c954d788131861fb04.tar.gz |
QmlJS: Support .import directives in js files.
* Allow .import...
* Fix parsing of JS files when using Lexer::scanDirectives()
* Clean up ImportInfo construction.
* Rename ImportInfo::id to ImportInfo::as.
Change-Id: I888da248f06dc6184db99aa74c3b50d7f2f5e491
Reviewed-on: http://codereview.qt-project.org/5625
Reviewed-by: Roberto Raggi <roberto.raggi@nokia.com>
-rw-r--r-- | src/libs/qmljs/parser/qmljsengine_p.cpp | 8 | ||||
-rw-r--r-- | src/libs/qmljs/parser/qmljsengine_p.h | 5 | ||||
-rw-r--r-- | src/libs/qmljs/parser/qmljsparser.cpp | 15 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsbind.cpp | 44 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsbind.h | 6 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsdocument.cpp | 38 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.cpp | 98 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsinterpreter.h | 14 | ||||
-rw-r--r-- | src/libs/qmljs/qmljslink.cpp | 24 | ||||
-rw-r--r-- | src/libs/qmljs/qmljsscopechain.cpp | 29 |
10 files changed, 186 insertions, 95 deletions
diff --git a/src/libs/qmljs/parser/qmljsengine_p.cpp b/src/libs/qmljs/parser/qmljsengine_p.cpp index 3d9a84e8f1..ca7597dac9 100644 --- a/src/libs/qmljs/parser/qmljsengine_p.cpp +++ b/src/libs/qmljs/parser/qmljsengine_p.cpp @@ -113,7 +113,7 @@ double integerFromString(const QString &str, int radix) Engine::Engine() - : _lexer(0) + : _lexer(0), _directives(0) { } Engine::~Engine() @@ -134,6 +134,12 @@ Lexer *Engine::lexer() const void Engine::setLexer(Lexer *lexer) { _lexer = lexer; } +void Engine::setDirectives(Directives *directives) +{ _directives = directives; } + +Directives *Engine::directives() const +{ return _directives; } + MemoryPool *Engine::pool() { return &_pool; } diff --git a/src/libs/qmljs/parser/qmljsengine_p.h b/src/libs/qmljs/parser/qmljsengine_p.h index 9297b38ca1..1aec0c5d77 100644 --- a/src/libs/qmljs/parser/qmljsengine_p.h +++ b/src/libs/qmljs/parser/qmljsengine_p.h @@ -56,6 +56,7 @@ QT_QML_BEGIN_NAMESPACE namespace QmlJS { class Lexer; +class Directives; class MemoryPool; class QML_PARSER_EXPORT DiagnosticMessage @@ -83,6 +84,7 @@ public: class QML_PARSER_EXPORT Engine { Lexer *_lexer; + Directives *_directives; MemoryPool _pool; QList<AST::SourceLocation> _comments; QString _extraCode; @@ -100,6 +102,9 @@ public: Lexer *lexer() const; void setLexer(Lexer *lexer); + void setDirectives(Directives *directives); + Directives *directives() const; + MemoryPool *pool(); inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); } diff --git a/src/libs/qmljs/parser/qmljsparser.cpp b/src/libs/qmljs/parser/qmljsparser.cpp index b85092b8da..61f901de8d 100644 --- a/src/libs/qmljs/parser/qmljsparser.cpp +++ b/src/libs/qmljs/parser/qmljsparser.cpp @@ -138,7 +138,20 @@ bool Parser::parse(int startToken) token_buffer[0].token = startToken; first_token = &token_buffer[0]; - last_token = &token_buffer[1]; + if (startToken == T_FEED_JS_PROGRAM) { + Directives ignoreDirectives; + Directives *directives = driver->directives(); + if (!directives) + directives = &ignoreDirectives; + lexer->scanDirectives(directives); + token_buffer[1].token = lexer->tokenKind(); + token_buffer[1].dval = lexer->tokenValue(); + token_buffer[1].loc = location(lexer); + token_buffer[1].spell = lexer->tokenSpell(); + last_token = &token_buffer[2]; + } else { + last_token = &token_buffer[1]; + } tos = -1; program = 0; diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp index d144a9eec3..1f4915fd64 100644 --- a/src/libs/qmljs/qmljsbind.cpp +++ b/src/libs/qmljs/qmljsbind.cpp @@ -60,11 +60,13 @@ using namespace QmlJS::AST; It allows AST to code model lookup through findQmlObject() and findFunctionScope(). */ -Bind::Bind(Document *doc, QList<DiagnosticMessage> *messages) +Bind::Bind(Document *doc, QList<DiagnosticMessage> *messages, bool isJsLibrary, const QList<ImportInfo> &jsImports) : _doc(doc), _currentObjectValue(0), _idEnvironment(0), _rootObjectValue(0), + _isJsLibrary(isJsLibrary), + _imports(jsImports), _diagnosticMessages(messages) { if (_doc) @@ -75,6 +77,11 @@ Bind::~Bind() { } +bool Bind::isJsLibrary() const +{ + return _isJsLibrary; +} + QList<ImportInfo> Bind::imports() const { return _imports; @@ -199,10 +206,6 @@ bool Bind::visit(AST::Program *) bool Bind::visit(UiImport *ast) { ComponentVersion version; - ImportInfo::Type type = ImportInfo::InvalidImport; - QString path; - QString name; - if (ast->versionToken.isValid()) { const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length); version = ComponentVersion(versionString); @@ -213,37 +216,18 @@ bool Bind::visit(UiImport *ast) } if (ast->importUri) { - type = ImportInfo::LibraryImport; - path = toString(ast->importUri, QDir::separator()); - name = toString(ast->importUri, QLatin1Char('.')); - - // treat Qt 4.7 as QtQuick 1.0 - if (path == QLatin1String("Qt") && version == ComponentVersion(4, 7)) { - path = QLatin1String("QtQuick"); - name = path; - version = ComponentVersion(1, 0); - } - if (!version.isValid()) { _diagnosticMessages->append( errorMessage(ast, tr("package import requires a version number"))); } + _imports += ImportInfo::moduleImport(toString(ast->importUri), version, + ast->importId.toString(), ast); } else if (!ast->fileName.isEmpty()) { - name = ast->fileName.toString(); - QFileInfo importFileInfo(name); - if (!importFileInfo.isAbsolute()) { - importFileInfo = QFileInfo(_doc->path() + QDir::separator() + name); - } - path = importFileInfo.absoluteFilePath(); - if (importFileInfo.isFile()) - type = ImportInfo::FileImport; - else if (importFileInfo.isDir()) - type = ImportInfo::DirectoryImport; - else { - type = ImportInfo::UnknownFileImport; - } + _imports += ImportInfo::pathImport(_doc->path(), ast->fileName.toString(), + version, ast->importId.toString(), ast); + } else { + _imports += ImportInfo::invalidImport(ast); } - _imports += ImportInfo(type, path, name, version, ast); return false; } diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h index f8a562c6e0..600b33ff3c 100644 --- a/src/libs/qmljs/qmljsbind.h +++ b/src/libs/qmljs/qmljsbind.h @@ -52,9 +52,11 @@ class QMLJS_EXPORT Bind: protected AST::Visitor Q_DECLARE_TR_FUNCTIONS(QmlJS::Bind) public: - Bind(Document *doc, QList<DiagnosticMessage> *messages); + Bind(Document *doc, QList<DiagnosticMessage> *messages, + bool isJsLibrary, const QList<ImportInfo> &jsImports); virtual ~Bind(); + bool isJsLibrary() const; QList<ImportInfo> imports() const; ObjectValue *idEnvironment() const; @@ -105,8 +107,8 @@ private: QMultiHash<QString, const ObjectValue *> _qmlObjectsByPrototypeName; QSet<AST::Node *> _groupedPropertyBindings; QHash<AST::Node *, ObjectValue *> _attachedJSScopes; - QStringList _includedScripts; + bool _isJsLibrary; QList<ImportInfo> _imports; QList<DiagnosticMessage> *_diagnosticMessages; diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 7455d421b7..843273ec91 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -211,6 +211,35 @@ QString Document::componentName() const return _componentName; } +namespace { +class CollectDirectives : public Directives +{ + QString documentPath; +public: + CollectDirectives(const QString &documentPath) + : documentPath(documentPath) + , isLibrary(false) + + {} + + virtual void pragmaLibrary() { isLibrary = true; } + virtual void importFile(const QString &jsfile, const QString &module) + { + imports += ImportInfo::pathImport( + documentPath, jsfile, LanguageUtils::ComponentVersion(), module); + } + + virtual void importModule(const QString &uri, const QString &version, const QString &module) + { + imports += ImportInfo::moduleImport(uri, LanguageUtils::ComponentVersion(version), module); + } + + bool isLibrary; + QList<ImportInfo> imports; +}; + +} // anonymous namespace + bool Document::parse_helper(int startToken) { Q_ASSERT(! _engine); @@ -225,11 +254,8 @@ bool Document::parse_helper(int startToken) QString source = _source; lexer.setCode(source, /*line = */ 1, /*qmlMode = */_language == QmlLanguage); - if (startToken == QmlJSGrammar::T_FEED_JS_PROGRAM) { - // ### use directives - Directives directives; - lexer.scanDirectives(&directives); - } + CollectDirectives collectDirectives(path()); + _engine->setDirectives(&collectDirectives); switch (startToken) { case QmlJSGrammar::T_FEED_UI_PROGRAM: @@ -248,7 +274,7 @@ bool Document::parse_helper(int startToken) _ast = parser.rootNode(); _diagnosticMessages = parser.diagnosticMessages(); - _bind = new Bind(this, &_diagnosticMessages); + _bind = new Bind(this, &_diagnosticMessages, collectDirectives.isLibrary, collectDirectives.imports); return _parsedCorrectly; } diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index a4ee4b3d19..581f240b0b 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1932,14 +1932,66 @@ ImportInfo::ImportInfo() { } -ImportInfo::ImportInfo(Type type, const QString &path, const QString &name, - ComponentVersion version, UiImport *ast) - : _type(type) - , _name(name) - , _path(path) - , _version(version) - , _ast(ast) +ImportInfo ImportInfo::moduleImport(QString uri, ComponentVersion version, + const QString &as, UiImport *ast) { + // treat Qt 4.7 as QtQuick 1.0 + if (uri == QLatin1String("Qt") && version == ComponentVersion(4, 7)) { + uri = QLatin1String("QtQuick"); + version = ComponentVersion(1, 0); + } + + ImportInfo info; + info._type = LibraryImport; + info._name = uri; + info._path = uri; + info._path.replace(QLatin1Char('.'), QDir::separator()); + info._version = version; + info._as = as; + info._ast = ast; + return info; +} + +ImportInfo ImportInfo::pathImport(const QString &docPath, const QString &path, + ComponentVersion version, const QString &as, UiImport *ast) +{ + ImportInfo info; + info._name = path; + + QFileInfo importFileInfo(path); + if (!importFileInfo.isAbsolute()) { + importFileInfo = QFileInfo(docPath + QDir::separator() + path); + } + info._path = importFileInfo.absoluteFilePath(); + + if (importFileInfo.isFile()) { + info._type = FileImport; + } else if (importFileInfo.isDir()) { + info._type = DirectoryImport; + } else { + info._type = UnknownFileImport; + } + + info._version = version; + info._as = as; + info._ast = ast; + return info; +} + +ImportInfo ImportInfo::invalidImport(UiImport *ast) +{ + ImportInfo info; + info._type = InvalidImport; + info._ast = ast; + return info; +} + +ImportInfo ImportInfo::implicitDirectoryImport(const QString &directory) +{ + ImportInfo info; + info._type = ImplicitDirectoryImport; + info._path = directory; + return info; } bool ImportInfo::isValid() const @@ -1962,11 +2014,9 @@ QString ImportInfo::path() const return _path; } -QString ImportInfo::id() const +QString ImportInfo::as() const { - if (_ast) - return _ast->importId.toString(); - return QString(); + return _as; } ComponentVersion ImportInfo::version() const @@ -2003,8 +2053,8 @@ const Value *TypeScope::lookupMember(const QString &name, const Context *context if (info.type() == ImportInfo::FileImport) continue; - if (!info.id().isEmpty()) { - if (info.id() == name) { + if (!info.as().isEmpty()) { + if (info.as() == name) { if (foundInObject) *foundInObject = this; return import; @@ -2033,8 +2083,8 @@ void TypeScope::processMembers(MemberProcessor *processor) const if (info.type() == ImportInfo::FileImport) continue; - if (!info.id().isEmpty()) { - processor->processProperty(info.id(), import); + if (!info.as().isEmpty()) { + processor->processProperty(info.as(), import); } else { import->processMembers(processor); } @@ -2061,7 +2111,7 @@ const Value *JSImportScope::lookupMember(const QString &name, const Context *, if (info.type() != ImportInfo::FileImport) continue; - if (info.id() == name) { + if (info.as() == name) { if (foundInObject) *foundInObject = this; return import; @@ -2082,7 +2132,7 @@ void JSImportScope::processMembers(MemberProcessor *processor) const const ImportInfo &info = i.info; if (info.type() == ImportInfo::FileImport) - processor->processProperty(info.id(), import); + processor->processProperty(info.as(), import); } } @@ -2094,12 +2144,12 @@ Imports::Imports(ValueOwner *valueOwner) void Imports::append(const Import &import) { // when doing lookup, imports with 'as' clause are looked at first - if (!import.info.id().isEmpty()) { + if (!import.info.as().isEmpty()) { _imports.append(import); } else { // find first as-import and prepend for (int i = 0; i < _imports.size(); ++i) { - if (!_imports.at(i).info.id().isEmpty()) { + if (!_imports.at(i).info.as().isEmpty()) { _imports.insert(i, import); return; } @@ -2123,8 +2173,8 @@ ImportInfo Imports::info(const QString &name, const Context *context) const const ObjectValue *import = i.object; const ImportInfo &info = i.info; - if (!info.id().isEmpty()) { - if (info.id() == firstId) + if (!info.as().isEmpty()) { + if (info.as() == firstId) return info; continue; } @@ -2156,9 +2206,9 @@ QString Imports::nameForImportedObject(const ObjectValue *value, const Context * const Value *v = import->lookupMember(value->className(), context); if (v == value) { QString result = value->className(); - if (!info.id().isEmpty()) { + if (!info.as().isEmpty()) { result.prepend(QLatin1Char('.')); - result.prepend(info.id()); + result.prepend(info.as()); } return result; } @@ -2230,7 +2280,7 @@ void Imports::dump() const const ObjectValue *import = i.object; const ImportInfo &info = i.info; - qDebug() << " " << info.path() << " " << info.version().toString() << " as " << info.id() << " : " << import; + qDebug() << " " << info.path() << " " << info.version().toString() << " as " << info.as() << " : " << import; MemberDumper dumper; import->processMembers(&dumper); } diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 92d1978e9f..8f9ec41da6 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -822,9 +822,14 @@ public: }; ImportInfo(); - ImportInfo(Type type, const QString &path, const QString &name = QString(), - LanguageUtils::ComponentVersion version = LanguageUtils::ComponentVersion(), - AST::UiImport *ast = 0); + + static ImportInfo moduleImport(QString uri, LanguageUtils::ComponentVersion version, + const QString &as, AST::UiImport *ast = 0); + static ImportInfo pathImport(const QString &docPath, const QString &path, + LanguageUtils::ComponentVersion version, + const QString &as, AST::UiImport *ast = 0); + static ImportInfo invalidImport(AST::UiImport *ast = 0); + static ImportInfo implicitDirectoryImport(const QString &directory); bool isValid() const; Type type() const; @@ -838,7 +843,7 @@ public: QString path() const; // null if the import has no 'as', otherwise the target id - QString id() const; + QString as() const; LanguageUtils::ComponentVersion version() const; AST::UiImport *ast() const; @@ -848,6 +853,7 @@ private: QString _name; QString _path; LanguageUtils::ComponentVersion _version; + QString _as; AST::UiImport *_ast; }; diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 8c2cda4568..1ff75045e0 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -233,15 +233,13 @@ Context::ImportsPerDocument LinkPrivate::linkImports() void LinkPrivate::populateImportedTypes(Imports *imports, Document::Ptr doc) { - if (! doc->qmlProgram()) - return; - // implicit imports: the <default> package is always available loadImplicitDefaultImports(imports); // implicit imports: // qml files in the same directory are available without explicit imports - loadImplicitDirectoryImports(imports, doc); + if (doc->isQmlDocument()) + loadImplicitDirectoryImports(imports, doc); // explicit imports, whether directories, files or libraries foreach (const ImportInfo &info, doc->bind()->imports()) { @@ -261,8 +259,10 @@ void LinkPrivate::populateImportedTypes(Imports *imports, Document::Ptr doc) import = importNonFile(doc, info); break; case ImportInfo::UnknownFileImport: - error(doc, info.ast()->fileNameToken, - Link::tr("file or directory not found")); + if (info.ast()) { + error(doc, info.ast()->fileNameToken, + Link::tr("file or directory not found")); + } break; default: break; @@ -400,9 +400,8 @@ bool LinkPrivate::importLibrary(Document::Ptr doc, import->libraryPath = libraryPath; const ComponentVersion version = importInfo.version(); - const UiImport *ast = importInfo.ast(); SourceLocation errorLoc; - if (ast) + if (const UiImport *ast = importInfo.ast()) errorLoc = locationFromRange(ast->firstSourceLocation(), ast->lastSourceLocation()); if (!libraryInfo.plugins().isEmpty()) { @@ -505,8 +504,7 @@ void LinkPrivate::loadQmldirComponents(ObjectValue *import, ComponentVersion ver void LinkPrivate::loadImplicitDirectoryImports(Imports *imports, Document::Ptr doc) { - ImportInfo implcitDirectoryImportInfo( - ImportInfo::ImplicitDirectoryImport, doc->path()); + ImportInfo implcitDirectoryImportInfo = ImportInfo::implicitDirectoryImport(doc->path()); Import directoryImport = importCache.value(ImportCacheKey(implcitDirectoryImportInfo)); if (!directoryImport.object) { @@ -523,15 +521,15 @@ void LinkPrivate::loadImplicitDefaultImports(Imports *imports) { const QString defaultPackage = CppQmlTypes::defaultPackage; if (valueOwner->cppQmlTypes().hasModule(defaultPackage)) { - ImportInfo info(ImportInfo::LibraryImport, defaultPackage); + const ComponentVersion maxVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion); + const ImportInfo info = ImportInfo::moduleImport(defaultPackage, maxVersion, QString()); Import import = importCache.value(ImportCacheKey(info)); if (!import.object) { import.info = info; import.object = new ObjectValue(valueOwner); foreach (const QmlObjectValue *object, valueOwner->cppQmlTypes().createObjectsForImport( - defaultPackage, - ComponentVersion(ComponentVersion::MaxVersion, ComponentVersion::MaxVersion))) { + defaultPackage, maxVersion)) { import.object->setMember(object->className(), object); } importCache.insert(ImportCacheKey(info), import); diff --git a/src/libs/qmljs/qmljsscopechain.cpp b/src/libs/qmljs/qmljsscopechain.cpp index d25ada859b..76016345b5 100644 --- a/src/libs/qmljs/qmljsscopechain.cpp +++ b/src/libs/qmljs/qmljsscopechain.cpp @@ -254,29 +254,30 @@ void ScopeChain::initializeRootScope() QmlComponentChain *chain = new QmlComponentChain(m_document); m_qmlComponentScope = QSharedPointer<const QmlComponentChain>(chain); + if (const Imports *imports = m_context->imports(m_document.data())) { + m_qmlTypes = imports->typeScope(); + m_jsImports = imports->jsImportScope(); + } + 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.path()) { - QmlComponentChain *component = new QmlComponentChain(otherDoc); - componentScopes.insert(otherDoc.data(), component); - chain->addInstantiatingComponent(component); - makeComponentChain(component, snapshot, &componentScopes); + // unless there's .pragma library + if (!m_document->bind()->isJsLibrary()) { + foreach (Document::Ptr otherDoc, snapshot) { + foreach (const ImportInfo &import, otherDoc->bind()->imports()) { + if (import.type() == ImportInfo::FileImport && m_document->fileName() == import.path()) { + 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(); } |