Remove qmltc prototype code (2/N)

- Migrate to the newer output ir classes (with adjustments)
- Deduplicate code writer and remove now-unused output helpers
  from the prototype version
- Remove old output ir

Change-Id: Ie7fe5e6d47e18477c65af02cabd89a890628442c
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Andrei Golubev 2022-03-03 14:03:58 +01:00
parent 3c680af4e9
commit 44b95b3fb2
16 changed files with 305 additions and 1035 deletions

View File

@ -71,15 +71,15 @@ class HelloWorld : public QObject
public:
HelloWorld(QQmlEngine * engine, QObject * parent = nullptr);
Q_SIGNALS:
void created();
public:
void setHello(const QString& hello_);
QString hello();
QBindable<QString> bindableHello();
Q_INVOKABLE void printHello(QString prefix, QString suffix);
signals:
void created();
// ...
};
//! [qmltc-hello-world-generated]

View File

@ -14,13 +14,10 @@ qt_internal_add_tool(${target_name}
qmltccompilerpieces.h
qmltcpropertyutils.h
prototype/generatedcodeprimitives.h
prototype/qml2cppcontext.h
prototype/qml2cppdefaultpasses.cpp prototype/qml2cppdefaultpasses.h
prototype/codegenerator.cpp prototype/codegenerator.h
prototype/codegeneratorutil.cpp prototype/codegeneratorutil.h
prototype/codegeneratorwriter.cpp prototype/codegeneratorwriter.h
prototype/qmlcompiler.h
DEFINES
QT_NO_CAST_FROM_ASCII
QT_NO_CAST_TO_ASCII

View File

@ -30,7 +30,7 @@
#include "prototype/qml2cppdefaultpasses.h"
#include "prototype/qml2cpppropertyutils.h"
#include "prototype/codegeneratorutil.h"
#include "prototype/codegeneratorwriter.h"
#include "qmltccodewriter.h"
#include "qmltccompiler.h"
@ -88,12 +88,12 @@ static QString figureReturnType(const QQmlJSMetaMethod &m)
return type;
}
static QList<QQmlJSAotVariable>
static QList<QmltcVariable>
compileMethodParameters(const QStringList &names,
const QList<QSharedPointer<const QQmlJSScope>> &types,
bool allowUnnamed = false)
{
QList<QQmlJSAotVariable> paramList;
QList<QmltcVariable> paramList;
const auto size = names.size();
paramList.reserve(size);
for (qsizetype i = 0; i < size; ++i) {
@ -102,8 +102,7 @@ compileMethodParameters(const QStringList &names,
Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified
if (name.isEmpty() && allowUnnamed)
name = u"unnamed_" + QString::number(i);
paramList.emplaceBack(
QQmlJSAotVariable { types[i]->augmentedInternalName(), name, QString() });
paramList.emplaceBack(QmltcVariable { types[i]->augmentedInternalName(), name, QString() });
}
return paramList;
}
@ -295,7 +294,6 @@ void CodeGenerator::constructObjects(QSet<QString> &requiredCppIncludes)
void CodeGenerator::generate()
{
GeneratedCode code;
const QString rootClassName = QFileInfo(m_url).baseName();
Q_ASSERT(!rootClassName.isEmpty());
const QString hPath = m_info->outputHFile;
@ -323,16 +321,16 @@ void CodeGenerator::generate()
};
const auto &root = m_objects.at(0).type;
QList<QQmlJSAotObject> compiledObjects;
QList<QmltcType> compiledObjects;
if (isComponent(root)) {
compiledObjects.reserve(1);
compiledObjects.emplaceBack(); // create new object
const auto compile = [this](QQmlJSAotObject &current, const CodeGenObject &object) {
const auto compile = [this](QmltcType &current, const CodeGenObject &object) {
this->compileQQmlComponentElements(current, object);
};
compileObject(compiledObjects.back(), m_objects.at(0), compile);
} else {
const auto compile = [this](QQmlJSAotObject &current, const CodeGenObject &object) {
const auto compile = [this](QmltcType &current, const CodeGenObject &object) {
this->compileObjectElements(current, object);
};
@ -359,15 +357,18 @@ void CodeGenerator::generate()
if (m_logger->hasErrors())
return;
QQmlJSProgram program { compiledObjects, m_urlMethod, url, hPath, cppPath,
m_info->outputNamespace, requiredCppIncludes };
QmltcProgram program {
url, cppPath, hPath, m_info->outputNamespace, requiredCppIncludes,
m_urlMethod, compiledObjects
};
// write everything
GeneratedCodeUtils codeUtils(code);
CodeGeneratorWriter::write(codeUtils, program);
QmltcOutput code;
QmltcOutputWrapper codeUtils(code);
QmltcCodeWriter::write(codeUtils, program);
writeToFile(hPath, code.header.toUtf8());
writeToFile(cppPath, code.implementation.toUtf8());
writeToFile(cppPath, code.cpp.toUtf8());
}
QString buildCallSpecialMethodValue(bool documentRoot, const QString &outerFlagName,
@ -382,8 +383,8 @@ QString buildCallSpecialMethodValue(bool documentRoot, const QString &outerFlagN
}
void CodeGenerator::compileObject(
QQmlJSAotObject &compiled, const CodeGenObject &object,
std::function<void(QQmlJSAotObject &, const CodeGenObject &)> compileElements)
QmltcType &compiled, const CodeGenObject &object,
std::function<void(QmltcType &, const CodeGenObject &)> compileElements)
{
if (object.type->isSingleton()) {
recordError(object.type->sourceLocation(), u"Singleton types are not supported"_qs);
@ -427,14 +428,14 @@ void CodeGenerator::compileObject(
compiled.handleOnCompleted.name = u"QML_handleOnCompleted"_qs;
compiled.handleOnCompleted.returnType = u"void"_qs;
QQmlJSAotVariable engine(u"QQmlEngine *"_qs, u"engine"_qs, QString());
QQmlJSAotVariable parent(u"QObject *"_qs, u"parent"_qs, u"nullptr"_qs);
QmltcVariable engine(u"QQmlEngine *"_qs, u"engine"_qs, QString());
QmltcVariable parent(u"QObject *"_qs, u"parent"_qs, u"nullptr"_qs);
compiled.baselineCtor.parameterList = { parent };
compiled.externalCtor.parameterList = { engine, parent };
QQmlJSAotVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData> &"_qs, u"parentContext"_qs,
QString());
QQmlJSAotVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs, QString());
QQmlJSAotVariable callSpecialMethodFlag(u"bool"_qs, u"callSpecialMethodNow"_qs, QString());
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData> &"_qs, u"parentContext"_qs,
QString());
QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs, QString());
QmltcVariable callSpecialMethodFlag(u"bool"_qs, u"callSpecialMethodNow"_qs, QString());
if (documentRoot) {
compiled.init.parameterList = { engine, ctxtdata, finalizeFlag, callSpecialMethodFlag };
compiled.endInit.parameterList = { engine, finalizeFlag };
@ -692,7 +693,7 @@ void CodeGenerator::compileObject(
// compiled.endInit.body << u"Qt::endPropertyUpdateGroup();"_qs;
}
void CodeGenerator::compileObjectElements(QQmlJSAotObject &compiled, const CodeGenObject &object)
void CodeGenerator::compileObjectElements(QmltcType &compiled, const CodeGenObject &object)
{
// compile enums
const auto enums = object.type->ownEnumerations();
@ -785,8 +786,7 @@ void CodeGenerator::compileObjectElements(QQmlJSAotObject &compiled, const CodeG
compileBinding(compiled, **it, object, { object.type, u"this"_qs, u""_qs, false });
}
void CodeGenerator::compileQQmlComponentElements(QQmlJSAotObject &compiled,
const CodeGenObject &object)
void CodeGenerator::compileQQmlComponentElements(QmltcType &compiled, const CodeGenObject &object)
{
Q_UNUSED(object);
@ -820,7 +820,7 @@ void CodeGenerator::compileQQmlComponentElements(QQmlJSAotObject &compiled,
compiled.init.body << u"}"_qs;
}
void CodeGenerator::compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &e)
void CodeGenerator::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
{
const auto intValues = e.values();
QStringList values;
@ -833,7 +833,7 @@ void CodeGenerator::compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &
u"Q_ENUM(%1)"_qs.arg(e.name()));
}
void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaProperty &p,
void CodeGenerator::compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
const QQmlJSScope::ConstPtr &owner)
{
Q_ASSERT(!p.isAlias()); // will be handled separately
@ -865,10 +865,10 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
// If p.isList(), it's a QQmlListProperty. Then you can write the underlying list through
// the QQmlListProperty object retrieved with the getter. Setting it would make no sense.
if (p.isWritable() && !p.isList()) {
QQmlJSAotMethod setter {};
QmltcMethod setter {};
setter.returnType = u"void"_qs;
setter.name = compilationData.write;
// QQmlJSAotVariable
// QmltcVariable
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
u""_qs);
setter.body << variableName + u".setValue(" + name + u"_);";
@ -878,7 +878,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
mocPieces << u"WRITE"_qs << setter.name;
}
QQmlJSAotMethod getter {};
QmltcMethod getter {};
getter.returnType = underlyingType;
getter.name = compilationData.read;
getter.body << u"return " + variableName + u".value();";
@ -888,7 +888,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
// 2. add bindable
if (!p.isList()) {
QQmlJSAotMethod bindable {};
QmltcMethod bindable {};
bindable.returnType = u"QBindable<" + underlyingType + u">";
bindable.name = compilationData.bindable;
bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
@ -916,7 +916,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject &current, const QQmlJSMetaPr
compilationData.notify);
}
void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaProperty &alias,
void CodeGenerator::compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner)
{
const QString aliasName = alias.propertyName();
@ -1040,7 +1040,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
Qml2CppPropertyData compilationData(aliasName);
// 1. add setter and getter
if (!info.readLine.isEmpty()) {
QQmlJSAotMethod getter {};
QmltcMethod getter {};
getter.returnType = info.underlyingType;
getter.name = compilationData.read;
getter.body += prologue;
@ -1052,13 +1052,13 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
} // else always an error?
if (!info.writeLine.isEmpty()) {
QQmlJSAotMethod setter {};
QmltcMethod setter {};
setter.returnType = u"void"_qs;
setter.name = compilationData.write;
QList<QQmlJSMetaMethod> methods = type->methods(resultingProperty.write());
if (methods.isEmpty()) {
// QQmlJSAotVariable
// QmltcVariable
setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(info.underlyingType),
aliasName + u"_", u""_qs);
} else {
@ -1072,7 +1072,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
parameterNames.reserve(setter.parameterList.size());
std::transform(setter.parameterList.cbegin(), setter.parameterList.cend(),
std::back_inserter(parameterNames),
[](const QQmlJSAotVariable &x) { return x.name; });
[](const QmltcVariable &x) { return x.name; });
QString commaSeparatedParameterNames = parameterNames.join(u", "_qs);
setter.body << info.writeLine.arg(commaSeparatedParameterNames) + u";";
} else {
@ -1086,7 +1086,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
// 2. add bindable
if (!info.bindableLine.isEmpty()) {
QQmlJSAotMethod bindable {};
QmltcMethod bindable {};
bindable.returnType = u"QBindable<" + info.underlyingType + u">";
bindable.name = compilationData.bindable;
bindable.body += prologue;
@ -1122,7 +1122,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject &current, const QQmlJSMetaPrope
}
}
void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMethod &m,
void CodeGenerator::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
const QmlIR::Function *f, const CodeGenObject &object)
{
Q_UNUSED(object);
@ -1131,7 +1131,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
const auto paramNames = m.parameterNames();
const auto paramTypes = m.parameterTypes();
Q_ASSERT(paramNames.size() == paramTypes.size());
const QList<QQmlJSAotVariable> paramList = compileMethodParameters(paramNames, paramTypes);
const QList<QmltcVariable> paramList = compileMethodParameters(paramNames, paramTypes);
const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
@ -1146,7 +1146,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
returnType, paramList);
}
QQmlJSAotMethod compiled {};
QmltcMethod compiled {};
compiled.returnType = returnType;
compiled.name = m.methodName();
compiled.parameterList = std::move(paramList);
@ -1154,7 +1154,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMeth
compiled.type = methodType;
compiled.access = m.access();
if (methodType != QQmlJSMetaMethod::Signal) {
compiled.declPreambles << u"Q_INVOKABLE"_qs; // TODO: do we need this for signals as well?
compiled.declarationPrefixes << u"Q_INVOKABLE"_qs;
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
} else {
compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal();
@ -1169,7 +1169,7 @@ static QString getPropertyOrAliasNameFromIr(const QmlIR::Document *doc, Iterator
return doc->stringAt(first->nameIndex);
}
void CodeGenerator::compileBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
void CodeGenerator::compileBinding(QmltcType &current, const QmlIR::Binding &binding,
const CodeGenObject &object,
const CodeGenerator::AccessorData &accessor)
{
@ -1579,26 +1579,26 @@ QString CodeGenerator::makeGensym(const QString &base)
}
// returns compiled script binding for "property changed" handler in a form of object type
static QQmlJSAotObject compileScriptBindingPropertyChangeHandler(
static QmltcType compileScriptBindingPropertyChangeHandler(
const QmlIR::Document *doc, const QmlIR::Binding &binding, const QmlIR::Object *irObject,
const QQmlJSAotMethod &urlMethod, const QString &functorCppType,
const QString &objectCppType, const QList<QQmlJSAotVariable> &slotParameters)
const QmltcMethod &urlMethod, const QString &functorCppType, const QString &objectCppType,
const QList<QmltcVariable> &slotParameters)
{
QQmlJSAotObject bindingFunctor {};
QmltcType bindingFunctor {};
bindingFunctor.cppType = functorCppType;
bindingFunctor.ignoreInit = true;
// default member variable and ctor:
const QString pointerToObject = objectCppType + u" *";
bindingFunctor.variables.emplaceBack(
QQmlJSAotVariable { pointerToObject, u"m_self"_qs, u"nullptr"_qs });
QmltcVariable { pointerToObject, u"m_self"_qs, u"nullptr"_qs });
bindingFunctor.baselineCtor.name = functorCppType;
bindingFunctor.baselineCtor.parameterList.emplaceBack(
QQmlJSAotVariable { pointerToObject, u"self"_qs, QString() });
QmltcVariable { pointerToObject, u"self"_qs, QString() });
bindingFunctor.baselineCtor.initializerList.emplaceBack(u"m_self(self)"_qs);
// call operator:
QQmlJSAotMethod callOperator {};
QmltcMethod callOperator {};
callOperator.returnType = u"void"_qs;
callOperator.name = u"operator()"_qs;
callOperator.parameterList = slotParameters;
@ -1628,7 +1628,7 @@ propertyForChangeHandler(const QQmlJSScope::ConstPtr &scope, QString name)
return {};
}
void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
void CodeGenerator::compileScriptBinding(QmltcType &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName,
const CodeGenObject &object, const QString &propertyName,
const QQmlJSScope::ConstPtr &propertyType,
@ -1667,7 +1667,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
};
// these only make sense when binding is on signal handler
QList<QQmlJSAotVariable> slotParameters;
QList<QmltcVariable> slotParameters;
QString signalName;
QString signalReturnType;
@ -1755,7 +1755,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
const QString slotName = makeGensym(signalName + u"_slot");
// SignalHander specific:
QQmlJSAotMethod slotMethod {};
QmltcMethod slotMethod {};
slotMethod.returnType = signalReturnType;
slotMethod.name = slotName;
slotMethod.parameterList = slotParameters;
@ -1832,7 +1832,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
+ accessor.name + u"))));";
current.variables.emplaceBack(
QQmlJSAotVariable { typeOfQmlBinding, bindingSymbolName, QString() });
QmltcVariable { typeOfQmlBinding, bindingSymbolName, QString() });
// current.ctor.initializerList << bindingSymbolName + u"()";
break;
}
@ -1840,7 +1840,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
}
// TODO: should use "compileScriptBinding" instead of custom code
void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
void CodeGenerator::compileScriptBindingOfComponent(QmltcType &current,
const QmlIR::Object *irObject,
const QQmlJSScope::ConstPtr objectType,
const QmlIR::Binding &binding,
@ -1880,7 +1880,7 @@ void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
const QString slotName = makeGensym(signalName + u"_slot");
// SignalHander specific:
QQmlJSAotMethod slotMethod {};
QmltcMethod slotMethod {};
slotMethod.returnType = signalReturnType;
slotMethod.name = slotName;
@ -1898,7 +1898,8 @@ void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject &current,
current.handleOnCompleted.body << slotName + u"();";
} else if (signalName == u"destruction"_qs) {
if (!current.dtor) {
current.dtor = QQmlJSAotSpecialMethod {};
// TODO: double-check that this stuff is actually correct now:
current.dtor = QmltcDtor {};
current.dtor->name = u"~" + current.cppType;
}
current.dtor->firstLines << slotName + u"();";
@ -1913,7 +1914,7 @@ void CodeGenerator::compileUrlMethod()
m_urlMethod.body << u"static QUrl docUrl = %1;"_qs.arg(
CodeGeneratorUtility::toResourcePath(m_info->resourcePath));
m_urlMethod.body << u"return docUrl;"_qs;
m_urlMethod.declPreambles << u"static"_qs;
m_urlMethod.declarationPrefixes << u"static"_qs;
m_urlMethod.modifiers << u"noexcept"_qs;
}

