1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#ifndef QMLTCCOMPILERPIECES_H
5#define QMLTCCOMPILERPIECES_H
6
7#include <QtCore/qscopeguard.h>
8#include <QtCore/qstringbuilder.h>
9#include <QtCore/qfileinfo.h>
10
11#include <private/qqmljsutils_p.h>
12#include <private/qqmlglobal_p.h>
13#include <private/qqmltranslation_p.h>
14
15#include "qmltcoutputir.h"
16#include "qmltcvisitor.h"
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \internal
22
23 Helper class that generates code for the output IR. Takes care of
24 complicated, repetitive, nasty logic which is better kept in a single
25 confined place.
26*/
27struct QmltcCodeGenerator
28{
29 static const QString privateEngineName;
30 static const QString typeCountName;
31
32 QString documentUrl;
33 QmltcVisitor *visitor = nullptr;
34
35 using InlineComponentOrDocumentRootName = QQmlJSScope::InlineComponentOrDocumentRootName;
36 using RootDocumentNameType = QQmlJSScope::RootDocumentNameType;
37
38 [[nodiscard]] inline decltype(auto) generate_initCode(QmltcType &current,
39 const QQmlJSScope::ConstPtr &type) const;
40 inline void generate_initCodeForTopLevelComponent(QmltcType &current,
41 const QQmlJSScope::ConstPtr &type);
42
43 inline void generate_qmltcInstructionCallCode(QmltcMethod *function,
44 const QQmlJSScope::ConstPtr &type,
45 const QString &baseInstructionArgs,
46 const QString &childInstructionArgs) const;
47 inline void generate_endInitCode(QmltcType &current, const QQmlJSScope::ConstPtr &type) const;
48 inline void generate_setComplexBindingsCode(QmltcType &current,
49 const QQmlJSScope::ConstPtr &type) const;
50
51 inline void generate_interfaceCallCode(QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
52 const QString &interfaceName,
53 const QString &interfaceCall) const;
54 inline void generate_beginClassCode(QmltcType &current,
55 const QQmlJSScope::ConstPtr &type) const;
56 inline void generate_completeComponentCode(QmltcType &current,
57 const QQmlJSScope::ConstPtr &type) const;
58 inline void generate_finalizeComponentCode(QmltcType &current,
59 const QQmlJSScope::ConstPtr &type) const;
60 inline void generate_handleOnCompletedCode(QmltcType &current,
61 const QQmlJSScope::ConstPtr &type) const;
62
63 static void generate_assignToProperty(QStringList *block, const QQmlJSScope::ConstPtr &type,
64 const QQmlJSMetaProperty &p, const QString &value,
65 const QString &accessor,
66 bool constructFromQObject = false);
67
68 static void generate_assignToListProperty(QStringList *block, const QQmlJSScope::ConstPtr &type,
69 const QQmlJSMetaProperty &p, const QStringList &value,
70 const QString &accessor, QString &qmlListVarName);
71
72 static void generate_setIdValue(QStringList *block, const QString &context, qsizetype index,
73 const QString &accessor, const QString &idString);
74
75 inline QString
76 generate_typeCount(const InlineComponentOrDocumentRootName &inlinedComponent) const
77 {
78 return generate_typeCount(p: [](const QQmlJSScope::ConstPtr &) { return false; },
79 inlinedComponent);
80 }
81
82 /*!
83 * \internal
84 * Generate the constexpr typeCount expression for given inlinedComponent. Leave
85 * inlinedComponent empty to generate the expression for the main component.
86 */
87 template<typename Predicate>
88 inline QString
89 generate_typeCount(Predicate p,
90 const InlineComponentOrDocumentRootName &inlinedComponent) const;
91
92 static void generate_callExecuteRuntimeFunction(QStringList *block, const QString &url,
93 QQmlJSMetaMethod::AbsoluteFunctionIndex index,
94 const QString &accessor,
95 const QString &returnType,
96 const QList<QmltcVariable> &parameters = {});
97
98 static void generate_createBindingOnProperty(QStringList *block, const QString &unitVarName,
99 const QString &scope, qsizetype functionIndex,
100 const QString &target,
101 const QQmlJSScope::ConstPtr &targetType,
102 int propertyIndex, const QQmlJSMetaProperty &p,
103 int valueTypeIndex, const QString &subTarget);
104
105 // Used in generate_createTranslationBindingOnProperty to transport its numerous arguments.
106 struct TranslationBindingInfo
107 {
108 QString unitVarName;
109 QString scope;
110 QString target;
111 int propertyIndex;
112 QQmlJSMetaProperty property;
113
114 QQmlTranslation data;
115
116 int valueTypeIndex;
117 // For the source location of the translation binding
118 uint line;
119 // For the source location of the translation binding
120 uint column;
121 };
122
123 static void generate_createTranslationBindingOnProperty(QStringList *block,
124 const TranslationBindingInfo &info);
125
126 static inline void generate_getCompilationUnitFromUrl();
127
128 struct PreparedValue
129 {
130 QStringList prologue;
131 QString value;
132 QStringList epilogue;
133 };
134
135 static PreparedValue wrap_mismatchingTypeConversion(const QQmlJSMetaProperty &p, QString value);
136 static PreparedValue wrap_extensionType(const QQmlJSScope::ConstPtr &type,
137 const QQmlJSMetaProperty &p, const QString &accessor);
138
139 static QString wrap_privateClass(const QString &accessor, const QQmlJSMetaProperty &p);
140 static QString wrap_qOverload(const QList<QmltcVariable> &parameters,
141 const QString &overloaded);
142 static QString wrap_addressof(const QString &addressed);
143
144 QString urlMethodName() const
145 {
146 using namespace Qt::StringLiterals;
147 QFileInfo fi(documentUrl);
148 return u"q_qmltc_docUrl_" + fi.fileName().replace(before: u".qml"_s, after: u""_s).replace(before: u'.', after: u'_');
149 }
150};
151
152/*!
153 \internal
154
155 Generates \a{current.init}'s code. The init method sets up a
156 QQmlContext for the object and (in case \a type is a document
157 root) calls other object creation methods, and a user-provided
158 initialization callback, in a well-defined order:
159 1. current.beginClass
160 2. current.endInit
161 3. user-provided initialization function
162 4. current.setComplexBindings
163 5. current.completeComponent
164 6. current.finalizeComponent
165 7. current.handleOnCompleted
166
167 This function returns a QScopeGuard with the final instructions that have to
168 be generated at a later point, once everything else is compiled.
169
170 \sa generate_initCodeForTopLevelComponent
171*/
172inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
173 const QQmlJSScope::ConstPtr &type) const
174{
175 using namespace Qt::StringLiterals;
176
177 // qmltc_init()'s parameters:
178 // * QQmltcObjectCreationHelper* creator
179 // * QQmlEngine* engine
180 // * const QQmlRefPointer<QQmlContextData>& parentContext
181 // * bool canFinalize [optional, when document root]
182 const bool isDocumentRoot = type == visitor->result();
183 const bool isInlineComponent = type->isInlineComponent();
184
185 current.init.body << u"Q_UNUSED(creator)"_s; // can happen sometimes
186
187 current.init.body << u"auto context = parentContext;"_s;
188
189 // if parent scope has a QML base type and is not a (current) document root,
190 // the parentContext we passed as input to this object is a context of
191 // another document. we need to fix it by using parentContext->parent()
192
193 const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
194 if (scope->isArrayScope())
195 return scope->parentScope();
196 return scope;
197 };
198
199 if (auto parentScope = realQmlScope(type->parentScope());
200 parentScope != visitor->result() && QQmlJSUtils::hasCompositeBase(scope: parentScope)) {
201 current.init.body << u"// NB: context->parent() is the context of this document"_s;
202 current.init.body << u"context = context->parent();"_s;
203 }
204
205 // any object with QML base class has to call base's init method
206 if (auto base = type->baseType(); base->isComposite()) {
207 QString lhs;
208 // init creates new context. for document root, it's going to be a real
209 // parent context, so store it temporarily in `context` variable
210 if (isDocumentRoot || isInlineComponent)
211 lhs = u"context = "_s;
212 current.init.body << u"// 0. call base's init method"_s;
213
214 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
215 return qmlType == type;
216 };
217 const QString creationOffset =
218 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
219
220 current.init.body << u"{"_s;
221 current.init.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
222 a: creationOffset);
223 current.init.body
224 << QStringLiteral("%1%2::%3(&subCreator, engine, context, /* finalize */ false);")
225 .arg(args&: lhs, args: base->internalName(), args&: current.init.name);
226 current.init.body << u"}"_s;
227 }
228
229 current.init.body
230 << QStringLiteral("auto %1 = QQmlEnginePrivate::get(engine);").arg(a: privateEngineName);
231 current.init.body << QStringLiteral("Q_UNUSED(%1)").arg(a: privateEngineName); // precaution
232
233 // when generating root or inlineComponents, we need to create a new (document-level) context.
234 // otherwise, just use existing context as is
235 if (isDocumentRoot || isInlineComponent) {
236 current.init.body << u"// 1. create new QML context for this document"_s;
237 current.init.body
238 << QStringLiteral(
239 "context = %1->createInternalContext(%1->compilationUnitFromUrl(%2()), "
240 "context, %3, true);")
241 .arg(args: privateEngineName, args: urlMethodName())
242 .arg(a: this->visitor->creationIndex(type));
243 } else {
244 current.init.body << u"// 1. use current context as this object's context"_s;
245 current.init.body << u"// context = context;"_s;
246 }
247
248 if (!type->baseType()->isComposite() || isDocumentRoot || isInlineComponent) {
249 current.init.body << u"// 2. set context for this object"_s;
250 current.init.body << QStringLiteral(
251 "%1->setInternalContext(this, context, QQmlContextData::%2);")
252 .arg(args: privateEngineName,
253 args: (isDocumentRoot ? u"DocumentRoot"_s
254 : u"OrdinaryObject"_s));
255 if (isDocumentRoot || isInlineComponent)
256 current.init.body << u"context->setContextObject(this);"_s;
257 }
258
259 // context is this document's context. we must remember it in each type
260 current.variables.emplaceBack(args: u"QQmlRefPointer<QQmlContextData>"_s, args: u"q_qmltc_thisContext"_s,
261 args: u"nullptr"_s);
262 current.init.body << u"%1::q_qmltc_thisContext = context;"_s.arg(a: type->internalName());
263
264 if (int id = visitor->runtimeId(type); id >= 0) {
265 current.init.body << u"// 3. set id since it is provided"_s;
266 QString idString = visitor->addressableScopes().id(scope: type, referrer: type);
267 if (idString.isEmpty())
268 idString = u"<unknown>"_s;
269 QmltcCodeGenerator::generate_setIdValue(block: &current.init.body, context: u"context"_s, index: id, accessor: u"this"_s,
270 idString);
271 }
272
273 // if type has an extension, create a dynamic meta object for it
274 bool hasExtension = false;
275 for (auto cppBase = QQmlJSScope::nonCompositeBaseType(type); cppBase;
276 cppBase = cppBase->baseType()) {
277 // QObject is special: we have a pseudo-extension on it due to builtins
278 if (cppBase->internalName() == u"QObject"_s)
279 break;
280 if (cppBase->extensionType().extensionSpecifier != QQmlJSScope::NotExtension) {
281 hasExtension = true;
282 break;
283 }
284 }
285 if (hasExtension) {
286 current.init.body << u"{"_s;
287 current.init.body << u"auto cppData = QmltcTypeData(this);"_s;
288 current.init.body << u"qmltcCreateDynamicMetaObject(this, cppData);"_s;
289 current.init.body << u"}"_s;
290 }
291
292 const auto generateFinalLines = [&current, isDocumentRoot, isInlineComponent]() {
293 if (isDocumentRoot || isInlineComponent) {
294 current.init.body << u"// 4. finish the document root creation"_s;
295 current.init.body << u"if (canFinalize) {"_s;
296 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
297 .arg(a: current.beginClass.name);
298 current.init.body << QStringLiteral(" %1(creator, engine);")
299 .arg(a: current.endInit.name);
300
301 current.init.body << QStringLiteral(" {");
302 current.init.body << QStringLiteral(" PropertyInitializer propertyInitializer(*this);");
303 current.init.body << QStringLiteral(" initializer(propertyInitializer);");
304 current.init.body << QStringLiteral(" %1(creator, engine, propertyInitializer.initializedCache);").arg(a: current.setComplexBindings.name);
305 current.init.body << QStringLiteral(" }");
306
307
308 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
309 .arg(a: current.completeComponent.name);
310 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
311 .arg(a: current.finalizeComponent.name);
312 current.init.body << QStringLiteral(" %1(creator);")
313 .arg(a: current.handleOnCompleted.name);
314 current.init.body << u"}"_s;
315 }
316 current.init.body << u"return context;"_s;
317 };
318
319 return QScopeGuard(generateFinalLines);
320}
321
322/*!
323 \internal
324
325 Generates \a{current.init}'s code in case when \a type is a top-level
326 Component type. The init method in this case mimics
327 QQmlObjectCreator::createComponent() logic.
328
329 \sa generate_initCode
330*/
331inline void
332QmltcCodeGenerator::generate_initCodeForTopLevelComponent(QmltcType &current,
333 const QQmlJSScope::ConstPtr &type)
334{
335 Q_UNUSED(type);
336
337 using namespace Qt::StringLiterals;
338
339 // since we create a document root as QQmlComponent, we only need to fake
340 // QQmlComponent construction in init:
341 current.init.body << u"// init QQmlComponent: see QQmlObjectCreator::createComponent()"_s;
342 current.init.body << u"{"_s;
343 // we already called QQmlComponent(parent) constructor. now we need:
344 // 1. QQmlComponent(engine, parent) logic:
345 current.init.body << u"// QQmlComponent(engine, parent):"_s;
346 current.init.body << u"auto d = QQmlComponentPrivate::get(this);"_s;
347 current.init.body << u"Q_ASSERT(d);"_s;
348 current.init.body << u"d->engine = engine;"_s;
349 current.init.body << u"QObject::connect(engine, &QObject::destroyed, this, [d]() {"_s;
350 current.init.body << u" d->state.creator.reset();"_s;
351 current.init.body << u" d->engine = nullptr;"_s;
352 current.init.body << u"});"_s;
353 // 2. QQmlComponent(engine, compilationUnit, start, parent) logic:
354 current.init.body << u"// QQmlComponent(engine, compilationUnit, start, parent):"_s;
355 current.init.body
356 << u"auto compilationUnit = QQmlEnginePrivate::get(engine)->compilationUnitFromUrl("
357 + QmltcCodeGenerator::urlMethodName() + u"());";
358 current.init.body << u"d->compilationUnit = compilationUnit;"_s;
359 current.init.body << u"d->start = 0;"_s;
360 current.init.body << u"d->url = compilationUnit->finalUrl();"_s;
361 current.init.body << u"d->progress = 1.0;"_s;
362 // 3. QQmlObjectCreator::createComponent() logic which is left:
363 current.init.body << u"// QQmlObjectCreator::createComponent():"_s;
364 current.init.body << u"d->creationContext = context;"_s;
365 current.init.body << u"Q_ASSERT(QQmlData::get(this, /*create*/ false));"_s;
366 current.init.body << u"}"_s;
367}
368
369/*!
370 \internal
371
372 A generic helper function that generates special qmltc instruction code
373 boilerplate, adding it to a passed \a function. This is a building block
374 used to generate e.g. QML_endInit code.
375*/
376inline void QmltcCodeGenerator::generate_qmltcInstructionCallCode(
377 QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
378 const QString &baseInstructionArgs, const QString &childInstructionArgs) const
379{
380 using namespace Qt::StringLiterals;
381
382 if (auto base = type->baseType(); base->isComposite()) {
383 function->body << u"// call base's method"_s;
384 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
385 return qmlType == type;
386 };
387 const QString creationOffset =
388 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
389 function->body << u"{"_s;
390 function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
391 a: creationOffset);
392 if (!baseInstructionArgs.isEmpty()) {
393 function->body << u"%1::%2(&subCreator, %3);"_s.arg(
394 args: base->internalName(), args&: function->name, args: baseInstructionArgs);
395 } else {
396 function->body << u"%1::%2(&subCreator);"_s.arg(args: base->internalName(), args&: function->name);
397 }
398 function->body << u"}"_s;
399 }
400
401 const bool isDocumentRoot = type == visitor->result();
402 const bool isInlineComponent = type->isInlineComponent();
403
404 if (!(isDocumentRoot
405 || isInlineComponent)) // document/inline component root does all the work here
406 return;
407 auto name = isInlineComponent
408 ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
409 : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
410 const auto types = visitor->pureQmlTypes(inlineComponent: name);
411 function->body << u"// call children's methods"_s;
412 for (qsizetype i = 1; i < types.size(); ++i) {
413 const auto &type = types[i];
414 Q_ASSERT(type->componentRootStatus() == QQmlJSScope::IsComponentRoot::No);
415 function->body << u"creator->get<%1>(%2)->%3(%4);"_s.arg(
416 args: type->internalName(), args: QString::number(i), args&: function->name, args: childInstructionArgs);
417 }
418 function->body << u"// call own method code"_s;
419}
420
421/*!
422 \internal
423
424 Generates \a{current.endInit}'s code. The endInit method creates bindings,
425 connects signals with slots and generally performs other within-object
426 initialization. Additionally, the QML document root's endInit calls endInit
427 methods of all the necessary QML types within the document.
428*/
429inline void QmltcCodeGenerator::generate_endInitCode(QmltcType &current,
430 const QQmlJSScope::ConstPtr &type) const
431{
432 using namespace Qt::StringLiterals;
433
434 // QML_endInit()'s parameters:
435 // * QQmltcObjectCreationHelper* creator
436 // * QQmlEngine* engine
437 current.endInit.body << u"Q_UNUSED(creator)"_s;
438 current.endInit.body << u"Q_UNUSED(engine)"_s;
439
440 generate_qmltcInstructionCallCode(function: &current.endInit, type, baseInstructionArgs: u"engine"_s, childInstructionArgs: u"creator, engine"_s);
441
442 if (visitor->hasDeferredBindings(type)) {
443 QString icName;
444 if (auto potentialICName = type->enclosingInlineComponentName();
445 std::holds_alternative<QQmlJSScope::InlineComponentNameType>(v: potentialICName))
446 icName =get<QQmlJSScope::InlineComponentNameType>(v&: potentialICName);
447 else
448 icName = u"{}"_s;
449 current.endInit.body << u"{ // defer bindings"_s;
450 current.endInit.body << u"auto ddata = QQmlData::get(this);"_s;
451 current.endInit.body << u"auto thisContext = ddata->outerContext;"_s;
452 current.endInit.body << u"Q_ASSERT(thisContext);"_s;
453 current.endInit.body << QStringLiteral("ddata->deferData(%1, "
454 "QQmlEnginePrivate::get(engine)->"
455 "compilationUnitFromUrl(%2()), thisContext, %3);")
456 .arg(args: QString::number(visitor->qmlIrObjectIndex(type)),
457 args: QmltcCodeGenerator::urlMethodName(), args&: icName);
458 current.endInit.body << u"}"_s;
459 }
460}
461
462/*!
463 \internal
464
465 Generates \a{current.setComplexBindings}'s code. The setComplexBindings
466 method creates complex bindings (such as script bindings). Additionally, the
467 QML document root's setComplexBindings calls setComplexBindings methods of
468 all the necessary QML types within the document.
469*/
470inline void
471QmltcCodeGenerator::generate_setComplexBindingsCode(QmltcType &current,
472 const QQmlJSScope::ConstPtr &type) const
473{
474 using namespace Qt::StringLiterals;
475
476 // QML_setComplexBindings()'s parameters:
477 // * QQmltcObjectCreationHelper* creator
478 // * QQmlEngine* engine
479 current.setComplexBindings.body << u"Q_UNUSED(creator)"_s;
480 current.setComplexBindings.body << u"Q_UNUSED(engine)"_s;
481
482 generate_qmltcInstructionCallCode(function: &current.setComplexBindings, type, baseInstructionArgs: u"engine"_s,
483 childInstructionArgs: u"creator, engine"_s);
484}
485
486/*!
487 \internal
488
489 A generic helper function that generates interface code boilerplate, adding
490 it to a passed \a function. This is a building block used to generate e.g.
491 QQmlParserStatus API calls.
492*/
493inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function,
494 const QQmlJSScope::ConstPtr &type,
495 const QString &interfaceName,
496 const QString &interfaceCall) const
497{
498 using namespace Qt::StringLiterals;
499
500 // function's parameters:
501 // * QQmltcObjectCreationHelper* creator
502 // * bool canFinalize [optional, when document root or inline component root]
503 const bool isDocumentRoot = type == visitor->result();
504 const bool isInlineComponent = type->isInlineComponent();
505 function->body << u"Q_UNUSED(creator)"_s;
506 if (isDocumentRoot || isInlineComponent)
507 function->body << u"Q_UNUSED(canFinalize)"_s;
508
509 if (auto base = type->baseType(); base->isComposite()) {
510 function->body << u"// call base's method"_s;
511 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
512 return qmlType == type;
513 };
514 const QString creationOffset =
515 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
516 function->body << u"{"_s;
517 function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
518 a: creationOffset);
519 function->body << u"%1::%2(&subCreator, /* finalize */ false);"_s.arg(args: base->internalName(),
520 args&: function->name);
521 function->body << u"}"_s;
522 }
523
524 if (!(isDocumentRoot || isInlineComponent))
525 return;
526
527 auto name = isInlineComponent
528 ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
529 : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
530
531 const auto types = visitor->pureQmlTypes(inlineComponent: name);
532 function->body << u"// call children's methods"_s;
533 for (qsizetype i = 1; i < types.size(); ++i) {
534 const auto &type = types[i];
535 Q_ASSERT(type->componentRootStatus() == QQmlJSScope::IsComponentRoot::No);
536 function->body << u"{"_s;
537 function->body << u"auto child = creator->get<%1>(%2);"_s.arg(args: type->internalName(),
538 args: QString::number(i));
539 function->body << u"child->%1(creator);"_s.arg(a: function->name);
540 if (type->hasInterface(name: interfaceName)) {
541 function->body << u"static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
542 args: interfaceName, args: type->internalName());
543 function->body << u"child->%1();"_s.arg(a: interfaceCall);
544 }
545 function->body << u"}"_s;
546 }
547
548 if (type->hasInterface(name: interfaceName)) {
549 function->body << u"if (canFinalize) {"_s;
550 function->body << u" // call own method"_s;
551 function->body << u" static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
552 args: interfaceName, args: type->internalName());
553 function->body << u" this->%1();"_s.arg(a: interfaceCall);
554 function->body << u"}"_s;
555 }
556}
557
558/*!
559 \internal
560
561 Generates \a{current.beginClass}'s code. The beginClass method optionally
562 calls QQmlParserStatus::classBegin() when \a type implements the
563 corresponding interface.
564*/
565inline void QmltcCodeGenerator::generate_beginClassCode(QmltcType &current,
566 const QQmlJSScope::ConstPtr &type) const
567{
568 using namespace Qt::StringLiterals;
569 generate_interfaceCallCode(function: &current.beginClass, type, interfaceName: u"QQmlParserStatus"_s, interfaceCall: u"classBegin"_s);
570}
571
572/*!
573 \internal
574
575 Generates \a{current.completeComponent}'s code. The completeComponent method
576 optionally calls QQmlParserStatus::componentComplete() when \a type
577 implements the corresponding interface.
578*/
579inline void
580QmltcCodeGenerator::generate_completeComponentCode(QmltcType &current,
581 const QQmlJSScope::ConstPtr &type) const
582{
583 using namespace Qt::StringLiterals;
584 generate_interfaceCallCode(function: &current.completeComponent, type, interfaceName: u"QQmlParserStatus"_s,
585 interfaceCall: u"componentComplete"_s);
586}
587
588/*!
589 \internal
590
591 Generates \a{current.finalizeComponent}'s code. The finalizeComponent method
592 optionally calls QQmlFinalizerHook::componentFinalized() when \a type
593 implements the corresponding interface.
594*/
595inline void
596QmltcCodeGenerator::generate_finalizeComponentCode(QmltcType &current,
597 const QQmlJSScope::ConstPtr &type) const
598{
599 using namespace Qt::StringLiterals;
600 generate_interfaceCallCode(function: &current.finalizeComponent, type, interfaceName: u"QQmlFinalizerHook"_s,
601 interfaceCall: u"componentFinalized"_s);
602}
603
604/*!
605 \internal
606
607 Generates \a{current.handleOnCompleted}'s code. The handleOnCompleted method
608 optionally calls a Component.onCompleted handler if that is present in \a
609 type.
610*/
611inline void
612QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType &current,
613 const QQmlJSScope::ConstPtr &type) const
614{
615 using namespace Qt::StringLiterals;
616
617 // QML_handleOnCompleted()'s parameters:
618 // * QQmltcObjectCreationHelper* creator
619 current.handleOnCompleted.body << u"Q_UNUSED(creator)"_s;
620
621 generate_qmltcInstructionCallCode(function: &current.handleOnCompleted, type, baseInstructionArgs: QString(), childInstructionArgs: u"creator"_s);
622}
623
624/*!
625 \internal
626
627 Generates a constexpr function consisting of a sum of type counts for a
628 current QML document. Predicate \a p acts as a stop condition to prematurely
629 end the sum generation.
630
631 The high-level idea:
632
633 Each qmltc-compiled document root has a notion of type count. Type count is
634 a number of types the current QML document contains (except for
635 Component-wrapped types) plus the sum of all type counts of all the QML
636 documents used in the current document: if current document has a type with
637 QML base type, this type's type count is added to the type count of the
638 current document.
639
640 To be able to lookup created objects during the creation process, one needs
641 to know an index of each object within the document + an offset of the
642 document. Index comes from QmltcVisitor and is basically a serial number of
643 a type in the document (index < type count of the document root type). The
644 offset is more indirect.
645
646 The current document always starts with an offset of 0, each type that has a
647 QML base type also "has a sub-document". Each sub-document has a non-0
648 offset X, where X is calculated as a sum of the current document's type
649 count and a cumulative type count of all the previous sub-documents that
650 appear before the sub-document of interest:
651
652 \code
653 // A.qml
654 Item { // offset: 0; number of types == 1 (document root) + 3 (children)
655
656 QmlBase1 { } // offset: 4 (number of types in A.qml itself)
657
658 QmlBase2 { } // offset: 4 + N, where N == typeCount(QmlBase1.qml)
659
660 QmlBase3 { } // offset: (4 + N) + M, where M == typeCount(QmlBase2.qml)
661
662 } // typeCount(A.qml) == 4 + N + M + O, where O == typeCount(QmlBase3.qml)
663 \endcode
664
665 As all objects are put into an array, schematically you can look at it in
666 the following way:
667
668 ```
669 count: 4 N M O
670 objects: aaaa|xxxxxxxxxxxxx|yyyyyyy|zzz
671 ^ ^ ^ ^
672 files: | QmlBase1.qml | QmlBase3.qml
673 A.qml QmlBase2.qml
674 ```
675
676 For the object lookup logic itself, see QQmltcObjectCreationHelper
677*/
678template<typename Predicate>
679inline QString QmltcCodeGenerator::generate_typeCount(
680 Predicate p, const InlineComponentOrDocumentRootName &inlinedComponent) const
681{
682 using namespace Qt::StringLiterals;
683
684 const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount =
685 visitor->qmlTypesWithQmlBases(inlinedComponentName: inlinedComponent);
686 QStringList components;
687 components.reserve(asize: 1 + typesWithBaseTypeCount.size());
688
689 Q_ASSERT(visitor->pureQmlTypes(inlinedComponent).size() > 0);
690 Q_ASSERT(visitor->typeCount(inlinedComponent)
691 >= visitor->pureQmlTypes(inlinedComponent).size());
692 qsizetype typeCount = visitor->typeCount(inlineComponent: inlinedComponent);
693
694 // add this document's type counts minus document root (if not an inline component)
695 if (std::holds_alternative<RootDocumentNameType>(v: inlinedComponent))
696 typeCount--;
697 components << QString::number(typeCount);
698
699 // traverse types with QML base classes
700 for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) {
701 if (p(t))
702 break;
703 QString typeCountTemplate = u"QQmltcObjectCreationHelper::typeCount<%1>()"_s;
704 if (t == visitor->result()) { // t is this document's root
705 components << typeCountTemplate.arg(a: t->baseTypeName());
706 } else if (t->isInlineComponent()) {
707 // inline components always have a base class, by definition
708 Q_ASSERT(t->baseType());
709 components << typeCountTemplate.arg(a: t->baseType()->internalName());
710 } else {
711 components << typeCountTemplate.arg(a: t->internalName());
712 }
713 }
714
715 return components.join(sep: u" + "_s);
716}
717
718QT_END_NAMESPACE
719
720#endif // QMLTCCOMPILERPIECES_H
721

source code of qtdeclarative/tools/qmltc/qmltccompilerpieces.h