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 QQmlContext for
156 the object and (in case \a type is a document root) calls other object
157 creation methods in a well-defined order:
158 1. current.beginClass
159 2. current.endInit
160 3. current.completeComponent
161 4. current.finalizeComponent
162 5. current.handleOnCompleted
163
164 This function returns a QScopeGuard with the final instructions that have to
165 be generated at a later point, once everything else is compiled.
166
167 \sa generate_initCodeForTopLevelComponent
168*/
169inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType &current,
170 const QQmlJSScope::ConstPtr &type) const
171{
172 using namespace Qt::StringLiterals;
173
174 // qmltc_init()'s parameters:
175 // * QQmltcObjectCreationHelper* creator
176 // * QQmlEngine* engine
177 // * const QQmlRefPointer<QQmlContextData>& parentContext
178 // * bool canFinalize [optional, when document root]
179 const bool isDocumentRoot = type == visitor->result();
180 const bool isInlineComponent = type->isInlineComponent();
181
182 current.init.body << u"Q_UNUSED(creator)"_s; // can happen sometimes
183
184 current.init.body << u"auto context = parentContext;"_s;
185
186 // if parent scope has a QML base type and is not a (current) document root,
187 // the parentContext we passed as input to this object is a context of
188 // another document. we need to fix it by using parentContext->parent()
189
190 const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) {
191 if (scope->isArrayScope())
192 return scope->parentScope();
193 return scope;
194 };
195
196 if (auto parentScope = realQmlScope(type->parentScope());
197 parentScope != visitor->result() && QQmlJSUtils::hasCompositeBase(scope: parentScope)) {
198 current.init.body << u"// NB: context->parent() is the context of this document"_s;
199 current.init.body << u"context = context->parent();"_s;
200 }
201
202 // any object with QML base class has to call base's init method
203 if (auto base = type->baseType(); base->isComposite()) {
204 QString lhs;
205 // init creates new context. for document root, it's going to be a real
206 // parent context, so store it temporarily in `context` variable
207 if (isDocumentRoot || isInlineComponent)
208 lhs = u"context = "_s;
209 current.init.body << u"// 0. call base's init method"_s;
210
211 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
212 return qmlType == type;
213 };
214 const QString creationOffset =
215 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
216
217 current.init.body << u"{"_s;
218 current.init.body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
219 a: creationOffset);
220 current.init.body
221 << QStringLiteral("%1%2::%3(&subCreator, engine, context, /* finalize */ false);")
222 .arg(args&: lhs, args: base->internalName(), args&: current.init.name);
223 current.init.body << u"}"_s;
224 }
225
226 current.init.body
227 << QStringLiteral("auto %1 = QQmlEnginePrivate::get(engine);").arg(a: privateEngineName);
228 current.init.body << QStringLiteral("Q_UNUSED(%1)").arg(a: privateEngineName); // precaution
229
230 // when generating root or inlineComponents, we need to create a new (document-level) context.
231 // otherwise, just use existing context as is
232 if (isDocumentRoot || isInlineComponent) {
233 current.init.body << u"// 1. create new QML context for this document"_s;
234 current.init.body
235 << QStringLiteral(
236 "context = %1->createInternalContext(%1->compilationUnitFromUrl(%2()), "
237 "context, %3, true);")
238 .arg(args: privateEngineName, args: urlMethodName())
239 .arg(a: this->visitor->creationIndex(type));
240 } else {
241 current.init.body << u"// 1. use current context as this object's context"_s;
242 current.init.body << u"// context = context;"_s;
243 }
244
245 if (!type->baseType()->isComposite() || isDocumentRoot || isInlineComponent) {
246 current.init.body << u"// 2. set context for this object"_s;
247 current.init.body << QStringLiteral(
248 "%1->setInternalContext(this, context, QQmlContextData::%2);")
249 .arg(args: privateEngineName,
250 args: (isDocumentRoot ? u"DocumentRoot"_s
251 : u"OrdinaryObject"_s));
252 if (isDocumentRoot || isInlineComponent)
253 current.init.body << u"context->setContextObject(this);"_s;
254 }
255
256 // context is this document's context. we must remember it in each type
257 current.variables.emplaceBack(args: u"QQmlRefPointer<QQmlContextData>"_s, args: u"q_qmltc_thisContext"_s,
258 args: u"nullptr"_s);
259 current.init.body << u"%1::q_qmltc_thisContext = context;"_s.arg(a: type->internalName());
260
261 if (int id = visitor->runtimeId(type); id >= 0) {
262 current.init.body << u"// 3. set id since it is provided"_s;
263 QString idString = visitor->addressableScopes().id(scope: type, referrer: type);
264 if (idString.isEmpty())
265 idString = u"<unknown>"_s;
266 QmltcCodeGenerator::generate_setIdValue(block: &current.init.body, context: u"context"_s, index: id, accessor: u"this"_s,
267 idString);
268 }
269
270 // if type has an extension, create a dynamic meta object for it
271 bool hasExtension = false;
272 for (auto cppBase = QQmlJSScope::nonCompositeBaseType(type); cppBase;
273 cppBase = cppBase->baseType()) {
274 // QObject is special: we have a pseudo-extension on it due to builtins
275 if (cppBase->internalName() == u"QObject"_s)
276 break;
277 if (cppBase->extensionType().extensionSpecifier != QQmlJSScope::NotExtension) {
278 hasExtension = true;
279 break;
280 }
281 }
282 if (hasExtension) {
283 current.init.body << u"{"_s;
284 current.init.body << u"auto cppData = QmltcTypeData(this);"_s;
285 current.init.body << u"qmltcCreateDynamicMetaObject(this, cppData);"_s;
286 current.init.body << u"}"_s;
287 }
288
289 const auto generateFinalLines = [&current, isDocumentRoot, isInlineComponent]() {
290 if (isDocumentRoot || isInlineComponent) {
291 current.init.body << u"// 4. finish the document root creation"_s;
292 current.init.body << u"if (canFinalize) {"_s;
293 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
294 .arg(a: current.beginClass.name);
295 current.init.body << QStringLiteral(" %1(creator, engine);")
296 .arg(a: current.endInit.name);
297 current.init.body << QStringLiteral(" %1(creator, engine);")
298 .arg(a: current.setComplexBindings.name);
299 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
300 .arg(a: current.completeComponent.name);
301 current.init.body << QStringLiteral(" %1(creator, /* finalize */ true);")
302 .arg(a: current.finalizeComponent.name);
303 current.init.body << QStringLiteral(" %1(creator);")
304 .arg(a: current.handleOnCompleted.name);
305 current.init.body << u"}"_s;
306 }
307 current.init.body << u"return context;"_s;
308 };
309
310 return QScopeGuard(generateFinalLines);
311}
312
313/*!
314 \internal
315
316 Generates \a{current.init}'s code in case when \a type is a top-level
317 Component type. The init method in this case mimics
318 QQmlObjectCreator::createComponent() logic.
319
320 \sa generate_initCode
321*/
322inline void
323QmltcCodeGenerator::generate_initCodeForTopLevelComponent(QmltcType &current,
324 const QQmlJSScope::ConstPtr &type)
325{
326 Q_UNUSED(type);
327
328 using namespace Qt::StringLiterals;
329
330 // since we create a document root as QQmlComponent, we only need to fake
331 // QQmlComponent construction in init:
332 current.init.body << u"// init QQmlComponent: see QQmlObjectCreator::createComponent()"_s;
333 current.init.body << u"{"_s;
334 // we already called QQmlComponent(parent) constructor. now we need:
335 // 1. QQmlComponent(engine, parent) logic:
336 current.init.body << u"// QQmlComponent(engine, parent):"_s;
337 current.init.body << u"auto d = QQmlComponentPrivate::get(this);"_s;
338 current.init.body << u"Q_ASSERT(d);"_s;
339 current.init.body << u"d->engine = engine;"_s;
340 current.init.body << u"QObject::connect(engine, &QObject::destroyed, this, [d]() {"_s;
341 current.init.body << u" d->state.creator.reset();"_s;
342 current.init.body << u" d->engine = nullptr;"_s;
343 current.init.body << u"});"_s;
344 // 2. QQmlComponent(engine, compilationUnit, start, parent) logic:
345 current.init.body << u"// QQmlComponent(engine, compilationUnit, start, parent):"_s;
346 current.init.body
347 << u"auto compilationUnit = QQmlEnginePrivate::get(engine)->compilationUnitFromUrl("
348 + QmltcCodeGenerator::urlMethodName() + u"());";
349 current.init.body << u"d->compilationUnit = compilationUnit;"_s;
350 current.init.body << u"d->start = 0;"_s;
351 current.init.body << u"d->url = compilationUnit->finalUrl();"_s;
352 current.init.body << u"d->progress = 1.0;"_s;
353 // 3. QQmlObjectCreator::createComponent() logic which is left:
354 current.init.body << u"// QQmlObjectCreator::createComponent():"_s;
355 current.init.body << u"d->creationContext = context;"_s;
356 current.init.body << u"Q_ASSERT(QQmlData::get(this, /*create*/ false));"_s;
357 current.init.body << u"}"_s;
358}
359
360/*!
361 \internal
362
363 A generic helper function that generates special qmltc instruction code
364 boilerplate, adding it to a passed \a function. This is a building block
365 used to generate e.g. QML_endInit code.
366*/
367inline void QmltcCodeGenerator::generate_qmltcInstructionCallCode(
368 QmltcMethod *function, const QQmlJSScope::ConstPtr &type,
369 const QString &baseInstructionArgs, const QString &childInstructionArgs) const
370{
371 using namespace Qt::StringLiterals;
372
373 if (auto base = type->baseType(); base->isComposite()) {
374 function->body << u"// call base's method"_s;
375 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
376 return qmlType == type;
377 };
378 const QString creationOffset =
379 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
380 function->body << u"{"_s;
381 function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
382 a: creationOffset);
383 if (!baseInstructionArgs.isEmpty()) {
384 function->body << u"%1::%2(&subCreator, %3);"_s.arg(
385 args: base->internalName(), args&: function->name, args: baseInstructionArgs);
386 } else {
387 function->body << u"%1::%2(&subCreator);"_s.arg(args: base->internalName(), args&: function->name);
388 }
389 function->body << u"}"_s;
390 }
391
392 const bool isDocumentRoot = type == visitor->result();
393 const bool isInlineComponent = type->isInlineComponent();
394
395 if (!(isDocumentRoot
396 || isInlineComponent)) // document/inline component root does all the work here
397 return;
398 auto name = isInlineComponent
399 ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
400 : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
401 const auto types = visitor->pureQmlTypes(inlineComponent: name);
402 function->body << u"// call children's methods"_s;
403 for (qsizetype i = 1; i < types.size(); ++i) {
404 const auto &type = types[i];
405 Q_ASSERT(!type->isComponentRootElement());
406 function->body << u"creator->get<%1>(%2)->%3(%4);"_s.arg(
407 args: type->internalName(), args: QString::number(i), args&: function->name, args: childInstructionArgs);
408 }
409 function->body << u"// call own method code"_s;
410}
411
412/*!
413 \internal
414
415 Generates \a{current.endInit}'s code. The endInit method creates bindings,
416 connects signals with slots and generally performs other within-object
417 initialization. Additionally, the QML document root's endInit calls endInit
418 methods of all the necessary QML types within the document.
419*/
420inline void QmltcCodeGenerator::generate_endInitCode(QmltcType &current,
421 const QQmlJSScope::ConstPtr &type) const
422{
423 using namespace Qt::StringLiterals;
424
425 // QML_endInit()'s parameters:
426 // * QQmltcObjectCreationHelper* creator
427 // * QQmlEngine* engine
428 current.endInit.body << u"Q_UNUSED(creator)"_s;
429 current.endInit.body << u"Q_UNUSED(engine)"_s;
430
431 generate_qmltcInstructionCallCode(function: &current.endInit, type, baseInstructionArgs: u"engine"_s, childInstructionArgs: u"creator, engine"_s);
432
433 if (visitor->hasDeferredBindings(type)) {
434 current.endInit.body << u"{ // defer bindings"_s;
435 current.endInit.body << u"auto ddata = QQmlData::get(this);"_s;
436 current.endInit.body << u"auto thisContext = ddata->outerContext;"_s;
437 current.endInit.body << u"Q_ASSERT(thisContext);"_s;
438 current.endInit.body << QStringLiteral("ddata->deferData(%1, "
439 "QQmlEnginePrivate::get(engine)->"
440 "compilationUnitFromUrl(%2()), thisContext);")
441 .arg(args: QString::number(visitor->qmlIrObjectIndex(type)),
442 args: QmltcCodeGenerator::urlMethodName());
443 current.endInit.body << u"}"_s;
444 }
445}
446
447/*!
448 \internal
449
450 Generates \a{current.setComplexBindings}'s code. The setComplexBindings
451 method creates complex bindings (such as script bindings). Additionally, the
452 QML document root's setComplexBindings calls setComplexBindings methods of
453 all the necessary QML types within the document.
454*/
455inline void
456QmltcCodeGenerator::generate_setComplexBindingsCode(QmltcType &current,
457 const QQmlJSScope::ConstPtr &type) const
458{
459 using namespace Qt::StringLiterals;
460
461 // QML_setComplexBindings()'s parameters:
462 // * QQmltcObjectCreationHelper* creator
463 // * QQmlEngine* engine
464 current.setComplexBindings.body << u"Q_UNUSED(creator)"_s;
465 current.setComplexBindings.body << u"Q_UNUSED(engine)"_s;
466
467 generate_qmltcInstructionCallCode(function: &current.setComplexBindings, type, baseInstructionArgs: u"engine"_s,
468 childInstructionArgs: u"creator, engine"_s);
469}
470
471/*!
472 \internal
473
474 A generic helper function that generates interface code boilerplate, adding
475 it to a passed \a function. This is a building block used to generate e.g.
476 QQmlParserStatus API calls.
477*/
478inline void QmltcCodeGenerator::generate_interfaceCallCode(QmltcMethod *function,
479 const QQmlJSScope::ConstPtr &type,
480 const QString &interfaceName,
481 const QString &interfaceCall) const
482{
483 using namespace Qt::StringLiterals;
484
485 // function's parameters:
486 // * QQmltcObjectCreationHelper* creator
487 // * bool canFinalize [optional, when document root or inline component root]
488 const bool isDocumentRoot = type == visitor->result();
489 const bool isInlineComponent = type->isInlineComponent();
490 function->body << u"Q_UNUSED(creator)"_s;
491 if (isDocumentRoot || isInlineComponent)
492 function->body << u"Q_UNUSED(canFinalize)"_s;
493
494 if (auto base = type->baseType(); base->isComposite()) {
495 function->body << u"// call base's method"_s;
496 const auto isCurrentType = [&](const QQmlJSScope::ConstPtr &qmlType) {
497 return qmlType == type;
498 };
499 const QString creationOffset =
500 generate_typeCount(p: isCurrentType, inlinedComponent: type->enclosingInlineComponentName());
501 function->body << u"{"_s;
502 function->body << u"QQmltcObjectCreationHelper subCreator(creator, %1);"_s.arg(
503 a: creationOffset);
504 function->body << u"%1::%2(&subCreator, /* finalize */ false);"_s.arg(args: base->internalName(),
505 args&: function->name);
506 function->body << u"}"_s;
507 }
508
509 if (!(isDocumentRoot || isInlineComponent))
510 return;
511
512 auto name = isInlineComponent
513 ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
514 : InlineComponentOrDocumentRootName(QQmlJSScope::RootDocumentNameType());
515
516 const auto types = visitor->pureQmlTypes(inlineComponent: name);
517 function->body << u"// call children's methods"_s;
518 for (qsizetype i = 1; i < types.size(); ++i) {
519 const auto &type = types[i];
520 Q_ASSERT(!type->isComponentRootElement());
521 function->body << u"{"_s;
522 function->body << u"auto child = creator->get<%1>(%2);"_s.arg(args: type->internalName(),
523 args: QString::number(i));
524 function->body << u"child->%1(creator);"_s.arg(a: function->name);
525 if (type->hasInterface(name: interfaceName)) {
526 function->body << u"static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
527 args: interfaceName, args: type->internalName());
528 function->body << u"child->%1();"_s.arg(a: interfaceCall);
529 }
530 function->body << u"}"_s;
531 }
532
533 if (type->hasInterface(name: interfaceName)) {
534 function->body << u"if (canFinalize) {"_s;
535 function->body << u" // call own method"_s;
536 function->body << u" static_assert(std::is_base_of<%1, %2>::value);"_s.arg(
537 args: interfaceName, args: type->internalName());
538 function->body << u" this->%1();"_s.arg(a: interfaceCall);
539 function->body << u"}"_s;
540 }
541}
542
543/*!
544 \internal
545
546 Generates \a{current.beginClass}'s code. The beginClass method optionally
547 calls QQmlParserStatus::classBegin() when \a type implements the
548 corresponding interface.
549*/
550inline void QmltcCodeGenerator::generate_beginClassCode(QmltcType &current,
551 const QQmlJSScope::ConstPtr &type) const
552{
553 using namespace Qt::StringLiterals;
554 generate_interfaceCallCode(function: &current.beginClass, type, interfaceName: u"QQmlParserStatus"_s, interfaceCall: u"classBegin"_s);
555}
556
557/*!
558 \internal
559
560 Generates \a{current.completeComponent}'s code. The completeComponent method
561 optionally calls QQmlParserStatus::componentComplete() when \a type
562 implements the corresponding interface.
563*/
564inline void
565QmltcCodeGenerator::generate_completeComponentCode(QmltcType &current,
566 const QQmlJSScope::ConstPtr &type) const
567{
568 using namespace Qt::StringLiterals;
569 generate_interfaceCallCode(function: &current.completeComponent, type, interfaceName: u"QQmlParserStatus"_s,
570 interfaceCall: u"componentComplete"_s);
571}
572
573/*!
574 \internal
575
576 Generates \a{current.finalizeComponent}'s code. The finalizeComponent method
577 optionally calls QQmlFinalizerHook::componentFinalized() when \a type
578 implements the corresponding interface.
579*/
580inline void
581QmltcCodeGenerator::generate_finalizeComponentCode(QmltcType &current,
582 const QQmlJSScope::ConstPtr &type) const
583{
584 using namespace Qt::StringLiterals;
585 generate_interfaceCallCode(function: &current.finalizeComponent, type, interfaceName: u"QQmlFinalizerHook"_s,
586 interfaceCall: u"componentFinalized"_s);
587}
588
589/*!
590 \internal
591
592 Generates \a{current.handleOnCompleted}'s code. The handleOnCompleted method
593 optionally calls a Component.onCompleted handler if that is present in \a
594 type.
595*/
596inline void
597QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType &current,
598 const QQmlJSScope::ConstPtr &type) const
599{
600 using namespace Qt::StringLiterals;
601
602 // QML_handleOnCompleted()'s parameters:
603 // * QQmltcObjectCreationHelper* creator
604 current.handleOnCompleted.body << u"Q_UNUSED(creator)"_s;
605
606 generate_qmltcInstructionCallCode(function: &current.handleOnCompleted, type, baseInstructionArgs: QString(), childInstructionArgs: u"creator"_s);
607}
608
609/*!
610 \internal
611
612 Generates a constexpr function consisting of a sum of type counts for a
613 current QML document. Predicate \a p acts as a stop condition to prematurely
614 end the sum generation.
615
616 The high-level idea:
617
618 Each qmltc-compiled document root has a notion of type count. Type count is
619 a number of types the current QML document contains (except for
620 Component-wrapped types) plus the sum of all type counts of all the QML
621 documents used in the current document: if current document has a type with
622 QML base type, this type's type count is added to the type count of the
623 current document.
624
625 To be able to lookup created objects during the creation process, one needs
626 to know an index of each object within the document + an offset of the
627 document. Index comes from QmltcVisitor and is basically a serial number of
628 a type in the document (index < type count of the document root type). The
629 offset is more indirect.
630
631 The current document always starts with an offset of 0, each type that has a
632 QML base type also "has a sub-document". Each sub-document has a non-0
633 offset X, where X is calculated as a sum of the current document's type
634 count and a cumulative type count of all the previous sub-documents that
635 appear before the sub-document of interest:
636
637 \code
638 // A.qml
639 Item { // offset: 0; number of types == 1 (document root) + 3 (children)
640
641 QmlBase1 { } // offset: 4 (number of types in A.qml itself)
642
643 QmlBase2 { } // offset: 4 + N, where N == typeCount(QmlBase1.qml)
644
645 QmlBase3 { } // offset: (4 + N) + M, where M == typeCount(QmlBase2.qml)
646
647 } // typeCount(A.qml) == 4 + N + M + O, where O == typeCount(QmlBase3.qml)
648 \endcode
649
650 As all objects are put into an array, schematically you can look at it in
651 the following way:
652
653 ```
654 count: 4 N M O
655 objects: aaaa|xxxxxxxxxxxxx|yyyyyyy|zzz
656 ^ ^ ^ ^
657 files: | QmlBase1.qml | QmlBase3.qml
658 A.qml QmlBase2.qml
659 ```
660
661 For the object lookup logic itself, see QQmltcObjectCreationHelper
662*/
663template<typename Predicate>
664inline QString QmltcCodeGenerator::generate_typeCount(
665 Predicate p, const InlineComponentOrDocumentRootName &inlinedComponent) const
666{
667 using namespace Qt::StringLiterals;
668
669 const QList<QQmlJSScope::ConstPtr> typesWithBaseTypeCount =
670 visitor->qmlTypesWithQmlBases(inlinedComponentName: inlinedComponent);
671 QStringList components;
672 components.reserve(asize: 1 + typesWithBaseTypeCount.size());
673
674 Q_ASSERT(visitor->pureQmlTypes(inlinedComponent).size() > 0);
675 Q_ASSERT(visitor->typeCount(inlinedComponent)
676 >= visitor->pureQmlTypes(inlinedComponent).size());
677 qsizetype typeCount = visitor->typeCount(inlineComponent: inlinedComponent);
678
679 // add this document's type counts minus document root (if not an inline component)
680 if (std::holds_alternative<RootDocumentNameType>(v: inlinedComponent))
681 typeCount--;
682 components << QString::number(typeCount);
683
684 // traverse types with QML base classes
685 for (const QQmlJSScope::ConstPtr &t : typesWithBaseTypeCount) {
686 if (p(t))
687 break;
688 QString typeCountTemplate = u"QQmltcObjectCreationHelper::typeCount<%1>()"_s;
689 if (t == visitor->result()) { // t is this document's root
690 components << typeCountTemplate.arg(a: t->baseTypeName());
691 } else if (t->isInlineComponent()) {
692 // inline components always have a base class, by definition
693 Q_ASSERT(t->baseType());
694 components << typeCountTemplate.arg(a: t->baseType()->internalName());
695 } else {
696 components << typeCountTemplate.arg(a: t->internalName());
697 }
698 }
699
700 return components.join(sep: u" + "_s);
701}
702
703QT_END_NAMESPACE
704
705#endif // QMLTCCOMPILERPIECES_H
706

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