View File

@ -30,8 +30,7 @@
#define CODEGENERATOR_H
#include "qmltctyperesolver.h"
#include "prototype/qmlcompiler.h"
#include "prototype/generatedcodeprimitives.h"
#include "qmltcoutputir.h"
#include "prototype/qml2cppcontext.h"
#include <QtCore/qlist.h>
@ -80,13 +79,13 @@ private:
// types ignored by the code generator
QSet<QQmlJSScope::ConstPtr> m_ignoredTypes;
QQmlJSAotMethod m_urlMethod;
QmltcMethod m_urlMethod;
// helper struct used for unique string generation
struct UniqueStringId
{
QString combined;
UniqueStringId(const QQmlJSAotObject &compiled, const QString &value)
UniqueStringId(const QmltcType &compiled, const QString &value)
: combined(compiled.cppType + u"_" + value)
{
Q_ASSERT(!compiled.cppType.isEmpty());
@ -141,19 +140,18 @@ private:
bool m_isAnonymous = false; // crutch to distinguish QML_ELEMENT from QML_ANONYMOUS
// code compilation functions that produce "compiled" entities
void
compileObject(QQmlJSAotObject &current, const CodeGenObject &object,
std::function<void(QQmlJSAotObject &, const CodeGenObject &)> compileElements);
void compileObjectElements(QQmlJSAotObject &current, const CodeGenObject &object);
void compileQQmlComponentElements(QQmlJSAotObject &current, const CodeGenObject &object);
void compileObject(QmltcType &current, const CodeGenObject &object,
std::function<void(QmltcType &, const CodeGenObject &)> compileElements);
void compileObjectElements(QmltcType &current, const CodeGenObject &object);
void compileQQmlComponentElements(QmltcType &current, const CodeGenObject &object);
void compileEnum(QQmlJSAotObject &current, const QQmlJSMetaEnum &e);
void compileProperty(QQmlJSAotObject &current, const QQmlJSMetaProperty &p,
void compileEnum(QmltcType &current, const QQmlJSMetaEnum &e);
void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
const QQmlJSScope::ConstPtr &owner);
void compileAlias(QQmlJSAotObject &current, const QQmlJSMetaProperty &alias,
void compileAlias(QmltcType &current, const QQmlJSMetaProperty &alias,
const QQmlJSScope::ConstPtr &owner);
void compileMethod(QQmlJSAotObject &current, const QQmlJSMetaMethod &m,
const QmlIR::Function *f, const CodeGenObject &object);
void compileMethod(QmltcType &current, const QQmlJSMetaMethod &m, const QmlIR::Function *f,
const CodeGenObject &object);
void compileUrlMethod(); // special case
// helper structure that holds the information necessary for most bindings,
@ -167,17 +165,17 @@ private:
QString propertyName; // usually empty
bool isValueType = false; // usually false
};
void compileBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
void compileBinding(QmltcType &current, const QmlIR::Binding &binding,
const CodeGenObject &object, const AccessorData &accessor);
// special case (for simplicity)
void compileScriptBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
void compileScriptBinding(QmltcType &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName, const CodeGenObject &object,
const QString &propertyName,
const QQmlJSScope::ConstPtr &propertyType,
const AccessorData &accessor);
// TODO: remove this special case
void compileScriptBindingOfComponent(QQmlJSAotObject &current, const QmlIR::Object *object,
void compileScriptBindingOfComponent(QmltcType &current, const QmlIR::Object *object,
const QQmlJSScope::ConstPtr objectType,
const QmlIR::Binding &binding,
const QString &propertyName);

View File

@ -37,11 +37,10 @@ QT_BEGIN_NAMESPACE
// NB: this variable would behave correctly as long as QML init and QML finalize
// are non-virtual functions
const QQmlJSAotVariable CodeGeneratorUtility::childrenOffsetVariable = { u"qsizetype"_qs,
u"QML_choffset"_qs,
QString() };
const QmltcVariable CodeGeneratorUtility::childrenOffsetVariable { u"qsizetype"_qs,
u"QML_choffset"_qs, QString() };
const QQmlJSAotVariable CodeGeneratorUtility::compilationUnitVariable = {
const QmltcVariable CodeGeneratorUtility::compilationUnitVariable {
u"QV4::ExecutableCompilationUnit *"_qs, u"QML_cu"_qs, QString()
};
@ -143,7 +142,7 @@ QStringList CodeGeneratorUtility::generate_assignToSpecialAlias(
QStringList CodeGeneratorUtility::generate_callExecuteRuntimeFunction(
const QString &url, qsizetype index, const QString &accessor, const QString &returnType,
const QList<QQmlJSAotVariable> &parameters)
const QList<QmltcVariable> &parameters)
{
QStringList code;
code.reserve(12); // should always be enough
@ -165,7 +164,7 @@ QStringList CodeGeneratorUtility::generate_callExecuteRuntimeFunction(
types << u"QMetaType::fromType<std::decay_t<" + returnType + u">>()";
}
for (const QQmlJSAotVariable &p : parameters) {
for (const QmltcVariable &p : parameters) {
args << u"const_cast<void *>(reinterpret_cast<const void *>(std::addressof(" + p.name
+ u")))";
types << u"QMetaType::fromType<std::decay_t<" + p.cppType + u">>()";
@ -214,12 +213,12 @@ QStringList CodeGeneratorUtility::generate_createBindingOnProperty(
return code;
}
QString CodeGeneratorUtility::generate_qOverload(const QList<QQmlJSAotVariable> &params,
QString CodeGeneratorUtility::generate_qOverload(const QList<QmltcVariable> &params,
const QString &overloaded)
{
QStringList types;
types.reserve(params.size());
for (const QQmlJSAotVariable &p : params)
for (const QmltcVariable &p : params)
types.emplaceBack(p.cppType);
return u"qOverload<" + types.join(u", "_qs) + u">(" + overloaded + u")";
}

View File

@ -29,7 +29,7 @@
#ifndef CODEGENERATORUTIL_H
#define CODEGENERATORUTIL_H
#include "prototype/qmlcompiler.h"
#include "qmltcoutputir.h"
#include <private/qqmljsscope_p.h>
#include <private/qqmljsmetatypes_p.h>
@ -53,10 +53,10 @@ struct CodeGeneratorUtility
// reference any object in the document by id, which automatically means
// that all ids have to be set up before we get to finalization (and the
// only place for it is init)
static const QQmlJSAotVariable childrenOffsetVariable;
static const QmltcVariable childrenOffsetVariable;
// represents QV4::ExecutableCompilationUnit
static const QQmlJSAotVariable compilationUnitVariable;
static const QmltcVariable compilationUnitVariable;
// helper functions:
static QString toResourcePath(const QString &s)
@ -84,13 +84,13 @@ struct CodeGeneratorUtility
static QStringList
generate_callExecuteRuntimeFunction(const QString &url, qsizetype index,
const QString &accessor, const QString &returnType,
const QList<QQmlJSAotVariable> &parameters = {});
const QList<QmltcVariable> &parameters = {});
static QStringList
generate_createBindingOnProperty(const QString &unitVarName, const QString &scope,
qsizetype functionIndex, const QString &target,
int propertyIndex, const QQmlJSMetaProperty &p,
int valueTypeIndex, const QString &subTarget);
static QString generate_qOverload(const QList<QQmlJSAotVariable> &parameters,
static QString generate_qOverload(const QList<QmltcVariable> &parameters,
const QString &overloaded);
static QString generate_addressof(const QString &addressed);
static QString generate_getPrivateClass(const QString &accessor, const QQmlJSMetaProperty &p);

View File

@ -1,480 +0,0 @@
/****************************************************************************
**
** 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 "codegeneratorwriter.h"
#include <private/qqmljsmetatypes_p.h>
#include <QtCore/qfileinfo.h>
#include <utility>
#include <functional>
QT_BEGIN_NAMESPACE
static constexpr char16_t newLine[] =
#ifdef Q_OS_WIN32
u"\r\n";
#else
u"\n";
#endif
static constexpr char newLineLatin1[] =
#ifdef Q_OS_WIN32
"\r\n";
#else
"\n";
#endif
static QString urlToMacro(const QString &url)
{
QFileInfo fi(url);
return u"Q_QMLTC_" + fi.baseName().toUpper();
}
static QString getFunctionCategory(const QQmlJSAotMethodBase &compiled)
{
QString category;
switch (compiled.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 QQmlJSAotMethod &compiled)
{
QString category = getFunctionCategory(static_cast<const QQmlJSAotMethodBase &>(compiled));
switch (compiled.type) {
case QQmlJSMetaMethod::Signal:
category = u"signals"_qs;
break;
case QQmlJSMetaMethod::Slot:
category += u" slots"_qs;
break;
case QQmlJSMetaMethod::Method:
break;
}
return category;
}
void CodeGeneratorWriter::writeGlobalHeader(GeneratedCodeUtils &code, const QString &sourceName,
const QString &hPath, const QString &cppPath,
const QString &outNamespace,
const QSet<QString> &requiredCppIncludes)
{
Q_UNUSED(newLineLatin1);
Q_UNUSED(cppPath);
const QString preamble =
u"// This code is auto-generated by the qmlcompiler tool from the file '" + sourceName
+ u"'" + newLine + u"// WARNING! All changes made in this file will be lost!" + newLine;
code.appendToHeader(preamble);
code.appendToImpl(preamble);
code.appendToHeader(u"// NOTE: This generated API is to be considered implementation detail.");
code.appendToHeader(
u"// It may change from version to version and should not be relied upon.");
const QString headerMacro = urlToMacro(sourceName);
code.appendToHeader(u"#ifndef %1_H"_qs.arg(headerMacro));
code.appendToHeader(u"#define %1_H"_qs.arg(headerMacro));
code.appendToHeader(u"#include <QtCore/qproperty.h>");
code.appendToHeader(u"#include <QtCore/qobject.h>");
code.appendToHeader(u"#include <QtCore/qcoreapplication.h>");
code.appendToHeader(u"#include <QtQml/qqmlengine.h>");
code.appendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
code.appendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
code.appendToHeader(u"#include <private/qqmlengine_p.h>"); // NB: private header
code.appendToHeader(u"#include <QQmlListProperty>"); // required by list properties
// include custom C++ includes required by used types
code.appendToHeader(u"// BEGIN(custom_cpp_includes)");
for (const auto &requiredInclude : requiredCppIncludes) {
code.appendToHeader(u"#include \"" + requiredInclude + u"\"");
}
code.appendToHeader(u"// END(custom_cpp_includes)");
code.appendToImpl(u"#include \"" + hPath + u"\""); // include own .h file
code.appendToImpl(u"#include <private/qqmlcppbinding_p.h>"); // QmltcSupportLib
code.appendToImpl(u"#include <private/qqmlcpponassignment_p.h>"); // QmltcSupportLib
code.appendToImpl(u"#include <private/qqmlobjectcreator_p.h>"); // createComponent()
code.appendToImpl(u"#include <private/qqmlcomponent_p.h>"); // QQmlComponentPrivate::get()
code.appendToImpl(u"");
code.appendToImpl(u"#include <private/qobject_p.h>"); // NB: for private properties
code.appendToImpl(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
code.appendToImpl(u""); // blank line
if (!outNamespace.isEmpty()) {
code.appendToHeader(u""); // blank line
code.appendToHeader(u"namespace %1 {"_qs.arg(outNamespace));
code.appendToImpl(u""); // blank line
code.appendToImpl(u"namespace %1 {"_qs.arg(outNamespace));
}
}
void CodeGeneratorWriter::writeGlobalFooter(GeneratedCodeUtils &code, const QString &sourceName,
const QString &hPath, const QString &cppPath,
const QString &outNamespace)
{
Q_UNUSED(code);
Q_UNUSED(hPath);
Q_UNUSED(cppPath);
if (!outNamespace.isEmpty()) {
code.appendToImpl(u"} // namespace %1"_qs.arg(outNamespace));
code.appendToImpl(u""); // blank line
code.appendToHeader(u"} // namespace %1"_qs.arg(outNamespace));
code.appendToHeader(u""); // blank line
}
code.appendToHeader(u"#endif // %1_H"_qs.arg(urlToMacro(sourceName)));
code.appendToHeader(u""); // blank line
}
static QString classString(const QQmlJSAotObject &compiled)
{
QString str = u"class " + compiled.cppType;
QStringList nonEmptyBaseClasses;
nonEmptyBaseClasses.reserve(compiled.baseClasses.size());
std::copy_if(compiled.baseClasses.cbegin(), compiled.baseClasses.cend(),
std::back_inserter(nonEmptyBaseClasses),
[](const QString &entry) { return !entry.isEmpty(); });
if (!nonEmptyBaseClasses.isEmpty())
str += u" : public " + nonEmptyBaseClasses.join(u", public "_qs);
return str;
}
template<typename Predicate>
static void dumpFunctions(GeneratedCodeUtils &code, const QList<QQmlJSAotMethod> &functions,
Predicate pred)
{
// functions are _ordered_ by access and kind. ordering is important to
// provide consistent output
QMap<QString, QList<const QQmlJSAotMethod *>> orderedFunctions;
for (const auto &function : functions) {
if (pred(function))
orderedFunctions[getFunctionCategory(function)].append(std::addressof(function));
}
for (auto it = orderedFunctions.cbegin(); it != orderedFunctions.cend(); ++it) {
code.appendToHeader(it.key() + u":", -1);
for (const QQmlJSAotMethod *function : qAsConst(it.value()))
CodeGeneratorWriter::write(code, *function);
}
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotObject &compiled)
{
code.appendToHeader(u""); // just new line
code.appendToImpl(u""); // just new line
// generate class preamble
code.appendToHeader(classString(compiled));
code.appendToHeader(u"{");
for (const QString &mocLine : qAsConst(compiled.mocCode))
code.appendToHeader(mocLine, 1);
for (const QString &otherLine : qAsConst(compiled.otherCode))
code.appendToHeader(otherLine, 1);
GeneratedCodeUtils::MemberNamespaceScope thisObjectScope(code, compiled.cppType);
Q_UNUSED(thisObjectScope);
{
GeneratedCodeUtils::HeaderIndentationScope headerIndentScope(code);
Q_UNUSED(headerIndentScope);
// first, write user-visible code, then everything else. someone might
// want to look at the generated code, so let's make an effort when
// writing it down
code.appendToHeader(u"// -----------------");
code.appendToHeader(u"// External C++ API:");
code.appendToHeader(u"public:", -1);
// NB: when non-document root, the externalCtor won't be public - but we
// really don't care about the output format of such types
if (!compiled.ignoreInit && compiled.externalCtor.access == QQmlJSMetaMethod::Public) {
// TODO: ignoreInit must be eliminated
CodeGeneratorWriter::write(code, compiled.externalCtor);
}
// generate dtor
if (compiled.dtor)
CodeGeneratorWriter::write(code, *compiled.dtor);
// generate enums
for (const auto &enumeration : qAsConst(compiled.enums))
CodeGeneratorWriter::write(code, enumeration);
// generate (visible) functions
const auto isUserVisibleFunction = [](const QQmlJSAotMethod &function) {
return function.userVisible;
};
dumpFunctions(code, compiled.functions, isUserVisibleFunction);
code.appendToHeader(u"// -----------------");
code.appendToHeader(u""); // blank line
code.appendToHeader(u"// Internal functionality (do NOT use it!):");
// below are the hidden parts of the class
// generate (rest of the) ctors
if (compiled.ignoreInit) { // TODO: this branch should be eliminated
Q_ASSERT(compiled.baselineCtor.access == QQmlJSMetaMethod::Public);
code.appendToHeader(u"public:", -1);
CodeGeneratorWriter::write(code, compiled.baselineCtor);
} else {
code.appendToHeader(u"protected:", -1);
if (compiled.externalCtor.access != QQmlJSMetaMethod::Public) {
Q_ASSERT(compiled.externalCtor.access == QQmlJSMetaMethod::Protected);
CodeGeneratorWriter::write(code, compiled.externalCtor);
}
CodeGeneratorWriter::write(code, compiled.baselineCtor);
CodeGeneratorWriter::write(code, compiled.init);
CodeGeneratorWriter::write(code, compiled.endInit);
CodeGeneratorWriter::write(code, compiled.completeComponent);
CodeGeneratorWriter::write(code, compiled.finalizeComponent);
CodeGeneratorWriter::write(code, compiled.handleOnCompleted);
// code.appendToHeader(u"public:", -1);
}
// generate child types
code.appendToHeader(u"// BEGIN(children)");
for (const auto &child : qAsConst(compiled.children))
CodeGeneratorWriter::write(code, child);
code.appendToHeader(u"// END(children)");
// generate functions
code.appendToHeader(u"// BEGIN(hidden_functions)");
dumpFunctions(code, compiled.functions, std::not_fn(isUserVisibleFunction));
code.appendToHeader(u"// END(hidden_functions)");
if (!compiled.variables.isEmpty() || !compiled.properties.isEmpty()) {
code.appendToHeader(u""); // blank line
code.appendToHeader(u"protected:", -1);
}
// generate variables
if (!compiled.variables.isEmpty()) {
code.appendToHeader(u"// BEGIN(variables)");
for (const auto &variable : qAsConst(compiled.variables))
CodeGeneratorWriter::write(code, variable);
code.appendToHeader(u"// END(variables)");
}
// generate properties
if (!compiled.properties.isEmpty()) {
code.appendToHeader(u"// BEGIN(properties)");
for (const auto &property : qAsConst(compiled.properties))
CodeGeneratorWriter::write(code, property);
code.appendToHeader(u"// END(properties)");
}
}
code.appendToHeader(u"};");
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotEnum &compiled)
{
code.appendToHeader(u"enum " + compiled.cppType + u" {");
for (qsizetype i = 0; i < compiled.keys.size(); ++i) {
QString str;
if (compiled.values.isEmpty()) {
str += compiled.keys.at(i) + u",";
} else {
str += compiled.keys.at(i) + u" = " + compiled.values.at(i) + u",";
}
code.appendToHeader(str, 1);
}
code.appendToHeader(u"};");
code.appendToHeader(compiled.ownMocLine);
}
// NB: property generation is only concerned with property declaration in the header
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotVariable &compiled)
{
if (compiled.defaultValue.isEmpty()) {
code.appendToHeader(compiled.cppType + u" " + compiled.name + u";");
} else {
code.appendToHeader(compiled.cppType + u" " + compiled.name + u" = " + compiled.defaultValue
+ u";");
}
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotProperty &prop)
{
Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet
code.appendToHeader(u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_qs.arg(
prop.containingClass, prop.cppType, prop.name, prop.signalName));
}
static QString appendSpace(const QString &s)
{
if (s.isEmpty())
return s;
return s + u" ";
}
static QString prependSpace(const QString &s)
{
if (s.isEmpty())
return s;
return u" " + s;
}
static std::pair<QString, QString> functionSignatures(const QQmlJSAotMethodBase &m)
{
const QString name = m.name;
const QList<QQmlJSAotVariable> &parameterList = m.parameterList;
QStringList headerParamList;
QStringList implParamList;
for (const QQmlJSAotVariable &variable : parameterList) {
const QString commonPart = variable.cppType + u" " + variable.name;
implParamList << commonPart;
headerParamList << commonPart;
if (!variable.defaultValue.isEmpty())
headerParamList.back() += u" = " + variable.defaultValue;
}
const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")"
+ prependSpace(m.modifiers.join(u" "));
const QString implSignature = name + u"(" + implParamList.join(u", "_qs) + u")"
+ prependSpace(m.modifiers.join(u" "));
return { headerSignature, implSignature };
}
static QString functionReturnType(const QQmlJSAotMethodBase &m)
{
return appendSpace(m.declPreambles.join(u" "_qs)) + m.returnType;
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotMethod &compiled)
{
const auto [hSignature, cppSignature] = functionSignatures(compiled);
// Note: augment return type with preambles in declaration
code.appendToHeader(functionReturnType(compiled) + u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = compiled.type;
if (methodType != QQmlJSMetaMethod::Signal) {
code.appendToImpl(u""); // just new line
code.appendToImpl(compiled.returnType);
code.appendSignatureToImpl(cppSignature);
code.appendToImpl(u"{");
{
GeneratedCodeUtils::ImplIndentationScope indentScope(code);
Q_UNUSED(indentScope);
for (const QString &line : qAsConst(compiled.firstLines))
code.appendToImpl(line);
for (const QString &line : qAsConst(compiled.body))
code.appendToImpl(line);
for (const QString &line : qAsConst(compiled.lastLines))
code.appendToImpl(line);
}
code.appendToImpl(u"}");
}
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotSpecialMethod &compiled)
{
const auto [hSignature, cppSignature] = functionSignatures(compiled);
const QString returnTypeWithSpace =
compiled.returnType.isEmpty() ? u""_qs : compiled.returnType + u" ";
code.appendToHeader(returnTypeWithSpace + hSignature + u";");
code.appendToImpl(u""); // just new line
if (!returnTypeWithSpace.isEmpty())
code.appendToImpl(returnTypeWithSpace);
code.appendSignatureToImpl(cppSignature);
if (!compiled.initializerList.isEmpty()) {
code.appendToImpl(u":", 1);
code.appendToImpl(compiled.initializerList.join(u","_qs + newLine + newLine
+ u" "_qs.repeated(code.implIndent + 1)),
1);
}
code.appendToImpl(u"{");
{
GeneratedCodeUtils::ImplIndentationScope indentScope(code);
Q_UNUSED(indentScope);
for (const QString &line : qAsConst(compiled.firstLines))
code.appendToImpl(line);
for (const QString &line : qAsConst(compiled.body))
code.appendToImpl(line);
for (const QString &line : qAsConst(compiled.lastLines))
code.appendToImpl(line);
}
code.appendToImpl(u"}");
}
void CodeGeneratorWriter::writeUrl(GeneratedCodeUtils &code, const QQmlJSAotMethod &urlMethod)
{
const auto [hSignature, _] = functionSignatures(urlMethod);
Q_UNUSED(_);
Q_ASSERT(!urlMethod.returnType.isEmpty());
code.appendToImpl(functionReturnType(urlMethod) + hSignature);
code.appendToImpl(u"{");
{
GeneratedCodeUtils::ImplIndentationScope indentScope(code);
Q_UNUSED(indentScope);
Q_ASSERT(urlMethod.firstLines.isEmpty() && urlMethod.lastLines.isEmpty());
for (const QString &line : qAsConst(urlMethod.body))
code.appendToImpl(line);
}
code.appendToImpl(u"}");
}
void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSProgram &compiled)
{
writeGlobalHeader(code, compiled.url, compiled.hPath, compiled.cppPath, compiled.outNamespace,
compiled.includes);
code.appendToImpl(u""); // just new line
writeUrl(code, compiled.urlMethod);
// forward declare objects before writing them
for (const QQmlJSAotObject &compiled : qAsConst(compiled.compiledObjects))
code.appendToHeader(u"class " + compiled.cppType + u";");
// write all the objects
for (const QQmlJSAotObject &compiled : qAsConst(compiled.compiledObjects))
write(code, compiled);
writeGlobalFooter(code, compiled.url, compiled.hPath, compiled.cppPath, compiled.outNamespace);
}
QT_END_NAMESPACE

View File

@ -1,61 +0,0 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#ifndef CODEGENERATORWRITER_H
#define CODEGENERATORWRITER_H
#include "generatedcodeprimitives.h"
#include "qmlcompiler.h"
QT_BEGIN_NAMESPACE
// writes compiled code into the GeneratedCode structure
struct CodeGeneratorWriter
{
static void writeGlobalHeader(GeneratedCodeUtils &code, const QString &sourceName,
const QString &hPath, const QString &cppPath,
const QString &outNamespace,
const QSet<QString> &requiredCppIncludes);
static void writeGlobalFooter(GeneratedCodeUtils &code, const QString &sourceName,
const QString &hPath, const QString &cppPath,
const QString &outNamespace);
static void write(GeneratedCodeUtils &code, const QQmlJSAotObject &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSAotEnum &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSAotVariable &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSAotProperty &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSAotMethod &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSAotSpecialMethod &compiled);
static void write(GeneratedCodeUtils &code, const QQmlJSProgram &compiled);
private:
static void writeUrl(GeneratedCodeUtils &code, const QQmlJSAotMethod &urlMethod);
};
QT_END_NAMESPACE
#endif // CODEGENERATORWRITER_H

View File

@ -1,130 +0,0 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#ifndef GENERATEDCODEPRIMITIVES_H
#define GENERATEDCODEPRIMITIVES_H
#include <QtCore/qstring.h>
#include <QtCore/qstack.h>
QT_BEGIN_NAMESPACE
// holds generated code for header and implementation files
struct GeneratedCode
{
QString header;
QString implementation;
};
// utility class that provides pretty-printing of the generated code into the
// GeneratedCode buffer
struct GeneratedCodeUtils
{
GeneratedCode &m_code; // buffer
QStack<QString> memberNamespaceStack; // member names scopes e.g. MyClass::MySubclass::
int headerIndent = 0; // header indentation level
int implIndent = 0; // implementation indentation level
GeneratedCodeUtils(GeneratedCode &code) : m_code(code) { }
// manages current scope of the generated code, which is necessary for
// implementation file generation. Example:
// class MyClass { MyClass(); }; - in header
// MyClass::MyClass() {} - in implementation file
// MemberNamespaceScope exists to be able to record and use "MyClass::"
struct MemberNamespaceScope
{
GeneratedCodeUtils &m_code;
MemberNamespaceScope(GeneratedCodeUtils &code, const QString &str) : m_code(code)
{
m_code.memberNamespaceStack.push(str);
}
~MemberNamespaceScope() { m_code.memberNamespaceStack.pop(); }
};
// manages current indentation scope: upon creation, increases current
// scope, which is decreased back upon deletion. this is used by append*
// functions that work with GeneratedCode::header to correctly indent the
// input
struct HeaderIndentationScope
{
GeneratedCodeUtils &m_code;
HeaderIndentationScope(GeneratedCodeUtils &code) : m_code(code) { ++m_code.headerIndent; }
~HeaderIndentationScope() { --m_code.headerIndent; }
};
// manages current indentation scope: upon creation, increases current
// scope, which is decreased back upon deletion. this is used by append*
// functions that work with GeneratedCode::implementation to correctly
// indent the input
struct ImplIndentationScope
{
GeneratedCodeUtils &m_code;
ImplIndentationScope(GeneratedCodeUtils &code) : m_code(code) { ++m_code.implIndent; }
~ImplIndentationScope() { --m_code.implIndent; }
};
// appends string \a what with extra indentation \a extraIndent to current
// GeneratedCode::header string
template<typename String>
void appendToHeader(const String &what, int extraIndent = 0)
{
constexpr char16_t newLine[] = u"\n";
m_code.header += QString((headerIndent + extraIndent) * 4, u' ') + what + newLine;
}
// appends string \a what with extra indentation \a extraIndent to current
// GeneratedCode::implementation string
template<typename String>
void appendToImpl(const String &what, int extraIndent = 0)
{
constexpr char16_t newLine[] = u"\n";
m_code.implementation += QString((implIndent + extraIndent) * 4, u' ') + what + newLine;
}
// appends string \a what with extra indentation \a extraIndent to current
// GeneratedCode::implementation string. this is a special case function
// that expects \a what to be a function signature as \a what is prepended
// with member scope related text. for example, string "foo()" is converted
// to string "MyClass::foo()" before append
template<typename String>
void appendSignatureToImpl(const String &what, int extraIndent = 0)
{
constexpr char16_t newLine[] = u"\n";
QString signatureScope;
for (const auto &subScope : memberNamespaceStack)
signatureScope += subScope + u"::";
m_code.implementation +=
signatureScope + QString((implIndent + extraIndent) * 4, u' ') + what + newLine;
}
};
QT_END_NAMESPACE
#endif // GENERATEDCODEPRIMITIVES_H

View File

@ -29,7 +29,6 @@
#ifndef QML2CPPCONTEXT_H
#define QML2CPPCONTEXT_H
#include "prototype/qmlcompiler.h"
#include "qmltctyperesolver.h"
#include <private/qqmljsdiagnosticmessage_p.h>

View File

@ -1,173 +0,0 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
#ifndef QMLCOMPILER_H
#define QMLCOMPILER_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <private/qqmljscompiler_p.h>
#include <private/qqmljsmetatypes_p.h>
QT_BEGIN_NAMESPACE
// TODO: rename the classes into Qmltc* pattern
// Below are the classes that represent a compiled QML types in a string data
// form. These classes should be used to generate C++ code.
// Represents QML->C++ compiled enumeration type
struct QQmlJSAotEnum
{
QString cppType; // C++ type of enum
QStringList keys; // enumerator
QStringList values; // enumerator value
QString ownMocLine; // special MOC line that follows enum declaration
QQmlJSAotEnum() = default;
QQmlJSAotEnum(const QString &t, const QStringList &ks, const QStringList &vs, const QString &l)
: cppType(t), keys(ks), values(vs), ownMocLine(l)
{
}
};
// Represents C++ member variable
struct QQmlJSAotVariable
{
QString cppType; // C++ type of a variable
QString name; // variable name
QString defaultValue; // optional default value
QQmlJSAotVariable() = default;
QQmlJSAotVariable(const QString &t, const QString &n, const QString &v)
: cppType(t), name(n), defaultValue(v)
{
}
};
struct QQmlJSAotProperty : QQmlJSAotVariable
{
QString containingClass;
QString signalName;
QQmlJSAotProperty() = default;
QQmlJSAotProperty(const QString t, const QString &n, const QString &c, const QString &s)
: QQmlJSAotVariable(t, n, QString()), containingClass(c), signalName(s)
{
}
};
struct QQmlJSAotMethodBase
{
QString returnType; // C++ return type
QString name; // C++ function name
QList<QQmlJSAotVariable> parameterList; // C++ function parameter list
QStringList body; // C++ code of function body by line
QStringList declPreambles; // e.g. "static" keyword
QStringList modifiers; // e.g. cv-qualifiers, ref-qualifier, noexcept, attributes
// TODO: these are only needed for Component.onCompleted -- any better way?
QStringList firstLines; // C++ to run at the very beginning of a function
QStringList lastLines; // C++ to run at the very end of a function
QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
};
// Represents QML->C++ compiled member function
struct QQmlJSAotMethod : QQmlJSAotMethodBase
{
QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
bool userVisible = false; // tells if a function is prioritized during the output generation
};
// Represents C++ special member function
struct QQmlJSAotSpecialMethod : QQmlJSAotMethodBase
{
QStringList initializerList; // C++ ctor initializer list
};
// Represents QML->C++ compiled class type that is used for C++ code generation
struct QQmlJSAotObject
{
QString cppType; // C++ class name of the QML object
QStringList baseClasses; // C++ class names of base classes
// TODO: also add "creation string"?
QStringList mocCode;
QStringList otherCode; // code that doesn't fit any category, e.g. friend declarations
// TODO: does it really need to be QHash and not QList?
// member types: enumerations and child types
QList<QQmlJSAotEnum> enums;
QList<QQmlJSAotObject> children; // these are pretty much always empty
// special member functions
QQmlJSAotSpecialMethod baselineCtor = {}; // does primary initialization
QQmlJSAotMethod init = {}; // begins secondary initialization
QQmlJSAotMethod endInit = {}; // ends initialization (with binding setup)
QQmlJSAotMethod completeComponent = {}; // calls componentComplete()
QQmlJSAotMethod finalizeComponent = {}; // invokes finalizer callbacks
QQmlJSAotMethod handleOnCompleted = {}; // calls Component.onCompleted
QQmlJSAotSpecialMethod externalCtor = {}; // calls baselineCtor, calls init
std::optional<QQmlJSAotSpecialMethod> dtor = {};
// member functions: methods, signals and slots
QList<QQmlJSAotMethod> functions;
// member variables
QList<QQmlJSAotVariable> variables;
// member properties
QList<QQmlJSAotProperty> properties;
// TODO: only needed for binding callables - should be revisited
bool ignoreInit = false; // specifies whether init and externalCtor should be ignored
};
struct QQmlJSProgram
{
QList<QQmlJSAotObject> compiledObjects;
QQmlJSAotMethod urlMethod;
QString url;
QString hPath;
QString cppPath;
QString outNamespace;
QSet<QString> includes;
};
QT_END_NAMESPACE
#endif // QMLCOMPILER_H

View File

@ -74,6 +74,20 @@ static QString getFunctionCategory(const QmltcMethod &method)
return category;
}
static QString appendSpace(const QString &s)
{
if (s.isEmpty())
return s;
return s + u" ";
}
static QString prependSpace(const QString &s)
{
if (s.isEmpty())
return s;
return u" " + s;
}
static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &method)
{
const QString name = method.name;
@ -89,11 +103,18 @@ static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &met
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")";
const QString headerSignature = name + u"(" + headerParamList.join(u", "_qs) + u")"
+ prependSpace(method.modifiers.join(u" "));
const QString cppSignature = name + u"(" + cppParamList.join(u", "_qs) + u")"
+ prependSpace(method.modifiers.join(u" "));
return { headerSignature, cppSignature };
}
static QString functionReturnType(const QmltcMethod &m)
{
return appendSpace(m.declarationPrefixes.join(u" "_qs)) + m.returnType;
}
void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
const QString &hPath, const QString &cppPath,
const QString &outNamespace,
@ -129,14 +150,18 @@ void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString
for (const auto &requiredInclude : requiredCppIncludes)
code.rawAppendToHeader(u"#include \"" + requiredInclude + u"\"");
code.rawAppendToHeader(u"// END(custom_cpp_includes)");
code.rawAppendToHeader(u"// qmltc support library:");
code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>");
code.rawAppendToCpp(u"#include \"" + hPath + u"\""); // include own .h file
code.rawAppendToCpp(u"// qmltc support library:");
code.rawAppendToCpp(u"#include <private/qqmlcppbinding_p.h>"); // QmltcSupportLib
code.rawAppendToCpp(u"#include <private/qqmlcpponassignment_p.h>"); // QmltcSupportLib
code.rawAppendToCpp(u""); // blank line
code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // createComponent()
code.rawAppendToCpp(u"#include <private/qqmlcomponent_p.h>"); // QQmlComponentPrivate::get()
code.rawAppendToCpp(u"");
code.rawAppendToCpp(u"#include <private/qobject_p.h>"); // NB: for private properties
code.rawAppendToCpp(u"#include <private/qqmlglobal_p.h>"); // QQml_setParent_noEvent()
code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
code.rawAppendToCpp(u""); // blank line
code.rawAppendToCpp(u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE");
@ -187,10 +212,6 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
{
writeGlobalHeader(code, program.url, program.hPath, program.cppPath, program.outNamespace,
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 */");
// url method comes first
writeUrl(code, program.urlMethod);
@ -207,12 +228,36 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &progra
writeToFile(program.cppPath, code.code().cpp.toUtf8());
}
template<typename Predicate>
static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &functions,
Predicate pred)
{
// functions are _ordered_ by access and kind. ordering is important to
// provide consistent output
QMap<QString, QList<const QmltcMethod *>> orderedFunctions;
for (const auto &function : functions) {
if (pred(function))
orderedFunctions[getFunctionCategory(function)].append(std::addressof(function));
}
for (auto it = orderedFunctions.cbegin(); it != orderedFunctions.cend(); ++it) {
code.rawAppendToHeader(it.key() + u":", -1);
for (const QmltcMethod *function : qAsConst(it.value()))
QmltcCodeWriter::write(code, *function);
}
}
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);
QStringList nonEmptyBaseClasses;
nonEmptyBaseClasses.reserve(type.baseClasses.size());
std::copy_if(type.baseClasses.cbegin(), type.baseClasses.cend(),
std::back_inserter(nonEmptyBaseClasses),
[](const QString &entry) { return !entry.isEmpty(); });
if (!nonEmptyBaseClasses.isEmpty())
str += u" : public " + nonEmptyBaseClasses.join(u", public "_qs);
return str;
};
@ -233,42 +278,70 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type)
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);
// first, write user-visible code, then everything else. someone might
// want to look at the generated code, so let's make an effort when
// writing it down
code.rawAppendToHeader(u"/* ----------------- */");
code.rawAppendToHeader(u"/* External C++ API */");
code.rawAppendToHeader(u"public:", -1);
// NB: when non-document root, the externalCtor won't be public - but we
// really don't care about the output format of such types
if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) {
// TODO: ignoreInit must be eliminated
QmltcCodeWriter::write(code, type.externalCtor);
}
// dtor
if (type.dtor)
QmltcCodeWriter::write(code, *type.dtor);
// 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);
QmltcCodeWriter::write(code, enumeration);
// child types
if (!type.children.isEmpty())
code.rawAppendToHeader(u""); // blank line
for (const auto &child : qAsConst(type.children))
write(code, child);
// visible functions
const auto isUserVisibleFunction = [](const QmltcMethod &function) {
return function.userVisible;
};
dumpFunctions(code, type.functions, isUserVisibleFunction);
// 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);
code.rawAppendToHeader(u"/* ----------------- */");
code.rawAppendToHeader(u""); // blank line
code.rawAppendToHeader(u"/* Internal functionality (do NOT use it!) */");
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);
// below are the hidden parts of the type
// (rest of the) ctors
if (type.ignoreInit) { // TODO: this branch should be eliminated
Q_ASSERT(type.baselineCtor.access == QQmlJSMetaMethod::Public);
code.rawAppendToHeader(u"public:", -1);
QmltcCodeWriter::write(code, type.baselineCtor);
} else {
code.rawAppendToHeader(u"protected:", -1);
if (type.externalCtor.access != QQmlJSMetaMethod::Public) {
Q_ASSERT(type.externalCtor.access == QQmlJSMetaMethod::Protected);
QmltcCodeWriter::write(code, type.externalCtor);
}
QmltcCodeWriter::write(code, type.baselineCtor);
QmltcCodeWriter::write(code, type.init);
QmltcCodeWriter::write(code, type.endInit);
QmltcCodeWriter::write(code, type.completeComponent);
QmltcCodeWriter::write(code, type.finalizeComponent);
QmltcCodeWriter::write(code, type.handleOnCompleted);
// code.rawAppendToHeader(u"public:", -1);
}
// children
for (const auto &child : qAsConst(type.children))
QmltcCodeWriter::write(code, child);
// (non-visible) functions
dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
// variables and properties
if (!type.variables.isEmpty() || !type.properties.isEmpty()) {
code.rawAppendToHeader(u""); // blank line
@ -314,10 +387,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
const auto [hSignature, cppSignature] = functionSignatures(method);
// Note: augment return type with preambles in declaration
QString prefix = method.declarationPrefixes.join(u' ');
if (!prefix.isEmpty())
prefix.append(u' ');
code.rawAppendToHeader(prefix + method.returnType + u" " + hSignature + u";");
code.rawAppendToHeader(functionReturnType(method) + u" " + hSignature + u";");
// do not generate method implementation if it is a signal
const auto methodType = method.type;
@ -329,39 +399,65 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
for (const QString &line : qAsConst(method.firstLines))
code.rawAppendToCpp(line);
for (const QString &line : qAsConst(method.body))
code.rawAppendToCpp(line);
for (const QString &line : qAsConst(method.lastLines))
code.rawAppendToCpp(line);
}
code.rawAppendToCpp(u"}");
}
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
template<typename WriteInitialization>
static void writeSpecialMethod(QmltcOutputWrapper &code, const QmltcMethodBase &specialMethod,
WriteInitialization writeInit)
{
const auto [hSignature, cppSignature] = functionSignatures(ctor);
QString prefix = ctor.declarationPrefixes.join(u' ');
if (!prefix.isEmpty())
prefix.append(u' ');
code.rawAppendToHeader(prefix + hSignature + u";");
const auto [hSignature, cppSignature] = functionSignatures(specialMethod);
code.rawAppendToHeader(hSignature + u";");
code.rawAppendToCpp(u""); // blank line
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);
}
writeInit(specialMethod);
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
for (const QString &line : qAsConst(ctor.body))
for (const QString &line : qAsConst(specialMethod.firstLines))
code.rawAppendToCpp(line);
for (const QString &line : qAsConst(specialMethod.body))
code.rawAppendToCpp(line);
for (const QString &line : qAsConst(specialMethod.lastLines))
code.rawAppendToCpp(line);
}
code.rawAppendToCpp(u"}");
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor)
{
const auto writeInitializerList = [&](const QmltcMethodBase &ctorBase) {
auto ctor = static_cast<const QmltcCtor &>(ctorBase);
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);
}
};
writeSpecialMethod(code, ctor, writeInitializerList);
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcDtor &dtor)
{
const auto noop = [](const QmltcMethodBase &) {};
writeSpecialMethod(code, dtor, noop);
}
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
{
const QString optionalPart = var.defaultValue.isEmpty() ? u""_qs : u" = " + var.defaultValue;
@ -370,7 +466,7 @@ void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProperty &prop)
{
Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet
Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet (or at all?)
code.rawAppendToHeader(u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_qs.arg(
prop.containingClass, prop.cppType, prop.name, prop.signalName));
}
@ -382,14 +478,12 @@ void QmltcCodeWriter::writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlM
const auto [hSignature, _] = functionSignatures(urlMethod);
Q_UNUSED(_);
// Note: augment return type with preambles in declaration
QString prefix = urlMethod.declarationPrefixes.join(u' ');
if (!prefix.isEmpty())
prefix.append(u' ');
code.rawAppendToCpp(prefix + urlMethod.returnType + u" " + hSignature);
code.rawAppendToCpp(functionReturnType(urlMethod) + hSignature);
code.rawAppendToCpp(u"{");
{
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
Q_UNUSED(cppIndent);
Q_ASSERT(urlMethod.firstLines.isEmpty() && urlMethod.lastLines.isEmpty());
for (const QString &line : qAsConst(urlMethod.body))
code.rawAppendToCpp(line);
}

