diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2022-03-03 14:03:58 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2022-03-08 18:35:23 +0100 |
commit | 44b95b3fb26013ff5dab75d4f894ca4f11298e3d (patch) | |
tree | 5a2b58d6de34975f62670ae86d7e507a1a80603a | |
parent | 3c680af4e9164b81548c1c633f3c0a2f0583aea4 (diff) |
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>
-rw-r--r-- | src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp | 6 | ||||
-rw-r--r-- | tools/qmltc/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegenerator.cpp | 115 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegenerator.h | 34 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegeneratorutil.cpp | 15 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegeneratorutil.h | 10 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegeneratorwriter.cpp | 480 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegeneratorwriter.h | 61 | ||||
-rw-r--r-- | tools/qmltc/prototype/generatedcodeprimitives.h | 130 | ||||
-rw-r--r-- | tools/qmltc/prototype/qml2cppcontext.h | 1 | ||||
-rw-r--r-- | tools/qmltc/prototype/qmlcompiler.h | 173 | ||||
-rw-r--r-- | tools/qmltc/qmltccodewriter.cpp | 218 | ||||
-rw-r--r-- | tools/qmltc/qmltccodewriter.h | 2 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 62 | ||||
-rw-r--r-- | tools/qmltc/qmltccompilerpieces.h | 2 | ||||
-rw-r--r-- | tools/qmltc/qmltcoutputir.h | 30 |
16 files changed, 306 insertions, 1036 deletions
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp index ddbf3baaaa..122084f1d8 100644 --- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp +++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp @@ -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] diff --git a/tools/qmltc/CMakeLists.txt b/tools/qmltc/CMakeLists.txt index 8408e4bd84..7e7941539e 100644 --- a/tools/qmltc/CMakeLists.txt +++ b/tools/qmltc/CMakeLists.txt @@ -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 diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp index 83c4b7d94b..cf1bb719ee 100644 --- a/tools/qmltc/prototype/codegenerator.cpp +++ b/tools/qmltc/prototype/codegenerator.cpp @@ -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 ¤t, const CodeGenObject &object) { + const auto compile = [this](QmltcType ¤t, const CodeGenObject &object) { this->compileQQmlComponentElements(current, object); }; compileObject(compiledObjects.back(), m_objects.at(0), compile); } else { - const auto compile = [this](QQmlJSAotObject ¤t, const CodeGenObject &object) { + const auto compile = [this](QmltcType ¤t, 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 ¤t, const QQmlJSMetaEnum &e) +void CodeGenerator::compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e) { const auto intValues = e.values(); QStringList values; @@ -833,7 +833,7 @@ void CodeGenerator::compileEnum(QQmlJSAotObject ¤t, const QQmlJSMetaEnum & u"Q_ENUM(%1)"_qs.arg(e.name())); } -void CodeGenerator::compileProperty(QQmlJSAotObject ¤t, const QQmlJSMetaProperty &p, +void CodeGenerator::compileProperty(QmltcType ¤t, const QQmlJSMetaProperty &p, const QQmlJSScope::ConstPtr &owner) { Q_ASSERT(!p.isAlias()); // will be handled separately @@ -865,10 +865,10 @@ void CodeGenerator::compileProperty(QQmlJSAotObject ¤t, 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 ¤t, 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 ¤t, 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 ¤t, const QQmlJSMetaPr compilationData.notify); } -void CodeGenerator::compileAlias(QQmlJSAotObject ¤t, const QQmlJSMetaProperty &alias, +void CodeGenerator::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias, const QQmlJSScope::ConstPtr &owner) { const QString aliasName = alias.propertyName(); @@ -1040,7 +1040,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject ¤t, 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 ¤t, 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 ¤t, 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 ¤t, 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 ¤t, const QQmlJSMetaPrope } } -void CodeGenerator::compileMethod(QQmlJSAotObject ¤t, const QQmlJSMetaMethod &m, +void CodeGenerator::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, const QmlIR::Function *f, const CodeGenObject &object) { Q_UNUSED(object); @@ -1131,7 +1131,7 @@ void CodeGenerator::compileMethod(QQmlJSAotObject ¤t, 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 ¤t, 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 ¤t, 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 ¤t, const QmlIR::Binding &binding, +void CodeGenerator::compileBinding(QmltcType ¤t, 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 ¤t, const QmlIR::Binding &binding, +void CodeGenerator::compileScriptBinding(QmltcType ¤t, 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 ¤t, 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 ¤t, 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 ¤t, 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 ¤t, const QmlIR:: } // TODO: should use "compileScriptBinding" instead of custom code -void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject ¤t, +void CodeGenerator::compileScriptBindingOfComponent(QmltcType ¤t, const QmlIR::Object *irObject, const QQmlJSScope::ConstPtr objectType, const QmlIR::Binding &binding, @@ -1880,7 +1880,7 @@ void CodeGenerator::compileScriptBindingOfComponent(QQmlJSAotObject ¤t, 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 ¤t, 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; } diff --git a/tools/qmltc/prototype/codegenerator.h b/tools/qmltc/prototype/codegenerator.h index 06cbb3b673..a18699d23c 100644 --- a/tools/qmltc/prototype/codegenerator.h +++ b/tools/qmltc/prototype/codegenerator.h @@ -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 ¤t, const CodeGenObject &object, - std::function<void(QQmlJSAotObject &, const CodeGenObject &)> compileElements); - void compileObjectElements(QQmlJSAotObject ¤t, const CodeGenObject &object); - void compileQQmlComponentElements(QQmlJSAotObject ¤t, const CodeGenObject &object); - - void compileEnum(QQmlJSAotObject ¤t, const QQmlJSMetaEnum &e); - void compileProperty(QQmlJSAotObject ¤t, const QQmlJSMetaProperty &p, + void compileObject(QmltcType ¤t, const CodeGenObject &object, + std::function<void(QmltcType &, const CodeGenObject &)> compileElements); + void compileObjectElements(QmltcType ¤t, const CodeGenObject &object); + void compileQQmlComponentElements(QmltcType ¤t, const CodeGenObject &object); + + void compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e); + void compileProperty(QmltcType ¤t, const QQmlJSMetaProperty &p, const QQmlJSScope::ConstPtr &owner); - void compileAlias(QQmlJSAotObject ¤t, const QQmlJSMetaProperty &alias, + void compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias, const QQmlJSScope::ConstPtr &owner); - void compileMethod(QQmlJSAotObject ¤t, const QQmlJSMetaMethod &m, - const QmlIR::Function *f, const CodeGenObject &object); + void compileMethod(QmltcType ¤t, 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 ¤t, const QmlIR::Binding &binding, + void compileBinding(QmltcType ¤t, const QmlIR::Binding &binding, const CodeGenObject &object, const AccessorData &accessor); // special case (for simplicity) - void compileScriptBinding(QQmlJSAotObject ¤t, const QmlIR::Binding &binding, + void compileScriptBinding(QmltcType ¤t, 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 ¤t, const QmlIR::Object *object, + void compileScriptBindingOfComponent(QmltcType ¤t, const QmlIR::Object *object, const QQmlJSScope::ConstPtr objectType, const QmlIR::Binding &binding, const QString &propertyName); diff --git a/tools/qmltc/prototype/codegeneratorutil.cpp b/tools/qmltc/prototype/codegeneratorutil.cpp index f9473a4114..878a1f4b07 100644 --- a/tools/qmltc/prototype/codegeneratorutil.cpp +++ b/tools/qmltc/prototype/codegeneratorutil.cpp @@ -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> ¶meters) + const QList<QmltcVariable> ¶meters) { 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> ¶ms, +QString CodeGeneratorUtility::generate_qOverload(const QList<QmltcVariable> ¶ms, 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")"; } diff --git a/tools/qmltc/prototype/codegeneratorutil.h b/tools/qmltc/prototype/codegeneratorutil.h index 563896637d..4f5518aeed 100644 --- a/tools/qmltc/prototype/codegeneratorutil.h +++ b/tools/qmltc/prototype/codegeneratorutil.h @@ -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> ¶meters = {}); + const QList<QmltcVariable> ¶meters = {}); 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> ¶meters, + static QString generate_qOverload(const QList<QmltcVariable> ¶meters, const QString &overloaded); static QString generate_addressof(const QString &addressed); static QString generate_getPrivateClass(const QString &accessor, const QQmlJSMetaProperty &p); diff --git a/tools/qmltc/prototype/codegeneratorwriter.cpp b/tools/qmltc/prototype/codegeneratorwriter.cpp deleted file mode 100644 index a932012b63..0000000000 --- a/tools/qmltc/prototype/codegeneratorwriter.cpp +++ /dev/null @@ -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> ¶meterList = 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 diff --git a/tools/qmltc/prototype/codegeneratorwriter.h b/tools/qmltc/prototype/codegeneratorwriter.h deleted file mode 100644 index d33a8affd4..0000000000 --- a/tools/qmltc/prototype/codegeneratorwriter.h +++ /dev/null @@ -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 diff --git a/tools/qmltc/prototype/generatedcodeprimitives.h b/tools/qmltc/prototype/generatedcodeprimitives.h deleted file mode 100644 index 72007caae3..0000000000 --- a/tools/qmltc/prototype/generatedcodeprimitives.h +++ /dev/null @@ -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 diff --git a/tools/qmltc/prototype/qml2cppcontext.h b/tools/qmltc/prototype/qml2cppcontext.h index a8b6aa7c90..3c17a632df 100644 --- a/tools/qmltc/prototype/qml2cppcontext.h +++ b/tools/qmltc/prototype/qml2cppcontext.h @@ -29,7 +29,6 @@ #ifndef QML2CPPCONTEXT_H #define QML2CPPCONTEXT_H -#include "prototype/qmlcompiler.h" #include "qmltctyperesolver.h" #include <private/qqmljsdiagnosticmessage_p.h> diff --git a/tools/qmltc/prototype/qmlcompiler.h b/tools/qmltc/prototype/qmlcompiler.h deleted file mode 100644 index 60174bdab1..0000000000 --- a/tools/qmltc/prototype/qmlcompiler.h +++ /dev/null @@ -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 diff --git a/tools/qmltc/qmltccodewriter.cpp b/tools/qmltc/qmltccodewriter.cpp index 6c2c018833..67e3c7f06b 100644 --- a/tools/qmltc/qmltccodewriter.cpp +++ b/tools/qmltc/qmltccodewriter.cpp @@ -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 - // enums - if (!type.enums.isEmpty()) { - code.rawAppendToHeader(u""); // blank line - code.rawAppendToHeader(u"public:", -1); + 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 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); } diff --git a/tools/qmltc/qmltccodewriter.h b/tools/qmltc/qmltccodewriter.h index 1d1e023d43..824a3f97db 100644 --- a/tools/qmltc/qmltccodewriter.h +++ b/tools/qmltc/qmltccodewriter.h @@ -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 }; diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp index 92007d690a..2b69a81b85 100644 --- a/tools/qmltc/qmltccompiler.cpp +++ b/tools/qmltc/qmltccompiler.cpp @@ -115,7 +115,7 @@ void QmltcCompiler::compileType(QmltcType ¤t, 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 ¤t, 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 ¤t, 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 diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h index e0c587b5f3..d78be5a652 100644 --- a/tools/qmltc/qmltccompilerpieces.h +++ b/tools/qmltc/qmltccompilerpieces.h @@ -147,7 +147,7 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType ¤t, 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; diff --git a/tools/qmltc/qmltcoutputir.h b/tools/qmltc/qmltcoutputir.h index 9d000cbc11..0a3e7617bd 100644 --- a/tools/qmltc/qmltcoutputir.h +++ b/tools/qmltc/qmltcoutputir.h @@ -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++ |