2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
#include "qmltccodewriter.h"
|
|
|
|
|
|
|
|
#include <QtCore/qfileinfo.h>
|
|
|
|
#include <QtCore/qstringbuilder.h>
|
2022-03-10 22:04:21 +00:00
|
|
|
#include <QtCore/qstring.h>
|
|
|
|
#include <QtCore/qmap.h>
|
|
|
|
#include <QtCore/qlist.h>
|
2021-09-17 15:01:56 +00:00
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
#include <utility>
|
|
|
|
|
2021-09-17 15:01:56 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
2021-09-17 15:01:56 +00:00
|
|
|
static QString urlToMacro(const QString &url)
|
|
|
|
{
|
|
|
|
QFileInfo fi(url);
|
|
|
|
return u"Q_QMLTC_" + fi.baseName().toUpper();
|
|
|
|
}
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
static QString getFunctionCategory(const QmltcMethodBase &method)
|
|
|
|
{
|
|
|
|
QString category;
|
|
|
|
switch (method.access) {
|
|
|
|
case QQmlJSMetaMethod::Private:
|
2022-03-21 09:21:18 +00:00
|
|
|
category = u"private"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
|
|
|
case QQmlJSMetaMethod::Protected:
|
2022-03-21 09:21:18 +00:00
|
|
|
category = u"protected"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
|
|
|
case QQmlJSMetaMethod::Public:
|
2022-03-21 09:21:18 +00:00
|
|
|
category = u"public"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return category;
|
|
|
|
}
|
|
|
|
|
|
|
|
static QString getFunctionCategory(const QmltcMethod &method)
|
|
|
|
{
|
|
|
|
QString category = getFunctionCategory(static_cast<const QmltcMethodBase &>(method));
|
|
|
|
switch (method.type) {
|
2023-05-05 07:30:27 +00:00
|
|
|
case QQmlJSMetaMethodType::Signal:
|
2022-03-21 09:21:18 +00:00
|
|
|
category = u"Q_SIGNALS"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
2023-05-05 07:30:27 +00:00
|
|
|
case QQmlJSMetaMethodType::Slot:
|
2022-03-21 09:21:18 +00:00
|
|
|
category += u" Q_SLOTS"_s;
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
2023-05-05 07:30:27 +00:00
|
|
|
case QQmlJSMetaMethodType::Method:
|
|
|
|
case QQmlJSMetaMethodType::StaticMethod:
|
2021-09-21 08:52:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return category;
|
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &method)
|
|
|
|
{
|
|
|
|
const QString name = method.name;
|
|
|
|
const QList<QmltcVariable> ¶meterList = method.parameterList;
|
|
|
|
|
|
|
|
QStringList headerParamList;
|
|
|
|
QStringList cppParamList;
|
|
|
|
for (const QmltcVariable &variable : parameterList) {
|
|
|
|
const QString commonPart = variable.cppType + u" " + variable.name;
|
|
|
|
cppParamList << commonPart;
|
|
|
|
headerParamList << commonPart;
|
|
|
|
if (!variable.defaultValue.isEmpty())
|
|
|
|
headerParamList.back() += u" = " + variable.defaultValue;
|
|
|
|
}
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString headerSignature = name + u"(" + headerParamList.join(u", "_s) + u")"
|
2022-03-03 13:03:58 +00:00
|
|
|
+ prependSpace(method.modifiers.join(u" "));
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString cppSignature = name + u"(" + cppParamList.join(u", "_s) + u")"
|
2022-03-03 13:03:58 +00:00
|
|
|
+ prependSpace(method.modifiers.join(u" "));
|
2021-09-21 08:52:24 +00:00
|
|
|
return { headerSignature, cppSignature };
|
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
static QString functionReturnType(const QmltcMethod &m)
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
return appendSpace(m.declarationPrefixes.join(u" "_s)) + m.returnType;
|
2022-03-03 13:03:58 +00:00
|
|
|
}
|
|
|
|
|
2021-09-17 15:01:56 +00:00
|
|
|
void QmltcCodeWriter::writeGlobalHeader(QmltcOutputWrapper &code, const QString &sourcePath,
|
|
|
|
const QString &hPath, const QString &cppPath,
|
2021-10-20 09:14:52 +00:00
|
|
|
const QString &outNamespace,
|
2021-09-17 15:01:56 +00:00
|
|
|
const QSet<QString> &requiredCppIncludes)
|
|
|
|
{
|
|
|
|
Q_UNUSED(cppPath);
|
|
|
|
const QString preamble = u"// This code is auto-generated by the qmltc tool from the file '"
|
|
|
|
+ sourcePath + u"'\n// WARNING! All changes made in this file will be lost!\n";
|
|
|
|
code.rawAppendToHeader(preamble);
|
|
|
|
code.rawAppendToCpp(preamble);
|
|
|
|
code.rawAppendToHeader(
|
|
|
|
u"// NOTE: This generated API is to be considered implementation detail.");
|
|
|
|
code.rawAppendToHeader(
|
|
|
|
u"// It may change from version to version and should not be relied upon.");
|
|
|
|
|
|
|
|
const QString headerMacro = urlToMacro(sourcePath);
|
2022-03-21 09:21:18 +00:00
|
|
|
code.rawAppendToHeader(u"#ifndef %1_H"_s.arg(headerMacro));
|
|
|
|
code.rawAppendToHeader(u"#define %1_H"_s.arg(headerMacro));
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
code.rawAppendToHeader(u"#include <QtCore/qproperty.h>");
|
|
|
|
code.rawAppendToHeader(u"#include <QtCore/qobject.h>");
|
|
|
|
code.rawAppendToHeader(u"#include <QtCore/qcoreapplication.h>");
|
qmltc: Allow setting initial values when creating a compiled type
`qmltc`-generated types currently do not allow setting any initial
values for the property of a component during creation.
For example, some component `Foo` with a property `bar`, will have no
way to set `bar` to a specific value from the C++ side of the code
before an instance of `Foo` is obtained by the user.
This lack of control prohibits the user from interacting with certain
processes that are part of the component creation.
For example, if a component provides am `onCompleted` binding that
depends on some of the values of its properties, the user is inhibited
from varying the per-instance values that `onCompleted` depends on, as
the user would be able to vary those values only after the component is
created and the `onCompleted` signal is emitted.
This differs, from example, from the `QQmlComponent` interface, where
the user is able to provide some initialization values as part of
the creation of an instance of the component.
To allow the user to have more control in the initialization of the
instance of a component, before it is fully created, `qmltc` generated
code now allows the user to provide an initialization callback that is
processed as part of the creation cycle of an instance of the component.
The callback provides the user with a generated proxy object,
`PropertyInitializer`, that only knows how to set the writable,
non-private properties of the component.
The generated code for the public constructor of a `qmltc`-generated
type was modified to provide an optional `initializer` parameter that
stores the callback.
The private `QML_init` method that `qmltc` generates for each type, that
performs the required setup to create an instance of a component, was
modified to allow for the same optional parameter, which is passed on by
the public constructor.
The body of `QML_init` was modified to call the new parameter, after
having performed the general set-up for the created instance but before
the instance is completed and before setting up "complex bindings" such
as an `onPropertyChanged` handler.
The callback is called with an instance of the generated proxy object
that is built on-site.
The proxy-object keeps track of the properties that were actually
initialized by the callback. This information is now passed down to
`QML_setComplexBindings`, which avoids setting up any unnecessary
bindings for the properties that were initialized.
A representation for the proxy object was added to the internal IR that
is used by `qmltc` when generating code.
The representation for a compiled C++ type was modified to store an
instance of the proxy object.
The newly stored instance is now populated as part of the general
compilation of a type, by the `compilePropertyInitializer` free-function
in "qmltccompiler.cpp".
The component responsible for the final code-generation,
`QmltcCodeWriter`, was modified to be able to generate code for the new
proxy object representation.
The documentation for `QmltcCodeGenerator::generate_initCode`, which
sets up the body for `QML_init`, was updated to reflect the new body.
A pre-existing issue in the documentation of the method, which failed to
enumerate all generated steps for the body, was fixed as part of the
change.
The preamble that `qmltc` generates for all generated header files
was modified to include "QtCore/qxpfunction.h", to have access to
`qxp::function_ref`, which is used to store the new callback parameter.
A pre-existing snapshot test had its snapshot file,
"src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp", updated to
reflect the changes to the generated code.
A new basic, non-exhaustive test case was added to the
available corpus of `qmltc` tests to test the basic workflow of
providing an initialization callback.
The documentation for `qmltc` was modified to mention the new parameter.
Task-number: QTBUG-120700
Change-Id: I246c1c3634982580d66b31fd891382559a9cc3ae
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-03-11 15:33:35 +00:00
|
|
|
code.rawAppendToHeader(u"#include <QtCore/qxpfunctional.h>");
|
2021-09-17 15:01:56 +00:00
|
|
|
code.rawAppendToHeader(u"#include <QtQml/qqmlengine.h>");
|
|
|
|
code.rawAppendToHeader(u"#include <QtCore/qurl.h>"); // used in engine execution
|
|
|
|
code.rawAppendToHeader(u"#include <QtQml/qqml.h>"); // used for attached properties
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"#include <private/qqmlengine_p.h>"); // executeRuntimeFunction(), etc.
|
2021-11-19 12:04:05 +00:00
|
|
|
code.rawAppendToHeader(u"#include <private/qqmltcobjectcreationhelper_p.h>"); // QmltcSupportLib
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
code.rawAppendToHeader(u"#include <QtQml/qqmllist.h>"); // QQmlListProperty
|
|
|
|
|
|
|
|
// include custom C++ includes required by used types
|
|
|
|
code.rawAppendToHeader(u"// BEGIN(custom_cpp_includes)");
|
|
|
|
for (const auto &requiredInclude : requiredCppIncludes)
|
|
|
|
code.rawAppendToHeader(u"#include \"" + requiredInclude + u"\"");
|
|
|
|
code.rawAppendToHeader(u"// END(custom_cpp_includes)");
|
|
|
|
|
|
|
|
code.rawAppendToCpp(u"#include \"" + hPath + u"\""); // include own .h file
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToCpp(u"// qmltc support library:");
|
|
|
|
code.rawAppendToCpp(u"#include <private/qqmlcppbinding_p.h>"); // QmltcSupportLib
|
|
|
|
code.rawAppendToCpp(u"#include <private/qqmlcpponassignment_p.h>"); // QmltcSupportLib
|
2022-10-12 13:56:00 +00:00
|
|
|
code.rawAppendToHeader(u"#include <private/qqmlcpptypehelpers_p.h> "); // QmltcSupportLib
|
2021-09-17 15:01:56 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // createComponent()
|
|
|
|
code.rawAppendToCpp(u"#include <private/qqmlcomponent_p.h>"); // QQmlComponentPrivate::get()
|
|
|
|
|
|
|
|
code.rawAppendToCpp(u"");
|
2021-09-17 15:01:56 +00:00
|
|
|
code.rawAppendToCpp(u"#include <private/qobject_p.h>"); // NB: for private properties
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToCpp(u"#include <private/qqmlobjectcreator_p.h>"); // for finalize callbacks
|
2022-04-28 09:03:09 +00:00
|
|
|
code.rawAppendToCpp(u"#include <QtQml/qqmlprivate.h>"); // QQmlPrivate::qmlExtendedObject()
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
code.rawAppendToCpp(u""); // blank line
|
2021-10-22 10:02:37 +00:00
|
|
|
code.rawAppendToCpp(u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE");
|
2022-12-21 15:31:20 +00:00
|
|
|
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
|
|
|
|
const QStringList namespaces = outNamespace.split(u"::"_s);
|
|
|
|
|
|
|
|
for (const QString ¤tNamespace : namespaces) {
|
|
|
|
code.rawAppendToHeader(u"namespace %1 {"_s.arg(currentNamespace));
|
|
|
|
code.rawAppendToCpp(u"namespace %1 {"_s.arg(currentNamespace));
|
2021-10-20 09:14:52 +00:00
|
|
|
}
|
2021-09-17 15:01:56 +00:00
|
|
|
}
|
|
|
|
|
qmltc: Allow setting initial values when creating a compiled type
`qmltc`-generated types currently do not allow setting any initial
values for the property of a component during creation.
For example, some component `Foo` with a property `bar`, will have no
way to set `bar` to a specific value from the C++ side of the code
before an instance of `Foo` is obtained by the user.
This lack of control prohibits the user from interacting with certain
processes that are part of the component creation.
For example, if a component provides am `onCompleted` binding that
depends on some of the values of its properties, the user is inhibited
from varying the per-instance values that `onCompleted` depends on, as
the user would be able to vary those values only after the component is
created and the `onCompleted` signal is emitted.
This differs, from example, from the `QQmlComponent` interface, where
the user is able to provide some initialization values as part of
the creation of an instance of the component.
To allow the user to have more control in the initialization of the
instance of a component, before it is fully created, `qmltc` generated
code now allows the user to provide an initialization callback that is
processed as part of the creation cycle of an instance of the component.
The callback provides the user with a generated proxy object,
`PropertyInitializer`, that only knows how to set the writable,
non-private properties of the component.
The generated code for the public constructor of a `qmltc`-generated
type was modified to provide an optional `initializer` parameter that
stores the callback.
The private `QML_init` method that `qmltc` generates for each type, that
performs the required setup to create an instance of a component, was
modified to allow for the same optional parameter, which is passed on by
the public constructor.
The body of `QML_init` was modified to call the new parameter, after
having performed the general set-up for the created instance but before
the instance is completed and before setting up "complex bindings" such
as an `onPropertyChanged` handler.
The callback is called with an instance of the generated proxy object
that is built on-site.
The proxy-object keeps track of the properties that were actually
initialized by the callback. This information is now passed down to
`QML_setComplexBindings`, which avoids setting up any unnecessary
bindings for the properties that were initialized.
A representation for the proxy object was added to the internal IR that
is used by `qmltc` when generating code.
The representation for a compiled C++ type was modified to store an
instance of the proxy object.
The newly stored instance is now populated as part of the general
compilation of a type, by the `compilePropertyInitializer` free-function
in "qmltccompiler.cpp".
The component responsible for the final code-generation,
`QmltcCodeWriter`, was modified to be able to generate code for the new
proxy object representation.
The documentation for `QmltcCodeGenerator::generate_initCode`, which
sets up the body for `QML_init`, was updated to reflect the new body.
A pre-existing issue in the documentation of the method, which failed to
enumerate all generated steps for the body, was fixed as part of the
change.
The preamble that `qmltc` generates for all generated header files
was modified to include "QtCore/qxpfunction.h", to have access to
`qxp::function_ref`, which is used to store the new callback parameter.
A pre-existing snapshot test had its snapshot file,
"src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp", updated to
reflect the changes to the generated code.
A new basic, non-exhaustive test case was added to the
available corpus of `qmltc` tests to test the basic workflow of
providing an initialization callback.
The documentation for `qmltc` was modified to mention the new parameter.
Task-number: QTBUG-120700
Change-Id: I246c1c3634982580d66b31fd891382559a9cc3ae
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-03-11 15:33:35 +00:00
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code,
|
|
|
|
const QmltcPropertyInitializer &propertyInitializer,
|
|
|
|
const QmltcType &wrappedType)
|
|
|
|
{
|
|
|
|
code.rawAppendToHeader(u"class " + propertyInitializer.name + u" {");
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
[[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"friend class " + wrappedType.cppType + u";");
|
|
|
|
}
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"public:"_s);
|
|
|
|
|
|
|
|
[[maybe_unused]] QmltcOutputWrapper::MemberNameScope typeScope(&code, propertyInitializer.name);
|
|
|
|
{
|
|
|
|
[[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
|
|
|
|
|
|
|
write(code, propertyInitializer.constructor);
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
|
|
|
|
for (const auto &propertySetter : propertyInitializer.propertySetters) {
|
|
|
|
write(code, propertySetter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
code.rawAppendToHeader(u"private:"_s);
|
|
|
|
|
|
|
|
{
|
|
|
|
[[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
|
|
|
|
|
|
|
write(code, propertyInitializer.component);
|
|
|
|
write(code, propertyInitializer.initializedCache);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"};"_s);
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
}
|
|
|
|
|
qmltc: Enforce basic required properties
Types in QML can expose "required" properties.
A required property is a property that should be initialized when an
instance of the type is built.
Instantiating a type without initializing its required properties is an
hard error.
Currently, `qmltc` generated types do not respect the semantic of
"required" properties.
Instead, `qmltc` will generally default-initialize a required property
without notifying the user of the error.
`qmtlc`, so as to respect the semantic of required properties, will now
require the user to pass an initial value for all required properties at
construction time.
To do so, `qmltc` will now generate a new inner record,
`RequiredPropertiesBundle`, for each compiled top-level type, that
contains the required amount of data to initialize each top-level
required property that is reachable from the compiled type.
An instance of `RequiredPropertiesBundle` will be required, as long as
the type presents at least one required property, in the user-facing
constructor for the generated type.
The information stored in the instance will later be used to provide an
initial value for each required property during the construction of the
component.
An intermediate representation for `RequiredPropertiesBundle` was added
to "qmltcoutputir.h".
`QmltcCodeWriter`, the component responsible for writing the final C++
code, was modified to take into consideration the presence, or lack
thereof, of a `RequiredPropertiesBundle` and output the necessary code
when required.
The code taking care of populating the various IRs was modified to
populate a `RequiredPropertiesBundle` for top-level components as
necessary.
Similarly, the code populating the parameters of the user-facing
constructor was modified to require an instance of
`RequiredPropertiesBundle` when necessary.
The code that populates the body of the user-facing constructor for
top-level types was modified to make use of the parameter by tying into
the existing structure for setting initial values.
`qmltc` uses a user-provided callback to allow the user to set the
initial values for properties when constructing a top-level component.
The body of the user-facing constructor was modified to compose the
user-provided callback with a callable that sets the initial values for
top-level required properties based on the bundle of data in the new
`RequiredPropertiesBundle` instance.
The code that populates the body of the user-facing constructor was
moved into its own free-function, `compileRootExternalConstructorBody`,
to be slightly more explicit about the structure of the code.
A new test was provided to evaluate, some basic cases for the new
behavior. Some pre-existing tests, which made use of required
properties, were modified to comply with the new generated API.
The documentation for `qmltc` was modified with a note about the new
behavior.
Task-number: QTBUG-120698
Change-Id: I1e916dcd91ae976629dad8adc7eacc6390bce7e9
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2024-03-25 12:13:28 +00:00
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle)
|
|
|
|
{
|
|
|
|
code.rawAppendToHeader(u"struct " + requiredPropertiesBundle.name + u" {");
|
|
|
|
|
|
|
|
{
|
|
|
|
[[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
|
|
|
|
|
|
|
for (const auto &member : requiredPropertiesBundle.members) {
|
|
|
|
write(code, member);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"};"_s);
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
}
|
|
|
|
|
2021-10-20 09:14:52 +00:00
|
|
|
void QmltcCodeWriter::writeGlobalFooter(QmltcOutputWrapper &code, const QString &sourcePath,
|
|
|
|
const QString &outNamespace)
|
2021-09-17 15:01:56 +00:00
|
|
|
{
|
2022-12-21 15:31:20 +00:00
|
|
|
const QStringList namespaces = outNamespace.split(u"::"_s);
|
|
|
|
|
|
|
|
for (auto it = namespaces.crbegin(), end = namespaces.crend(); it != end; it++) {
|
|
|
|
code.rawAppendToCpp(u"} // namespace %1"_s.arg(*it));
|
|
|
|
code.rawAppendToHeader(u"} // namespace %1"_s.arg(*it));
|
2021-10-20 09:14:52 +00:00
|
|
|
}
|
2021-09-17 15:01:56 +00:00
|
|
|
|
2022-12-21 15:31:20 +00:00
|
|
|
code.rawAppendToHeader(u""); // blank line
|
2022-03-21 09:21:18 +00:00
|
|
|
code.rawAppendToHeader(u"#endif // %1_H"_s.arg(urlToMacro(sourcePath)));
|
2021-09-17 15:01:56 +00:00
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
}
|
|
|
|
|
|
|
|
static void writeToFile(const QString &path, const QByteArray &data)
|
|
|
|
{
|
|
|
|
// When not using dependency files, changing a single qml invalidates all
|
|
|
|
// qml files and would force the recompilation of everything. To avoid that,
|
|
|
|
// we check if the data is equal to the existing file, if yes, don't touch
|
|
|
|
// it so the build system will not recompile unnecessary things.
|
|
|
|
//
|
|
|
|
// If the build system use dependency file, we should anyway touch the file
|
|
|
|
// so qmltc is not re-run
|
|
|
|
QFileInfo fi(path);
|
|
|
|
if (fi.exists() && fi.size() == data.size()) {
|
|
|
|
QFile oldFile(path);
|
2024-03-23 12:44:46 +00:00
|
|
|
if (oldFile.open(QIODevice::ReadOnly)) {
|
|
|
|
if (oldFile.readAll() == data)
|
|
|
|
return;
|
|
|
|
}
|
2021-09-17 15:01:56 +00:00
|
|
|
}
|
|
|
|
QFile file(path);
|
2024-03-23 12:44:46 +00:00
|
|
|
if (!file.open(QIODevice::WriteOnly))
|
|
|
|
qFatal("Could not open file %s", qPrintable(path));
|
2021-09-17 15:01:56 +00:00
|
|
|
file.write(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &program)
|
|
|
|
{
|
2021-10-20 09:14:52 +00:00
|
|
|
writeGlobalHeader(code, program.url, program.hPath, program.cppPath, program.outNamespace,
|
|
|
|
program.includes);
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2021-11-12 08:28:33 +00:00
|
|
|
// url method comes first
|
|
|
|
writeUrl(code, program.urlMethod);
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
// forward declare all the types first
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QmltcType &type : std::as_const(program.compiledTypes))
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToHeader(u"class " + type.cppType + u";");
|
|
|
|
// write all the types and their content
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QmltcType &type : std::as_const(program.compiledTypes))
|
2022-12-22 16:24:16 +00:00
|
|
|
write(code, type, program.exportMacro);
|
2021-11-19 12:04:05 +00:00
|
|
|
|
|
|
|
// add typeCount definitions. after all types have been written down (so
|
|
|
|
// they are now complete types as per C++). practically, this only concerns
|
|
|
|
// document root type
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QmltcType &type : std::as_const(program.compiledTypes)) {
|
2021-11-19 12:04:05 +00:00
|
|
|
if (!type.typeCount)
|
|
|
|
continue;
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
2022-03-21 09:21:18 +00:00
|
|
|
code.rawAppendToHeader(u"constexpr %1 %2::%3()"_s.arg(type.typeCount->returnType,
|
|
|
|
type.cppType, type.typeCount->name));
|
2021-11-19 12:04:05 +00:00
|
|
|
code.rawAppendToHeader(u"{");
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &line : std::as_const(type.typeCount->body))
|
2021-11-19 12:04:05 +00:00
|
|
|
code.rawAppendToHeader(line, 1);
|
|
|
|
code.rawAppendToHeader(u"}");
|
|
|
|
}
|
|
|
|
|
2021-10-20 09:14:52 +00:00
|
|
|
writeGlobalFooter(code, program.url, program.outNamespace);
|
2021-09-17 15:01:56 +00:00
|
|
|
|
|
|
|
writeToFile(program.hPath, code.code().header.toUtf8());
|
|
|
|
writeToFile(program.cppPath, code.code().cpp.toUtf8());
|
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
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);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QmltcMethod *function : std::as_const(it.value()))
|
2022-03-03 13:03:58 +00:00
|
|
|
QmltcCodeWriter::write(code, *function);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-22 16:24:16 +00:00
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type,
|
|
|
|
const QString &exportMacro)
|
2021-09-21 08:52:24 +00:00
|
|
|
{
|
|
|
|
const auto constructClassString = [&]() {
|
2022-12-22 16:24:16 +00:00
|
|
|
QString str = u"class "_s;
|
|
|
|
if (!exportMacro.isEmpty())
|
|
|
|
str.append(exportMacro).append(u" "_s);
|
|
|
|
str.append(type.cppType);
|
2022-03-03 13:03:58 +00:00
|
|
|
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())
|
2022-03-21 09:21:18 +00:00
|
|
|
str += u" : public " + nonEmptyBaseClasses.join(u", public "_s);
|
2021-09-21 08:52:24 +00:00
|
|
|
return str;
|
|
|
|
};
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
code.rawAppendToCpp(u""); // blank line
|
|
|
|
|
|
|
|
code.rawAppendToHeader(constructClassString());
|
|
|
|
code.rawAppendToHeader(u"{");
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &mocLine : std::as_const(type.mocCode))
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToHeader(mocLine, 1);
|
|
|
|
|
|
|
|
QmltcOutputWrapper::MemberNameScope typeScope(&code, type.cppType);
|
|
|
|
Q_UNUSED(typeScope);
|
|
|
|
{
|
|
|
|
QmltcOutputWrapper::HeaderIndentationScope headerIndent(&code);
|
|
|
|
Q_UNUSED(headerIndent);
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
// 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
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToHeader(u"/* ----------------- */");
|
|
|
|
code.rawAppendToHeader(u"/* External C++ API */");
|
|
|
|
code.rawAppendToHeader(u"public:", -1);
|
|
|
|
|
qmltc: Allow setting initial values when creating a compiled type
`qmltc`-generated types currently do not allow setting any initial
values for the property of a component during creation.
For example, some component `Foo` with a property `bar`, will have no
way to set `bar` to a specific value from the C++ side of the code
before an instance of `Foo` is obtained by the user.
This lack of control prohibits the user from interacting with certain
processes that are part of the component creation.
For example, if a component provides am `onCompleted` binding that
depends on some of the values of its properties, the user is inhibited
from varying the per-instance values that `onCompleted` depends on, as
the user would be able to vary those values only after the component is
created and the `onCompleted` signal is emitted.
This differs, from example, from the `QQmlComponent` interface, where
the user is able to provide some initialization values as part of
the creation of an instance of the component.
To allow the user to have more control in the initialization of the
instance of a component, before it is fully created, `qmltc` generated
code now allows the user to provide an initialization callback that is
processed as part of the creation cycle of an instance of the component.
The callback provides the user with a generated proxy object,
`PropertyInitializer`, that only knows how to set the writable,
non-private properties of the component.
The generated code for the public constructor of a `qmltc`-generated
type was modified to provide an optional `initializer` parameter that
stores the callback.
The private `QML_init` method that `qmltc` generates for each type, that
performs the required setup to create an instance of a component, was
modified to allow for the same optional parameter, which is passed on by
the public constructor.
The body of `QML_init` was modified to call the new parameter, after
having performed the general set-up for the created instance but before
the instance is completed and before setting up "complex bindings" such
as an `onPropertyChanged` handler.
The callback is called with an instance of the generated proxy object
that is built on-site.
The proxy-object keeps track of the properties that were actually
initialized by the callback. This information is now passed down to
`QML_setComplexBindings`, which avoids setting up any unnecessary
bindings for the properties that were initialized.
A representation for the proxy object was added to the internal IR that
is used by `qmltc` when generating code.
The representation for a compiled C++ type was modified to store an
instance of the proxy object.
The newly stored instance is now populated as part of the general
compilation of a type, by the `compilePropertyInitializer` free-function
in "qmltccompiler.cpp".
The component responsible for the final code-generation,
`QmltcCodeWriter`, was modified to be able to generate code for the new
proxy object representation.
The documentation for `QmltcCodeGenerator::generate_initCode`, which
sets up the body for `QML_init`, was updated to reflect the new body.
A pre-existing issue in the documentation of the method, which failed to
enumerate all generated steps for the body, was fixed as part of the
change.
The preamble that `qmltc` generates for all generated header files
was modified to include "QtCore/qxpfunction.h", to have access to
`qxp::function_ref`, which is used to store the new callback parameter.
A pre-existing snapshot test had its snapshot file,
"src/qml/doc/snippets/qmltc/special/HelloWorld.qml.cpp", updated to
reflect the changes to the generated code.
A new basic, non-exhaustive test case was added to the
available corpus of `qmltc` tests to test the basic workflow of
providing an initialization callback.
The documentation for `qmltc` was modified to mention the new parameter.
Task-number: QTBUG-120700
Change-Id: I246c1c3634982580d66b31fd891382559a9cc3ae
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2024-03-11 15:33:35 +00:00
|
|
|
if (!type.propertyInitializer.name.isEmpty())
|
|
|
|
write(code, type.propertyInitializer, type);
|
|
|
|
|
qmltc: Enforce basic required properties
Types in QML can expose "required" properties.
A required property is a property that should be initialized when an
instance of the type is built.
Instantiating a type without initializing its required properties is an
hard error.
Currently, `qmltc` generated types do not respect the semantic of
"required" properties.
Instead, `qmltc` will generally default-initialize a required property
without notifying the user of the error.
`qmtlc`, so as to respect the semantic of required properties, will now
require the user to pass an initial value for all required properties at
construction time.
To do so, `qmltc` will now generate a new inner record,
`RequiredPropertiesBundle`, for each compiled top-level type, that
contains the required amount of data to initialize each top-level
required property that is reachable from the compiled type.
An instance of `RequiredPropertiesBundle` will be required, as long as
the type presents at least one required property, in the user-facing
constructor for the generated type.
The information stored in the instance will later be used to provide an
initial value for each required property during the construction of the
component.
An intermediate representation for `RequiredPropertiesBundle` was added
to "qmltcoutputir.h".
`QmltcCodeWriter`, the component responsible for writing the final C++
code, was modified to take into consideration the presence, or lack
thereof, of a `RequiredPropertiesBundle` and output the necessary code
when required.
The code taking care of populating the various IRs was modified to
populate a `RequiredPropertiesBundle` for top-level components as
necessary.
Similarly, the code populating the parameters of the user-facing
constructor was modified to require an instance of
`RequiredPropertiesBundle` when necessary.
The code that populates the body of the user-facing constructor for
top-level types was modified to make use of the parameter by tying into
the existing structure for setting initial values.
`qmltc` uses a user-provided callback to allow the user to set the
initial values for properties when constructing a top-level component.
The body of the user-facing constructor was modified to compose the
user-provided callback with a callable that sets the initial values for
top-level required properties based on the bundle of data in the new
`RequiredPropertiesBundle` instance.
The code that populates the body of the user-facing constructor was
moved into its own free-function, `compileRootExternalConstructorBody`,
to be slightly more explicit about the structure of the code.
A new test was provided to evaluate, some basic cases for the new
behavior. Some pre-existing tests, which made use of required
properties, were modified to comply with the new generated API.
The documentation for `qmltc` was modified with a note about the new
behavior.
Task-number: QTBUG-120698
Change-Id: I1e916dcd91ae976629dad8adc7eacc6390bce7e9
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
2024-03-25 12:13:28 +00:00
|
|
|
if (type.requiredPropertiesBundle)
|
|
|
|
write(code, *type.requiredPropertiesBundle);
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
// 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);
|
2022-09-28 18:04:44 +00:00
|
|
|
if (type.staticCreate)
|
|
|
|
QmltcCodeWriter::write(code, *type.staticCreate);
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
2022-03-03 13:03:58 +00:00
|
|
|
|
|
|
|
// dtor
|
|
|
|
if (type.dtor)
|
|
|
|
QmltcCodeWriter::write(code, *type.dtor);
|
|
|
|
|
|
|
|
// enums
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const auto &enumeration : std::as_const(type.enums))
|
2022-03-03 13:03:58 +00:00
|
|
|
QmltcCodeWriter::write(code, enumeration);
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
// visible functions
|
|
|
|
const auto isUserVisibleFunction = [](const QmltcMethod &function) {
|
|
|
|
return function.userVisible;
|
|
|
|
};
|
|
|
|
dumpFunctions(code, type.functions, isUserVisibleFunction);
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToHeader(u"/* ----------------- */");
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
code.rawAppendToHeader(u"/* Internal functionality (do NOT use it!) */");
|
2021-09-21 08:52:24 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
// 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);
|
2022-07-27 15:03:44 +00:00
|
|
|
QmltcCodeWriter::write(code, type.setComplexBindings);
|
2021-11-19 12:04:05 +00:00
|
|
|
QmltcCodeWriter::write(code, type.beginClass);
|
2022-03-03 13:03:58 +00:00
|
|
|
QmltcCodeWriter::write(code, type.completeComponent);
|
|
|
|
QmltcCodeWriter::write(code, type.finalizeComponent);
|
|
|
|
QmltcCodeWriter::write(code, type.handleOnCompleted);
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
2021-10-29 09:56:25 +00:00
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
// children
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const auto &child : std::as_const(type.children))
|
2022-12-22 16:24:16 +00:00
|
|
|
QmltcCodeWriter::write(code, child, exportMacro);
|
2022-03-03 13:03:58 +00:00
|
|
|
|
|
|
|
// (non-visible) functions
|
|
|
|
dumpFunctions(code, type.functions, std::not_fn(isUserVisibleFunction));
|
|
|
|
|
2021-10-29 09:56:25 +00:00
|
|
|
// variables and properties
|
|
|
|
if (!type.variables.isEmpty() || !type.properties.isEmpty()) {
|
|
|
|
code.rawAppendToHeader(u""); // blank line
|
|
|
|
code.rawAppendToHeader(u"protected:", -1);
|
|
|
|
}
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const auto &property : std::as_const(type.properties))
|
2021-10-29 09:56:25 +00:00
|
|
|
write(code, property);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const auto &variable : std::as_const(type.variables))
|
2021-10-29 09:56:25 +00:00
|
|
|
write(code, variable);
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
|
2021-11-19 12:04:05 +00:00
|
|
|
code.rawAppendToHeader(u"private:", -1);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &otherLine : std::as_const(type.otherCode))
|
2021-11-19 12:04:05 +00:00
|
|
|
code.rawAppendToHeader(otherLine, 1);
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
if (type.typeCount) {
|
2021-11-19 12:04:05 +00:00
|
|
|
// add typeCount declaration, definition is added later
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToHeader(u""); // blank line
|
2021-10-22 10:02:37 +00:00
|
|
|
code.rawAppendToHeader(u"protected:");
|
2022-03-21 09:21:18 +00:00
|
|
|
code.rawAppendToHeader(u"constexpr static %1 %2();"_s.arg(type.typeCount->returnType,
|
|
|
|
type.typeCount->name),
|
2021-09-21 08:52:24 +00:00
|
|
|
1);
|
|
|
|
}
|
|
|
|
|
|
|
|
code.rawAppendToHeader(u"};");
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcEnum &enumeration)
|
|
|
|
{
|
|
|
|
code.rawAppendToHeader(u"enum " + enumeration.cppType + u" {");
|
|
|
|
for (qsizetype i = 0; i < enumeration.keys.size(); ++i) {
|
|
|
|
QString str;
|
|
|
|
if (enumeration.values.isEmpty()) {
|
|
|
|
str += enumeration.keys.at(i) + u",";
|
|
|
|
} else {
|
|
|
|
str += enumeration.keys.at(i) + u" = " + enumeration.values.at(i) + u",";
|
|
|
|
}
|
|
|
|
code.rawAppendToHeader(str, 1);
|
|
|
|
}
|
|
|
|
code.rawAppendToHeader(u"};");
|
2021-10-29 10:43:44 +00:00
|
|
|
code.rawAppendToHeader(enumeration.ownMocLine);
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method)
|
|
|
|
{
|
|
|
|
const auto [hSignature, cppSignature] = functionSignatures(method);
|
|
|
|
// Note: augment return type with preambles in declaration
|
2023-05-05 07:30:27 +00:00
|
|
|
code.rawAppendToHeader((method.type == QQmlJSMetaMethodType::StaticMethod
|
2022-09-28 18:04:44 +00:00
|
|
|
? u"static " + functionReturnType(method)
|
|
|
|
: functionReturnType(method))
|
|
|
|
+ u" " + hSignature + u";");
|
2021-09-21 08:52:24 +00:00
|
|
|
|
|
|
|
// do not generate method implementation if it is a signal
|
|
|
|
const auto methodType = method.type;
|
2023-05-05 07:30:27 +00:00
|
|
|
if (methodType != QQmlJSMetaMethodType::Signal) {
|
2022-10-17 13:27:12 +00:00
|
|
|
code.rawAppendToCpp(u""_s); // blank line
|
|
|
|
if (method.comments.size() > 0) {
|
|
|
|
code.rawAppendToCpp(u"/*! \\internal"_s);
|
|
|
|
for (const auto &comment : method.comments)
|
|
|
|
code.rawAppendToCpp(comment, 1);
|
|
|
|
code.rawAppendToCpp(u"*/"_s);
|
|
|
|
}
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToCpp(method.returnType);
|
|
|
|
code.rawAppendSignatureToCpp(cppSignature);
|
|
|
|
code.rawAppendToCpp(u"{");
|
|
|
|
{
|
|
|
|
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
|
|
|
|
Q_UNUSED(cppIndent);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &line : std::as_const(method.body))
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToCpp(line);
|
|
|
|
}
|
|
|
|
code.rawAppendToCpp(u"}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
template<typename WriteInitialization>
|
|
|
|
static void writeSpecialMethod(QmltcOutputWrapper &code, const QmltcMethodBase &specialMethod,
|
|
|
|
WriteInitialization writeInit)
|
2021-09-21 08:52:24 +00:00
|
|
|
{
|
2022-03-03 13:03:58 +00:00
|
|
|
const auto [hSignature, cppSignature] = functionSignatures(specialMethod);
|
|
|
|
code.rawAppendToHeader(hSignature + u";");
|
2021-09-21 08:52:24 +00:00
|
|
|
|
|
|
|
code.rawAppendToCpp(u""); // blank line
|
|
|
|
code.rawAppendSignatureToCpp(cppSignature);
|
2022-03-03 13:03:58 +00:00
|
|
|
|
|
|
|
writeInit(specialMethod);
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToCpp(u"{");
|
|
|
|
{
|
|
|
|
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
|
|
|
|
Q_UNUSED(cppIndent);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &line : std::as_const(specialMethod.body))
|
2022-03-03 13:03:58 +00:00
|
|
|
code.rawAppendToCpp(line);
|
2021-09-21 08:52:24 +00:00
|
|
|
}
|
|
|
|
code.rawAppendToCpp(u"}");
|
|
|
|
}
|
|
|
|
|
2022-03-03 13:03:58 +00:00
|
|
|
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(
|
2022-03-21 09:21:18 +00:00
|
|
|
ctor.initializerList.join(u",\n\n" + u" "_s.repeated(code.cppIndent + 1)),
|
2022-03-03 13:03:58 +00:00
|
|
|
1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
writeSpecialMethod(code, ctor, writeInitializerList);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcDtor &dtor)
|
|
|
|
{
|
|
|
|
const auto noop = [](const QmltcMethodBase &) {};
|
|
|
|
writeSpecialMethod(code, dtor, noop);
|
|
|
|
}
|
|
|
|
|
2021-09-21 08:52:24 +00:00
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var)
|
|
|
|
{
|
2022-03-21 09:21:18 +00:00
|
|
|
const QString optionalPart = var.defaultValue.isEmpty() ? u""_s : u" = " + var.defaultValue;
|
2021-09-21 08:52:24 +00:00
|
|
|
code.rawAppendToHeader(var.cppType + u" " + var.name + optionalPart + u";");
|
|
|
|
}
|
|
|
|
|
2021-10-29 09:56:25 +00:00
|
|
|
void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProperty &prop)
|
|
|
|
{
|
2022-03-03 13:03:58 +00:00
|
|
|
Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet (or at all?)
|
2022-03-21 09:21:18 +00:00
|
|
|
code.rawAppendToHeader(u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_s.arg(
|
2021-10-29 09:56:25 +00:00
|
|
|
prop.containingClass, prop.cppType, prop.name, prop.signalName));
|
|
|
|
}
|
|
|
|
|
2021-11-12 08:28:33 +00:00
|
|
|
void QmltcCodeWriter::writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod)
|
|
|
|
{
|
|
|
|
// unlike ordinary methods, url function only exists in .cpp
|
|
|
|
Q_ASSERT(!urlMethod.returnType.isEmpty());
|
|
|
|
const auto [hSignature, _] = functionSignatures(urlMethod);
|
|
|
|
Q_UNUSED(_);
|
|
|
|
// Note: augment return type with preambles in declaration
|
2022-04-26 08:42:16 +00:00
|
|
|
code.rawAppendToCpp(functionReturnType(urlMethod) + u" " + hSignature);
|
2021-11-12 08:28:33 +00:00
|
|
|
code.rawAppendToCpp(u"{");
|
|
|
|
{
|
|
|
|
QmltcOutputWrapper::CppIndentationScope cppIndent(&code);
|
|
|
|
Q_UNUSED(cppIndent);
|
2022-10-06 09:30:50 +00:00
|
|
|
for (const QString &line : std::as_const(urlMethod.body))
|
2021-11-12 08:28:33 +00:00
|
|
|
code.rawAppendToCpp(line);
|
|
|
|
}
|
|
|
|
code.rawAppendToCpp(u"}");
|
|
|
|
}
|
|
|
|
|
2021-09-17 15:01:56 +00:00
|
|
|
QT_END_NAMESPACE
|