View File

@ -49,9 +49,11 @@ struct QmltcCodeWriter
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 QmltcDtor &dtor);
static void write(QmltcOutputWrapper &code, const QmltcVariable &var);
static void write(QmltcOutputWrapper &code, const QmltcProperty &prop);
private:
static void writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod); // special
};

View File

@ -115,7 +115,7 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
current.baseClasses = { baseClass };
if (!documentRoot) {
// make document root a friend to allow it to access init and finalize
// make document root a friend to allow it to access init and endInit
current.otherCode << u"friend class %1;"_qs.arg(rootType->internalName());
} else {
// make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to
@ -147,68 +147,70 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
};
// add special member functions
current.basicCtor.access = QQmlJSMetaMethod::Protected;
current.baselineCtor.access = QQmlJSMetaMethod::Protected;
current.init.access = QQmlJSMetaMethod::Protected;
current.finalize.access = QQmlJSMetaMethod::Protected;
current.fullCtor.access = QQmlJSMetaMethod::Public;
current.endInit.access = QQmlJSMetaMethod::Protected;
current.externalCtor.access = QQmlJSMetaMethod::Public;
current.basicCtor.name = current.cppType;
current.fullCtor.name = current.cppType;
current.baselineCtor.name = current.cppType;
current.externalCtor.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;
current.endInit.name = u"qmltc_finalize"_qs;
current.endInit.returnType = u"void"_qs;
QmltcVariable creator(u"QQmltcObjectCreationHelper*"_qs, u"creator"_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.baselineCtor.parameterList = { parent };
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_qs, u"parentContext"_qs);
QmltcVariable finalizeFlag(u"bool"_qs, u"canFinalize"_qs);
if (documentRoot) {
current.fullCtor.parameterList = { engine, parent };
current.externalCtor.parameterList = { engine, parent };
current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
current.finalize.parameterList = { creator, engine, finalizeFlag };
current.endInit.parameterList = { creator, engine, finalizeFlag };
} else {
current.fullCtor.parameterList = { creator, engine, parent };
current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
current.finalize.parameterList = { creator, engine };
current.endInit.parameterList = { creator, engine };
}
current.fullCtor.initializerList = { current.basicCtor.name + u"(" + parent.name + u")" };
current.externalCtor.initializerList = { current.baselineCtor.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")" };
current.baselineCtor.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");";
current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
}
QmltcCodeGenerator generator { rootType };
// compilation stub:
current.fullCtor.body << u"Q_UNUSED(engine);"_qs;
current.finalize.body << u"Q_UNUSED(engine);"_qs;
current.finalize.body << u"Q_UNUSED(creator);"_qs;
current.externalCtor.body << u"Q_UNUSED(engine);"_qs;
current.endInit.body << u"Q_UNUSED(engine);"_qs;
current.endInit.body << u"Q_UNUSED(creator);"_qs;
if (documentRoot) {
current.fullCtor.body << u"// document root:"_qs;
current.externalCtor.body << u"// document root:"_qs;
// if it's document root, we want to create our QQmltcObjectCreationBase
// that would store all the created objects
current.fullCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_qs.arg(
current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_qs.arg(
type->internalName());
current.fullCtor.body << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs;
current.externalCtor.body
<< u"QQmltcObjectCreationHelper creator = objectHolder.view();"_qs;
// now call init
current.fullCtor.body << current.init.name
current.externalCtor.body << current.init.name
+ u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
u"finalize */ true);";
u"endInit */ true);";
current.finalize.body << u"Q_UNUSED(canFinalize);"_qs;
current.endInit.body << u"Q_UNUSED(canFinalize);"_qs;
} else {
current.fullCtor.body << u"// not document root:"_qs;
current.externalCtor.body << u"// not document root:"_qs;
// just call init, we don't do any setup here otherwise
current.fullCtor.body << current.init.name
current.externalCtor.body << current.init.name
+ u"(creator, engine, QQmlData::get(parent)->outerContext);";
}
@ -353,9 +355,9 @@ void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty
const QString storageName = variableName + u"_storage";
current.variables.emplaceBack(u"QList<" + p.type()->internalName() + u" *>", storageName,
QString());
current.basicCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
+ u"(this, std::addressof(" + storageName
+ u")))");
current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
+ u"(this, std::addressof(" + storageName
+ u")))");
}
// along with property, also add relevant moc code, so that we can use the

