diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 65c500c119d1beb6c3bf725e7467b7058d2650fb..50e7a81034de22e5040e2e8f02c548c586819d17 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -128,6 +128,14 @@ private: friend class Snapshot; }; +class QMLJS_EXPORT ModuleApiInfo +{ +public: + QString uri; + LanguageUtils::ComponentVersion version; + QString cppName; +}; + class QMLJS_EXPORT LibraryInfo { public: @@ -152,6 +160,7 @@ private: QList<QmlDirParser::TypeInfo> _typeinfos; typedef QList<LanguageUtils::FakeMetaObject::ConstPtr> FakeMetaObjectList; FakeMetaObjectList _metaObjects; + QList<ModuleApiInfo> _moduleApis; PluginTypeInfoStatus _dumpStatus; QString _dumpError; @@ -176,6 +185,12 @@ public: void setMetaObjects(const FakeMetaObjectList &objects) { _metaObjects = objects; } + QList<ModuleApiInfo> moduleApis() const + { return _moduleApis; } + + void setModuleApis(const QList<ModuleApiInfo> &apis) + { _moduleApis = apis; } + bool isValid() const { return _status == Found; } diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index b448212e3d96530821434707be90a7a02a665211..3a1ff62e8c2278afb13e3db70b4f02cf798b858f 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -1285,13 +1285,10 @@ CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInf QString error, warning; QFile file(qmlTypeFile.absoluteFilePath()); if (file.open(QIODevice::ReadOnly)) { - QString contents = QString::fromUtf8(file.readAll()); + QByteArray contents = file.readAll(); file.close(); - TypeDescriptionReader reader(contents); - if (!reader(&newObjects)) - error = reader.errorMessage(); - warning = reader.warningMessage(); + parseQmlTypeDescriptions(contents, &newObjects, 0, &error, &warning); } else { error = file.errorString(); } @@ -1312,13 +1309,14 @@ CppQmlTypesLoader::BuiltinObjects CppQmlTypesLoader::loadQmlTypes(const QFileInf void CppQmlTypesLoader::parseQmlTypeDescriptions(const QByteArray &xml, BuiltinObjects *newObjects, + QList<ModuleApiInfo> *newModuleApis, QString *errorMessage, QString *warningMessage) { errorMessage->clear(); warningMessage->clear(); TypeDescriptionReader reader(QString::fromUtf8(xml)); - if (!reader(newObjects)) { + if (!reader(newObjects, newModuleApis)) { if (reader.errorMessage().isEmpty()) { *errorMessage = QLatin1String("unknown error"); } else { diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index c914d28d0f8e9ee41637b86c0b1faba6ee8c042c..7b95f2d31fc72a6cf0154f1dec07d5ce536eb0fd 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -643,7 +643,7 @@ public: static void parseQmlTypeDescriptions( const QByteArray &qmlTypes, BuiltinObjects *newObjects, - QString *errorMessage, QString *warningMessage); + QList<ModuleApiInfo> *newModuleApis, QString *errorMessage, QString *warningMessage); }; class QMLJS_EXPORT CppQmlTypes diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index 3e8cc72f147d701dec928c829027e346572decb2..0c78ab56c24fc150ab97f58958bfb1a7fe0ebc5f 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -89,6 +89,8 @@ public: QHash<ImportCacheKey, Import> importCache; + QHash<QString, QList<ModuleApiInfo> > importableModuleApis; + Document::Ptr document; QList<DiagnosticMessage> *diagnosticMessages; @@ -231,6 +233,8 @@ Context::ImportsPerDocument LinkPrivate::linkImports() void LinkPrivate::populateImportedTypes(Imports *imports, Document::Ptr doc) { + importableModuleApis.clear(); + // implicit imports: the <default> package is always available loadImplicitDefaultImports(imports); @@ -315,6 +319,18 @@ Import LinkPrivate::importFileOrDirectory(Document::Ptr doc, const ImportInfo &i return import; } +static ModuleApiInfo findBestModuleApi(const QList<ModuleApiInfo> &apis, const ComponentVersion &version) +{ + ModuleApiInfo best; + foreach (const ModuleApiInfo &moduleApi, apis) { + if (moduleApi.version <= version + && (!best.version.isValid() || best.version < moduleApi.version)) { + best = moduleApi; + } + } + return best; +} + /* import Qt 4.6 import Qt 4.6 as Xxx @@ -372,6 +388,13 @@ Import LinkPrivate::importNonFile(Document::Ptr doc, const ImportInfo &importInf } } + // check module apis that previous imports may have enabled + ModuleApiInfo moduleApi = findBestModuleApi(importableModuleApis.value(packageName), version); + if (moduleApi.version.isValid()) { + importFound = true; + import.object->setPrototype(valueOwner->cppQmlTypes().objectByCppName(moduleApi.name)); + } + if (!importFound && importInfo.ast()) { import.valid = false; error(doc, locationFromRange(importInfo.ast()->firstSourceLocation(), @@ -442,6 +465,21 @@ bool LinkPrivate::importLibrary(Document::Ptr doc, foreach (const CppComponentValue *object, valueOwner->cppQmlTypes().createObjectsForImport(packageName, version)) { import->object->setMember(object->className(), object); } + + // all but no-uri module apis become available for import + QList<ModuleApiInfo> noUriModuleApis; + foreach (const ModuleApiInfo &moduleApi, libraryInfo.moduleApis()) { + if (moduleApi.uri.isEmpty()) { + noUriModuleApis += moduleApi; + } else { + importableModuleApis[moduleApi.uri] += moduleApi; + } + } + + // if a module api has no uri, it shares the same name + ModuleApiInfo sameUriModuleApi = findBestModuleApi(noUriModuleApis, version); + if (sameUriModuleApi.version.isValid()) + import->object->setPrototype(valueOwner->cppQmlTypes().objectByCppName(sameUriModuleApi.name)); } } diff --git a/src/libs/qmljs/qmljstypedescriptionreader.cpp b/src/libs/qmljs/qmljstypedescriptionreader.cpp index 02b7a034653f8f4687e9af9b96f4dcdcfb0b8007..44b7a47e15f7fb74112f58d2f8a652a870d362bc 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.cpp +++ b/src/libs/qmljs/qmljstypedescriptionreader.cpp @@ -59,7 +59,9 @@ TypeDescriptionReader::~TypeDescriptionReader() { } -bool TypeDescriptionReader::operator()(QHash<QString, FakeMetaObject::ConstPtr> *objects) +bool TypeDescriptionReader::operator()( + QHash<QString, FakeMetaObject::ConstPtr> *objects, + QList<ModuleApiInfo> *moduleApis) { Engine engine; @@ -77,6 +79,7 @@ bool TypeDescriptionReader::operator()(QHash<QString, FakeMetaObject::ConstPtr> } _objects = objects; + _moduleApis = moduleApis; readDocument(parser.ast()); return _errorMessage.isEmpty(); @@ -152,8 +155,11 @@ void TypeDescriptionReader::readModule(UiObjectDefinition *ast) continue; } - if (typeName == QLatin1String("Component")) + if (typeName == QLatin1String("Component")) { readComponent(component); + } else if (typeName == QLatin1String("ModuleApi")) { + readModuleApi(component); + } } } @@ -226,6 +232,40 @@ void TypeDescriptionReader::readComponent(UiObjectDefinition *ast) _objects->insert(fmo->className(), fmo); } +void TypeDescriptionReader::readModuleApi(UiObjectDefinition *ast) +{ + ModuleApiInfo apiInfo; + + for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) { + UiObjectMember *member = it->member; + UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member); + + if (script) { + const QString name = toString(script->qualifiedId); + if (name == "uri") { + apiInfo.uri = readStringBinding(script); + } else if (name == "version") { + apiInfo.version = readNumericVersionBinding(script); + } else if (name == "name") { + apiInfo.name = readStringBinding(script); + } else { + addWarning(script->firstSourceLocation(), + "Expected only uri, version and name script bindings"); + } + } else { + addWarning(member->firstSourceLocation(), "Expected only script bindings"); + } + } + + if (!apiInfo.version.isValid()) { + addError(ast->firstSourceLocation(), "ModuleApi definition has no or invalid 'version' binding"); + return; + } + + if (_moduleApis) + _moduleApis->append(apiInfo); +} + void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo) { FakeMetaMethod fmm; @@ -438,6 +478,30 @@ double TypeDescriptionReader::readNumericBinding(AST::UiScriptBinding *ast) return numericLit->value; } +ComponentVersion TypeDescriptionReader::readNumericVersionBinding(UiScriptBinding *ast) +{ + ComponentVersion invalidVersion; + + if (!ast || !ast->statement) { + addError(ast->colonToken, "Expected numeric literal after colon"); + return invalidVersion; + } + + ExpressionStatement *expStmt = AST::cast<ExpressionStatement *>(ast->statement); + if (!expStmt) { + addError(ast->statement->firstSourceLocation(), "Expected numeric literal after colon"); + return invalidVersion; + } + + NumericLiteral *numericLit = AST::cast<NumericLiteral *>(expStmt->expression); + if (!numericLit) { + addError(expStmt->firstSourceLocation(), "Expected numeric literal after colon"); + return invalidVersion; + } + + return ComponentVersion(_source.mid(numericLit->literalToken.begin(), numericLit->literalToken.length)); +} + int TypeDescriptionReader::readIntBinding(AST::UiScriptBinding *ast) { double v = readNumericBinding(ast); diff --git a/src/libs/qmljs/qmljstypedescriptionreader.h b/src/libs/qmljs/qmljstypedescriptionreader.h index f8e5366e9d210e01dd04af035e49da8884902275..6292d5f280033e0babeee7e0aa9f7bc04f120343 100644 --- a/src/libs/qmljs/qmljstypedescriptionreader.h +++ b/src/libs/qmljs/qmljstypedescriptionreader.h @@ -34,6 +34,8 @@ #define QMLJSTYPEDESCRIPTIONREADER_H #include <languageutils/fakemetaobject.h> +#include <languageutils/componentversion.h> +#include "qmljsdocument.h" // for Q_DECLARE_TR_FUNCTIONS #include <QtCore/QCoreApplication> @@ -60,7 +62,9 @@ public: explicit TypeDescriptionReader(const QString &data); ~TypeDescriptionReader(); - bool operator()(QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *objects); + bool operator()( + QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *objects, + QList<ModuleApiInfo> *moduleApis); QString errorMessage() const; QString warningMessage() const; @@ -68,6 +72,7 @@ private: void readDocument(AST::UiProgram *ast); void readModule(AST::UiObjectDefinition *ast); void readComponent(AST::UiObjectDefinition *ast); + void readModuleApi(AST::UiObjectDefinition *ast); void readSignalOrMethod(AST::UiObjectDefinition *ast, bool isMethod, LanguageUtils::FakeMetaObject::Ptr fmo); void readProperty(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo); void readEnum(AST::UiObjectDefinition *ast, LanguageUtils::FakeMetaObject::Ptr fmo); @@ -76,6 +81,7 @@ private: QString readStringBinding(AST::UiScriptBinding *ast); bool readBoolBinding(AST::UiScriptBinding *ast); double readNumericBinding(AST::UiScriptBinding *ast); + LanguageUtils::ComponentVersion readNumericVersionBinding(AST::UiScriptBinding *ast); int readIntBinding(AST::UiScriptBinding *ast); void readExports(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo); void readMetaObjectRevisions(AST::UiScriptBinding *ast, LanguageUtils::FakeMetaObject::Ptr fmo); @@ -88,6 +94,7 @@ private: QString _errorMessage; QString _warningMessage; QHash<QString, LanguageUtils::FakeMetaObject::ConstPtr> *_objects; + QList<ModuleApiInfo> *_moduleApis; }; } // namespace QmlJS diff --git a/src/plugins/qmljstools/qmljsplugindumper.cpp b/src/plugins/qmljstools/qmljsplugindumper.cpp index 921d8b994cc734738fbaf43b30b457d0b9d22052..a574eb989f1c8e98c815990c757bc78923dc2177 100644 --- a/src/plugins/qmljstools/qmljsplugindumper.cpp +++ b/src/plugins/qmljstools/qmljsplugindumper.cpp @@ -257,21 +257,6 @@ static void printParseWarnings(const QString &libraryPath, const QString &warnin "%2").arg(libraryPath, warning)); } -static QList<FakeMetaObject::ConstPtr> parseHelper(const QByteArray &qmlTypeDescriptions, - QString *error, - QString *warning) -{ - QList<FakeMetaObject::ConstPtr> ret; - QHash<QString, FakeMetaObject::ConstPtr> newObjects; - CppQmlTypesLoader::parseQmlTypeDescriptions(qmlTypeDescriptions, &newObjects, - error, warning); - - if (error->isEmpty()) { - ret = newObjects.values(); - } - return ret; -} - void PluginDumper::qmlPluginTypeDumpDone(int exitCode) { QProcess *process = qobject_cast<QProcess *>(sender()); @@ -295,13 +280,16 @@ void PluginDumper::qmlPluginTypeDumpDone(int exitCode) const QByteArray output = process->readAllStandardOutput(); QString error; QString warning; - QList<FakeMetaObject::ConstPtr> objectsList = parseHelper(output, &error, &warning); + CppQmlTypesLoader::BuiltinObjects objectsList; + QList<ModuleApiInfo> moduleApis; + CppQmlTypesLoader::parseQmlTypeDescriptions(output, &objectsList, &moduleApis, &error, &warning); if (exitCode == 0) { if (!error.isEmpty()) { libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpError, qmldumpErrorMessage(libraryPath, error)); } else { - libraryInfo.setMetaObjects(objectsList); + libraryInfo.setMetaObjects(objectsList.values()); + libraryInfo.setModuleApis(moduleApis); libraryInfo.setPluginTypeInfoStatus(LibraryInfo::DumpDone); } @@ -352,6 +340,7 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths, QStringList errors; QStringList warnings; QList<FakeMetaObject::ConstPtr> objects; + QList<ModuleApiInfo> moduleApis; foreach (const QString &qmltypesFilePath, qmltypesFilePaths) { Utils::FileReader reader; @@ -362,14 +351,21 @@ void PluginDumper::loadQmltypesFile(const QStringList &qmltypesFilePaths, QString error; QString warning; - objects += parseHelper(reader.data(), &error, &warning); - if (!error.isEmpty()) + CppQmlTypesLoader::BuiltinObjects newObjects; + QList<ModuleApiInfo> newModuleApis; + CppQmlTypesLoader::parseQmlTypeDescriptions(reader.data(), &newObjects, &newModuleApis, &error, &warning); + if (!error.isEmpty()) { errors += tr("Failed to parse '%1'.\nError: %2").arg(qmltypesFilePath, error); + } else { + objects += newObjects.values(); + moduleApis += newModuleApis; + } if (!warning.isEmpty()) warnings += warning; } libraryInfo.setMetaObjects(objects); + libraryInfo.setModuleApis(moduleApis); if (errors.isEmpty()) { libraryInfo.setPluginTypeInfoStatus(LibraryInfo::TypeInfoFileDone); } else {