| 1 | // Copyright (C) 2021 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
| 3 | |
| 4 | #include "qmltccodewriter.h" |
| 5 | |
| 6 | #include <QtCore/qfileinfo.h> |
| 7 | #include <QtCore/qstringbuilder.h> |
| 8 | #include <QtCore/qstring.h> |
| 9 | #include <QtCore/qmap.h> |
| 10 | #include <QtCore/qlist.h> |
| 11 | |
| 12 | #include <utility> |
| 13 | |
| 14 | QT_BEGIN_NAMESPACE |
| 15 | |
| 16 | using namespace Qt::StringLiterals; |
| 17 | |
| 18 | static QString urlToMacro(const QString &url) |
| 19 | { |
| 20 | QFileInfo fi(url); |
| 21 | return u"Q_QMLTC_" + fi.baseName().toUpper(); |
| 22 | } |
| 23 | |
| 24 | static QString getFunctionCategory(const QmltcMethodBase &method) |
| 25 | { |
| 26 | QString category; |
| 27 | switch (method.access) { |
| 28 | case QQmlJSMetaMethod::Private: |
| 29 | category = u"private"_s ; |
| 30 | break; |
| 31 | case QQmlJSMetaMethod::Protected: |
| 32 | category = u"protected"_s ; |
| 33 | break; |
| 34 | case QQmlJSMetaMethod::Public: |
| 35 | category = u"public"_s ; |
| 36 | break; |
| 37 | } |
| 38 | return category; |
| 39 | } |
| 40 | |
| 41 | static QString getFunctionCategory(const QmltcMethod &method) |
| 42 | { |
| 43 | QString category = getFunctionCategory(method: static_cast<const QmltcMethodBase &>(method)); |
| 44 | switch (method.type) { |
| 45 | case QQmlJSMetaMethodType::Signal: |
| 46 | category = u"Q_SIGNALS"_s ; |
| 47 | break; |
| 48 | case QQmlJSMetaMethodType::Slot: |
| 49 | category += u" Q_SLOTS"_s ; |
| 50 | break; |
| 51 | case QQmlJSMetaMethodType::Method: |
| 52 | case QQmlJSMetaMethodType::StaticMethod: |
| 53 | break; |
| 54 | } |
| 55 | return category; |
| 56 | } |
| 57 | |
| 58 | static QString appendSpace(const QString &s) |
| 59 | { |
| 60 | if (s.isEmpty()) |
| 61 | return s; |
| 62 | return s + u" " ; |
| 63 | } |
| 64 | |
| 65 | static QString prependSpace(const QString &s) |
| 66 | { |
| 67 | if (s.isEmpty()) |
| 68 | return s; |
| 69 | return u" " + s; |
| 70 | } |
| 71 | |
| 72 | static std::pair<QString, QString> functionSignatures(const QmltcMethodBase &method) |
| 73 | { |
| 74 | const QString name = method.name; |
| 75 | const QList<QmltcVariable> ¶meterList = method.parameterList; |
| 76 | |
| 77 | QStringList ; |
| 78 | QStringList cppParamList; |
| 79 | for (const QmltcVariable &variable : parameterList) { |
| 80 | const QString commonPart = variable.cppType + u" " + variable.name; |
| 81 | cppParamList << commonPart; |
| 82 | headerParamList << commonPart; |
| 83 | if (!variable.defaultValue.isEmpty()) |
| 84 | headerParamList.back() += u" = " + variable.defaultValue; |
| 85 | } |
| 86 | |
| 87 | const QString = name + u"(" + headerParamList.join(sep: u", "_s ) + u")" |
| 88 | + prependSpace(s: method.modifiers.join(sep: u" " )); |
| 89 | const QString cppSignature = name + u"(" + cppParamList.join(sep: u", "_s ) + u")" |
| 90 | + prependSpace(s: method.modifiers.join(sep: u" " )); |
| 91 | return { headerSignature, cppSignature }; |
| 92 | } |
| 93 | |
| 94 | static QString functionReturnType(const QmltcMethod &m) |
| 95 | { |
| 96 | return appendSpace(s: m.declarationPrefixes.join(sep: u" "_s )) + m.returnType; |
| 97 | } |
| 98 | |
| 99 | void QmltcCodeWriter::(QmltcOutputWrapper &code, const QString &sourcePath, |
| 100 | const QString &hPath, const QString &cppPath, |
| 101 | const QString &outNamespace, |
| 102 | const QSet<QString> &requiredCppIncludes) |
| 103 | { |
| 104 | Q_UNUSED(cppPath); |
| 105 | const QString preamble = u"// This code is auto-generated by the qmltc tool from the file '" |
| 106 | + sourcePath + u"'\n// WARNING! All changes made in this file will be lost!\n" ; |
| 107 | code.rawAppendToHeader(what: preamble); |
| 108 | code.rawAppendToCpp(what: preamble); |
| 109 | code.rawAppendToHeader( |
| 110 | what: u"// NOTE: This generated API is to be considered implementation detail." ); |
| 111 | code.rawAppendToHeader( |
| 112 | what: u"// It may change from version to version and should not be relied upon." ); |
| 113 | |
| 114 | const QString = urlToMacro(url: sourcePath); |
| 115 | code.rawAppendToHeader(what: u"#ifndef %1_H"_s .arg(a: headerMacro)); |
| 116 | code.rawAppendToHeader(what: u"#define %1_H"_s .arg(a: headerMacro)); |
| 117 | |
| 118 | code.rawAppendToHeader(what: u"#include <QtCore/qproperty.h>" ); |
| 119 | code.rawAppendToHeader(what: u"#include <QtCore/qobject.h>" ); |
| 120 | code.rawAppendToHeader(what: u"#include <QtCore/qcoreapplication.h>" ); |
| 121 | code.rawAppendToHeader(what: u"#include <QtCore/qxpfunctional.h>" ); |
| 122 | code.rawAppendToHeader(what: u"#include <QtQml/qqmlengine.h>" ); |
| 123 | code.rawAppendToHeader(what: u"#include <QtCore/qurl.h>" ); // used in engine execution |
| 124 | code.rawAppendToHeader(what: u"#include <QtQml/qqml.h>" ); // used for attached properties |
| 125 | |
| 126 | code.rawAppendToHeader(what: u"#include <private/qqmlengine_p.h>" ); // executeRuntimeFunction(), etc. |
| 127 | code.rawAppendToHeader(what: u"#include <private/qqmltcobjectcreationhelper_p.h>" ); // QmltcSupportLib |
| 128 | |
| 129 | code.rawAppendToHeader(what: u"#include <QtQml/qqmllist.h>" ); // QQmlListProperty |
| 130 | |
| 131 | // include custom C++ includes required by used types |
| 132 | code.rawAppendToHeader(what: u"// BEGIN(custom_cpp_includes)" ); |
| 133 | for (const auto &requiredInclude : requiredCppIncludes) |
| 134 | code.rawAppendToHeader(what: u"#include \"" + requiredInclude + u"\"" ); |
| 135 | code.rawAppendToHeader(what: u"// END(custom_cpp_includes)" ); |
| 136 | |
| 137 | code.rawAppendToCpp(what: u"#include \"" + hPath + u"\"" ); // include own .h file |
| 138 | code.rawAppendToCpp(what: u"// qmltc support library:" ); |
| 139 | code.rawAppendToCpp(what: u"#include <private/qqmlcppbinding_p.h>" ); // QmltcSupportLib |
| 140 | code.rawAppendToCpp(what: u"#include <private/qqmlcpponassignment_p.h>" ); // QmltcSupportLib |
| 141 | code.rawAppendToHeader(what: u"#include <private/qqmlcpptypehelpers_p.h> " ); // QmltcSupportLib |
| 142 | |
| 143 | code.rawAppendToCpp(what: u"#include <private/qqmlobjectcreator_p.h>" ); // createComponent() |
| 144 | code.rawAppendToCpp(what: u"#include <private/qqmlcomponent_p.h>" ); // QQmlComponentPrivate::get() |
| 145 | |
| 146 | code.rawAppendToCpp(what: u"" ); |
| 147 | code.rawAppendToCpp(what: u"#include <private/qobject_p.h>" ); // NB: for private properties |
| 148 | code.rawAppendToCpp(what: u"#include <private/qqmlobjectcreator_p.h>" ); // for finalize callbacks |
| 149 | code.rawAppendToCpp(what: u"#include <QtQml/qqmlprivate.h>" ); // QQmlPrivate::qmlExtendedObject() |
| 150 | |
| 151 | code.rawAppendToCpp(what: u"" ); // blank line |
| 152 | code.rawAppendToCpp(what: u"QT_USE_NAMESPACE // avoid issues with QT_NAMESPACE" ); |
| 153 | |
| 154 | code.rawAppendToHeader(what: u"" ); // blank line |
| 155 | |
| 156 | const QStringList namespaces = outNamespace.split(sep: u"::"_s ); |
| 157 | |
| 158 | for (const QString ¤tNamespace : namespaces) { |
| 159 | code.rawAppendToHeader(what: u"namespace %1 {"_s .arg(a: currentNamespace)); |
| 160 | code.rawAppendToCpp(what: u"namespace %1 {"_s .arg(a: currentNamespace)); |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, |
| 165 | const QmltcPropertyInitializer &propertyInitializer, |
| 166 | const QmltcType &wrappedType) |
| 167 | { |
| 168 | code.rawAppendToHeader(what: u"class " + propertyInitializer.name + u" {" ); |
| 169 | |
| 170 | { |
| 171 | { |
| 172 | [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope (&code); |
| 173 | |
| 174 | code.rawAppendToHeader(what: u"friend class " + wrappedType.cppType + u";" ); |
| 175 | } |
| 176 | |
| 177 | code.rawAppendToHeader(what: u"public:"_s ); |
| 178 | |
| 179 | [[maybe_unused]] QmltcOutputWrapper::MemberNameScope typeScope(&code, propertyInitializer.name); |
| 180 | { |
| 181 | [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope (&code); |
| 182 | |
| 183 | write(code, ctor: propertyInitializer.constructor); |
| 184 | code.rawAppendToHeader(what: u"" ); // blank line |
| 185 | |
| 186 | for (const auto &propertySetter : propertyInitializer.propertySetters) { |
| 187 | write(code, method: propertySetter); |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | code.rawAppendToHeader(what: u"" ); // blank line |
| 192 | code.rawAppendToHeader(what: u"private:"_s ); |
| 193 | |
| 194 | { |
| 195 | [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope (&code); |
| 196 | |
| 197 | write(code, var: propertyInitializer.component); |
| 198 | write(code, var: propertyInitializer.initializedCache); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | code.rawAppendToHeader(what: u"};"_s ); |
| 203 | code.rawAppendToHeader(what: u"" ); // blank line |
| 204 | } |
| 205 | |
| 206 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcRequiredPropertiesBundle &requiredPropertiesBundle) |
| 207 | { |
| 208 | code.rawAppendToHeader(what: u"struct " + requiredPropertiesBundle.name + u" {" ); |
| 209 | |
| 210 | { |
| 211 | [[maybe_unused]] QmltcOutputWrapper::HeaderIndentationScope (&code); |
| 212 | |
| 213 | for (const auto &member : requiredPropertiesBundle.members) { |
| 214 | write(code, var: member); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | code.rawAppendToHeader(what: u"};"_s ); |
| 219 | code.rawAppendToHeader(what: u"" ); // blank line |
| 220 | } |
| 221 | |
| 222 | void QmltcCodeWriter::(QmltcOutputWrapper &code, const QString &sourcePath, |
| 223 | const QString &outNamespace) |
| 224 | { |
| 225 | const QStringList namespaces = outNamespace.split(sep: u"::"_s ); |
| 226 | |
| 227 | for (auto it = namespaces.crbegin(), end = namespaces.crend(); it != end; it++) { |
| 228 | code.rawAppendToCpp(what: u"} // namespace %1"_s .arg(a: *it)); |
| 229 | code.rawAppendToHeader(what: u"} // namespace %1"_s .arg(a: *it)); |
| 230 | } |
| 231 | |
| 232 | code.rawAppendToHeader(what: u"" ); // blank line |
| 233 | code.rawAppendToHeader(what: u"#endif // %1_H"_s .arg(a: urlToMacro(url: sourcePath))); |
| 234 | code.rawAppendToHeader(what: u"" ); // blank line |
| 235 | } |
| 236 | |
| 237 | static void writeToFile(const QString &path, const QByteArray &data) |
| 238 | { |
| 239 | // When not using dependency files, changing a single qml invalidates all |
| 240 | // qml files and would force the recompilation of everything. To avoid that, |
| 241 | // we check if the data is equal to the existing file, if yes, don't touch |
| 242 | // it so the build system will not recompile unnecessary things. |
| 243 | // |
| 244 | // If the build system use dependency file, we should anyway touch the file |
| 245 | // so qmltc is not re-run |
| 246 | QFileInfo fi(path); |
| 247 | if (fi.exists() && fi.size() == data.size()) { |
| 248 | QFile oldFile(path); |
| 249 | if (oldFile.open(flags: QIODevice::ReadOnly)) { |
| 250 | if (oldFile.readAll() == data) |
| 251 | return; |
| 252 | } |
| 253 | } |
| 254 | QFile file(path); |
| 255 | if (!file.open(flags: QIODevice::WriteOnly)) |
| 256 | qFatal(msg: "Could not open file %s" , qPrintable(path)); |
| 257 | file.write(data); |
| 258 | } |
| 259 | |
| 260 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProgram &program) |
| 261 | { |
| 262 | writeGlobalHeader(code, sourcePath: program.url, hPath: program.hPath, cppPath: program.cppPath, outNamespace: program.outNamespace, |
| 263 | requiredCppIncludes: program.includes); |
| 264 | |
| 265 | // url method comes first |
| 266 | writeUrl(code, urlMethod: program.urlMethod); |
| 267 | |
| 268 | // forward declare all the types first |
| 269 | for (const QmltcType &type : std::as_const(t: program.compiledTypes)) |
| 270 | code.rawAppendToHeader(what: u"class " + type.cppType + u";" ); |
| 271 | // write all the types and their content |
| 272 | for (const QmltcType &type : std::as_const(t: program.compiledTypes)) |
| 273 | write(code, type, exportMacro: program.exportMacro); |
| 274 | |
| 275 | // add typeCount definitions. after all types have been written down (so |
| 276 | // they are now complete types as per C++). practically, this only concerns |
| 277 | // document root type |
| 278 | for (const QmltcType &type : std::as_const(t: program.compiledTypes)) { |
| 279 | if (!type.typeCount) |
| 280 | continue; |
| 281 | code.rawAppendToHeader(what: u"" ); // blank line |
| 282 | code.rawAppendToHeader(what: u"constexpr %1 %2::%3()"_s .arg(args: type.typeCount->returnType, |
| 283 | args: type.cppType, args: type.typeCount->name)); |
| 284 | code.rawAppendToHeader(what: u"{" ); |
| 285 | for (const QString &line : std::as_const(t: type.typeCount->body)) |
| 286 | code.rawAppendToHeader(what: line, extraIndent: 1); |
| 287 | code.rawAppendToHeader(what: u"}" ); |
| 288 | } |
| 289 | |
| 290 | writeGlobalFooter(code, sourcePath: program.url, outNamespace: program.outNamespace); |
| 291 | |
| 292 | writeToFile(path: program.hPath, data: code.code().header.toUtf8()); |
| 293 | writeToFile(path: program.cppPath, data: code.code().cpp.toUtf8()); |
| 294 | } |
| 295 | |
| 296 | template<typename Predicate> |
| 297 | static void dumpFunctions(QmltcOutputWrapper &code, const QList<QmltcMethod> &functions, |
| 298 | Predicate pred) |
| 299 | { |
| 300 | // functions are _ordered_ by access and kind. ordering is important to |
| 301 | // provide consistent output |
| 302 | QMap<QString, QList<const QmltcMethod *>> orderedFunctions; |
| 303 | for (const auto &function : functions) { |
| 304 | if (pred(function)) |
| 305 | orderedFunctions[getFunctionCategory(method: function)].append(t: std::addressof(r: function)); |
| 306 | } |
| 307 | |
| 308 | for (auto it = orderedFunctions.cbegin(); it != orderedFunctions.cend(); ++it) { |
| 309 | code.rawAppendToHeader(what: it.key() + u":" , extraIndent: -1); |
| 310 | for (const QmltcMethod *function : std::as_const(t: it.value())) |
| 311 | QmltcCodeWriter::write(code, method: *function); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcType &type, |
| 316 | const QString &exportMacro) |
| 317 | { |
| 318 | const auto constructClassString = [&]() { |
| 319 | QString str = u"class "_s ; |
| 320 | if (!exportMacro.isEmpty()) |
| 321 | str.append(s: exportMacro).append(s: u" "_s ); |
| 322 | str.append(s: type.cppType); |
| 323 | QStringList nonEmptyBaseClasses; |
| 324 | nonEmptyBaseClasses.reserve(asize: type.baseClasses.size()); |
| 325 | std::copy_if(first: type.baseClasses.cbegin(), last: type.baseClasses.cend(), |
| 326 | result: std::back_inserter(x&: nonEmptyBaseClasses), |
| 327 | pred: [](const QString &entry) { return !entry.isEmpty(); }); |
| 328 | if (!nonEmptyBaseClasses.isEmpty()) |
| 329 | str += u" : public " + nonEmptyBaseClasses.join(sep: u", public "_s ); |
| 330 | return str; |
| 331 | }; |
| 332 | |
| 333 | code.rawAppendToHeader(what: u"" ); // blank line |
| 334 | code.rawAppendToCpp(what: u"" ); // blank line |
| 335 | |
| 336 | code.rawAppendToHeader(what: constructClassString()); |
| 337 | code.rawAppendToHeader(what: u"{" ); |
| 338 | for (const QString &mocLine : std::as_const(t: type.mocCode)) |
| 339 | code.rawAppendToHeader(what: mocLine, extraIndent: 1); |
| 340 | |
| 341 | QmltcOutputWrapper::MemberNameScope typeScope(&code, type.cppType); |
| 342 | Q_UNUSED(typeScope); |
| 343 | { |
| 344 | QmltcOutputWrapper::HeaderIndentationScope (&code); |
| 345 | Q_UNUSED(headerIndent); |
| 346 | |
| 347 | // first, write user-visible code, then everything else. someone might |
| 348 | // want to look at the generated code, so let's make an effort when |
| 349 | // writing it down |
| 350 | |
| 351 | code.rawAppendToHeader(what: u"/* ----------------- */" ); |
| 352 | code.rawAppendToHeader(what: u"/* External C++ API */" ); |
| 353 | code.rawAppendToHeader(what: u"public:" , extraIndent: -1); |
| 354 | |
| 355 | if (!type.propertyInitializer.name.isEmpty()) |
| 356 | write(code, propertyInitializer: type.propertyInitializer, wrappedType: type); |
| 357 | |
| 358 | if (type.requiredPropertiesBundle) |
| 359 | write(code, requiredPropertiesBundle: *type.requiredPropertiesBundle); |
| 360 | |
| 361 | // NB: when non-document root, the externalCtor won't be public - but we |
| 362 | // really don't care about the output format of such types |
| 363 | if (!type.ignoreInit && type.externalCtor.access == QQmlJSMetaMethod::Public) { |
| 364 | // TODO: ignoreInit must be eliminated |
| 365 | |
| 366 | QmltcCodeWriter::write(code, ctor: type.externalCtor); |
| 367 | if (type.staticCreate) |
| 368 | QmltcCodeWriter::write(code, method: *type.staticCreate); |
| 369 | } |
| 370 | |
| 371 | // dtor |
| 372 | if (type.dtor) |
| 373 | QmltcCodeWriter::write(code, dtor: *type.dtor); |
| 374 | |
| 375 | // enums |
| 376 | for (const auto &enumeration : std::as_const(t: type.enums)) |
| 377 | QmltcCodeWriter::write(code, enumeration); |
| 378 | |
| 379 | // visible functions |
| 380 | const auto isUserVisibleFunction = [](const QmltcMethod &function) { |
| 381 | return function.userVisible; |
| 382 | }; |
| 383 | dumpFunctions(code, functions: type.functions, pred: isUserVisibleFunction); |
| 384 | |
| 385 | code.rawAppendToHeader(what: u"/* ----------------- */" ); |
| 386 | code.rawAppendToHeader(what: u"" ); // blank line |
| 387 | code.rawAppendToHeader(what: u"/* Internal functionality (do NOT use it!) */" ); |
| 388 | |
| 389 | // below are the hidden parts of the type |
| 390 | |
| 391 | // (rest of the) ctors |
| 392 | if (type.ignoreInit) { // TODO: this branch should be eliminated |
| 393 | Q_ASSERT(type.baselineCtor.access == QQmlJSMetaMethod::Public); |
| 394 | code.rawAppendToHeader(what: u"public:" , extraIndent: -1); |
| 395 | QmltcCodeWriter::write(code, ctor: type.baselineCtor); |
| 396 | } else { |
| 397 | code.rawAppendToHeader(what: u"protected:" , extraIndent: -1); |
| 398 | if (type.externalCtor.access != QQmlJSMetaMethod::Public) { |
| 399 | Q_ASSERT(type.externalCtor.access == QQmlJSMetaMethod::Protected); |
| 400 | QmltcCodeWriter::write(code, ctor: type.externalCtor); |
| 401 | } |
| 402 | QmltcCodeWriter::write(code, ctor: type.baselineCtor); |
| 403 | QmltcCodeWriter::write(code, method: type.init); |
| 404 | QmltcCodeWriter::write(code, method: type.endInit); |
| 405 | QmltcCodeWriter::write(code, method: type.setComplexBindings); |
| 406 | QmltcCodeWriter::write(code, method: type.beginClass); |
| 407 | QmltcCodeWriter::write(code, method: type.completeComponent); |
| 408 | QmltcCodeWriter::write(code, method: type.finalizeComponent); |
| 409 | QmltcCodeWriter::write(code, method: type.handleOnCompleted); |
| 410 | } |
| 411 | |
| 412 | // children |
| 413 | for (const auto &child : std::as_const(t: type.children)) |
| 414 | QmltcCodeWriter::write(code, type: child, exportMacro); |
| 415 | |
| 416 | // (non-visible) functions |
| 417 | dumpFunctions(code, functions: type.functions, pred: std::not_fn(fn: isUserVisibleFunction)); |
| 418 | |
| 419 | // variables and properties |
| 420 | if (!type.variables.isEmpty() || !type.properties.isEmpty()) { |
| 421 | code.rawAppendToHeader(what: u"" ); // blank line |
| 422 | code.rawAppendToHeader(what: u"protected:" , extraIndent: -1); |
| 423 | } |
| 424 | for (const auto &property : std::as_const(t: type.properties)) |
| 425 | write(code, prop: property); |
| 426 | for (const auto &variable : std::as_const(t: type.variables)) |
| 427 | write(code, var: variable); |
| 428 | } |
| 429 | |
| 430 | code.rawAppendToHeader(what: u"private:" , extraIndent: -1); |
| 431 | for (const QString &otherLine : std::as_const(t: type.otherCode)) |
| 432 | code.rawAppendToHeader(what: otherLine, extraIndent: 1); |
| 433 | |
| 434 | if (type.typeCount) { |
| 435 | // add typeCount declaration, definition is added later |
| 436 | code.rawAppendToHeader(what: u"" ); // blank line |
| 437 | code.rawAppendToHeader(what: u"protected:" ); |
| 438 | code.rawAppendToHeader(what: u"constexpr static %1 %2();"_s .arg(args: type.typeCount->returnType, |
| 439 | args: type.typeCount->name), |
| 440 | extraIndent: 1); |
| 441 | } |
| 442 | |
| 443 | code.rawAppendToHeader(what: u"};" ); |
| 444 | } |
| 445 | |
| 446 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcEnum &enumeration) |
| 447 | { |
| 448 | code.rawAppendToHeader(what: u"enum " + enumeration.cppType + u" {" ); |
| 449 | for (qsizetype i = 0; i < enumeration.keys.size(); ++i) { |
| 450 | QString str; |
| 451 | if (enumeration.values.isEmpty()) { |
| 452 | str += enumeration.keys.at(i) + u"," ; |
| 453 | } else { |
| 454 | str += enumeration.keys.at(i) + u" = " + enumeration.values.at(i) + u"," ; |
| 455 | } |
| 456 | code.rawAppendToHeader(what: str, extraIndent: 1); |
| 457 | } |
| 458 | code.rawAppendToHeader(what: u"};" ); |
| 459 | code.rawAppendToHeader(what: enumeration.ownMocLine); |
| 460 | } |
| 461 | |
| 462 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcMethod &method) |
| 463 | { |
| 464 | const auto [hSignature, cppSignature] = functionSignatures(method); |
| 465 | // Note: augment return type with preambles in declaration |
| 466 | code.rawAppendToHeader(what: (method.type == QQmlJSMetaMethodType::StaticMethod |
| 467 | ? u"static " + functionReturnType(m: method) |
| 468 | : functionReturnType(m: method)) |
| 469 | + u" " + hSignature + u";" ); |
| 470 | |
| 471 | // do not generate method implementation if it is a signal |
| 472 | const auto methodType = method.type; |
| 473 | if (methodType != QQmlJSMetaMethodType::Signal) { |
| 474 | code.rawAppendToCpp(what: u""_s ); // blank line |
| 475 | if (method.comments.size() > 0) { |
| 476 | code.rawAppendToCpp(what: u"/*! \\internal"_s ); |
| 477 | for (const auto & : method.comments) |
| 478 | code.rawAppendToCpp(what: comment, extraIndent: 1); |
| 479 | code.rawAppendToCpp(what: u"*/"_s ); |
| 480 | } |
| 481 | code.rawAppendToCpp(what: method.returnType); |
| 482 | code.rawAppendSignatureToCpp(what: cppSignature); |
| 483 | code.rawAppendToCpp(what: u"{" ); |
| 484 | { |
| 485 | QmltcOutputWrapper::CppIndentationScope cppIndent(&code); |
| 486 | Q_UNUSED(cppIndent); |
| 487 | for (const QString &line : std::as_const(t: method.body)) |
| 488 | code.rawAppendToCpp(what: line); |
| 489 | } |
| 490 | code.rawAppendToCpp(what: u"}" ); |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | template<typename WriteInitialization> |
| 495 | static void writeSpecialMethod(QmltcOutputWrapper &code, const QmltcMethodBase &specialMethod, |
| 496 | WriteInitialization writeInit) |
| 497 | { |
| 498 | const auto [hSignature, cppSignature] = functionSignatures(method: specialMethod); |
| 499 | code.rawAppendToHeader(what: hSignature + u";" ); |
| 500 | |
| 501 | code.rawAppendToCpp(what: u"" ); // blank line |
| 502 | code.rawAppendSignatureToCpp(what: cppSignature); |
| 503 | |
| 504 | writeInit(specialMethod); |
| 505 | |
| 506 | code.rawAppendToCpp(what: u"{" ); |
| 507 | { |
| 508 | QmltcOutputWrapper::CppIndentationScope cppIndent(&code); |
| 509 | Q_UNUSED(cppIndent); |
| 510 | for (const QString &line : std::as_const(t: specialMethod.body)) |
| 511 | code.rawAppendToCpp(what: line); |
| 512 | } |
| 513 | code.rawAppendToCpp(what: u"}" ); |
| 514 | } |
| 515 | |
| 516 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcCtor &ctor) |
| 517 | { |
| 518 | const auto writeInitializerList = [&](const QmltcMethodBase &ctorBase) { |
| 519 | auto ctor = static_cast<const QmltcCtor &>(ctorBase); |
| 520 | if (!ctor.initializerList.isEmpty()) { |
| 521 | code.rawAppendToCpp(what: u":" , extraIndent: 1); |
| 522 | // double \n to make separate initializer list lines stand out more |
| 523 | code.rawAppendToCpp( |
| 524 | what: ctor.initializerList.join(sep: u",\n\n" + u" "_s .repeated(times: code.cppIndent + 1)), |
| 525 | extraIndent: 1); |
| 526 | } |
| 527 | }; |
| 528 | |
| 529 | writeSpecialMethod(code, specialMethod: ctor, writeInit: writeInitializerList); |
| 530 | } |
| 531 | |
| 532 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcDtor &dtor) |
| 533 | { |
| 534 | const auto noop = [](const QmltcMethodBase &) {}; |
| 535 | writeSpecialMethod(code, specialMethod: dtor, writeInit: noop); |
| 536 | } |
| 537 | |
| 538 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcVariable &var) |
| 539 | { |
| 540 | const QString optionalPart = var.defaultValue.isEmpty() ? u""_s : u" = " + var.defaultValue; |
| 541 | code.rawAppendToHeader(what: var.cppType + u" " + var.name + optionalPart + u";" ); |
| 542 | } |
| 543 | |
| 544 | void QmltcCodeWriter::write(QmltcOutputWrapper &code, const QmltcProperty &prop) |
| 545 | { |
| 546 | Q_ASSERT(prop.defaultValue.isEmpty()); // we don't support it yet (or at all?) |
| 547 | code.rawAppendToHeader(what: u"Q_OBJECT_BINDABLE_PROPERTY(%1, %2, %3, &%1::%4)"_s .arg( |
| 548 | args: prop.containingClass, args: prop.cppType, args: prop.name, args: prop.signalName)); |
| 549 | } |
| 550 | |
| 551 | void QmltcCodeWriter::writeUrl(QmltcOutputWrapper &code, const QmltcMethod &urlMethod) |
| 552 | { |
| 553 | // unlike ordinary methods, url function only exists in .cpp |
| 554 | Q_ASSERT(!urlMethod.returnType.isEmpty()); |
| 555 | const auto [hSignature, _] = functionSignatures(method: urlMethod); |
| 556 | Q_UNUSED(_); |
| 557 | // Note: augment return type with preambles in declaration |
| 558 | code.rawAppendToCpp(what: functionReturnType(m: urlMethod) + u" " + hSignature); |
| 559 | code.rawAppendToCpp(what: u"{" ); |
| 560 | { |
| 561 | QmltcOutputWrapper::CppIndentationScope cppIndent(&code); |
| 562 | Q_UNUSED(cppIndent); |
| 563 | for (const QString &line : std::as_const(t: urlMethod.body)) |
| 564 | code.rawAppendToCpp(what: line); |
| 565 | } |
| 566 | code.rawAppendToCpp(what: u"}" ); |
| 567 | } |
| 568 | |
| 569 | QT_END_NAMESPACE |
| 570 | |