View File

@ -147,7 +147,7 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
current.init.body << u"// 4. call finalize in the document root"_qs;
current.init.body << u"if (canFinalize) {"_qs;
current.init.body << QStringLiteral(" %1(creator, engine, /* finalize */ true);")
.arg(current.finalize.name);
.arg(current.endInit.name);
current.init.body << u"}"_qs;
}
current.init.body << u"return context;"_qs;

View File

@ -92,6 +92,12 @@ struct QmltcMethodBase
QStringList body; // C++ function code
QQmlJSMetaMethod::Access access = QQmlJSMetaMethod::Public; // access specifier
QStringList declarationPrefixes;
QStringList modifiers; // cv-qualifiers, ref-qualifier, noexcept, attributes
// TODO: these are only needed for Component.onCompleted/onDestruction. this
// has to be re-written anyhow later
QStringList firstLines; // C++ to run at the very beginning of a function
QStringList lastLines; // C++ to run at the very end of a function
};
// Represents QML -> C++ compiled function
@ -99,6 +105,9 @@ struct QmltcMethod : QmltcMethodBase
{
QString returnType; // C++ return type
QQmlJSMetaMethod::Type type = QQmlJSMetaMethod::Method; // Qt function type
// TODO: should be a better way to handle this
bool userVisible = false; // tells if a function is prioritized during the output generation
};
// Represents C++ ctor of a type
@ -107,6 +116,11 @@ struct QmltcCtor : QmltcMethodBase
QStringList initializerList; // C++ ctor's initializer list
};
// Represents C++ dtor of a type
struct QmltcDtor : QmltcMethodBase
{
};
// Represents QML -> C++ compiled type
struct QmltcType
{
@ -120,10 +134,15 @@ struct QmltcType
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.)
QmltcCtor baselineCtor {}; // does basic contruction
QmltcCtor externalCtor {}; // calls basicCtor, calls init
QmltcMethod init {}; // starts object initialization (context setup), calls finalize
QmltcMethod endInit {}; // ends object initialization (with binding setup)
QmltcMethod completeComponent {}; // calls componentComplete()
QmltcMethod finalizeComponent {}; // calls componentFinalized()
QmltcMethod handleOnCompleted {}; // calls Component.onCompleted
std::optional<QmltcDtor> dtor {};
// member functions: methods, signals and slots
QList<QmltcMethod> functions;
@ -133,6 +152,9 @@ struct QmltcType
// QML document root specific:
std::optional<QmltcVariable> typeCount; // the number of QML types defined in a document
// TODO: only needed for binding callables - should not be needed, generally
bool ignoreInit = false; // specifies whether init and externalCtor should be ignored
};
// Represents whole QML program, compiled to C++