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