From a7f4e5fab5f46ec392345770c017de2106f9615a Mon Sep 17 00:00:00 2001 From: Christian Kamm <christian.d.kamm@nokia.com> Date: Fri, 27 May 2011 10:43:06 +0200 Subject: [PATCH] QmlJS: Separate imported types and imported JS scopes. Task-number: QTCREATORBUG-4981 Change-Id: I06d3e428ca4928296a3d5977aeff29fc3217c37c Reviewed-on: http://codereview.qt.nokia.com/175 Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com> --- src/libs/qmljs/qmljsinterpreter.cpp | 132 +++++++++++++----- src/libs/qmljs/qmljsinterpreter.h | 71 +++++++--- src/libs/qmljs/qmljslink.cpp | 48 ++++--- src/libs/qmljs/qmljslink.h | 12 +- src/libs/qmljs/qmljsscopebuilder.cpp | 5 +- .../designercore/model/texttomodelmerger.cpp | 4 +- src/plugins/qmljseditor/qmljshoverhandler.cpp | 6 +- 7 files changed, 190 insertions(+), 88 deletions(-) diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index 3e316c23c28..1530e307f05 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1308,6 +1308,7 @@ void StringValue::accept(ValueVisitor *visitor) const ScopeChain::ScopeChain() : globalScope(0) , qmlTypes(0) + , jsImports(0) { } @@ -1369,7 +1370,8 @@ void ScopeChain::update() _all += ids; if (qmlTypes) _all += qmlTypes; - // qmlTypes are not added on purpose + if (jsImports) + _all += jsImports; _all += jsScopes; } @@ -1412,18 +1414,18 @@ ScopeChain &Context::scopeChain() return _scopeChain; } -const TypeEnvironment *Context::typeEnvironment(const QmlJS::Document *doc) const +const Imports *Context::imports(const QmlJS::Document *doc) const { if (!doc) return 0; - return _typeEnvironments.value(doc, 0); + return _imports.value(doc).data(); } -void Context::setTypeEnvironment(const QmlJS::Document *doc, const TypeEnvironment *typeEnvironment) +void Context::setImports(const QmlJS::Document *doc, const Imports *imports) { if (!doc) return; - _typeEnvironments[doc] = typeEnvironment; + _imports[doc] = QSharedPointer<const Imports>(imports); } const Value *Context::lookup(const QString &name, const ObjectValue **foundInScope) const @@ -1446,7 +1448,10 @@ const Value *Context::lookup(const QString &name, const ObjectValue **foundInSco const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId *qmlTypeName) const { - const ObjectValue *objectValue = typeEnvironment(doc); + const Imports *importsObj = imports(doc); + if (!importsObj) + return 0; + const ObjectValue *objectValue = importsObj->typeScope(); if (!objectValue) return 0; @@ -1466,7 +1471,12 @@ const ObjectValue *Context::lookupType(const QmlJS::Document *doc, UiQualifiedId const ObjectValue *Context::lookupType(const QmlJS::Document *doc, const QStringList &qmlTypeName) const { - const ObjectValue *objectValue = typeEnvironment(doc); + const Imports *importsObj = imports(doc); + if (!importsObj) + return 0; + const ObjectValue *objectValue = importsObj->typeScope(); + if (!objectValue) + return 0; foreach (const QString &name, qmlTypeName) { if (!objectValue) @@ -3398,25 +3408,30 @@ UiImport *ImportInfo::ast() const return _ast; } -TypeEnvironment::Import::Import() +Import::Import() : object(0) {} -TypeEnvironment::TypeEnvironment(Engine *engine) +TypeScope::TypeScope(const Imports *imports, Engine *engine) : ObjectValue(engine) + , _imports(imports) { } -const Value *TypeEnvironment::lookupMember(const QString &name, const Context *context, +const Value *TypeScope::lookupMember(const QString &name, const Context *context, const ObjectValue **foundInObject, bool) const { - QListIterator<Import> it(_imports); + QListIterator<Import> it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; + // JS import has no types + if (info.type() == ImportInfo::FileImport) + continue; + if (!info.id().isEmpty()) { if (info.id() == name) { if (foundInObject) @@ -3426,48 +3441,91 @@ const Value *TypeEnvironment::lookupMember(const QString &name, const Context *c continue; } - if (info.type() == ImportInfo::FileImport) { - if (import->className() == name) { - if (foundInObject) - *foundInObject = this; - return import; - } - } else { - if (const Value *v = import->lookupMember(name, context, foundInObject)) - return v; - } + if (const Value *v = import->lookupMember(name, context, foundInObject)) + return v; } if (foundInObject) *foundInObject = 0; return 0; } -void TypeEnvironment::processMembers(MemberProcessor *processor) const +void TypeScope::processMembers(MemberProcessor *processor) const { - QListIterator<Import> it(_imports); + QListIterator<Import> it(_imports->all()); it.toBack(); while (it.hasPrevious()) { const Import &i = it.previous(); const ObjectValue *import = i.object; const ImportInfo &info = i.info; + // JS import has no types + if (info.type() == ImportInfo::FileImport) + continue; + if (!info.id().isEmpty()) { processor->processProperty(info.id(), import); } else { - if (info.type() == ImportInfo::FileImport) - processor->processProperty(import->className(), import); - else - import->processMembers(processor); + import->processMembers(processor); } } } -void TypeEnvironment::addImport(const Import &import) +JSImportScope::JSImportScope(const Imports *imports, Engine *engine) + : ObjectValue(engine) + , _imports(imports) +{ +} + +const Value *JSImportScope::lookupMember(const QString &name, const Context *, + const ObjectValue **foundInObject, bool) const +{ + QListIterator<Import> it(_imports->all()); + it.toBack(); + while (it.hasPrevious()) { + const Import &i = it.previous(); + const ObjectValue *import = i.object; + const ImportInfo &info = i.info; + + // JS imports are always: import "somefile.js" as Foo + if (info.type() != ImportInfo::FileImport) + continue; + + if (info.id() == name) { + if (foundInObject) + *foundInObject = this; + return import; + } + } + if (foundInObject) + *foundInObject = 0; + return 0; +} + +void JSImportScope::processMembers(MemberProcessor *processor) const +{ + QListIterator<Import> it(_imports->all()); + it.toBack(); + while (it.hasPrevious()) { + const Import &i = it.previous(); + const ObjectValue *import = i.object; + const ImportInfo &info = i.info; + + if (info.type() == ImportInfo::FileImport) + processor->processProperty(info.id(), import); + } +} + +Imports::Imports(Engine *engine) + : _typeScope(new TypeScope(this, engine)) + , _jsImportScope(new JSImportScope(this, engine)) +{} + +void Imports::append(const Import &import) { _imports.append(import); } -ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *context) const +ImportInfo Imports::info(const QString &name, const Context *context) const { QString firstId = name; int dotIdx = firstId.indexOf(QLatin1Char('.')); @@ -3498,11 +3556,21 @@ ImportInfo TypeEnvironment::importInfo(const QString &name, const Context *conte return ImportInfo(); } -QList<TypeEnvironment::Import> TypeEnvironment::imports() const +QList<Import> Imports::all() const { return _imports; } +const TypeScope *Imports::typeScope() const +{ + return _typeScope; +} + +const JSImportScope *Imports::jsImportScope() const +{ + return _jsImportScope; +} + #ifdef QT_DEBUG class MemberDumper: public MemberProcessor @@ -3541,9 +3609,9 @@ public: } }; -void TypeEnvironment::dump() const +void Imports::dump() const { - qDebug() << "Type environment contents, in search order:"; + qDebug() << "Imports contents, in search order:"; QListIterator<Import> it(_imports); it.toBack(); while (it.hasPrevious()) { diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index 8af4981c60b..5c68ca493df 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -71,8 +71,9 @@ class FunctionValue; class Reference; class ColorValue; class AnchorLineValue; -class TypeEnvironment; -class AttachedTypeEnvironment; +class Imports; +class TypeScope; +class JSImportScope; typedef QList<const Value *> ValueList; @@ -303,7 +304,8 @@ public: const ObjectValue *globalScope; QSharedPointer<const QmlComponentChain> qmlComponentScope; QList<const ObjectValue *> qmlScopeObjects; - const TypeEnvironment *qmlTypes; + const TypeScope *qmlTypes; + const JSImportScope *jsImports; QList<const ObjectValue *> jsScopes; // rebuilds the flat list of all scopes @@ -326,8 +328,8 @@ public: const ScopeChain &scopeChain() const; ScopeChain &scopeChain(); - const TypeEnvironment *typeEnvironment(const Document *doc) const; - void setTypeEnvironment(const Document *doc, const TypeEnvironment *typeEnvironment); + const Imports *imports(const Document *doc) const; + void setImports(const Document *doc, const Imports *imports); const Value *lookup(const QString &name, const ObjectValue **foundInScope = 0) const; const ObjectValue *lookupType(const Document *doc, AST::UiQualifiedId *qmlTypeName) const; @@ -345,7 +347,7 @@ private: Snapshot _snapshot; QSharedPointer<Engine> _engine; QHash<const ObjectValue *, Properties> _properties; - QHash<const Document *, const TypeEnvironment *> _typeEnvironments; + QHash<const Document *, QSharedPointer<const Imports> > _imports; ScopeChain _scopeChain; int _qmlScopeObjectIndex; bool _qmlScopeObjectSet; @@ -1030,32 +1032,59 @@ private: AST::UiImport *_ast; }; -class QMLJS_EXPORT TypeEnvironment: public ObjectValue +class QMLJS_EXPORT Import { +public: + Import(); + + // const! + ObjectValue *object; + ImportInfo info; + // uri imports: path to library, else empty + QString libraryPath; +}; + +class Imports; + +class QMLJS_EXPORT TypeScope: public ObjectValue { public: - class Import { - public: - Import(); + TypeScope(const Imports *imports, Engine *engine); - // const! - ObjectValue *object; - ImportInfo info; - // uri imports: path to library, else empty - QString libraryPath; - }; + virtual const Value *lookupMember(const QString &name, const Context *context, + const ObjectValue **foundInObject = 0, + bool examinePrototypes = true) const; + virtual void processMembers(MemberProcessor *processor) const; +private: + const Imports *_imports; +}; + +class QMLJS_EXPORT JSImportScope: public ObjectValue +{ public: - TypeEnvironment(Engine *engine); + JSImportScope(const Imports *imports, Engine *engine); virtual const Value *lookupMember(const QString &name, const Context *context, const ObjectValue **foundInObject = 0, bool examinePrototypes = true) const; virtual void processMembers(MemberProcessor *processor) const; - void addImport(const Import &import); +private: + const Imports *_imports; +}; + +class QMLJS_EXPORT Imports +{ +public: + Imports(Engine *engine); + + void append(const Import &import); + + ImportInfo info(const QString &name, const Context *context) const; + QList<Import> all() const; - ImportInfo importInfo(const QString &name, const Context *context) const; - QList<Import> imports() const; + const TypeScope *typeScope() const; + const JSImportScope *jsImportScope() const; #ifdef QT_DEBUG void dump() const; @@ -1065,6 +1094,8 @@ private: // holds imports in the order they appeared, // lookup order is back to front QList<Import> _imports; + TypeScope *_typeScope; + JSImportScope *_jsImportScope; }; } } // namespace QmlJS::Interpreter diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index e18d10444d9..d5c883dacc9 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -89,7 +89,7 @@ public: Interpreter::Context *context; QStringList importPaths; - QHash<ImportCacheKey, TypeEnvironment::Import> importCache; + QHash<ImportCacheKey, Import> importCache; Document::Ptr doc; QList<DiagnosticMessage> *diagnosticMessages; @@ -161,22 +161,22 @@ void Link::linkImports() if (d->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); + Imports *imports = new Imports(engine()); + populateImportedTypes(imports, d->doc); + d->context->setImports(d->doc.data(), imports); } 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); + Imports *imports = new Imports(engine()); + populateImportedTypes(imports, doc); + d->context->setImports(doc.data(), imports); } } -void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) +void Link::populateImportedTypes(Imports *imports, Document::Ptr doc) { Q_D(Link); @@ -184,15 +184,15 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) return; // implicit imports: the <default> package is always available - loadImplicitDefaultImports(typeEnv); + loadImplicitDefaultImports(imports); // implicit imports: // qml files in the same directory are available without explicit imports - loadImplicitDirectoryImports(typeEnv, doc); + loadImplicitDirectoryImports(imports, doc); // explicit imports, whether directories, files or libraries foreach (const ImportInfo &info, doc->bind()->imports()) { - TypeEnvironment::Import import = d->importCache.value(ImportCacheKey(info)); + Import import = d->importCache.value(ImportCacheKey(info)); if (!import.object) { switch (info.type()) { @@ -210,7 +210,7 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) d->importCache.insert(ImportCacheKey(info), import); } if (import.object) - typeEnv->addImport(import); + imports->append(import); } } @@ -221,12 +221,14 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) import "content" 4.6 as Xxx import "http://www.ovi.com/" as Ovi + + import "file.js" as Foo */ -TypeEnvironment::Import Link::importFileOrDirectory(Document::Ptr doc, const ImportInfo &importInfo) +Import Link::importFileOrDirectory(Document::Ptr doc, const ImportInfo &importInfo) { Q_D(Link); - TypeEnvironment::Import import; + Import import; import.info = importInfo; import.object = 0; @@ -259,11 +261,11 @@ TypeEnvironment::Import Link::importFileOrDirectory(Document::Ptr doc, const Imp import Qt 4.6 as Xxx (import com.nokia.qt is the same as the ones above) */ -TypeEnvironment::Import Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo) +Import Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo) { Q_D(Link); - TypeEnvironment::Import import; + Import import; import.info = importInfo; import.object = new ObjectValue(engine()); @@ -305,7 +307,7 @@ TypeEnvironment::Import Link::importNonFile(Document::Ptr doc, const ImportInfo bool Link::importLibrary(Document::Ptr doc, const QString &libraryPath, - TypeEnvironment::Import *import, + Import *import, const QString &importPath) { Q_D(Link); @@ -435,32 +437,32 @@ void Link::loadQmldirComponents(Interpreter::ObjectValue *import, ComponentVersi } } -void Link::loadImplicitDirectoryImports(TypeEnvironment *typeEnv, Document::Ptr doc) +void Link::loadImplicitDirectoryImports(Imports *imports, Document::Ptr doc) { Q_D(Link); ImportInfo implcitDirectoryImportInfo( ImportInfo::ImplicitDirectoryImport, doc->path()); - TypeEnvironment::Import directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo)); + Import directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo)); if (!directoryImport.object) { directoryImport = importFileOrDirectory(doc, implcitDirectoryImportInfo); if (directoryImport.object) d->importCache.insert(ImportCacheKey(implcitDirectoryImportInfo), directoryImport); } if (directoryImport.object) { - typeEnv->addImport(directoryImport); + imports->append(directoryImport); } } -void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv) +void Link::loadImplicitDefaultImports(Imports *imports) { Q_D(Link); const QString defaultPackage = CppQmlTypes::defaultPackage; if (engine()->cppQmlTypes().hasPackage(defaultPackage)) { ImportInfo info(ImportInfo::LibraryImport, defaultPackage); - TypeEnvironment::Import import = d->importCache.value(ImportCacheKey(info)); + Import import = d->importCache.value(ImportCacheKey(info)); if (!import.object) { import.info = info; import.object = new ObjectValue(engine()); @@ -470,6 +472,6 @@ void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv) } d->importCache.insert(ImportCacheKey(info), import); } - typeEnv->addImport(import); + imports->append(import); } } diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 338dd11d226..20b8272057e 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -73,25 +73,25 @@ private: void linkImports(); - void populateImportedTypes(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc); - Interpreter::TypeEnvironment::Import importFileOrDirectory( + void populateImportedTypes(Interpreter::Imports *imports, Document::Ptr doc); + Interpreter::Import importFileOrDirectory( Document::Ptr doc, const Interpreter::ImportInfo &importInfo); - Interpreter::TypeEnvironment::Import importNonFile( + Interpreter::Import importNonFile( Document::Ptr doc, const Interpreter::ImportInfo &importInfo); void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace); bool importLibrary(Document::Ptr doc, const QString &libraryPath, - Interpreter::TypeEnvironment::Import *import, + Interpreter::Import *import, const QString &importPath = QString()); void loadQmldirComponents(Interpreter::ObjectValue *import, LanguageUtils::ComponentVersion version, const LibraryInfo &libraryInfo, const QString &libraryPath); - void loadImplicitDirectoryImports(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc); - void loadImplicitDefaultImports(Interpreter::TypeEnvironment *typeEnv); + void loadImplicitDirectoryImports(Interpreter::Imports *imports, Document::Ptr doc); + void loadImplicitDefaultImports(Interpreter::Imports *imports); void error(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message); void warning(const Document::Ptr &doc, const AST::SourceLocation &loc, const QString &message); diff --git a/src/libs/qmljs/qmljsscopebuilder.cpp b/src/libs/qmljs/qmljsscopebuilder.cpp index 62f43c1e5c2..5fb853aa0a2 100644 --- a/src/libs/qmljs/qmljsscopebuilder.cpp +++ b/src/libs/qmljs/qmljsscopebuilder.cpp @@ -127,8 +127,9 @@ void ScopeBuilder::initializeRootScope() componentScopes.insert(_doc.data(), chain); makeComponentChain(_doc, snapshot, chain, &componentScopes); - if (const TypeEnvironment *typeEnvironment = _context->typeEnvironment(_doc.data())) { - scopeChain.qmlTypes = typeEnvironment; + 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 diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index ebdaf7b5ed2..a5367d49ffd 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -358,8 +358,8 @@ public: majorVersion = ComponentVersion::NoVersion; minorVersion = ComponentVersion::NoVersion; - const Interpreter::TypeEnvironment *typeEnv = m_lookupContext->context()->typeEnvironment(m_lookupContext->document().data()); - Interpreter::ImportInfo importInfo = typeEnv->importInfo(fullTypeName, m_context); + const Interpreter::Imports *imports = m_lookupContext->context()->imports(m_lookupContext->document().data()); + Interpreter::ImportInfo importInfo = imports->info(fullTypeName, m_context); if (importInfo.isValid() && importInfo.type() == Interpreter::ImportInfo::LibraryImport) { QString name = importInfo.name().replace("\\", "."); majorVersion = importInfo.version().majorVersion(); diff --git a/src/plugins/qmljseditor/qmljshoverhandler.cpp b/src/plugins/qmljseditor/qmljshoverhandler.cpp index c7b07a30f8e..aee2154618e 100644 --- a/src/plugins/qmljseditor/qmljshoverhandler.cpp +++ b/src/plugins/qmljseditor/qmljshoverhandler.cpp @@ -219,11 +219,11 @@ void HoverHandler::handleOrdinaryMatch(const LookupContext::Ptr &lookupContext, void HoverHandler::handleImport(const LookupContext::Ptr &lookupContext, AST::UiImport *node) { - const Interpreter::TypeEnvironment *typeEnv = lookupContext->context()->typeEnvironment(lookupContext->document().data()); - if (!typeEnv) + const Interpreter::Imports *imports = lookupContext->context()->imports(lookupContext->document().data()); + if (!imports) return; - foreach (const Interpreter::TypeEnvironment::Import &import, typeEnv->imports()) { + foreach (const Interpreter::Import &import, imports->all()) { if (import.info.ast() == node) { if (import.info.type() == Interpreter::ImportInfo::LibraryImport && !import.libraryPath.isEmpty()) { -- GitLab