diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2022-01-27 16:29:39 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-01-31 20:45:29 +0000 |
commit | 3342e25c616588779ab80b37b5d91d983b5f9595 (patch) | |
tree | df42994ee342f7b644f2101cfea3767b5bde60fb | |
parent | 9a975279977d0252e1d4031e02eb432ed55f41b4 (diff) |
Improve the structure of the output generated by qmltc
Make an effort to separate user-visible APIs from internal code relevant
to qmltc
In the process of doing it, make tst_qmltc_examples::helloWorld() test
less brittle by using QMap instead of QHash when dumping C++ member
functions of the type. QHash does not guarantee that the keys are
ordered while QMap does (via operator "<")
Change-Id: I1495e1755d3fd77950acb3820ad2b9c5e3cdee33
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit 0ad51325c7432c8a8da38580d26721455252e64f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp | 2 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegenerator.cpp | 12 | ||||
-rw-r--r-- | tools/qmltc/prototype/codegeneratorwriter.cpp | 101 | ||||
-rw-r--r-- | tools/qmltc/prototype/qmlcompiler.h | 1 |
4 files changed, 81 insertions, 35 deletions
diff --git a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp index ca1d438d2c..ddbf3baaaa 100644 --- a/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp +++ b/src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp @@ -67,6 +67,7 @@ class HelloWorld : public QObject Q_OBJECT QML_ELEMENT Q_PROPERTY(QString hello WRITE setHello READ hello BINDABLE bindableHello) + public: HelloWorld(QQmlEngine * engine, QObject * parent = nullptr); @@ -74,7 +75,6 @@ public: void setHello(const QString& hello_); QString hello(); QBindable<QString> bindableHello(); - Q_INVOKABLE void printHello(QString prefix, QString suffix); signals: diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp index f60c8b71f9..ff71db989b 100644 --- a/tools/qmltc/prototype/codegenerator.cpp +++ b/tools/qmltc/prototype/codegenerator.cpp @@ -880,6 +880,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject ¤t, const QQmlJSMetaPr u""_qs); setter.body << variableName + u".setValue(" + name + u"_);"; setter.body << u"emit " + compilationData.notify + u"();"; + setter.userVisible = true; current.functions.emplaceBack(setter); mocPieces << u"WRITE"_qs << setter.name; } @@ -888,6 +889,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject ¤t, const QQmlJSMetaPr getter.returnType = underlyingType; getter.name = compilationData.read; getter.body << u"return " + variableName + u".value();"; + getter.userVisible = true; current.functions.emplaceBack(getter); mocPieces << u"READ"_qs << getter.name; @@ -898,6 +900,7 @@ void CodeGenerator::compileProperty(QQmlJSAotObject ¤t, const QQmlJSMetaPr bindable.name = compilationData.bindable; bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName + u"));"; + bindable.userVisible = true; current.functions.emplaceBack(bindable); mocPieces << u"BINDABLE"_qs << bindable.name; } @@ -1050,6 +1053,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject ¤t, const QQmlJSMetaPrope getter.body += prologue; getter.body << u"return " + info.readLine + u";"; // getter.body += writeSpecificEpilogue; + getter.userVisible = true; current.functions.emplaceBack(getter); mocLines << u"READ"_qs << getter.name; } // else always an error? @@ -1082,6 +1086,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject ¤t, const QQmlJSMetaPrope setter.body << info.writeLine + u";"; } setter.body += writeSpecificEpilogue; + setter.userVisible = true; current.functions.emplaceBack(setter); mocLines << u"WRITE"_qs << setter.name; } @@ -1093,6 +1098,7 @@ void CodeGenerator::compileAlias(QQmlJSAotObject ¤t, const QQmlJSMetaPrope bindable.name = compilationData.bindable; bindable.body += prologue; bindable.body << u"return " + info.bindableLine + u";"; + bindable.userVisible = true; current.functions.emplaceBack(bindable); mocLines << u"BINDABLE"_qs << bindable.name; } @@ -1154,8 +1160,12 @@ void CodeGenerator::compileMethod(QQmlJSAotObject ¤t, const QQmlJSMetaMeth compiled.body = std::move(code); compiled.type = methodType; compiled.access = m.access(); - if (methodType != QQmlJSMetaMethod::Signal) + if (methodType != QQmlJSMetaMethod::Signal) { compiled.declPreambles << u"Q_INVOKABLE"_qs; // TODO: do we need this for signals as well? + compiled.userVisible = m.access() == QQmlJSMetaMethod::Public; + } else { + compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal(); + } current.functions.emplaceBack(compiled); } diff --git a/tools/qmltc/prototype/codegeneratorwriter.cpp b/tools/qmltc/prototype/codegeneratorwriter.cpp index 13b8adb301..d43a94084e 100644 --- a/tools/qmltc/prototype/codegeneratorwriter.cpp +++ b/tools/qmltc/prototype/codegeneratorwriter.cpp @@ -32,6 +32,7 @@ #include <QtCore/qfileinfo.h> #include <utility> +#include <functional> static constexpr char16_t newLine[] = #ifdef Q_OS_WIN32 @@ -176,6 +177,25 @@ static QString classString(const QQmlJSAotObject &compiled) 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 @@ -196,37 +216,61 @@ void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotObject GeneratedCodeUtils::HeaderIndentationScope headerIndentScope(code); Q_UNUSED(headerIndentScope); - // generate ctor - if (compiled.ignoreInit) { // TODO: this branch should be eliminated - // NB: here the ctor should be public - code.appendToHeader(getFunctionCategory(compiled.baselineCtor) + u":", -1); - CodeGeneratorWriter::write(code, compiled.baselineCtor); - } else { - Q_ASSERT(compiled.baselineCtor.access == compiled.init.access); - code.appendToHeader(getFunctionCategory(compiled.init) + u":", -1); - CodeGeneratorWriter::write(code, compiled.baselineCtor); - CodeGeneratorWriter::write(code, compiled.init); + // 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 - // NB: when non-document root, this ctor won't be public - code.appendToHeader(getFunctionCategory(compiled.externalCtor) + u":", -1); CodeGeneratorWriter::write(code, compiled.externalCtor); - code.appendToHeader(u"protected:", -1); - 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 dtor if (compiled.dtor) CodeGeneratorWriter::write(code, *compiled.dtor); // generate enums - code.appendToHeader(u"// BEGIN(enumerations)"); for (const auto &enumeration : qAsConst(compiled.enums)) CodeGeneratorWriter::write(code, enumeration); - code.appendToHeader(u"// END(enumerations)"); + + // 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)"); @@ -235,18 +279,9 @@ void CodeGeneratorWriter::write(GeneratedCodeUtils &code, const QQmlJSAotObject code.appendToHeader(u"// END(children)"); // generate functions - code.appendToHeader(u"// BEGIN(functions)"); - // functions are special as they are grouped by access and kind - QHash<QString, QList<const QQmlJSAotMethod *>> functionsByCategory; - for (const auto &function : qAsConst(compiled.functions)) - functionsByCategory[getFunctionCategory(function)].append(std::addressof(function)); - - for (auto it = functionsByCategory.cbegin(); it != functionsByCategory.cend(); ++it) { - code.appendToHeader(it.key() + u":", -1); - for (const QQmlJSAotMethod *function : qAsConst(it.value())) - CodeGeneratorWriter::write(code, *function); - } - code.appendToHeader(u"// END(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 diff --git a/tools/qmltc/prototype/qmlcompiler.h b/tools/qmltc/prototype/qmlcompiler.h index 4ac515957f..3c770612cc 100644 --- a/tools/qmltc/prototype/qmlcompiler.h +++ b/tools/qmltc/prototype/qmlcompiler.h @@ -122,6 +122,7 @@ struct QQmlJSAotMethodBase 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 |