| 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 |  | 
| 18 | QT_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 | */ | 
| 27 | struct 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 ¤t, | 
| 39 |                                                           const QQmlJSScope::ConstPtr &type) const; | 
| 40 |     inline void generate_initCodeForTopLevelComponent(QmltcType ¤t, | 
| 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 ¤t, const QQmlJSScope::ConstPtr &type) const; | 
| 48 |     inline void generate_setComplexBindingsCode(QmltcType ¤t, | 
| 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 ¤t, | 
| 55 |                                         const QQmlJSScope::ConstPtr &type) const; | 
| 56 |     inline void generate_completeComponentCode(QmltcType ¤t, | 
| 57 |                                                const QQmlJSScope::ConstPtr &type) const; | 
| 58 |     inline void generate_finalizeComponentCode(QmltcType ¤t, | 
| 59 |                                                const QQmlJSScope::ConstPtr &type) const; | 
| 60 |     inline void generate_handleOnCompletedCode(QmltcType ¤t, | 
| 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> ¶meters = {}); | 
| 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> ¶meters, | 
| 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 | */ | 
| 172 | inline decltype(auto) QmltcCodeGenerator::generate_initCode(QmltcType ¤t, | 
| 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: ¤t.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 = [¤t, 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 | */ | 
| 331 | inline void | 
| 332 | QmltcCodeGenerator::generate_initCodeForTopLevelComponent(QmltcType ¤t, | 
| 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 | */ | 
| 376 | inline 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 | */ | 
| 429 | inline void QmltcCodeGenerator::generate_endInitCode(QmltcType ¤t, | 
| 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: ¤t.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 | */ | 
| 470 | inline void | 
| 471 | QmltcCodeGenerator::generate_setComplexBindingsCode(QmltcType ¤t, | 
| 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: ¤t.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 | */ | 
| 493 | inline 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 | */ | 
| 565 | inline void QmltcCodeGenerator::generate_beginClassCode(QmltcType ¤t, | 
| 566 |                                                         const QQmlJSScope::ConstPtr &type) const | 
| 567 | { | 
| 568 |     using namespace Qt::StringLiterals; | 
| 569 |     generate_interfaceCallCode(function: ¤t.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 | */ | 
| 579 | inline void | 
| 580 | QmltcCodeGenerator::generate_completeComponentCode(QmltcType ¤t, | 
| 581 |                                                    const QQmlJSScope::ConstPtr &type) const | 
| 582 | { | 
| 583 |     using namespace Qt::StringLiterals; | 
| 584 |     generate_interfaceCallCode(function: ¤t.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 | */ | 
| 595 | inline void | 
| 596 | QmltcCodeGenerator::generate_finalizeComponentCode(QmltcType ¤t, | 
| 597 |                                                    const QQmlJSScope::ConstPtr &type) const | 
| 598 | { | 
| 599 |     using namespace Qt::StringLiterals; | 
| 600 |     generate_interfaceCallCode(function: ¤t.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 | */ | 
| 611 | inline void | 
| 612 | QmltcCodeGenerator::generate_handleOnCompletedCode(QmltcType ¤t, | 
| 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: ¤t.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 | */ | 
| 678 | template<typename Predicate> | 
| 679 | inline 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 |  | 
| 718 | QT_END_NAMESPACE | 
| 719 |  | 
| 720 | #endif // QMLTCCOMPILERPIECES_H | 
| 721 |  |