| 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 |  |