qmltc: learn to generate minimum amount of code

- Finish output ir and the writer for it
- Add simple compiler logic to generate dull class declarations

Task-number: QTBUG-84368
Change-Id: I75be0f44b84ad3cfb1d862072d58b3bf87063d31
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Andrei Golubev 2021-09-21 10:52:24 +02:00
parent 8dab4a81a0
commit ee469d528a
13 changed files with 584 additions and 16 deletions

View File

@ -1009,6 +1009,8 @@ bool QQmlJSImportVisitor::visit(UiObjectDefinition *definition)
QQmlJSScope::resolveTypes(m_currentScope, m_rootScopeImports, &m_usedTypes);
addDefaultProperties();
if (m_currentScope->scopeType() == QQmlJSScope::QMLScope)
m_qmlTypes.append(m_currentScope);
return true;
}

View File

@ -72,6 +72,7 @@ public:
return m_signalHandlers;
}
QSet<QQmlJSScope::ConstPtr> literalScopesToCheck() const { return m_literalScopesToCheck; }
QList<QQmlJSScope::ConstPtr> qmlScopes() const { return m_qmlTypes; }
static QString implicitImportDirectory(
const QString &localFile, QQmlJSResourceFileMapper *mapper);
@ -152,6 +153,7 @@ protected:
QQmlJSScope::ConstPtr m_globalScope;
QHash<QString, QQmlJSScope::ConstPtr> m_scopesById;
QHash<QString, QQmlJSScope::ConstPtr> m_rootScopeImports;
QList<QQmlJSScope::ConstPtr> m_qmlTypes;
// Maps all qmlNames to the source location of their import
QMultiHash<QString, QQmlJS::SourceLocation> m_importTypeLocationMap;

View File

@ -70,10 +70,16 @@ void tst_qmltc::initTestCase()
void tst_qmltc::qmlNameConflictResolution()
{
// we can include user-renamed files
QQmlEngine e;
// Note: the C++ class name is derived from the source qml file path, not
// the output .h/.cpp, so: NameConflict class name for NameConflict.qml
q_qmltc::NameConflict created(&e); // note: declared in ResolvedNameConflict.h
}
void tst_qmltc::helloWorld()
{
QQmlEngine e;
q_qmltc::HelloWorld created(&e);
QSKIP("Nothing is supported yet.");
}

View File

