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

qmldump: Make Creator able to read qmldump's new output.

Task-number: QTCREATORBUG-3048
parent bffe90b2
......@@ -33,6 +33,8 @@
#include "componentversion.h"
#include <QtCore/QString>
using namespace LanguageUtils;
const int ComponentVersion::NoVersion = -1;
......@@ -47,6 +49,23 @@ ComponentVersion::ComponentVersion(int major, int minor)
{
}
ComponentVersion::ComponentVersion(const QString &versionString)
: _major(NoVersion), _minor(NoVersion)
{
int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1)
return;
bool ok = false;
int maybeMajor = versionString.left(dotIdx).toInt(&ok);
if (!ok)
return;
int maybeMinor = versionString.mid(dotIdx + 1).toInt(&ok);
if (!ok)
return;
_major = maybeMajor;
_minor = maybeMinor;
}
ComponentVersion::~ComponentVersion()
{
}
......@@ -56,6 +75,12 @@ bool ComponentVersion::isValid() const
return _major >= 0 && _minor >= 0;
}
QString ComponentVersion::toString() const
{
return QString("%1.%2").arg(QString::number(_major),
QString::number(_minor));
}
namespace LanguageUtils {
bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
......
......@@ -48,6 +48,7 @@ public:
ComponentVersion();
ComponentVersion(int major, int minor);
explicit ComponentVersion(const QString &versionString);
~ComponentVersion();
int majorVersion() const
......@@ -56,6 +57,7 @@ public:
{ return _minor; }
bool isValid() const;
QString toString() const;
};
bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
......
......@@ -35,6 +35,9 @@
using namespace LanguageUtils;
FakeMetaEnum::FakeMetaEnum()
{}
FakeMetaEnum::FakeMetaEnum(const QString &name)
: m_name(name)
{}
......@@ -42,6 +45,9 @@ FakeMetaEnum::FakeMetaEnum(const QString &name)
QString FakeMetaEnum::name() const
{ return m_name; }
void FakeMetaEnum::setName(const QString &name)
{ m_name = name; }
void FakeMetaEnum::addKey(const QString &key, int value)
{ m_keys.append(key); m_values.append(value); }
......@@ -61,9 +67,20 @@ FakeMetaMethod::FakeMetaMethod(const QString &name, const QString &returnType)
, m_methodAccess(FakeMetaMethod::Public)
{}
FakeMetaMethod::FakeMetaMethod()
: m_methodTy(FakeMetaMethod::Method)
, m_methodAccess(FakeMetaMethod::Public)
{}
QString FakeMetaMethod::methodName() const
{ return m_name; }
void FakeMetaMethod::setMethodName(const QString &name)
{ m_name = name; }
void FakeMetaMethod::setReturnType(const QString &type)
{ m_returnType = type; }
QStringList FakeMetaMethod::parameterNames() const
{ return m_paramNames; }
......@@ -108,16 +125,20 @@ FakeMetaObject::FakeMetaObject()
{
}
QString FakeMetaObject::className() const
{ return m_className; }
void FakeMetaObject::setClassName(const QString &name)
{ m_className = name; }
void FakeMetaObject::addExport(const QString &name, const QString &package, ComponentVersion version)
{
Export exp;
exp.type = name;
exp.package = package;
exp.version = version;
exp.packageNameVersion = QString::fromLatin1("%1.%2 %3.%4").arg(
exp.packageNameVersion = QString::fromLatin1("%1.%2 %3").arg(
package, name,
QString::number(version.majorVersion()),
QString::number(version.minorVersion()));
version.toString());
m_exports.append(exp);
}
QList<FakeMetaObject::Export> FakeMetaObject::exports() const
......
......@@ -51,9 +51,11 @@ class LANGUAGEUTILS_EXPORT FakeMetaEnum {
QList<int> m_values;
public:
FakeMetaEnum(const QString &name);
FakeMetaEnum();
explicit FakeMetaEnum(const QString &name);
QString name() const;
void setName(const QString &name);
void addKey(const QString &key, int value);
QString key(int index) const;
......@@ -76,9 +78,14 @@ public:
};
public:
FakeMetaMethod(const QString &name, const QString &returnType = QString());
FakeMetaMethod();
explicit FakeMetaMethod(const QString &name, const QString &returnType = QString());
QString methodName() const;
void setMethodName(const QString &name);
void setReturnType(const QString &type);
QStringList parameterNames() const;
QStringList parameterTypes() const;
void addParameter(const QString &name, const QString &type);
......@@ -131,6 +138,7 @@ public:
};
private:
QString m_className;
QList<Export> m_exports;
ConstPtr m_super;
QString m_superName;
......@@ -144,6 +152,9 @@ private:
public:
FakeMetaObject();
QString className() const;
void setClassName(const QString &name);
void addExport(const QString &name, const QString &package, ComponentVersion version);
QList<Export> exports() const;
......
......@@ -27,7 +27,8 @@ HEADERS += \
$$PWD/qmljspropertyreader.h \
$$PWD/qmljsrewriter.h \
$$PWD/qmljsicons.h \
$$PWD/qmljsdelta.h
$$PWD/qmljsdelta.h \
$$PWD/qmljstypedescriptionreader.h
SOURCES += \
$$PWD/qmljsbind.cpp \
......@@ -45,7 +46,8 @@ SOURCES += \
$$PWD/qmljspropertyreader.cpp \
$$PWD/qmljsrewriter.cpp \
$$PWD/qmljsicons.cpp \
$$PWD/qmljsdelta.cpp
$$PWD/qmljsdelta.cpp \
$$PWD/qmljstypedescriptionreader.cpp
RESOURCES += \
$$PWD/qmljs.qrc
......
......@@ -254,11 +254,8 @@ bool Bind::visit(UiImport *ast)
if (ast->versionToken.isValid()) {
const QString versionString = _doc->source().mid(ast->versionToken.offset, ast->versionToken.length);
const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx != -1) {
version = ComponentVersion(versionString.left(dotIdx).toInt(),
versionString.mid(dotIdx + 1).toInt());
} else {
version = ComponentVersion(versionString);
if (!version.isValid()) {
_diagnosticMessages->append(
errorMessage(ast->versionToken, tr("expected two numbers separated by a dot")));
}
......
......@@ -1945,7 +1945,7 @@ const Value *Function::invoke(const Activation *activation) const
QList<FakeMetaObject::ConstPtr> CppQmlTypesLoader::builtinObjects;
QStringList CppQmlTypesLoader::load(const QFileInfoList &xmlFiles)
QStringList CppQmlTypesLoader::loadXml(const QFileInfoList &xmlFiles)
{
QMap<QString, FakeMetaObject::Ptr> newObjects;
QStringList errorMsgs;
......@@ -2117,10 +2117,10 @@ bool CppQmlTypes::hasPackage(const QString &package) const
QString CppQmlTypes::qualifiedName(const QString &package, const QString &type, ComponentVersion version)
{
return QString("%1.%2 %3.%4").arg(
return QString("%1.%2 %3").arg(
package, type,
QString::number(version.majorVersion()),
QString::number(version.minorVersion()));
version.toString());
}
QmlObjectValue *CppQmlTypes::typeByQualifiedName(const QString &name) const
......
......@@ -585,7 +585,7 @@ class QMLJS_EXPORT CppQmlTypesLoader
{
public:
/** \return an empty list when successful, error messages otherwise. */
static QStringList load(const QFileInfoList &xmlFiles);
static QStringList loadXml(const QFileInfoList &xmlFiles);
static QList<LanguageUtils::FakeMetaObject::ConstPtr> builtinObjects;
// parses the xml string and fills the newObjects map
......
#include "qmljstypedescriptionreader.h"
#include "parser/qmljsparser_p.h"
#include "parser/qmljslexer_p.h"
#include "parser/qmljsengine_p.h"
#include "parser/qmljsnodepool_p.h"
#include "parser/qmljsast_p.h"
#include "parser/qmljsastvisitor_p.h"
#include "qmljsbind.h"
#include <QtCore/QIODevice>
#include <QtCore/QBuffer>
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace LanguageUtils;
TypeDescriptionReader::TypeDescriptionReader(const QString &data)
: _source(data)
, _objects(0)
{
}
TypeDescriptionReader::~TypeDescriptionReader()
{
}
bool TypeDescriptionReader::operator()(QMap<QString, FakeMetaObject::Ptr> *objects)
{
QString fileName("typeDescription");
Engine engine;
NodePool pool(fileName, &engine);
Lexer lexer(&engine);
Parser parser(&engine);
lexer.setCode(_source, /*line = */ 1);
if (!parser.parse()) {
_errorMessage = QString("%1:%2: %3").arg(
QString::number(parser.errorLineNumber()),
QString::number(parser.errorColumnNumber()),
parser.errorMessage());
return false;
}
_objects = objects;
readDocument(parser.ast());
return _errorMessage.isEmpty();
}
QString TypeDescriptionReader::errorMessage() const
{
return _errorMessage;
}
void TypeDescriptionReader::readDocument(UiProgram *ast)
{
if (!ast) {
addError(SourceLocation(), "Could not parse document");
return;
}
if (!ast->imports || ast->imports->next) {
addError(SourceLocation(), "Expected a single import");
return;
}
UiImport *import = ast->imports->import;
if (Bind::toString(import->importUri) != QLatin1String("QtQuick.tooling")) {
addError(import->importToken, "Expected import of QtQuick.tooling");
return;
}
ComponentVersion version;
const QString versionString = _source.mid(import->versionToken.offset, import->versionToken.length);
const int dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx != -1) {
version = ComponentVersion(versionString.left(dotIdx).toInt(),
versionString.mid(dotIdx + 1).toInt());
}
if (version != ComponentVersion(1, 0)) {
addError(import->versionToken, "Expected version 1.0");
return;
}
if (!ast->members || !ast->members->member || ast->members->next) {
addError(SourceLocation(), "Expected document to contain a single object definition");
return;
}
UiObjectDefinition *module = dynamic_cast<UiObjectDefinition *>(ast->members->member);
if (!module) {
addError(SourceLocation(), "Expected document to contain a single object definition");
return;
}
if (Bind::toString(module->qualifiedTypeNameId) != "Module") {
addError(SourceLocation(), "Expected document to contain a Module {} member");
return;
}
readModule(module);
}
void TypeDescriptionReader::readModule(UiObjectDefinition *ast)
{
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
if (!component || Bind::toString(component->qualifiedTypeNameId) != "Component") {
addError(member->firstSourceLocation(), "Expected only 'Component' object definitions");
return;
}
readComponent(component);
}
}
void TypeDescriptionReader::addError(const SourceLocation &loc, const QString &message)
{
_errorMessage += QString("%1:%2: %3\n").arg(
QString::number(loc.startLine),
QString::number(loc.startColumn),
message);
}
void TypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
FakeMetaObject::Ptr fmo(new FakeMetaObject);
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
if (component) {
QString name = Bind::toString(component->qualifiedTypeNameId);
if (name == "Property") {
readProperty(component, fmo);
} else if (name == "Method" || name == "Signal") {
readSignalOrMethod(component, name == "Method", fmo);
} else if (name == "Enum") {
readEnum(component, fmo);
} else {
addError(component->firstSourceLocation(), "Expected only Property, Method, Signal and Enum object definitions");
return;
}
} else if (script) {
QString name = Bind::toString(script->qualifiedId);
if (name == "name") {
fmo->setClassName(readStringBinding(script));
} else if (name == "prototype") {
fmo->setSuperclassName(readStringBinding(script));
} else if (name == "defaultProperty") {
fmo->setDefaultPropertyName(readStringBinding(script));
} else if (name == "exports") {
readExports(script, fmo);
} else {
addError(script->firstSourceLocation(), "Expected only name, prototype, defaultProperty and exports script bindings");
return;
}
} else {
addError(member->firstSourceLocation(), "Expected only script bindings and object definitions");
return;
}
}
if (fmo->className().isEmpty()) {
addError(ast->firstSourceLocation(), "Component definition is missing a name binding");
return;
}
_objects->insert(fmo->className(), fmo);
}
void TypeDescriptionReader::readSignalOrMethod(UiObjectDefinition *ast, bool isMethod, FakeMetaObject::Ptr fmo)
{
FakeMetaMethod fmm;
// ### confusion between Method and Slot. Method should be removed.
if (isMethod)
fmm.setMethodType(FakeMetaMethod::Slot);
else
fmm.setMethodType(FakeMetaMethod::Signal);
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiObjectDefinition *component = dynamic_cast<UiObjectDefinition *>(member);
UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
if (component) {
QString name = Bind::toString(component->qualifiedTypeNameId);
if (name == "Parameter") {
readParameter(component, &fmm);
} else {
addError(component->firstSourceLocation(), "Expected only Parameter object definitions");
return;
}
} else if (script) {
QString name = Bind::toString(script->qualifiedId);
if (name == "name") {
fmm.setMethodName(readStringBinding(script));
} else if (name == "type") {
fmm.setReturnType(readStringBinding(script));
} else {
addError(script->firstSourceLocation(), "Expected only name and type script bindings");
return;
}
} else {
addError(member->firstSourceLocation(), "Expected only script bindings and object definitions");
return;
}
}
if (fmm.methodName().isEmpty()) {
addError(ast->firstSourceLocation(), "Method or Signal is missing a name script binding");
return;
}
fmo->addMethod(fmm);
}
void TypeDescriptionReader::readProperty(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
{
QString name;
QString type;
bool isPointer = false;
bool isReadonly = false;
bool isList = false;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
if (!script) {
addError(member->firstSourceLocation(), "Expected script binding");
return;
}
QString id = Bind::toString(script->qualifiedId);
if (id == "name") {
name = readStringBinding(script);
} else if (id == "type") {
type = readStringBinding(script);
} else if (id == "isPointer") {
isPointer = readBoolBinding(script);
} else if (id == "isReadonly") {
isReadonly = readBoolBinding(script);
} else if (id == "isList") {
isList = readBoolBinding(script);
} else {
addError(script->firstSourceLocation(), "Expected only type, name, isPointer, isReadonly and isList script bindings");
return;
}
}
if (name.isEmpty() || type.isEmpty()) {
addError(ast->firstSourceLocation(), "Property object is missing a name or type script binding");
return;
}
fmo->addProperty(FakeMetaProperty(name, type, isList, !isReadonly, isPointer));
}
void TypeDescriptionReader::readEnum(UiObjectDefinition *ast, FakeMetaObject::Ptr fmo)
{
FakeMetaEnum fme;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
if (!script) {
addError(member->firstSourceLocation(), "Expected script binding");
return;
}
QString name = Bind::toString(script->qualifiedId);
if (name == "name") {
fme.setName(readStringBinding(script));
} else if (name == "values") {
readEnumValues(script, &fme);
} else {
addError(script->firstSourceLocation(), "Expected only name and values script bindings");
return;
}
}
fmo->addEnum(fme);
}
void TypeDescriptionReader::readParameter(UiObjectDefinition *ast, FakeMetaMethod *fmm)
{
QString name;
QString type;
for (UiObjectMemberList *it = ast->initializer->members; it; it = it->next) {
UiObjectMember *member = it->member;
UiScriptBinding *script = dynamic_cast<UiScriptBinding *>(member);
if (!script) {
addError(member->firstSourceLocation(), "Expected script binding");
return;
}
QString id = Bind::toString(script->qualifiedId);
if (id == "name") {
id = readStringBinding(script);
} else if (id == "type") {
type = readStringBinding(script);
} else if (id == "isPointer") {
// ### unhandled
} else if (id == "isReadonly") {
// ### unhandled
} else if (id == "isList") {
// ### unhandled
} else {
addError(script->firstSourceLocation(), "Expected only name and type script bindings");
return;
}
}
fmm->addParameter(name, type);
}
QString TypeDescriptionReader::readStringBinding(UiScriptBinding *ast)
{
if (!ast || !ast->statement) {
addError(ast->colonToken, "Expected string after colon");
return QString();
}
ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
addError(ast->statement->firstSourceLocation(), "Expected string after colon");
return QString();
}
StringLiteral *stringLit = dynamic_cast<StringLiteral *>(expStmt->expression);
if (!stringLit) {
addError(expStmt->firstSourceLocation(), "Expected string after colon");
return QString();
}
return stringLit->value->asString();
}
bool TypeDescriptionReader::readBoolBinding(AST::UiScriptBinding *ast)
{
if (!ast || !ast->statement) {
addError(ast->colonToken, "Expected boolean after colon");
return false;
}
ExpressionStatement *expStmt = dynamic_cast<ExpressionStatement *>(ast->statement);
if (!expStmt) {
addError(ast->statement->firstSourceLocation(), "Expected boolean after colon");
return false;
}