Commit e307bc25 authored by Christian Kamm's avatar Christian Kamm
Browse files

QmlJS: Store plugin metatypes in LibraryInfo. Rework type loading.

parent 206c190e
......@@ -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;
};
......
......@@ -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; }
};
......
......@@ -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()
......
......@@ -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; }
......
......@@ -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)
......
......@@ -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);
}
......@@ -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;
};
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment