diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index eba83283f00..53c32f14f80 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -982,6 +982,7 @@ public: enum Type { InvalidImport, ImplicitDirectoryImport, + ImplicitLibraryImport, LibraryImport, FileImport, DirectoryImport, diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 2e6c91530ac..6fc158fac59 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -40,12 +40,12 @@ #include "qmljsscopebuilder.h" #include "qmljsmodelmanagerinterface.h" -#include - #include #include #include +#include + using namespace LanguageUtils; using namespace QmlJS; using namespace QmlJS::Interpreter; @@ -174,33 +174,15 @@ void Link::populateImportedTypes(TypeEnvironment *typeEnv, Document::Ptr doc) return; // implicit imports: the package is always available - const QString defaultPackage = CppQmlTypes::defaultPackage; - if (engine()->cppQmlTypes().hasPackage(defaultPackage)) { - ImportInfo info(ImportInfo::LibraryImport, defaultPackage); - ObjectValue *import = d->importCache.value(ImportCacheKey(info)); - if (!import) { - import = new ObjectValue(engine()); - foreach (QmlObjectValue *object, - engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) { - import->setProperty(object->className(), object); - } - d->importCache.insert(ImportCacheKey(info), import); - } - typeEnv->addImport(import, info); - } - + loadImplicitDefaultImports(typeEnv); // implicit imports: // qml files in the same directory are available without explicit imports - 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); - } - if (directoryImport) - typeEnv->addImport(directoryImport, implcitImportInfo); + loadImplicitDirectoryImports(typeEnv, doc); + + // implicit imports: + // a qmldir file in the same directory gets automatically imported at the highest version + loadImplicitLibraryImports(typeEnv, doc->path()); // explicit imports, whether directories, files or libraries foreach (const ImportInfo &info, doc->bind()->imports()) { @@ -305,23 +287,7 @@ ObjectValue *Link::importNonFile(Document::Ptr doc, const ImportInfo &importInfo } } - QSet importedTypes; - foreach (const QmlDirParser::Component &component, libraryInfo.components()) { - if (importedTypes.contains(component.typeName)) - continue; - - ComponentVersion componentVersion(component.majorVersion, - component.minorVersion); - if (version < componentVersion) - continue; - - importedTypes.insert(component.typeName); - if (Document::Ptr importedDoc = d->snapshot.document( - libraryPath + QDir::separator() + component.fileName)) { - if (ObjectValue *v = importedDoc->bind()->rootObjectValue()) - import->setProperty(component.typeName, v); - } - } + loadQmldirComponents(import, version, libraryInfo, libraryPath); break; } @@ -369,3 +335,104 @@ void Link::warning(const Document::Ptr &doc, const AST::SourceLocation &loc, con if (doc->fileName() == d->doc->fileName()) d->diagnosticMessages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message)); } + +void Link::loadQmldirComponents(Interpreter::ObjectValue *import, ComponentVersion version, + const LibraryInfo &libraryInfo, const QString &libraryPath) +{ + Q_D(Link); + + QSet importedTypes; + foreach (const QmlDirParser::Component &component, libraryInfo.components()) { + if (importedTypes.contains(component.typeName)) + continue; + + ComponentVersion componentVersion(component.majorVersion, + component.minorVersion); + if (version < componentVersion) + continue; + + importedTypes.insert(component.typeName); + if (Document::Ptr importedDoc = d->snapshot.document( + libraryPath + QDir::separator() + component.fileName)) { + if (ObjectValue *v = importedDoc->bind()->rootObjectValue()) + import->setProperty(component.typeName, v); + } + } +} + +void Link::loadImplicitDirectoryImports(TypeEnvironment *typeEnv, Document::Ptr doc) +{ + Q_D(Link); + + ImportInfo implcitDirectoryImportInfo(ImportInfo::ImplicitDirectoryImport, doc->path()); + ObjectValue *directoryImport = d->importCache.value(ImportCacheKey(implcitDirectoryImportInfo)); + if (!directoryImport) { + directoryImport = importFile(doc, implcitDirectoryImportInfo); + if (directoryImport) + d->importCache.insert(ImportCacheKey(implcitDirectoryImportInfo), directoryImport); + } + if (directoryImport) + typeEnv->addImport(directoryImport, implcitDirectoryImportInfo); +} + +void Link::loadImplicitLibraryImports(TypeEnvironment *typeEnv, const QString &path) +{ + Q_D(Link); + + ImportInfo implicitLibraryImportInfo(ImportInfo::ImplicitLibraryImport, path); + ObjectValue *libraryImport = d->importCache.value(ImportCacheKey(implicitLibraryImportInfo)); + LibraryInfo libraryInfo = d->snapshot.libraryInfo(path); + if (!libraryImport && libraryInfo.isValid()) { + libraryImport = new ObjectValue(engine()); + ComponentVersion latestVersion(std::numeric_limits::max(), + std::numeric_limits::max()); + + loadQmldirComponents(libraryImport, latestVersion, libraryInfo, path); + + // ### since there is no way of determining the plugin URI, we can't dump + // the plugin if it has not been dumped already. + if (!libraryInfo.plugins().isEmpty() + && libraryInfo.dumpStatus() == LibraryInfo::DumpDone) { + // add types to the engine + engine()->cppQmlTypes().load(engine(), libraryInfo.metaObjects()); + + // guess a likely URI + QMap uris; + foreach (const FakeMetaObject::ConstPtr &fmo, libraryInfo.metaObjects()) { + foreach (const FakeMetaObject::Export &exp, fmo->exports()) { + ++uris[exp.package]; + } + } + if (!uris.isEmpty()) { + const QString uri = (uris.end() - 1).key(); + + foreach (QmlObjectValue *object, + engine()->cppQmlTypes().typesForImport(uri, latestVersion)) { + libraryImport->setProperty(object->className(), object); + } + } + } + } + if (libraryImport) + typeEnv->addImport(libraryImport, implicitLibraryImportInfo); +} + +void Link::loadImplicitDefaultImports(TypeEnvironment *typeEnv) +{ + Q_D(Link); + + const QString defaultPackage = CppQmlTypes::defaultPackage; + if (engine()->cppQmlTypes().hasPackage(defaultPackage)) { + ImportInfo info(ImportInfo::LibraryImport, defaultPackage); + ObjectValue *import = d->importCache.value(ImportCacheKey(info)); + if (!import) { + import = new ObjectValue(engine()); + foreach (QmlObjectValue *object, + engine()->cppQmlTypes().typesForImport(defaultPackage, ComponentVersion())) { + import->setProperty(object->className(), object); + } + d->importCache.insert(ImportCacheKey(info), import); + } + typeEnv->addImport(import, info); + } +} diff --git a/src/libs/qmljs/qmljslink.h b/src/libs/qmljs/qmljslink.h index 6b09ccf039e..ad19a54781d 100644 --- a/src/libs/qmljs/qmljslink.h +++ b/src/libs/qmljs/qmljslink.h @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -73,6 +74,14 @@ private: Interpreter::ObjectValue *importNonFile(Document::Ptr doc, const Interpreter::ImportInfo &importInfo); void importObject(Bind *bind, const QString &name, Interpreter::ObjectValue *object, NameId *targetNamespace); + void loadQmldirComponents(Interpreter::ObjectValue *import, + LanguageUtils::ComponentVersion version, + const LibraryInfo &libraryInfo, + const QString &libraryPath); + void loadImplicitDirectoryImports(Interpreter::TypeEnvironment *typeEnv, Document::Ptr doc); + void loadImplicitLibraryImports(Interpreter::TypeEnvironment *typeEnv, const QString &path); + void loadImplicitDefaultImports(Interpreter::TypeEnvironment *typeEnv); + 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);