diff --git a/src/libs/qmljs/qmljscomponentversion.h b/src/libs/qmljs/qmljscomponentversion.h index d0a837701b3840ff1c07d4e08ca6e8fa0fb715e3..97edc9dec51cca95f36b5fcdd7f217486cebb344 100644 --- a/src/libs/qmljs/qmljscomponentversion.h +++ b/src/libs/qmljs/qmljscomponentversion.h @@ -22,6 +22,13 @@ public: int minor() const { return _minor; } + // something in the GNU headers introduces the major() and minor() defines, + // use these two to disambiguate when necessary + int majorVersion() const + { return _major; } + int minorVersion() const + { return _minor; } + bool isValid() const; }; diff --git a/src/libs/qmljs/qmljsdocument.h b/src/libs/qmljs/qmljsdocument.h index 51b4e5665ff665067c70d1c2789b78001edf06fa..39931c54a40929e810e1a5de586f03116fcd1a08 100644 --- a/src/libs/qmljs/qmljsdocument.h +++ b/src/libs/qmljs/qmljsdocument.h @@ -44,6 +44,10 @@ namespace QmlJS { class Bind; class Snapshot; +namespace Interpreter { + class FakeMetaObject; +} + class QMLJS_EXPORT Document { public: @@ -113,6 +117,8 @@ class QMLJS_EXPORT LibraryInfo bool _valid; QList<QmlDirParser::Component> _components; QList<QmlDirParser::Plugin> _plugins; + typedef QList<const Interpreter::FakeMetaObject *> FakeMetaObjectList; + FakeMetaObjectList _metaObjects; public: LibraryInfo(); @@ -125,6 +131,12 @@ public: QList<QmlDirParser::Plugin> plugins() const { return _plugins; } + FakeMetaObjectList metaObjects() const + { return _metaObjects; } + + void setMetaObjects(const FakeMetaObjectList &objects) + { _metaObjects = objects; } + bool isValid() const { return _valid; } }; diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp index cca0d0c5b3111531b7f8c366e1b8487c9a8604d4..713cec30a30168f25c7619477953b2f826978371 100644 --- a/src/libs/qmljs/qmljsinterpreter.cpp +++ b/src/libs/qmljs/qmljsinterpreter.cpp @@ -41,7 +41,6 @@ #include <QtCore/QMetaObject> #include <QtCore/QMetaProperty> #include <QtCore/QXmlStreamReader> -#include <QtCore/QCoreApplication> #include <QtCore/QProcess> #include <QtCore/QDebug> @@ -205,6 +204,7 @@ class FakeMetaObject { QString m_name; QString m_package; + QString m_packageNameVersion; ComponentVersion m_version; const FakeMetaObject *m_super; QString m_superName; @@ -218,7 +218,11 @@ class FakeMetaObject { public: FakeMetaObject(const QString &name, const QString &package, ComponentVersion version) : m_name(name), m_package(package), m_version(version), m_super(0) - {} + { + m_packageNameVersion = QString::fromLatin1("%1.%2 %3.%4").arg( + package, name, + QString::number(version.majorVersion()), QString::number(version.minorVersion())); + } void setSuperclassName(const QString &superclass) { m_superName = superclass; } @@ -233,6 +237,8 @@ public: { return m_name; } QString packageName() const { return m_package; } + QString packageClassVersionString() const + { return m_packageNameVersion; } void addEnum(const FakeMetaEnum &fakeEnum) { m_enumNameToIndex.insert(fakeEnum.name(), m_enums.size()); m_enums.append(fakeEnum); } @@ -916,10 +922,10 @@ bool QmlObjectValue::hasChildInPackage() const { if (!packageName().isEmpty()) return true; - QHashIterator<QString, FakeMetaObject *> it(CppQmlTypesLoader::instance()->objects); + QHashIterator<QString, QmlObjectValue *> it(engine()->cppQmlTypes().types()); while (it.hasNext()) { it.next(); - const FakeMetaObject *other = it.value(); + const FakeMetaObject *other = it.value()->_metaObject; if (other->packageName().isEmpty()) continue; for (const FakeMetaObject *iter = other; iter; iter = iter->superClass()) { @@ -1933,29 +1939,7 @@ const Value *Function::invoke(const Activation *activation) const // typing environment //////////////////////////////////////////////////////////////////////////////// -CppQmlTypesLoader::CppQmlTypesLoader() -{ - QDir qmldumpExecutable(QCoreApplication::applicationDirPath()); -#ifndef Q_OS_WIN - m_qmldumpPath = qmldumpExecutable.absoluteFilePath(QLatin1String("qmldump")); -#else - m_qmldumpPath = qmldumpExecutable.absoluteFilePath(QLatin1String("qmldump.exe")); -#endif - QFileInfo qmldumpFileInfo(m_qmldumpPath); - if (!qmldumpFileInfo.exists()) { - qWarning() << "QmlJS::Interpreter::CppQmlTypesLoader: qmldump executable does not exist at" << m_qmldumpPath; - m_qmldumpPath.clear(); - } else if (!qmldumpFileInfo.isFile()) { - qWarning() << "QmlJS::Interpreter::CppQmlTypesLoader: " << m_qmldumpPath << " is not a file"; - m_qmldumpPath.clear(); - } -} - -CppQmlTypesLoader *CppQmlTypesLoader::instance() -{ - static CppQmlTypesLoader _instance; - return &_instance; -} +QList<const FakeMetaObject *> CppQmlTypesLoader::objectsFromXml; QStringList CppQmlTypesLoader::load(const QFileInfoList &xmlFiles) { @@ -1976,76 +1960,70 @@ QStringList CppQmlTypesLoader::load(const QFileInfoList &xmlFiles) } } - if (errorMsgs.isEmpty()) - addObjects(newObjects); - - return errorMsgs; -} + if (errorMsgs.isEmpty()) { + setSuperClasses(&newObjects); -void CppQmlTypesLoader::loadPluginTypes(const QString &pluginPath) -{ - if (m_qmldumpPath.isEmpty()) - return; + // we need to go from QList<T *> of newObjects.values() to QList<const T *> + // and there seems to be no better way + QMapIterator<QString, FakeMetaObject *> it(newObjects); + while (it.hasNext()) { + it.next(); + objectsFromXml.append(it.value()); + } + } - QProcess *process = new QProcess(this); - connect(process, SIGNAL(finished(int)), SLOT(processDone(int))); - process->start(m_qmldumpPath, QStringList(pluginPath)); + return errorMsgs; } -void CppQmlTypesLoader::processDone(int exitCode) +QString CppQmlTypesLoader::parseQmlTypeXml(const QByteArray &xml, QMap<QString, FakeMetaObject *> *newObjects) { - QMap<QString, FakeMetaObject *> newObjects; - - QProcess *process = qobject_cast<QProcess *>(sender()); - if (process && exitCode == 0) { - const QByteArray output = process->readAllStandardOutput(); - QmlXmlReader read(output); - if (read(&newObjects)) - addObjects(newObjects); + QmlXmlReader reader(xml); + if (!reader(newObjects)) { + if (reader.errorMessage().isEmpty()) + return QLatin1String("unknown error"); + return reader.errorMessage(); } - process->deleteLater(); + setSuperClasses(newObjects); + return QString(); } -void CppQmlTypesLoader::addObjects(QMap<QString, FakeMetaObject *> &newObjects) +void CppQmlTypesLoader::setSuperClasses(QMap<QString, FakeMetaObject *> *newObjects) { - QMapIterator<QString, FakeMetaObject *> it(newObjects); + QMapIterator<QString, FakeMetaObject *> it(*newObjects); while (it.hasNext()) { it.next(); FakeMetaObject *obj = it.value(); - //if (objects.contains(it.key())) - // qWarning() << "QmlJS::Interpreter::MetaTypeSystem: Found duplicate type" << it.key(); const QString superName = obj->superclassName(); if (! superName.isEmpty()) { - FakeMetaObject *superClass = objects.value(superName); - if (!superClass) - superClass = newObjects.value(superName); + FakeMetaObject *superClass = newObjects->value(superName); if (superClass) obj->setSuperclass(superClass); - //else - // qWarning() << "QmlJS::Interpreter::MetaTypeSystem: Can't find superclass" << superName << "for" << it.key(); + else + qWarning() << "QmlJS::Interpreter::MetaTypeSystem: Can't find superclass" << superName << "for" << it.key(); } - - objects.insert(it.key(), obj); } } -void CppQmlTypes::reload(Engine *interpreter) +void CppQmlTypes::load(Interpreter::Engine *engine, const QList<const FakeMetaObject *> &objects) { - QHash<const FakeMetaObject *, QmlObjectValue *> qmlObjects; - _importedTypes.clear(); + // load + foreach (const FakeMetaObject *metaObject, objects) { + // make sure we're not loading duplicate objects + if (_typesByFullyQualifiedName.contains(metaObject->packageClassVersionString())) + continue; - foreach (const FakeMetaObject *metaObject, CppQmlTypesLoader::instance()->objects) { - QmlObjectValue *objectValue = new QmlObjectValue(metaObject, interpreter); - qmlObjects.insert(metaObject, objectValue); - _importedTypes[metaObject->packageName()].append(objectValue); + QmlObjectValue *objectValue = new QmlObjectValue(metaObject, engine); + _typesByPackage[metaObject->packageName()].append(objectValue); + _typesByFullyQualifiedName[metaObject->packageClassVersionString()] = objectValue; } - foreach (const FakeMetaObject *metaObject, CppQmlTypesLoader::instance()->objects) { - QmlObjectValue *objectValue = qmlObjects.value(metaObject); - if (!objectValue) + // set prototype correctly + foreach (const FakeMetaObject *metaObject, objects) { + QmlObjectValue *objectValue = _typesByFullyQualifiedName.value(metaObject->packageClassVersionString()); + if (!objectValue || !metaObject->superClass()) continue; - objectValue->setPrototype(qmlObjects.value(metaObject->superClass(), 0)); + objectValue->setPrototype(_typesByFullyQualifiedName.value(metaObject->superClass()->packageClassVersionString())); } } @@ -2053,7 +2031,7 @@ QList<QmlObjectValue *> CppQmlTypes::typesForImport(const QString &packageName, { QMap<QString, QmlObjectValue *> objectValuesByName; - foreach (QmlObjectValue *qmlObjectValue, _importedTypes.value(packageName)) { + foreach (QmlObjectValue *qmlObjectValue, _typesByPackage.value(packageName)) { if (qmlObjectValue->version() <= version) { // we got a candidate. const QString typeName = qmlObjectValue->className(); @@ -2084,7 +2062,7 @@ QmlObjectValue *CppQmlTypes::typeForImport(const QString &qualifiedName) const } QmlObjectValue *previousCandidate = 0; - foreach (QmlObjectValue *qmlObjectValue, _importedTypes.value(packageName)) { + foreach (QmlObjectValue *qmlObjectValue, _typesByPackage.value(packageName)) { const QString typeName = qmlObjectValue->className(); if (typeName != name) continue; @@ -2105,7 +2083,7 @@ QmlObjectValue *CppQmlTypes::typeForImport(const QString &qualifiedName) const bool CppQmlTypes::hasPackage(const QString &package) const { - return _importedTypes.contains(package); + return _typesByPackage.contains(package); } ConvertToNumber::ConvertToNumber(Engine *engine) @@ -2381,7 +2359,7 @@ Engine::Engine() { initializePrototypes(); - _cppQmlTypes.reload(this); + _cppQmlTypes.load(this, CppQmlTypesLoader::objectsFromXml); } Engine::~Engine() diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h index ffa53b869ef615c04df96cfc478c69e144a250b0..3cc59dea2c5409c2c2b8cae25c1f23b327266cfc 100644 --- a/src/libs/qmljs/qmljsinterpreter.h +++ b/src/libs/qmljs/qmljsinterpreter.h @@ -512,40 +512,35 @@ private: // typing environment //////////////////////////////////////////////////////////////////////////////// -class QMLJS_EXPORT CppQmlTypesLoader : public QObject +class QMLJS_EXPORT CppQmlTypesLoader { - Q_OBJECT public: - CppQmlTypesLoader(); - static CppQmlTypesLoader *instance(); - - QHash<QString, FakeMetaObject *> objects; - /** \return an empty list when successful, error messages otherwise. */ - QStringList load(const QFileInfoList &xmlFiles); - - void loadPluginTypes(const QString &pluginPath); - -private slots: - void processDone(int exitCode); + static QStringList load(const QFileInfoList &xmlFiles); + static QList<const FakeMetaObject *> objectsFromXml; + // parses the xml string and fills the newObjects map + static QString parseQmlTypeXml(const QByteArray &xml, QMap<QString, FakeMetaObject *> *newObjects); private: - void addObjects(QMap<QString, FakeMetaObject *> &newObjects); - QString m_qmldumpPath; + static void setSuperClasses(QMap<QString, FakeMetaObject *> *newObjects); }; class QMLJS_EXPORT CppQmlTypes { public: - void reload(Interpreter::Engine *interpreter); + void load(Interpreter::Engine *interpreter, const QList<const FakeMetaObject *> &objects); QList<Interpreter::QmlObjectValue *> typesForImport(const QString &prefix, ComponentVersion version) const; Interpreter::QmlObjectValue *typeForImport(const QString &qualifiedName) const; bool hasPackage(const QString &package) const; + QHash<QString, QmlObjectValue *> types() const + { return _typesByFullyQualifiedName; } + private: - QHash<QString, QList<QmlObjectValue *> > _importedTypes; + QHash<QString, QList<QmlObjectValue *> > _typesByPackage; + QHash<QString, QmlObjectValue *> _typesByFullyQualifiedName; }; class ConvertToNumber: protected ValueVisitor // ECMAScript ToInt() @@ -695,6 +690,8 @@ public: QString typeId(const Value *value); // typing: + CppQmlTypes &cppQmlTypes() + { return _cppQmlTypes; } const CppQmlTypes &cppQmlTypes() const { return _cppQmlTypes; } diff --git a/src/libs/qmljs/qmljslink.cpp b/src/libs/qmljs/qmljslink.cpp index d9656d21dbbf2de9c0f37fbdedbbc6f987f5dbd8..72aea3581167fc4adb9f00288f91686b508be50f 100644 --- a/src/libs/qmljs/qmljslink.cpp +++ b/src/libs/qmljs/qmljslink.cpp @@ -269,49 +269,55 @@ void Link::importNonFile(Interpreter::ObjectValue *typeEnv, Document::Ptr doc, A return; } - // if the package is in the meta type system, use it - if (engine()->cppQmlTypes().hasPackage(packageName)) { - foreach (QmlObjectValue *object, engine()->cppQmlTypes().typesForImport(packageName, version)) { - namespaceObject->setProperty(object->className(), object); - } - return; - } else { - // check the filesystem - const QString packagePath = Bind::toString(import->importUri, QDir::separator()); - foreach (const QString &importPath, _importPaths) { - QDir dir(importPath); - if (!dir.cd(packagePath)) - continue; + bool importFound = false; - const LibraryInfo libraryInfo = _snapshot.libraryInfo(dir.path()); - if (!libraryInfo.isValid()) - continue; + // check the filesystem + const QString packagePath = Bind::toString(import->importUri, QDir::separator()); + foreach (const QString &importPath, _importPaths) { + QDir dir(importPath); + if (!dir.cd(packagePath)) + continue; - if (!libraryInfo.plugins().isEmpty()) - _context->setDocumentImportsPlugins(doc.data()); + const LibraryInfo libraryInfo = _snapshot.libraryInfo(dir.path()); + if (!libraryInfo.isValid()) + continue; - QSet<QString> importedTypes; - foreach (const QmlDirParser::Component &component, libraryInfo.components()) { - if (importedTypes.contains(component.typeName)) - continue; + importFound = true; - ComponentVersion componentVersion(component.majorVersion, component.minorVersion); - if (version < componentVersion) - continue; + if (!libraryInfo.plugins().isEmpty()) + engine()->cppQmlTypes().load(engine(), libraryInfo.metaObjects()); - importedTypes.insert(component.typeName); - if (Document::Ptr importedDoc = _snapshot.document(dir.filePath(component.fileName))) { - if (importedDoc->bind()->rootObjectValue()) - namespaceObject->setProperty(component.typeName, importedDoc->bind()->rootObjectValue()); - } + QSet<QString> 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 = _snapshot.document(dir.filePath(component.fileName))) { + if (importedDoc->bind()->rootObjectValue()) + namespaceObject->setProperty(component.typeName, importedDoc->bind()->rootObjectValue()); } + } - return; + break; + } + + // if there are cpp-based types for this package, use them too + if (engine()->cppQmlTypes().hasPackage(packageName)) { + importFound = true; + foreach (QmlObjectValue *object, engine()->cppQmlTypes().typesForImport(packageName, version)) { + namespaceObject->setProperty(object->className(), object); } } - error(doc, locationFromRange(import->firstSourceLocation(), import->lastSourceLocation()), - tr("package not found")); + if (!importFound) { + error(doc, locationFromRange(import->firstSourceLocation(), import->lastSourceLocation()), + tr("package not found")); + } } UiQualifiedId *Link::qualifiedTypeNameId(Node *node) diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.cpp b/src/plugins/qmljseditor/qmljsmodelmanager.cpp index a418d023c93c21e0397e559f57744a036e026656..a7db3264661b8c510470d85f70e39c39061429fb 100644 --- a/src/plugins/qmljseditor/qmljsmodelmanager.cpp +++ b/src/plugins/qmljseditor/qmljsmodelmanager.cpp @@ -47,6 +47,7 @@ #include <QtConcurrentRun> #include <qtconcurrent/runextensions.h> #include <QTextStream> +#include <QCoreApplication> #include <QDebug> @@ -85,7 +86,7 @@ void ModelManager::loadQmlTypeDescriptions() QDir::Files, QDir::Name); - const QStringList errors = Interpreter::CppQmlTypesLoader::instance()->load(xmlFiles); + const QStringList errors = Interpreter::CppQmlTypesLoader::load(xmlFiles); foreach (const QString &error, errors) qWarning() << qPrintable(error); } @@ -195,7 +196,7 @@ void ModelManager::onLibraryInfoUpdated(const QString &path, const LibraryInfo & QMutexLocker locker(&m_mutex); if (!_snapshot.libraryInfo(path).isValid()) - QmlJS::Interpreter::CppQmlTypesLoader::instance()->loadPluginTypes(path); + loadQmlPluginTypes(path); _snapshot.insertLibraryInfo(path, info); } @@ -435,3 +436,64 @@ static QStringList environmentImportPaths() return paths; } + +void ModelManager::loadQmlPluginTypes(const QString &pluginPath) +{ + static QString qmldumpPath; + if (qmldumpPath.isNull()) { + QDir qmldumpExecutable(QCoreApplication::applicationDirPath()); +#ifndef Q_OS_WIN + qmldumpPath = qmldumpExecutable.absoluteFilePath(QLatin1String("qmldump")); +#else + qmldumpPath = qmldumpExecutable.absoluteFilePath(QLatin1String("qmldump.exe")); +#endif + QFileInfo qmldumpFileInfo(qmldumpPath); + if (!qmldumpFileInfo.exists()) { + qWarning() << "ModelManager::loadQmlPluginTypes: qmldump executable does not exist at" << qmldumpPath; + qmldumpPath.clear(); + } else if (!qmldumpFileInfo.isFile()) { + qWarning() << "ModelManager::loadQmlPluginTypes: " << qmldumpPath << " is not a file"; + qmldumpPath.clear(); + } + + } + if (qmldumpPath.isEmpty()) + return; + + QProcess *process = new QProcess(this); + connect(process, SIGNAL(finished(int)), SLOT(qmlPluginTypeDumpDone(int))); + process->start(qmldumpPath, QStringList(pluginPath)); + m_runningQmldumps.insert(process, pluginPath); +} + +void ModelManager::qmlPluginTypeDumpDone(int exitCode) +{ + QProcess *process = qobject_cast<QProcess *>(sender()); + if (!process) + return; + process->deleteLater(); + if (exitCode != 0) + return; + + const QByteArray output = process->readAllStandardOutput(); + QMap<QString, Interpreter::FakeMetaObject *> newObjects; + const QString error = Interpreter::CppQmlTypesLoader::parseQmlTypeXml(output, &newObjects); + if (!error.isEmpty()) + return; + + // convert from QList<T *> to QList<const T *> + QList<const Interpreter::FakeMetaObject *> objectsList; + QMapIterator<QString, Interpreter::FakeMetaObject *> it(newObjects); + while (it.hasNext()) { + it.next(); + objectsList.append(it.value()); + } + + const QString libraryPath = m_runningQmldumps.take(process); + + QMutexLocker locker(&m_mutex); + + LibraryInfo libraryInfo = _snapshot.libraryInfo(libraryPath); + libraryInfo.setMetaObjects(objectsList); + _snapshot.insertLibraryInfo(libraryPath, libraryInfo); +} diff --git a/src/plugins/qmljseditor/qmljsmodelmanager.h b/src/plugins/qmljseditor/qmljsmodelmanager.h index 0585356109839f3a3e72f6466ece9520ac0307fe..b72d9d318dcbb4aef2dcdd44b0715d7bae1d0614 100644 --- a/src/plugins/qmljseditor/qmljsmodelmanager.h +++ b/src/plugins/qmljseditor/qmljsmodelmanager.h @@ -37,6 +37,7 @@ #include <QFuture> #include <QFutureSynchronizer> #include <QMutex> +#include <QProcess> namespace Core { class ICore; @@ -74,6 +75,7 @@ private Q_SLOTS: // this should be executed in the GUI thread. void onDocumentUpdated(QmlJS::Document::Ptr doc); void onLibraryInfoUpdated(const QString &path, const QmlJS::LibraryInfo &info); + void qmlPluginTypeDumpDone(int exitCode); protected: struct WorkingCopy @@ -94,6 +96,7 @@ protected: bool emitDocChangedOnDisk); void loadQmlTypeDescriptions(); + void loadQmlPluginTypes(const QString &pluginPath); private: static bool matchesMimeType(const Core::MimeType &fileMimeType, const Core::MimeType &knownMimeType); @@ -103,6 +106,7 @@ private: QmlJS::Snapshot _snapshot; QStringList m_projectImportPaths; QStringList m_defaultImportPaths; + QHash<QProcess *, QString> m_runningQmldumps; QFutureSynchronizer<void> m_synchronizer; }; diff --git a/src/tools/qml/qmldump/main.cpp b/src/tools/qml/qmldump/main.cpp index 5eec77a6ad08d08d6de50cb006fdbafdc6b88bf8..cc313e2a6843a8798c827d4515e8e08bc8f51e11 100644 --- a/src/tools/qml/qmldump/main.cpp +++ b/src/tools/qml/qmldump/main.cpp @@ -17,7 +17,6 @@ static QHash<QByteArray, const QDeclarativeType *> qmlTypeByCppName; static QHash<QByteArray, QByteArray> cppToQml; -static QByteArray pluginPackage; QByteArray convertToQmlType(const QByteArray &cppName) { @@ -187,8 +186,6 @@ public: void dump(const QMetaObject *meta, QXmlStreamWriter *xml) { QByteArray qmlTypeName = convertToQmlType(meta->className()); - if (!pluginPackage.isEmpty() && !qmlTypeName.startsWith(pluginPackage)) - return; xml->writeStartElement("type"); @@ -258,7 +255,6 @@ int main(int argc, char *argv[]) if (pluginPath.exists() && pluginPath.isDir()) { pluginImportPath = pluginPath.absolutePath(); pluginImportName = pluginPath.fileName(); - pluginPackage = (pluginImportName + ".").toLatin1(); } } @@ -280,6 +276,7 @@ int main(int argc, char *argv[]) QByteArray code = importCode; code += "Item {}"; QDeclarativeComponent c(engine); + c.setData(code, QUrl("xxx")); c.create(); if (!c.errors().isEmpty()) @@ -335,7 +332,12 @@ int main(int argc, char *argv[]) QDeclarativeComponent c(engine); c.setData(code, QUrl("xxx")); - processObject(c.create(), &metas); + + QObject *object = c.create(); + if (object) + processObject(object, &metas); + else + qDebug() << "Could not create" << tyName << ":" << c.errorString(); } QByteArray bytes;