@ -9,7 +9,7 @@ qt_internal_add_tool(${target_name}
qmltccodewriter.h qmltccodewriter.cpp
qmltcoutputir.h
qmltctyperesolver.h
qmltcvisitor.h
qmltcvisitor.h qmltcvisitor.cpp
qmltccompiler.h qmltccompiler.cpp
DEFINES
QT_NO_CAST_FROM_ASCII

View File

@ -28,6 +28,7 @@
#include "qmltccommandlineutils.h"
#include "qmltccompiler.h"
#include "qmltcvisitor.h"
#include <QtQml/private/qqmlirbuilder_p.h>
#include <private/qqmljscompiler_p.h>
@ -43,6 +44,11 @@
#include <cstdio>
void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
{
logger.setCategoryError(Log_Compiler, true);
}
int main(int argc, char **argv)
{
// Produce reliably the same output for the same input by disabling QHash's
@ -158,6 +164,7 @@ int main(int argc, char **argv)
QQmlJSImporter importer { importPaths, /* resource file mapper */ nullptr };
QQmlJSLogger logger(url, sourceCode, /* silent */ false);
setupLogger(logger);
QmltcVisitor visitor(&importer, &logger, implicitImportDirectory, qmltypesFiles);
// Type resolving is only static here due the inability to resolve parent
// properties dynamically (QTBUG-95530). Indirect type storage is used as
@ -167,7 +174,7 @@ int main(int argc, char **argv)
QQmlJSTypeResolver::Static, &logger };
typeResolver.init(visitor);
QmltcCompiler compiler(url, &typeResolver, &logger);
QmltcCompiler compiler(url, &typeResolver, &visitor, &logger);
compiler.compile(info);
if (logger.hasWarnings() || logger.hasErrors()) {

View File

@ -31,6 +31,8 @@
#include <QtCore/qfileinfo.h>
#include <QtCore/qstringbuilder.h>
#include <utility>
QT_BEGIN_NAMESPACE
static QString urlToMacro(const QString &url)
@ -39,6 +41,59 @@ static QString urlToMacro(const QString &url)
return u"Q_QMLTC_" + fi.baseName().toUpper();
}
static QString getFunctionCategory(const QmltcMethodBase &method)
{
QString category;
switch (method.access) {
case QQmlJSMetaMethod::Private:
category = u"private"_qs;
break;
case QQmlJSMetaMethod::Protected:
category = u"protected"_qs;
break;
case QQmlJSMetaMethod::Public:
category = u"public"_qs;
break;
}
return category;
}
static QString getFunctionCategory(const QmltcMethod &method)
{
QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method));
switch (method.type) {
case QQmlJSMetaMethod::Signal:
category = u"Q_SIGNALS"_qs;
break;
case QQmlJSMetaMethod::Slot:
category += u" Q_SLOTS"_qs;
break;
case QQmlJSMetaMethod::Method:
break;
}
return category;
}
static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &method)
{
const QString name = method.name;
const QList<QmltcVariable> &parameterList = method.parameterList;
QStringList headerParamList;
QStringList cppParamList;
for (const QmltcVariable &variable : parameterList) {
const QString commonPart = variable.cppType + u" " + variable.name;
cppParamList << commonPart;
headerParamList << commonPart;
if (!variable.defaultValue.isEmpty())
headerParamList.back() += u" = " + variable.defaultValue;
}
const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")";
const QString cppSignature = name + u"(" + cppParamList.join(u", "_qs) + u")";
return { headerSignature, cppSignature };
}
void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &hPath, const QString &cppPath,
const QSet<QString> &requiredCppIncludes)
@ -120,12 +175,177 @@ static void writeToFile(const QString &path, const QByteArray &data)
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &program)
{
writeGlobalHeader(code, program.url, program.hPath, program.cppPath, program.includes);
// TODO: keep the "NOT IMPLEMENTED" as long as we don't actually compile
// useful code
code.rawAppendToHeader(u"/* QMLTC: NOT IMPLEMENTED */");
code.rawAppendToCpp(u"/* QMLTC: NOT IMPLEMENTED */");
// forward declare all the types first
for (const QmltcType &type : qAsConst(program.compiledTypes))
code.rawAppendToHeader(u"class " + type.cppType + u";");
// write all the types and their content
for (const QmltcType &type : qAsConst(program.compiledTypes))
write(code, type);
writeGlobalFooter(code, program.url);
writeToFile(program.hPath, code.code().header.toUtf8());
writeToFile(program.cppPath, code.code().cpp.toUtf8());
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
{
const auto constructClassString = [&]() {
QString str = u"class " + type.cppType;
if (!type.baseClasses.isEmpty())
str += u" : public " + type.baseClasses.join(u", public "_qs);
return str;
};
code.rawAppendToHeader(u""); // blank line
code.rawAppendToCpp(u""); // blank line
code.rawAppendToHeader(constructClassString());
code.rawAppendToHeader(u"{");
for (const QString &mocLine : qAsConst(type.mocCode))
code.rawAppendToHeader(mocLine, 1);
QmltcOutputWrapper::MemberNameScope typeScope(&code, type.cppType);
Q_UNUSED(typeScope);
{
QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
Q_UNUSED(headerIndent);
// special member functions
code.rawAppendToHeader(u"protected:", -1);
write(code, type.basicCtor);
write(code, type.init);
write(code, type.finalize);
// NB: externalCtor might not be public when the type is QML singleton
code.rawAppendToHeader(getFunctionCategory(type.fullCtor) + u":", -1);
write(code, type.fullCtor);
// enums
if (!type.enums.isEmpty()) {
code.rawAppendToHeader(u""); // blank line
code.rawAppendToHeader(u"public:", -1);
}
for (const auto &enumeration : qAsConst(type.enums))
write(code, enumeration);
// child types
if (!type.children.isEmpty())
code.rawAppendToHeader(u""); // blank line
for (const auto &child : qAsConst(type.children))
write(code, child);
// variables (mostly properties)
if (!type.variables.isEmpty()) {
code.rawAppendToHeader(u""); // blank line
code.rawAppendToHeader(u"protected:", -1); // TODO: or private?
}
for (const auto &variable : qAsConst(type.variables))
write(code, variable);
// functions (special case due to functions/signals/slots, etc.)
QHash<QString, QList<const QmltcMethod *>> functionsByCategory;
for (const auto &function : qAsConst(type.functions))
functionsByCategory[getFunctionCategory(function)].append(&function);
if (!functionsByCategory.isEmpty())
code.rawAppendToHeader(u""); // blank line
for (auto it = functionsByCategory.cbegin(); it != functionsByCategory.cend(); ++it) {
code.rawAppendToHeader(it.key() + u":", -1);
for (const QmltcMethod *function : qAsConst(it.value()))
write(code, *function);
}
}
if (type.documentRootType) {
code.rawAppendToHeader(u""); // blank line
code.rawAppendToHeader(u"private:");
code.rawAppendToHeader(u"friend class " + *type.documentRootType + u";", 1);
}
if (type.typeCount) {
code.rawAppendToHeader(u""); // blank line
code.rawAppendToHeader(u"public:");
code.rawAppendToHeader(u"constexpr static uint qmltc_typeCount = "
+ QString::number(*type.typeCount) + u";",
1);
}
code.rawAppendToHeader(u"};");
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcEnum &enumeration)
{
code.rawAppendToHeader(u"enum " + enumeration.cppType + u" {");
for (qsizetype i = 0; i < enumeration.keys.size(); ++i) {
QString str;
if (enumeration.values.isEmpty()) {
str += enumeration.keys.at(i) + u",";
} else {
str += enumeration.keys.at(i) + u" = " + enumeration.values.at(i) + u",";
}
code.rawAppendToHeader(str, 1);
}
code.rawAppendToHeader(u"};");
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
const auto [hSignature, cppSignature] = functionSignatures(method);
// Note: augment return type with preambles in declaration
code.rawAppendToHeader(method.returnType + u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = method.type;
if (methodType != QQmlJSMetaMethod::Signal) {
code.rawAppendToCpp(u""); // blank line
code.rawAppendToCpp(method.returnType);
code.rawAppendSignatureToCpp(cppSignature);
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
for (const QString &line : qAsConst(method.body))
code.rawAppendToCpp(line);
}
code.rawAppendToCpp(u"}");
}
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
{
const auto [hSignature, cppSignature] = functionSignatures(ctor);
const QString returnTypeWithSpace = ctor.returnType.isEmpty() ? u""_qs : ctor.returnType + u" ";
code.rawAppendToHeader(returnTypeWithSpace + hSignature + u";");
code.rawAppendToCpp(u""); // blank line
if (!returnTypeWithSpace.isEmpty())
code.rawAppendToCpp(returnTypeWithSpace);
code.rawAppendSignatureToCpp(cppSignature);
if (!ctor.initializerList.isEmpty()) {
code.rawAppendToCpp(u":", 1);
// double \n to make separate initializer list lines stand out more
code.rawAppendToCpp(
ctor.initializerList.join(u",\n\n" + u" "_qs.repeated(code.cppIndent + 1)), 1);
}
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
for (const QString &line : qAsConst(ctor.body))
code.rawAppendToCpp(line);
}
code.rawAppendToCpp(u"}");
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
{
const QString optionalPart = var.defaultValue.isEmpty() ? u""_qs : u" = " + var.defaultValue;
code.rawAppendToHeader(var.cppType + u" " + var.name + optionalPart + u";");
}
QT_END_NAMESPACE

View File

@ -43,6 +43,11 @@ struct QmltcCodeWriter
const QSet<QString> &requiredCppIncludes);
static void writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath);
static void write(QmltcOutputWrapper &code, const QmltcProgram &program);
static void write(QmltcOutputWrapper &code, const QmltcType &type);
static void write(QmltcOutputWrapper &code, const QmltcEnum &enumeration);
static void write(QmltcOutputWrapper &code, const QmltcMethod &method);
static void write(QmltcOutputWrapper &code, const QmltcCtor &ctor);
static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
};
QT_END_NAMESPACE

