summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kamm <christian.d.kamm@nokia.com>2011-09-27 15:12:22 +0200
committerChristian Kamm <christian.d.kamm@nokia.com>2011-09-28 08:52:56 +0200
commit82cbd0a9740010021853d1c954d788131861fb04 (patch)
tree939dffe4b442fbe54f072c74e38309893da0ee35
parentfaa792f3cdf7141004ea83cfc1406fc678520b58 (diff)
downloadqt-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.cpp8
-rw-r--r--src/libs/qmljs/parser/qmljsengine_p.h5
-rw-r--r--src/libs/qmljs/parser/qmljsparser.cpp15
-rw-r--r--src/libs/qmljs/qmljsbind.cpp44
-rw-r--r--src/libs/qmljs/qmljsbind.h6
-rw-r--r--src/libs/qmljs/qmljsdocument.cpp38
-rw-r--r--src/libs/qmljs/qmljsinterpreter.cpp98
-rw-r--r--src/libs/qmljs/qmljsinterpreter.h14
-rw-r--r--src/libs/qmljs/qmljslink.cpp24
-rw-r--r--src/libs/qmljs/qmljsscopechain.cpp29
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();
}