OSDN Git Service

QmlJS: Support module apis defined by QML modules.
authorChristian Kamm <christian.d.kamm@nokia.com>
Wed, 12 Oct 2011 06:36:02 +0000 (08:36 +0200)
committerChristian Kamm <christian.d.kamm@nokia.com>
Fri, 21 Oct 2011 06:21:00 +0000 (08:21 +0200)
Change-Id: I18ec9daf8088f7db5ff2da11da14b539f501bab3
Reviewed-by: Fawzi Mohamed <fawzi.mohamed@nokia.com>
src/libs/qmljs/qmljsdocument.h
src/libs/qmljs/qmljsinterpreter.cpp
src/libs/qmljs/qmljsinterpreter.h
src/libs/qmljs/qmljslink.cpp
src/libs/qmljs/qmljstypedescriptionreader.cpp
src/libs/qmljs/qmljstypedescriptionreader.h
src/plugins/qmljstools/qmljsplugindumper.cpp

index 65c500c..50e7a81 100644 (file)
@@ -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; }
 
index b448212..3a1ff62 100644 (file)
@@ -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 {
index c914d28..7b95f2d 100644 (file)
@@ -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
index 3e8cc72..0c78ab5 100644 (file)
@@ -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));
         }
     }
 
index 02b7a03..44b7a47 100644 (file)
@@ -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);
index f8e5366..6292d5f 100644 (file)
@@ -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
index 921d8b9..a574eb9 100644 (file)
@@ -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 {