View File

@ -32,26 +32,113 @@
QT_BEGIN_NAMESPACE
QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QQmlJSLogger *logger)
: m_url(url), m_typeResolver(resolver), m_logger(logger)
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
QQmlJSLogger *logger)
: m_url(url), m_typeResolver(resolver), m_visitor(visitor), m_logger(logger)
{
Q_UNUSED(m_url);
Q_UNUSED(m_typeResolver);
Q_UNUSED(m_logger);
Q_ASSERT(!hasErrors());
}
void QmltcCompiler::compile(const QmltcCompilerInfo &info)
{
m_info = info;
Q_ASSERT(!m_info.outputCppFile.isEmpty());
Q_ASSERT(!m_info.outputHFile.isEmpty());
Q_ASSERT(!m_info.resourcePath.isEmpty());
const QList<QQmlJSScope::ConstPtr> types = m_visitor->qmlScopes();
QList<QmltcType> compiledTypes;
compiledTypes.reserve(types.size());
for (const QQmlJSScope::ConstPtr &type : types) {
compiledTypes.emplaceBack(); // creates empty type
compileType(compiledTypes.back(), type);
if (hasErrors())
return;
}
QmltcProgram program;
program.url = m_url;
program.cppPath = m_info.outputCppFile;
program.hPath = m_info.outputHFile;
program.compiledTypes = compiledTypes;
QmltcOutput out;
QmltcOutputWrapper code(out);
QmltcCodeWriter::write(code, program);
}
void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type)
{
if (type->isSingleton()) {
recordError(type->sourceLocation(), u"Singleton types are not supported"_qs);
return;
}
current.cppType = type->internalName();
const QString baseClass = type->baseType()->internalName();
const bool documentRoot = (type == m_visitor->result());
const bool baseTypeIsCompiledQml = false; // TODO: support this in QmltcTypeResolver
current.baseClasses = { baseClass };
if (!documentRoot) {
current.documentRootType = m_visitor->result()->internalName();
} else {
current.typeCount = int(m_visitor->qmlScopes().size());
}
// add special member functions
current.basicCtor.access = QQmlJSMetaMethod::Protected;
current.init.access = QQmlJSMetaMethod::Protected;
current.finalize.access = QQmlJSMetaMethod::Protected;
current.fullCtor.access = QQmlJSMetaMethod::Public;
current.basicCtor.name = current.cppType;
current.fullCtor.name = current.cppType;
current.init.name = u"qmltc_init"_qs;
current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_qs;
current.finalize.name = u"qmltc_finalize"_qs;
current.finalize.returnType = u"void"_qs;
QmltcVariable engine(u"QQmlEngine*"_qs, u"engine"_qs);
QmltcVariable parent(u"QObject*"_qs, u"parent"_qs, u"nullptr"_qs);
current.basicCtor.parameterList = { parent };
current.fullCtor.parameterList = { engine, parent };
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_qs, u"parentContext"_qs);
QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs);
if (documentRoot) {
current.init.parameterList = { engine, ctxtdata, finalizeFlag };
current.finalize.parameterList = { engine, finalizeFlag };
} else {
current.init.parameterList = { engine, ctxtdata };
current.finalize.parameterList = { engine };
}
current.fullCtor.initializerList = { current.basicCtor.name + u"(" + parent.name + u")" };
if (baseTypeIsCompiledQml) {
// call parent's (QML type's) basic ctor from this. that one will take
// care about QObject::setParent()
current.basicCtor.initializerList = { baseClass + u"(" + parent.name + u")" };
} else {
// default call to ctor is enough, but QQml_setParent_noEvent() is
// needed (note: faster? version of QObject::setParent())
current.basicCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
}
// compilation stub:
current.fullCtor.body << u"Q_UNUSED(engine);"_qs;
current.init.body << u"Q_UNUSED(engine);"_qs;
current.init.body << u"Q_UNUSED(parentContext);"_qs;
current.finalize.body << u"Q_UNUSED(engine);"_qs;
if (documentRoot) {
current.init.body << u"Q_UNUSED(canFinalize);"_qs;
current.finalize.body << u"Q_UNUSED(canFinalize);"_qs;
}
current.init.body << u"return nullptr;"_qs;
}
QT_END_NAMESPACE

View File

@ -31,6 +31,7 @@
#include "qmltctyperesolver.h"
#include "qmltcvisitor.h"
#include "qmltcoutputir.h"
#include <QtCore/qcommandlineparser.h>
#include <QtCore/qcoreapplication.h>
@ -50,14 +51,33 @@ struct QmltcCompilerInfo
class QmltcCompiler
{
public:
QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QQmlJSLogger *logger);
QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor,
QQmlJSLogger *logger);
void compile(const QmltcCompilerInfo &info);
private:
QString m_url; // QML input file url
QmltcTypeResolver *m_typeResolver = nullptr;
QmltcVisitor *m_visitor = nullptr;
QQmlJSLogger *m_logger = nullptr;
QmltcCompilerInfo m_info {}; // miscellaneous input/output information
void compileType(QmltcType &current, const QQmlJSScope::ConstPtr &type);
bool hasErrors() const { return m_logger->hasErrors(); } // TODO: count warnings as errors?
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
QQmlJSLoggerCategory category = Log_Compiler)
{
// pretty much any compiler error is a critical error (we cannot
// generate code - compilation fails)
m_logger->logCritical(message, category, location);
}
void recordError(const QV4::CompiledData::Location &location, const QString &message,
QQmlJSLoggerCategory category = Log_Compiler)
{
recordError(QQmlJS::SourceLocation { 0, 0, location.line, location.column }, message,
category);
}
};
QT_END_NAMESPACE

View File

@ -34,14 +34,104 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qset.h>
#include <private/qqmljsmetatypes_p.h>
#include <optional>
QT_BEGIN_NAMESPACE
// Below are the classes that represent compiled QML types in a string data
// form. These classes are used to generate C++ code.
// Represents C++ variable
struct QmltcVariable
{
QString cppType; // C++ type of a variable
QString name; // variable name
QString defaultValue; // optional initialization value
QmltcVariable() = default;
// special ctor for QList's emplace back
QmltcVariable(const QString &t, const QString &n, const QString &v = QString())
: cppType(t), name(n), defaultValue(v)
{
}
};
// Represents QML -> C++ compiled enumeration type
struct QmltcEnum
{
QString cppType; // C++ type of an enum
QStringList keys; // enumerator keys
QStringList values; // enumerator values
QmltcEnum() = default;
QmltcEnum(const QString &t, const QStringList &ks, const QStringList &vs)
: cppType(t), keys(ks), values(vs)
{
}
};
struct QmltcMethodBase
{
QString returnType; // C++ return type
QString name; // C++ function name
QList<QmltcVariable> parameterList; // C++ function parameter list
QStringList body; // C++ function code
QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
};
// Represents QML -> C++ compiled function
struct QmltcMethod : QmltcMethodBase
{
QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
};
// Represents C++ ctor of a type
struct QmltcCtor : QmltcMethodBase
{
QStringList initializerList; // C++ ctor's initializer list
};
// Represents QML -> C++ compiled type
struct QmltcType
{
QString cppType; // C++ type of the QML type
QStringList baseClasses; // C++ type names of base classes
QStringList mocCode; // Qt MOC code
// member types: enumerations and child types
QList<QmltcEnum> enums;
QList<QmltcType> children; // these are pretty much always empty
// special member functions:
QmltcCtor basicCtor = {}; // does basic contruction
QmltcCtor fullCtor = {}; // calls basicCtor, calls init
QmltcMethod init = {}; // starts object initialization (context setup), calls finalize
QmltcMethod finalize = {}; // finalizes object (bindings, special interface calls, etc.)
// member functions: methods, signals and slots
QList<QmltcMethod> functions;
// member variables: properties and just variables
QList<QmltcVariable> variables;
// we want to use certain private/protected functions by document root, so
// record it to make it a friend
std::optional<QString> documentRootType;
// QML document root specific:
std::optional<int> typeCount = 0; // the number of QML types defined in a document
};
// Represents whole QML program, compiled to C++
struct QmltcProgram
{
QString url;
QString cppPath;
QString hPath;
QString url; // QML file url
QString cppPath; // C++ output .cpp path
QString hPath; // C++ output .h path
QSet<QString> includes; // non-default C++ include files
QList<QmltcType> compiledTypes; // all QML types that are compiled to C++
};
QT_END_NAMESPACE

View File

@ -29,6 +29,7 @@
#ifndef QMLTCOUTPUTPRIMITIVES_H
#define QMLTCOUTPUTPRIMITIVES_H
#include <QtCore/qstack.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringbuilder.h>
@ -56,12 +57,45 @@ public:
QmltcOutputWrapper(QmltcOutput &code) : m_code(code) { }
const QmltcOutput &code() const { return m_code; }
QStack<QString> memberScopes; // member name scopes e.g. MyClass::MySubclass::
int headerIndent = 0; // header indentation level
int cppIndent = 0; // cpp indentation level
// manages current scope of the generated code, which is necessary for
// cpp file generation. Example:
// class MyClass { MyClass(); }; - in header
// MyClass::MyClass() {} - in cpp
// MemberNameScope makes sure "MyClass::" is recorded
struct MemberNameScope
{
QmltcOutputWrapper *m_code;
MemberNameScope(QmltcOutputWrapper *code, const QString &str) : m_code(code)
{
m_code->memberScopes.push(str);
}
~MemberNameScope() { m_code->memberScopes.pop(); }
};
struct HeaderIndentationScope
{
QmltcOutputWrapper *m_code;
HeaderIndentationScope(QmltcOutputWrapper *code) : m_code(code) { ++m_code->headerIndent; }
~HeaderIndentationScope() { --m_code->headerIndent; }
};
struct CppIndentationScope
{
QmltcOutputWrapper *m_code;
CppIndentationScope(QmltcOutputWrapper *code) : m_code(code) { ++m_code->cppIndent; }
~CppIndentationScope() { --m_code->cppIndent; }
};
// appends string \a what with extra indentation \a extraIndent to current
// header string
template<typename String>
void rawAppendToHeader(const String &what, int extraIndent = 0)
{
rawAppend(m_code.header, what, extraIndent);
rawAppend(m_code.header, what, headerIndent + extraIndent);
}
// appends string \a what with extra indentation \a extraIndent to current
@ -69,7 +103,18 @@ public:
template<typename String>
void rawAppendToCpp(const String &what, int extraIndent = 0)
{
rawAppend(m_code.cpp, what, extraIndent);
rawAppend(m_code.cpp, what, cppIndent + extraIndent);
}
// special case of rawAppendToCpp that makes sure that string "foo()"
// becomes "MyClass::foo()"
template<typename String>
void rawAppendSignatureToCpp(const String &what, int extraIndent = 0)
{
QString signatureScope;
for (const auto &scope : memberScopes)
signatureScope += scope + u"::";
rawAppendToCpp(signatureScope + what, extraIndent);
}
};

View File

@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qmltcvisitor.h"
#include <QtCore/qfileinfo.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
static QString uniqueNameFromPieces(const QStringList &pieces, QHash<QString, int> &repetitions)
{
QString possibleName = pieces.join(u'_');
const int count = repetitions[possibleName]++;
if (count > 0)
possibleName.append(u"_" + QString::number(count));
return possibleName;
}
QmltcVisitor::QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory, const QStringList &qmltypesFiles)
: QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmltypesFiles)
{
m_qmlTypeNames.append(QFileInfo(logger->fileName()).baseName()); // put document root
}
bool QmltcVisitor::visit(QQmlJS::AST::UiObjectDefinition *object)
{
if (!QQmlJSImportVisitor::visit(object))
return false;
Q_ASSERT(m_currentScope->scopeType() == QQmlJSScope::QMLScope);
Q_ASSERT(m_currentScope->internalName().isEmpty());
Q_ASSERT(!m_currentScope->baseTypeName().isEmpty());
if (m_currentScope != m_exportedRootScope) // not document root
m_qmlTypeNames.append(m_currentScope->baseTypeName());
// give C++-relevant internal names to QMLScopes, we can use them later in compiler
m_currentScope->setInternalName(uniqueNameFromPieces(m_qmlTypeNames, m_qmlTypeNameCounts));
return true;
}
void QmltcVisitor::endVisit(QQmlJS::AST::UiObjectDefinition *)
{
m_qmlTypeNames.removeLast();
}
QT_END_NAMESPACE

View File

@ -31,6 +31,7 @@
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qlist.h>
#include <QtQml/private/qqmlirbuilder_p.h>
#include <private/qqmljsimportvisitor_p.h>
@ -43,10 +44,17 @@ class QmltcVisitor : public QQmlJSImportVisitor
public:
QmltcVisitor(QQmlJSImporter *importer, QQmlJSLogger *logger,
const QString &implicitImportDirectory,
const QStringList &qmltypesFiles = QStringList())
: QQmlJSImportVisitor(importer, logger, implicitImportDirectory, qmltypesFiles)
{
}
const QStringList &qmltypesFiles = QStringList());
bool visit(QQmlJS::AST::UiObjectDefinition *) override;
void endVisit(QQmlJS::AST::UiObjectDefinition *) override;
// NB: overwrite result() method to return ConstPtr
QQmlJSScope::ConstPtr result() const { return QQmlJSImportVisitor::result(); }
protected:
QStringList m_qmlTypeNames; // names of QML types arranged as a stack
QHash<QString, int> m_qmlTypeNameCounts;
};
QT_END_NAMESPACE