| 1 | // Copyright (C) 2021 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 | 
| 3 |  | 
| 4 | #include "qmltccompiler.h" | 
| 5 | #include "qmltcoutputir.h" | 
| 6 | #include "qmltccodewriter.h" | 
| 7 | #include "qmltcpropertyutils.h" | 
| 8 | #include "qmltccompilerpieces.h" | 
| 9 |  | 
| 10 | #include <QtCore/qloggingcategory.h> | 
| 11 | #include <QtQml/private/qqmlsignalnames_p.h> | 
| 12 | #include <private/qqmljsutils_p.h> | 
| 13 |  | 
| 14 | #include <algorithm> | 
| 15 |  | 
| 16 | QT_BEGIN_NAMESPACE | 
| 17 | using namespace Qt::StringLiterals; | 
| 18 |  | 
| 19 | bool qIsReferenceTypeList(const QQmlJSMetaProperty &p) | 
| 20 | { | 
| 21 |     if (QQmlJSScope::ConstPtr type = p.type()) | 
| 22 |         return type->isListProperty(); | 
| 23 |     return false; | 
| 24 | } | 
| 25 |  | 
| 26 | static QList<QQmlJSMetaProperty> unboundRequiredProperties( | 
| 27 |     const QQmlJSScope::ConstPtr &type, | 
| 28 |     QmltcTypeResolver *resolver | 
| 29 | ) { | 
| 30 |     QList<QQmlJSMetaProperty> requiredProperties{}; | 
| 31 |  | 
| 32 |     auto isPropertyRequired = [&type, &resolver](const auto &property) { | 
| 33 |         if (!type->isPropertyRequired(name: property.propertyName())) | 
| 34 |             return false; | 
| 35 |  | 
| 36 |         if (type->hasPropertyBindings(name: property.propertyName())) | 
| 37 |             return false; | 
| 38 |  | 
| 39 |         if (property.isAlias()) { | 
| 40 |             QQmlJSUtils::AliasResolutionVisitor aliasVisitor; | 
| 41 |  | 
| 42 |             QQmlJSUtils::ResolvedAlias result = | 
| 43 |                     QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor); | 
| 44 |  | 
| 45 |             if (result.kind != QQmlJSUtils::AliasTarget_Property) | 
| 46 |                 return false; | 
| 47 |  | 
| 48 |             // If the top level alias targets a property that is in | 
| 49 |             // the top level scope and that property is required, then | 
| 50 |             // we will already pick up the property during one of the | 
| 51 |             // iterations. | 
| 52 |             // Setting the property or the alias is the same so we | 
| 53 |             // discard one of the two, as otherwise we would require | 
| 54 |             // the user to pass two values for the same property ,in | 
| 55 |             // this case the alias. | 
| 56 |             // | 
| 57 |             // For example in: | 
| 58 |             // | 
| 59 |             // ``` | 
| 60 |             // Item { | 
| 61 |             //   id: self | 
| 62 |             //   required property int foo | 
| 63 |             //   property alias bar: self.foo | 
| 64 |             // } | 
| 65 |             // ``` | 
| 66 |             // | 
| 67 |             // Both foo and bar are required but setting one or the | 
| 68 |             // other is the same operation so that we should choose | 
| 69 |             // only one. | 
| 70 |             if (result.owner == type && | 
| 71 |                 type->isPropertyRequired(name: result.property.propertyName())) | 
| 72 |                 return false; | 
| 73 |  | 
| 74 |             if (result.owner->hasPropertyBindings(name: result.property.propertyName())) | 
| 75 |                 return false; | 
| 76 |         } | 
| 77 |  | 
| 78 |         return true; | 
| 79 |     }; | 
| 80 |  | 
| 81 |     const auto properties = type->properties(); | 
| 82 |     std::copy_if(first: properties.cbegin(), last: properties.cend(), | 
| 83 |                     result: std::back_inserter(x&: requiredProperties), pred: isPropertyRequired); | 
| 84 |     std::sort(first: requiredProperties.begin(), last: requiredProperties.end(), | 
| 85 |         comp: [](const auto &left, const auto &right) { | 
| 86 |             return left.propertyName() < right.propertyName(); | 
| 87 |     }); | 
| 88 |  | 
| 89 |     return requiredProperties; | 
| 90 | } | 
| 91 |  | 
| 92 |  | 
| 93 | // Populates the internal representation for a | 
| 94 | // RequiredPropertiesBundle, a class that acts as a bundle of initial | 
| 95 | // values that should be set for the required properties of a type. | 
| 96 | static void compileRequiredPropertiesBundle( | 
| 97 |     QmltcType ¤t, | 
| 98 |     const QQmlJSScope::ConstPtr &type, | 
| 99 |     QmltcTypeResolver *resolver | 
| 100 | ) { | 
| 101 |  | 
| 102 |     QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver); | 
| 103 |  | 
| 104 |     if (requiredProperties.isEmpty()) | 
| 105 |         return; | 
| 106 |  | 
| 107 |     current.requiredPropertiesBundle.emplace(); | 
| 108 |     current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s ; | 
| 109 |  | 
| 110 |     current.requiredPropertiesBundle->members.reserve(asize: requiredProperties.size()); | 
| 111 |     std::transform(first: requiredProperties.cbegin(), last: requiredProperties.cend(), | 
| 112 |                    result: std::back_inserter(x&: current.requiredPropertiesBundle->members), | 
| 113 |                    unary_op: [](const QQmlJSMetaProperty &property) { | 
| 114 |                        QString type = qIsReferenceTypeList(p: property) | 
| 115 |                                ? u"const QList<%1*>&"_s .arg( | 
| 116 |                                        a: property.type()->valueType()->internalName()) | 
| 117 |                                : u"passByConstRefOrValue<%1>"_s .arg( | 
| 118 |                                        a: property.type()->augmentedInternalName()); | 
| 119 |                        return QmltcVariable{ type, property.propertyName() }; | 
| 120 |                    }); | 
| 121 | } | 
| 122 |  | 
| 123 | static void compileRootExternalConstructorBody( | 
| 124 |     QmltcType& current, | 
| 125 |     const QQmlJSScope::ConstPtr &type | 
| 126 | ) { | 
| 127 |     current.externalCtor.body << u"// document root:"_s ; | 
| 128 |     // if it's document root, we want to create our QQmltcObjectCreationBase | 
| 129 |     // that would store all the created objects | 
| 130 |     current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s .arg( | 
| 131 |             a: type->internalName()); | 
| 132 |     current.externalCtor.body | 
| 133 |             << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s ; | 
| 134 |     current.externalCtor.body << u"creator.set(0, this);"_s ; // special case | 
| 135 |  | 
| 136 |     QString initializerName = u"initializer"_s ; | 
| 137 |     if (current.requiredPropertiesBundle) { | 
| 138 |         // Compose new initializer based on the initial values for required properties. | 
| 139 |         current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s ; | 
| 140 |         for (const auto& member : current.requiredPropertiesBundle->members) { | 
| 141 |             current.externalCtor.body << u"    propertyInitializer.%1(requiredPropertiesBundle.%2);"_s .arg( | 
| 142 |                 args: QmltcPropertyData(member.name).write, args: member.name | 
| 143 |             ); | 
| 144 |         } | 
| 145 |         current.externalCtor.body << u"    initializer(propertyInitializer);"_s ; | 
| 146 |         current.externalCtor.body << u"};"_s ; | 
| 147 |  | 
| 148 |         initializerName = u"newInitializer"_s ; | 
| 149 |     } | 
| 150 |  | 
| 151 |     // now call init | 
| 152 |     current.externalCtor.body << current.init.name | 
| 153 |                     + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "  | 
| 154 |                         u"endInit */ true, %1);"_s .arg(a: initializerName); | 
| 155 | }; | 
| 156 |  | 
| 157 | Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler" , QtWarningMsg); | 
| 158 |  | 
| 159 | const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s ; | 
| 160 | const QString QmltcCodeGenerator::typeCountName = u"q_qmltc_typeCount"_s ; | 
| 161 |  | 
| 162 | QmltcCompiler::QmltcCompiler(const QString &url, QmltcTypeResolver *resolver, QmltcVisitor *visitor, | 
| 163 |                              QQmlJSLogger *logger) | 
| 164 |     : m_url(url), m_typeResolver(resolver), m_visitor(visitor), m_logger(logger) | 
| 165 | { | 
| 166 |     Q_UNUSED(m_typeResolver); | 
| 167 |     Q_ASSERT(!hasErrors()); | 
| 168 | } | 
| 169 |  | 
| 170 | // needed due to std::unique_ptr<CodeGenerator> with CodeGenerator being | 
| 171 | // incomplete type in the header (~std::unique_ptr<> fails with a static_assert) | 
| 172 | QmltcCompiler::~QmltcCompiler() = default; | 
| 173 |  | 
| 174 | QString QmltcCompiler::newSymbol(const QString &base) | 
| 175 | { | 
| 176 |     QString symbol = base; | 
| 177 |     symbol.replace(before: QLatin1String("." ), after: QLatin1String("_" )); | 
| 178 |     while (symbol.startsWith(c: QLatin1Char('_')) && symbol.size() >= 2 | 
| 179 |            && (symbol[1].isUpper() || symbol[1] == QLatin1Char('_'))) { | 
| 180 |         symbol.remove(i: 0, len: 1); | 
| 181 |     } | 
| 182 |     if (!m_symbols.contains(key: symbol)) { | 
| 183 |         m_symbols.insert(key: symbol, value: 1); | 
| 184 |     } else { | 
| 185 |         symbol += u"_"  + QString::number(m_symbols[symbol]++); | 
| 186 |     } | 
| 187 |     return symbol; | 
| 188 | } | 
| 189 |  | 
| 190 | void QmltcCompiler::compile(const QmltcCompilerInfo &info) | 
| 191 | { | 
| 192 |     m_info = info; | 
| 193 |     Q_ASSERT(!m_info.outputCppFile.isEmpty()); | 
| 194 |     Q_ASSERT(!m_info.outputHFile.isEmpty()); | 
| 195 |     Q_ASSERT(!m_info.resourcePath.isEmpty()); | 
| 196 |  | 
| 197 |     // Note: we only compile "pure" QML types. any component-wrapped type is | 
| 198 |     // expected to appear through a binding | 
| 199 |  | 
| 200 |     const auto isComponent = [](const QQmlJSScope::ConstPtr &type) { | 
| 201 |         auto base = type->baseType(); | 
| 202 |         return base && base->internalName() == u"QQmlComponent"_s ; | 
| 203 |     }; | 
| 204 |  | 
| 205 |     QmltcCodeGenerator generator { .documentUrl: m_url, .visitor: m_visitor }; | 
| 206 |  | 
| 207 |     QmltcMethod urlMethod; | 
| 208 |     compileUrlMethod(urlMethod, urlMethodName: generator.urlMethodName()); | 
| 209 |     m_urlMethodName = urlMethod.name; | 
| 210 |  | 
| 211 |     // sort inline components to compile them in the right order | 
| 212 |     // a inherits b => b needs to be defined in the cpp file before a! | 
| 213 |     // r is the root => r needs to be compiled at the end! | 
| 214 |     // otherwise => sort them by inline component names to have consistent output | 
| 215 |     auto sortedInlineComponentNames = m_visitor->inlineComponentNames(); | 
| 216 |     std::sort(first: sortedInlineComponentNames.begin(), last: sortedInlineComponentNames.end(), | 
| 217 |               comp: [&](const InlineComponentOrDocumentRootName &a, | 
| 218 |                   const InlineComponentOrDocumentRootName &b) { | 
| 219 |                   const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(ptr: &a); | 
| 220 |                   const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(ptr: &b); | 
| 221 |  | 
| 222 |                   if (inlineComponentAName == inlineComponentBName) | 
| 223 |                       return false; | 
| 224 |  | 
| 225 |                   // the root comes at last, so (a < b) == true when b is the root and a is not | 
| 226 |                   if (inlineComponentAName && !inlineComponentBName) | 
| 227 |                       return true; | 
| 228 |  | 
| 229 |                   // b requires a to be declared before b when b inherits from a, therefore (a < b) | 
| 230 |                   // == true | 
| 231 |                   if (inlineComponentAName && inlineComponentBName) { | 
| 232 |                       QQmlJSScope::ConstPtr inlineComponentA = m_visitor->inlineComponent(inlineComponentName: a); | 
| 233 |                       QQmlJSScope::ConstPtr inlineComponentB = m_visitor->inlineComponent(inlineComponentName: b); | 
| 234 |                       if (inlineComponentB->inherits(base: inlineComponentA)) { | 
| 235 |                           return true; | 
| 236 |                       } else if (inlineComponentA->inherits(base: inlineComponentB)) { | 
| 237 |                           return false; | 
| 238 |                       } else { | 
| 239 |                           // fallback to default sorting based on names | 
| 240 |                           return *inlineComponentAName < *inlineComponentBName; | 
| 241 |                       } | 
| 242 |                   } | 
| 243 |                   Q_ASSERT(!inlineComponentAName || !inlineComponentBName); | 
| 244 |                   // a is the root or both a and b are the root | 
| 245 |                   return false; | 
| 246 |               }); | 
| 247 |  | 
| 248 |     QList<QmltcType> compiledTypes; | 
| 249 |     for (const auto &inlineComponent : sortedInlineComponentNames) { | 
| 250 |         const QList<QQmlJSScope::ConstPtr> &pureTypes = m_visitor->pureQmlTypes(inlineComponent); | 
| 251 |         Q_ASSERT(!pureTypes.empty()); | 
| 252 |         const QQmlJSScope::ConstPtr &root = pureTypes.front(); | 
| 253 |         if (isComponent(root)) { | 
| 254 |             compiledTypes.emplaceBack(); // create empty type | 
| 255 |             const auto compile = [&](QmltcType ¤t, const QQmlJSScope::ConstPtr &type) { | 
| 256 |                 generator.generate_initCodeForTopLevelComponent(current, type); | 
| 257 |             }; | 
| 258 |             compileType(current&: compiledTypes.back(), type: root, compileElements: compile); | 
| 259 |         } else { | 
| 260 |             const auto compile = [this](QmltcType ¤t, const QQmlJSScope::ConstPtr &type) { | 
| 261 |                 compileTypeElements(current, type); | 
| 262 |             }; | 
| 263 |  | 
| 264 |             for (const auto &type : pureTypes) { | 
| 265 |                 Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope); | 
| 266 |                 compiledTypes.emplaceBack(); // create empty type | 
| 267 |                 compileType(current&: compiledTypes.back(), type, compileElements: compile); | 
| 268 |             } | 
| 269 |         } | 
| 270 |     } | 
| 271 |  | 
| 272 |     if (hasErrors()) | 
| 273 |         return; | 
| 274 |  | 
| 275 |     QmltcProgram program; | 
| 276 |     program.url = m_url; | 
| 277 |     program.cppPath = m_info.outputCppFile; | 
| 278 |     program.hPath = m_info.outputHFile; | 
| 279 |     program.outNamespace = m_info.outputNamespace; | 
| 280 |     program.exportMacro = m_info.exportMacro; | 
| 281 |     program.compiledTypes = compiledTypes; | 
| 282 |     program.includes = m_visitor->cppIncludeFiles(); | 
| 283 |     if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty()) | 
| 284 |         program.includes += (m_info.exportInclude); | 
| 285 |     program.urlMethod = urlMethod; | 
| 286 |  | 
| 287 |     QmltcOutput out; | 
| 288 |     QmltcOutputWrapper code(out); | 
| 289 |     QmltcCodeWriter::write(code, program); | 
| 290 | } | 
| 291 |  | 
| 292 | void QmltcCompiler::compileUrlMethod(QmltcMethod &urlMethod, const QString &urlMethodName) | 
| 293 | { | 
| 294 |     urlMethod.name = urlMethodName; | 
| 295 |     urlMethod.returnType = u"const QUrl&"_s ; | 
| 296 |     urlMethod.body << u"static QUrl url {QStringLiteral(\"qrc:%1\")};"_s .arg(a: m_info.resourcePath); | 
| 297 |     urlMethod.body << u"return url;"_s ; | 
| 298 |     urlMethod.declarationPrefixes << u"static"_s ; | 
| 299 |     urlMethod.modifiers << u"noexcept"_s ; | 
| 300 | } | 
| 301 |  | 
| 302 | void QmltcCompiler::compileType( | 
| 303 |         QmltcType ¤t, const QQmlJSScope::ConstPtr &type, | 
| 304 |         std::function<void(QmltcType &, const QQmlJSScope::ConstPtr &)> compileElements) | 
| 305 | { | 
| 306 |     Q_ASSERT(!type->internalName().isEmpty()); | 
| 307 |     current.cppType = type->internalName(); | 
| 308 |     Q_ASSERT(!type->baseType()->internalName().isEmpty()); | 
| 309 |     const QString baseClass = type->baseType()->internalName(); | 
| 310 |  | 
| 311 |     const auto rootType = m_visitor->result(); | 
| 312 |     const InlineComponentOrDocumentRootName name = type->enclosingInlineComponentName(); | 
| 313 |     QQmlJSScope::ConstPtr inlineComponentType = m_visitor->inlineComponent(inlineComponentName: name); | 
| 314 |     Q_ASSERT(inlineComponentType); | 
| 315 |     const bool documentRoot = (type == rootType); | 
| 316 |     const bool inlineComponent = type->isInlineComponent(); | 
| 317 |     const bool isAnonymous = !documentRoot || type->internalName().at(i: 0).isLower(); | 
| 318 |     const bool isSingleton = type->isSingleton(); | 
| 319 |  | 
| 320 |     QmltcCodeGenerator generator { .documentUrl: m_url, .visitor: m_visitor }; | 
| 321 |  | 
| 322 |     current.baseClasses = { baseClass }; | 
| 323 |     if (!documentRoot) { | 
| 324 |         // make document root a friend to allow it to access init and endInit | 
| 325 |         const QString rootInternalName = | 
| 326 |                 m_visitor->inlineComponent(inlineComponentName: type->enclosingInlineComponentName())->internalName(); | 
| 327 |         if (rootInternalName != current.cppType) // avoid GCC13 warning on self-befriending | 
| 328 |             current.otherCode << "friend class %1;"_L1 .arg(args: rootInternalName); | 
| 329 |     } | 
| 330 |     if (documentRoot || inlineComponent) { | 
| 331 |         auto name = type->inlineComponentName() | 
| 332 |                 ? InlineComponentOrDocumentRootName(*type->inlineComponentName()) | 
| 333 |                 : InlineComponentOrDocumentRootName(RootDocumentNameType()); | 
| 334 |         // make QQmltcObjectCreationBase<DocumentRoot> a friend to allow it to | 
| 335 |         // be created for the root object | 
| 336 |         current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s .arg( | 
| 337 |                 a: inlineComponentType->internalName()); | 
| 338 |         // generate typeCount for all components (root + inlineComponents) | 
| 339 |         QmltcMethod typeCountMethod; | 
| 340 |         typeCountMethod.name = QmltcCodeGenerator::typeCountName; | 
| 341 |         typeCountMethod.returnType = u"uint"_s ; | 
| 342 |         typeCountMethod.body << u"return "  + generator.generate_typeCount(inlinedComponent: name) + u";" ; | 
| 343 |         current.typeCount = typeCountMethod; | 
| 344 |     } else { | 
| 345 |         // make an immediate parent a friend since that parent | 
| 346 |         // would create the object through a non-public constructor | 
| 347 |         const auto realQmlScope = [](const QQmlJSScope::ConstPtr &scope) { | 
| 348 |             if (scope->isArrayScope()) | 
| 349 |                 return scope->parentScope(); | 
| 350 |             return scope; | 
| 351 |         }; | 
| 352 |  | 
| 353 |         const auto& realScope = realQmlScope(type->parentScope()); | 
| 354 |         if (realScope != rootType) { | 
| 355 |             current.otherCode << u"friend class %1;"_s .arg(a: realScope->internalName()); | 
| 356 |         } | 
| 357 |     } | 
| 358 |  | 
| 359 |     // make QQmltcObjectCreationHelper a friend of every type since it provides | 
| 360 |     // useful helper methods for all types | 
| 361 |     current.otherCode << u"friend class QT_PREPEND_NAMESPACE(QQmltcObjectCreationHelper);"_s ; | 
| 362 |  | 
| 363 |     current.mocCode = { | 
| 364 |         u"Q_OBJECT"_s , | 
| 365 |         // Note: isAnonymous holds for non-root types in the document as well | 
| 366 |         type->isInlineComponent() ? (u"QML_NAMED_ELEMENT(%1)"_s .arg(a: *type->inlineComponentName())) | 
| 367 |                                   : (isAnonymous ? u"QML_ANONYMOUS"_s  : u"QML_ELEMENT"_s ), | 
| 368 |     }; | 
| 369 |  | 
| 370 |     // add special member functions | 
| 371 |     current.baselineCtor.access = QQmlJSMetaMethod::Protected; | 
| 372 |     if (documentRoot || inlineComponent || isSingleton) { | 
| 373 |         current.externalCtor.access = QQmlJSMetaMethod::Public; | 
| 374 |     } else { | 
| 375 |         current.externalCtor.access = QQmlJSMetaMethod::Protected; | 
| 376 |     } | 
| 377 |     current.init.access = QQmlJSMetaMethod::Protected; | 
| 378 |     current.beginClass.access = QQmlJSMetaMethod::Protected; | 
| 379 |     current.endInit.access = QQmlJSMetaMethod::Protected; | 
| 380 |     current.setComplexBindings.access = QQmlJSMetaMethod::Protected; | 
| 381 |     current.completeComponent.access = QQmlJSMetaMethod::Protected; | 
| 382 |     current.finalizeComponent.access = QQmlJSMetaMethod::Protected; | 
| 383 |     current.handleOnCompleted.access = QQmlJSMetaMethod::Protected; | 
| 384 |  | 
| 385 |     current.propertyInitializer.name = u"PropertyInitializer"_s ; | 
| 386 |     current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public; | 
| 387 |     current.propertyInitializer.constructor.name = current.propertyInitializer.name; | 
| 388 |     current.propertyInitializer.constructor.parameterList = { | 
| 389 |         QmltcVariable(u"%1&"_s .arg(a: current.cppType), u"component"_s ) | 
| 390 |     }; | 
| 391 |     current.propertyInitializer.component.cppType = current.cppType + u"&" ; | 
| 392 |     current.propertyInitializer.component.name = u"component"_s ; | 
| 393 |     current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s ; | 
| 394 |     current.propertyInitializer.initializedCache.name = u"initializedCache"_s ; | 
| 395 |  | 
| 396 |     current.baselineCtor.name = current.cppType; | 
| 397 |     current.externalCtor.name = current.cppType; | 
| 398 |     current.init.name = u"QML_init"_s ; | 
| 399 |     current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_s ; | 
| 400 |     current.beginClass.name = u"QML_beginClass"_s ; | 
| 401 |     current.beginClass.returnType = u"void"_s ; | 
| 402 |     current.endInit.name = u"QML_endInit"_s ; | 
| 403 |     current.endInit.returnType = u"void"_s ; | 
| 404 |     current.setComplexBindings.name = u"QML_setComplexBindings"_s ; | 
| 405 |     current.setComplexBindings.returnType = u"void"_s ; | 
| 406 |     current.completeComponent.name = u"QML_completeComponent"_s ; | 
| 407 |     current.completeComponent.returnType = u"void"_s ; | 
| 408 |     current.finalizeComponent.name = u"QML_finalizeComponent"_s ; | 
| 409 |     current.finalizeComponent.returnType = u"void"_s ; | 
| 410 |     current.handleOnCompleted.name = u"QML_handleOnCompleted"_s ; | 
| 411 |     current.handleOnCompleted.returnType = u"void"_s ; | 
| 412 |     QmltcVariable creator(u"QQmltcObjectCreationHelper*"_s , u"creator"_s ); | 
| 413 |     QmltcVariable engine(u"QQmlEngine*"_s , u"engine"_s ); | 
| 414 |     QmltcVariable parent(u"QObject*"_s , u"parent"_s , u"nullptr"_s ); | 
| 415 |     QmltcVariable initializedCache( | 
| 416 |         u"[[maybe_unused]] const QSet<QString>&"_s , | 
| 417 |         u"initializedCache"_s , | 
| 418 |         u"{}"_s  | 
| 419 |     ); | 
| 420 |     QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s , u"parentContext"_s ); | 
| 421 |     QmltcVariable finalizeFlag(u"bool"_s , u"canFinalize"_s ); | 
| 422 |     current.baselineCtor.parameterList = { parent }; | 
| 423 |     current.endInit.parameterList = { creator, engine }; | 
| 424 |     current.setComplexBindings.parameterList = { creator, engine, initializedCache }; | 
| 425 |     current.handleOnCompleted.parameterList = { creator }; | 
| 426 |  | 
| 427 |     if (documentRoot || inlineComponent) { | 
| 428 |         const QmltcVariable initializer( | 
| 429 |             u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s .arg(a: current.propertyInitializer.name), | 
| 430 |             u"initializer"_s , | 
| 431 |             u"[](%1&){}"_s .arg(a: current.propertyInitializer.name)); | 
| 432 |  | 
| 433 |         current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer }; | 
| 434 |         current.beginClass.parameterList = { creator, finalizeFlag }; | 
| 435 |         current.completeComponent.parameterList = { creator, finalizeFlag }; | 
| 436 |         current.finalizeComponent.parameterList = { creator, finalizeFlag }; | 
| 437 |  | 
| 438 |         compileRequiredPropertiesBundle(current, type, resolver: m_typeResolver); | 
| 439 |  | 
| 440 |         if (current.requiredPropertiesBundle) { | 
| 441 |             QmltcVariable bundle{ | 
| 442 |                 u"const %1&"_s .arg(a: current.requiredPropertiesBundle->name), | 
| 443 |                 u"requiredPropertiesBundle"_s , | 
| 444 |             }; | 
| 445 |             current.externalCtor.parameterList = { engine, bundle, parent, initializer }; | 
| 446 |         } else { | 
| 447 |             current.externalCtor.parameterList = { engine, parent, initializer }; | 
| 448 |         } | 
| 449 |     } else { | 
| 450 |         current.externalCtor.parameterList = { creator, engine, parent }; | 
| 451 |         current.init.parameterList = { creator, engine, ctxtdata }; | 
| 452 |         current.beginClass.parameterList = { creator }; | 
| 453 |         current.completeComponent.parameterList = { creator }; | 
| 454 |         current.finalizeComponent.parameterList = { creator }; | 
| 455 |     } | 
| 456 |  | 
| 457 |     current.externalCtor.initializerList = { current.baselineCtor.name + u"("  + parent.name | 
| 458 |                                              + u")"  }; | 
| 459 |     if (QQmlJSUtils::hasCompositeBase(scope: type)) { | 
| 460 |         // call parent's (QML type's) basic ctor from this. that one will take | 
| 461 |         // care about QObject::setParent() | 
| 462 |         current.baselineCtor.initializerList = { baseClass + u"("  + parent.name + u")"  }; | 
| 463 |     } else { | 
| 464 |         // default call to ctor is enough, but QQml_setParent_noEvent() is | 
| 465 |         // needed (note: faster? version of QObject::setParent()) | 
| 466 |         current.baselineCtor.body << u"QQml_setParent_noEvent(this, "  + parent.name + u");" ; | 
| 467 |     } | 
| 468 |  | 
| 469 |     // compilation stub: | 
| 470 |     current.externalCtor.body << u"Q_UNUSED(engine)"_s ; | 
| 471 |     if (documentRoot || inlineComponent) { | 
| 472 |         compileRootExternalConstructorBody(current, type); | 
| 473 |     } else { | 
| 474 |         current.externalCtor.body << u"// not document root:"_s ; | 
| 475 |         // just call init, we don't do any setup here otherwise | 
| 476 |         current.externalCtor.body << current.init.name | 
| 477 |                         + u"(creator, engine, QQmlData::get(parent)->outerContext);" ; | 
| 478 |     } | 
| 479 |  | 
| 480 |     if (isSingleton) { | 
| 481 |         // see https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON for context | 
| 482 |         current.mocCode.append(t: u"QML_SINGLETON"_s ); | 
| 483 |         auto &staticCreate = current.staticCreate.emplace(); | 
| 484 |         staticCreate.comments | 
| 485 |                 << u"Used by the engine for singleton creation."_s  | 
| 486 |                 << u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s ; | 
| 487 |         staticCreate.type = QQmlJSMetaMethodType::StaticMethod; | 
| 488 |         staticCreate.access = QQmlJSMetaMethod::Public; | 
| 489 |         staticCreate.name = u"create"_s ; | 
| 490 |         staticCreate.returnType = u"%1 *"_s .arg(a: current.cppType); | 
| 491 |         QmltcVariable jsEngine(u"QJSEngine*"_s , u"jsEngine"_s ); | 
| 492 |         staticCreate.parameterList = { engine, jsEngine }; | 
| 493 |         staticCreate.body << u"Q_UNUSED(jsEngine);"_s  | 
| 494 |                           << u"%1 *result = new %1(engine, nullptr);"_s .arg(a: current.cppType) | 
| 495 |                           << u"return result;"_s ; | 
| 496 |     } | 
| 497 |     auto postponedQmlContextSetup = generator.generate_initCode(current, type); | 
| 498 |     generator.generate_endInitCode(current, type); | 
| 499 |     generator.generate_setComplexBindingsCode(current, type); | 
| 500 |     generator.generate_beginClassCode(current, type); | 
| 501 |     generator.generate_completeComponentCode(current, type); | 
| 502 |     generator.generate_finalizeComponentCode(current, type); | 
| 503 |     generator.generate_handleOnCompletedCode(current, type); | 
| 504 |  | 
| 505 |     compileElements(current, type); | 
| 506 | } | 
| 507 |  | 
| 508 | template<typename Iterator> | 
| 509 | static Iterator partitionBindings(Iterator first, Iterator last) | 
| 510 | { | 
| 511 |     // NB: the code generator cares about script bindings being processed at a | 
| 512 |     // later point, so we should sort or partition the range. we do a stable | 
| 513 |     // partition since the relative order of binding evaluation affects the UI | 
| 514 |     return std::stable_partition(first, last, [](const QQmlJSMetaPropertyBinding &b) { | 
| 515 |         // we want complex bindings to be at the end, so do the negation | 
| 516 |         return !QmltcCompiler::isComplexBinding(binding: b); | 
| 517 |     }); | 
| 518 | } | 
| 519 |  | 
| 520 | // Populates the propertyInitializer of the current type based on the | 
| 521 | // available properties. | 
| 522 | // | 
| 523 | // A propertyInitializer is a generated class that provides a | 
| 524 | // restricted interface that only allows setting property values and | 
| 525 | // internally keep tracks of which properties where actually set, | 
| 526 | // intended to be used to allow the user to set up the initial values | 
| 527 | // when creating an instance of a component. | 
| 528 | // | 
| 529 | // For each property of the current type that is known, is not private | 
| 530 | // and is writable, a setter method is generated. | 
| 531 | // Each setter method knows how to set a specific property, so as to | 
| 532 | // provide a strongly typed interface to property setting, as if the | 
| 533 | // relevant C++ type was used directly. | 
| 534 | // | 
| 535 | // Each setter uses the write method for the proprerty when available | 
| 536 | // and otherwise falls back to a the more generic | 
| 537 | // `QObject::setProperty` for properties where a WRITE method is not | 
| 538 | // available or in scope. | 
| 539 | static void compilePropertyInitializer(QmltcType ¤t, const QQmlJSScope::ConstPtr &type) { | 
| 540 |     static auto isFromExtension = [](const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &scope) { | 
| 541 |         return scope->ownerOfProperty(self: scope, name: property.propertyName()).extensionSpecifier != QQmlJSScope::NotExtension; | 
| 542 |     }; | 
| 543 |  | 
| 544 |     current.propertyInitializer.constructor.initializerList << u"component{component}"_s ; | 
| 545 |  | 
| 546 |     auto properties = type->properties().values(); | 
| 547 |     for (auto& property: properties) { | 
| 548 |         if (property.index() == -1) continue; | 
| 549 |         if (property.isPrivate()) continue; | 
| 550 |         if (!property.isWritable() && !qIsReferenceTypeList(p: property)) continue; | 
| 551 |  | 
| 552 |         const QString name = property.propertyName(); | 
| 553 |  | 
| 554 |         current.propertyInitializer.propertySetters.emplace_back(); | 
| 555 |         auto& compiledSetter = current.propertyInitializer.propertySetters.back(); | 
| 556 |  | 
| 557 |         compiledSetter.userVisible = true; | 
| 558 |         compiledSetter.returnType = u"void"_s ; | 
| 559 |         compiledSetter.name = QmltcPropertyData(property).write; | 
| 560 |  | 
| 561 |         if (qIsReferenceTypeList(p: property)) { | 
| 562 |             compiledSetter.parameterList.emplaceBack( | 
| 563 |                 args: QQmlJSUtils::constRefify(type: u"QList<%1*>"_s .arg(a: property.type()->valueType()->internalName())), | 
| 564 |                 args: name + u"_" , args: QString() | 
| 565 |             ); | 
| 566 |         } else { | 
| 567 |             compiledSetter.parameterList.emplaceBack( | 
| 568 |                 args: QQmlJSUtils::constRefify(type: getUnderlyingType(p: property)), args: name + u"_" , args: QString() | 
| 569 |             ); | 
| 570 |         } | 
| 571 |  | 
| 572 |         if (qIsReferenceTypeList(p: property)) { | 
| 573 |             compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s .arg( | 
| 574 |                args&: current.propertyInitializer.component.name, args: name | 
| 575 |             ); | 
| 576 |             compiledSetter.body << u"list_ref_.clear();"_s ; | 
| 577 |             compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s .arg(a: name); | 
| 578 |             compiledSetter.body << u"    list_ref_.append(list_item_);"_s ; | 
| 579 |         } else if ( | 
| 580 |             QQmlJSUtils::bindablePropertyHasDefaultAccessor(p: property, accessor: QQmlJSUtils::PropertyAccessor_Write) | 
| 581 |         ) { | 
| 582 |             compiledSetter.body  << u"%1.%2().setValue(%3_);"_s .arg( | 
| 583 |                 args&: current.propertyInitializer.component.name, args: property.bindable(), args: name); | 
| 584 |         } else if (type->hasOwnProperty(name)) { | 
| 585 |             compiledSetter.body << u"%1.%2(%3_);"_s .arg( | 
| 586 |                 args&: current.propertyInitializer.component.name, args: QmltcPropertyData(property).write, args: name); | 
| 587 |         } else if (property.write().isEmpty() || isFromExtension(property, type)) { | 
| 588 |             // We can end here if a WRITE method is not available or | 
| 589 |             // if the method is available but not in this scope, so | 
| 590 |             // that we fallback to the string-based setters.. | 
| 591 |             // | 
| 592 |             // For example, types that makes use of QML_EXTENDED | 
| 593 |             // types, will have the extension types properties | 
| 594 |             // available and with a WRITE method, but the WRITE method | 
| 595 |             // will not be available to the extended type, from C++, | 
| 596 |             // as the type does not directly inherit from the | 
| 597 |             // extension type. | 
| 598 |             // | 
| 599 |             // We specifically scope `setProperty` to `QObject` as | 
| 600 |             // certain types might have shadowed the method. | 
| 601 |             // For example, in QtQuick, some types have a property | 
| 602 |             // called `property` with a `setProperty` WRITE method | 
| 603 |             // that will produce the shadowing. | 
| 604 |             compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s .arg( | 
| 605 |                 args&: current.propertyInitializer.component.name, args: name); | 
| 606 |         } else { | 
| 607 |             compiledSetter.body << u"%1.%2(%3_);"_s .arg( | 
| 608 |                 args&: current.propertyInitializer.component.name, args: property.write(), args: name); | 
| 609 |         } | 
| 610 |  | 
| 611 |         compiledSetter.body << u"%1.insert(QStringLiteral(\"%2\"));"_s .arg( | 
| 612 |             args&: current.propertyInitializer.initializedCache.name, args: name); | 
| 613 |     } | 
| 614 | } | 
| 615 |  | 
| 616 | void QmltcCompiler::compileTypeElements(QmltcType ¤t, const QQmlJSScope::ConstPtr &type) | 
| 617 | { | 
| 618 |     // compile components of a type: | 
| 619 |     // - enums | 
| 620 |     // - properties | 
| 621 |     // - methods | 
| 622 |     // - bindings | 
| 623 |  | 
| 624 |     const auto enums = type->ownEnumerations(); | 
| 625 |     current.enums.reserve(asize: enums.size()); | 
| 626 |     for (auto it = enums.begin(); it != enums.end(); ++it) | 
| 627 |         compileEnum(current, e: it.value()); | 
| 628 |  | 
| 629 |     auto properties = type->ownProperties().values(); | 
| 630 |     current.properties.reserve(asize: properties.size()); | 
| 631 |     // Note: index() is the (future) meta property index, so make sure given | 
| 632 |     // properties are ordered by that index before compiling | 
| 633 |     std::sort(first: properties.begin(), last: properties.end(), | 
| 634 |               comp: [](const QQmlJSMetaProperty &x, const QQmlJSMetaProperty &y) { | 
| 635 |                   return x.index() < y.index(); | 
| 636 |               }); | 
| 637 |     for (const QQmlJSMetaProperty &p : std::as_const(t&: properties)) { | 
| 638 |         if (p.index() == -1) { | 
| 639 |             recordError(location: type->sourceLocation(), | 
| 640 |                         message: u"Internal error: property '%1' has incomplete information"_s .arg( | 
| 641 |                                 a: p.propertyName())); | 
| 642 |             continue; | 
| 643 |         } | 
| 644 |         if (p.isAlias()) { | 
| 645 |             compileAlias(current, alias: p, owner: type); | 
| 646 |         } else { | 
| 647 |             compileProperty(current, p, owner: type); | 
| 648 |         } | 
| 649 |     } | 
| 650 |  | 
| 651 |     const auto methods = type->ownMethods(); | 
| 652 |     for (const QQmlJSMetaMethod &m : methods) | 
| 653 |         compileMethod(current, m, owner: type); | 
| 654 |  | 
| 655 |     auto bindings = type->ownPropertyBindingsInQmlIROrder(); | 
| 656 |     partitionBindings(first: bindings.begin(), last: bindings.end()); | 
| 657 |  | 
| 658 |     compilePropertyInitializer(current, type); | 
| 659 |     compileBinding(current, bindingStart: bindings.begin(), bindingEnd: bindings.end(), type, accessor: { .scope: type }); | 
| 660 | } | 
| 661 |  | 
| 662 | void QmltcCompiler::compileEnum(QmltcType ¤t, const QQmlJSMetaEnum &e) | 
| 663 | { | 
| 664 |     const auto intValues = e.values(); | 
| 665 |     QStringList values; | 
| 666 |     values.reserve(asize: intValues.size()); | 
| 667 |     std::transform(first: intValues.cbegin(), last: intValues.cend(), result: std::back_inserter(x&: values), | 
| 668 |                    unary_op: [](int x) { return QString::number(x); }); | 
| 669 |  | 
| 670 |     // structure: (C++ type name, enum keys, enum values, MOC line) | 
| 671 |     current.enums.emplaceBack(args: e.name(), args: e.keys(), args: std::move(values), | 
| 672 |                               args: u"Q_ENUM(%1)"_s .arg(a: e.name())); | 
| 673 | } | 
| 674 |  | 
| 675 | static QList<QmltcVariable> | 
| 676 | compileMethodParameters(const QList<QQmlJSMetaParameter> ¶meterInfos, bool allowUnnamed = false) | 
| 677 | { | 
| 678 |     QList<QmltcVariable> parameters; | 
| 679 |     const auto size = parameterInfos.size(); | 
| 680 |     parameters.reserve(asize: size); | 
| 681 |     for (qsizetype i = 0; i < size; ++i) { | 
| 682 |         const auto &p = parameterInfos[i]; | 
| 683 |         Q_ASSERT(p.type()); // assume verified | 
| 684 |         QString name = p.name(); | 
| 685 |         Q_ASSERT(allowUnnamed || !name.isEmpty()); // assume verified | 
| 686 |         if (name.isEmpty() && allowUnnamed) | 
| 687 |             name = u"unnamed_"  + QString::number(i); | 
| 688 |  | 
| 689 |         QString internalName; | 
| 690 |         const QQmlJSScope::AccessSemantics semantics = p.type()->accessSemantics(); | 
| 691 |  | 
| 692 |         switch (semantics) { | 
| 693 |         case QQmlJSScope::AccessSemantics::Reference: | 
| 694 |             if (p.typeQualifier() == QQmlJSMetaParameter::Const) | 
| 695 |                 internalName = u"const "_s ; | 
| 696 |             internalName += u"%1*"_s .arg(a: p.type()->internalName()); | 
| 697 |             break; | 
| 698 |         case QQmlJSScope::AccessSemantics::Value: | 
| 699 |         case QQmlJSScope::AccessSemantics::Sequence: | 
| 700 |             internalName = u"passByConstRefOrValue<%1>"_s .arg(a: p.type()->internalName()); | 
| 701 |             break; | 
| 702 |         case QQmlJSScope::AccessSemantics::None: | 
| 703 |             Q_ASSERT(false); // or maybe print an error message | 
| 704 |         } | 
| 705 |         parameters.emplaceBack(args&: internalName, args&: name, args: QString()); | 
| 706 |     } | 
| 707 |     return parameters; | 
| 708 | } | 
| 709 |  | 
| 710 | static QString figureReturnType(const QQmlJSMetaMethod &m) | 
| 711 | { | 
| 712 |     const bool isVoidMethod = | 
| 713 |             m.returnTypeName() == u"void"  || m.methodType() == QQmlJSMetaMethodType::Signal; | 
| 714 |     Q_ASSERT(isVoidMethod || m.returnType()); | 
| 715 |     QString type; | 
| 716 |     if (isVoidMethod) { | 
| 717 |         type = u"void"_s ; | 
| 718 |     } else { | 
| 719 |         type = m.returnType()->augmentedInternalName(); | 
| 720 |     } | 
| 721 |     return type; | 
| 722 | } | 
| 723 |  | 
| 724 | void QmltcCompiler::compileMethod(QmltcType ¤t, const QQmlJSMetaMethod &m, | 
| 725 |                                   const QQmlJSScope::ConstPtr &owner) | 
| 726 | { | 
| 727 |     const auto returnType = figureReturnType(m); | 
| 728 |  | 
| 729 |     const QList<QmltcVariable> compiledParams = compileMethodParameters(parameterInfos: m.parameters()); | 
| 730 |     const auto methodType = m.methodType(); | 
| 731 |  | 
| 732 |     QStringList code; | 
| 733 |     if (methodType != QQmlJSMetaMethodType::Signal) { | 
| 734 |         QmltcCodeGenerator urlGenerator { .documentUrl: m_url, .visitor: m_visitor }; | 
| 735 |         QmltcCodeGenerator::generate_callExecuteRuntimeFunction( | 
| 736 |                 block: &code, url: urlGenerator.urlMethodName() + u"()" , | 
| 737 |                 index: owner->ownRuntimeFunctionIndex(index: m.jsFunctionIndex()), accessor: u"this"_s , returnType, | 
| 738 |                 parameters: compiledParams); | 
| 739 |     } | 
| 740 |  | 
| 741 |     QmltcMethod compiled {}; | 
| 742 |     compiled.returnType = returnType; | 
| 743 |     compiled.name = m.methodName(); | 
| 744 |     compiled.parameterList = std::move(compiledParams); | 
| 745 |     compiled.body = std::move(code); | 
| 746 |     compiled.type = methodType; | 
| 747 |     compiled.access = m.access(); | 
| 748 |     if (methodType != QQmlJSMetaMethodType::Signal) { | 
| 749 |         compiled.declarationPrefixes << u"Q_INVOKABLE"_s ; | 
| 750 |         compiled.userVisible = m.access() == QQmlJSMetaMethod::Public; | 
| 751 |     } else { | 
| 752 |         compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal(); | 
| 753 |     } | 
| 754 |     current.functions.emplaceBack(args&: compiled); | 
| 755 | } | 
| 756 |  | 
| 757 | /*! \internal | 
| 758 |    Compiles an extra set of methods for Lists, that makes manipulating lists easier from C++ | 
| 759 |    for the user. | 
| 760 | */ | 
| 761 | void QmltcCompiler::(QmltcType ¤t, const QQmlJSMetaProperty &p) | 
| 762 | { | 
| 763 |     QmltcPropertyData data(p); | 
| 764 |     const QString valueType = p.type()->valueType()->internalName() + u'*'; | 
| 765 |     const QString variableName = data.read + u"()"_s ; | 
| 766 |     const QStringList ownershipWarning = { | 
| 767 |         u"\\note {This method does not change the ownership of its argument."_s , | 
| 768 |         u"The caller is responsible for setting the argument's \\c {QObject::parent} or"_s , | 
| 769 |         u"for ensuring that the argument lives long enough."_s , | 
| 770 |         u"For example, an argument created with \\c {createObject()} that has no parent"_s , | 
| 771 |         u"will eventually be garbage-collected, leaving a dangling pointer.}"_s  | 
| 772 |     }; | 
| 773 |  | 
| 774 |     // generate append() sugar for users | 
| 775 |     { | 
| 776 |         QmltcMethod append{}; | 
| 777 |         append.comments.emplaceBack(args: u"\\brief Append an element to %1."_s .arg(a: data.read)); | 
| 778 |         append.comments << ownershipWarning; | 
| 779 |         append.returnType = u"void"_s ; | 
| 780 |         append.name = u"%1Append"_s .arg(a: data.read); | 
| 781 |         append.parameterList.emplaceBack(args: valueType, args: u"toBeAppended"_s ); | 
| 782 |  | 
| 783 |         append.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 784 |         append.body | 
| 785 |                 << u"q_qmltc_localList.append(std::addressof(q_qmltc_localList), toBeAppended);"_s ; | 
| 786 |         // append.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587 is | 
| 787 |         // resolved | 
| 788 |         append.userVisible = true; | 
| 789 |         current.functions.emplaceBack(args&: append); | 
| 790 |     } | 
| 791 |  | 
| 792 |     // generate count() sugar for users | 
| 793 |     { | 
| 794 |         QmltcMethod count{}; | 
| 795 |         count.comments.emplaceBack(args: u"\\brief Number of elements in %1."_s .arg(a: data.read)); | 
| 796 |         count.returnType = u"int"_s ; | 
| 797 |         count.name = u"%1Count"_s .arg(a: data.read); | 
| 798 |  | 
| 799 |         count.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 800 |         count.body << u"int result = q_qmltc_localList.count(std::addressof(q_qmltc_localList));"_s ; | 
| 801 |         count.body << u"return result;"_s ; | 
| 802 |         count.userVisible = true; | 
| 803 |         current.functions.emplaceBack(args&: count); | 
| 804 |     } | 
| 805 |  | 
| 806 |     // generate at() sugar for users | 
| 807 |     { | 
| 808 |         QmltcMethod at{}; | 
| 809 |         at.comments.emplaceBack(args: u"\\brief Access an element in %1."_s .arg(a: data.read)); | 
| 810 |         at.returnType = valueType; | 
| 811 |         at.name = u"%1At"_s .arg(a: data.read); | 
| 812 |         at.parameterList.emplaceBack(args: u"qsizetype"_s , args: u"position"_s , args: QString()); | 
| 813 |  | 
| 814 |         at.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 815 |         at.body << u"auto result = q_qmltc_localList.at(std::addressof(q_qmltc_localList), position);"_s ; | 
| 816 |         at.body << u"return result;"_s ; | 
| 817 |         at.userVisible = true; | 
| 818 |         current.functions.emplaceBack(args&: at); | 
| 819 |     } | 
| 820 |  | 
| 821 |     // generate clear() sugar for users | 
| 822 |     { | 
| 823 |         QmltcMethod clear{}; | 
| 824 |         clear.comments.emplaceBack(args: u"\\brief Clear %1."_s .arg(a: data.read)); | 
| 825 |         clear.returnType = u"void"_s ; | 
| 826 |         clear.name = u"%1Clear"_s .arg(a: data.read); | 
| 827 |  | 
| 828 |         clear.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 829 |         clear.body << u"q_qmltc_localList.clear(std::addressof(q_qmltc_localList));"_s ; | 
| 830 |         // clear.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587 is | 
| 831 |         // resolved | 
| 832 |         clear.userVisible = true; | 
| 833 |         current.functions.emplaceBack(args&: clear); | 
| 834 |     } | 
| 835 |  | 
| 836 |     // generate replace() sugar for users | 
| 837 |     { | 
| 838 |         QmltcMethod replace{}; | 
| 839 |         replace.comments.emplaceBack(args: u"\\brief Replace an element in %1."_s .arg(a: data.read)); | 
| 840 |         replace.comments << ownershipWarning; | 
| 841 |         replace.returnType = u"void"_s ; | 
| 842 |         replace.name = u"%1Replace"_s .arg(a: data.read); | 
| 843 |         replace.parameterList.emplaceBack(args: u"qsizetype"_s , args: u"position"_s , args: QString()); | 
| 844 |         replace.parameterList.emplaceBack(args: valueType, args: u"element"_s , | 
| 845 |                                           args: QString()); | 
| 846 |  | 
| 847 |         replace.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 848 |         replace.body | 
| 849 |                 << u"q_qmltc_localList.replace(std::addressof(q_qmltc_localList), position, element);"_s ; | 
| 850 |         // replace.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when QTBUG-106587 | 
| 851 |         // is resolved | 
| 852 |         replace.userVisible = true; | 
| 853 |         current.functions.emplaceBack(args&: replace); | 
| 854 |     } | 
| 855 |  | 
| 856 |     // generate removeLast() sugar for users | 
| 857 |     { | 
| 858 |         QmltcMethod removeLast{}; | 
| 859 |         removeLast.comments.emplaceBack(args: u"\\brief Remove the last element in %1."_s .arg(a: data.read)); | 
| 860 |         removeLast.returnType = u"void"_s ; | 
| 861 |         removeLast.name = u"%1RemoveLast"_s .arg(a: data.read); | 
| 862 |  | 
| 863 |         removeLast.body << u"auto q_qmltc_localList = %1;"_s .arg(a: variableName); | 
| 864 |         removeLast.body << u"q_qmltc_localList.removeLast(std::addressof(q_qmltc_localList));"_s ; | 
| 865 |         // removeLast.body << u"Q_EMIT %1();"_s.arg(data.notify); // uncomment this when | 
| 866 |         // QTBUG-106587 is resolved | 
| 867 |  | 
| 868 |         removeLast.userVisible = true; | 
| 869 |         current.functions.emplaceBack(args&: removeLast); | 
| 870 |     } | 
| 871 | } | 
| 872 |  | 
| 873 | void QmltcCompiler::compileProperty(QmltcType ¤t, const QQmlJSMetaProperty &p, | 
| 874 |                                     const QQmlJSScope::ConstPtr &owner) | 
| 875 | { | 
| 876 |     Q_ASSERT(!p.isAlias()); // will be handled separately | 
| 877 |     Q_ASSERT(p.type()); | 
| 878 |  | 
| 879 |     const QString name = p.propertyName(); | 
| 880 |     const QString variableName = u"m_"  + name; | 
| 881 |     const QString underlyingType = getUnderlyingType(p); | 
| 882 |     if (qIsReferenceTypeList(p)) { | 
| 883 |         const QString storageName = variableName + u"_storage" ; | 
| 884 |         current.variables.emplaceBack( | 
| 885 |                     args: u"QList<"  + p.type()->valueType()->internalName() + u"*>" , args: storageName, | 
| 886 |                     args: QString()); | 
| 887 |         current.baselineCtor.initializerList.emplaceBack(args: variableName + u"("  + underlyingType | 
| 888 |                                                          + u"(this, std::addressof("  + storageName | 
| 889 |                                                          + u")))" ); | 
| 890 |         compileExtraListMethods(current, p); | 
| 891 |     } | 
| 892 |  | 
| 893 |     // along with property, also add relevant moc code, so that we can use the | 
| 894 |     // property in Qt/QML contexts | 
| 895 |     QStringList mocPieces; | 
| 896 |     mocPieces.reserve(asize: 10); | 
| 897 |     mocPieces << underlyingType << name; | 
| 898 |  | 
| 899 |     QmltcPropertyData compilationData(p); | 
| 900 |  | 
| 901 |     // 1. add setter and getter | 
| 902 |     // If p.isList(), it's a QQmlListProperty. Then you can write the underlying list through | 
| 903 |     // the QQmlListProperty object retrieved with the getter. Setting it would make no sense. | 
| 904 |     if (p.isWritable() && !qIsReferenceTypeList(p)) { | 
| 905 |         QmltcMethod setter {}; | 
| 906 |         setter.returnType = u"void"_s ; | 
| 907 |         setter.name = compilationData.write; | 
| 908 |         // QmltcVariable | 
| 909 |         setter.parameterList.emplaceBack(args: QQmlJSUtils::constRefify(type: underlyingType), args: name + u"_" , | 
| 910 |                                          args: u""_s ); | 
| 911 |         setter.body << variableName + u".setValue("  + name + u"_);" ; | 
| 912 |         setter.body << u"Q_EMIT "  + compilationData.notify + u"();" ; | 
| 913 |         setter.userVisible = true; | 
| 914 |         current.functions.emplaceBack(args&: setter); | 
| 915 |         mocPieces << u"WRITE"_s  << setter.name; | 
| 916 |     } | 
| 917 |  | 
| 918 |     QmltcMethod getter {}; | 
| 919 |     getter.returnType = underlyingType; | 
| 920 |     getter.name = compilationData.read; | 
| 921 |     getter.body << u"return "  + variableName + u".value();" ; | 
| 922 |     getter.userVisible = true; | 
| 923 |     current.functions.emplaceBack(args&: getter); | 
| 924 |     mocPieces << u"READ"_s  << getter.name; | 
| 925 |  | 
| 926 |     // 2. add bindable | 
| 927 |     if (!qIsReferenceTypeList(p)) { | 
| 928 |         QmltcMethod bindable {}; | 
| 929 |         bindable.returnType = u"QBindable<"  + underlyingType + u">" ; | 
| 930 |         bindable.name = compilationData.bindable; | 
| 931 |         bindable.body << u"return QBindable<"  + underlyingType + u">(std::addressof("  + variableName | 
| 932 |                         + u"));" ; | 
| 933 |         bindable.userVisible = true; | 
| 934 |         current.functions.emplaceBack(args&: bindable); | 
| 935 |         mocPieces << u"BINDABLE"_s  << bindable.name; | 
| 936 |     } | 
| 937 |  | 
| 938 |     // 3. add/check notify (actually, this is already done inside QmltcVisitor) | 
| 939 |  | 
| 940 |     if (owner->isPropertyRequired(name)) | 
| 941 |         mocPieces << u"REQUIRED"_s ; | 
| 942 |  | 
| 943 |     // 4. add moc entry | 
| 944 |     // e.g. Q_PROPERTY(QString p READ getP WRITE setP BINDABLE bindableP) | 
| 945 |     current.mocCode << u"Q_PROPERTY("  + mocPieces.join(sep: u" "_s ) + u")" ; | 
| 946 |  | 
| 947 |     // 5. add extra moc entry if this property is marked default | 
| 948 |     if (name == owner->defaultPropertyName()) | 
| 949 |         current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s .arg(a: name); | 
| 950 |  | 
| 951 |     // structure: (C++ type name, name, C++ class name, C++ signal name) | 
| 952 |     current.properties.emplaceBack(args: underlyingType, args: variableName, args&: current.cppType, | 
| 953 |                                    args&: compilationData.notify); | 
| 954 | } | 
| 955 |  | 
| 956 | /*! | 
| 957 |  * \internal | 
| 958 |  * | 
| 959 |  * Models one step of the alias resolution. If the current alias to be resolved | 
| 960 |  * points to \c {x.y.z} and that \c {x.y} is already resolved, then this struct | 
| 961 |  * contains the information on how to obtain the \c {z} part from \c {x.y}. | 
| 962 |  */ | 
| 963 | struct AliasResolutionFrame | 
| 964 | { | 
| 965 |     /*! | 
| 966 |      * \internal | 
| 967 |      * | 
| 968 |      * Placeholder for the current resolved state. It is replaced later with | 
| 969 |      * the result from previous resolutions from the \c QStack<AliasResolutionFrame>. | 
| 970 |      * | 
| 971 |      * \sa unpackFrames() | 
| 972 |      */ | 
| 973 |     static QString inVar; | 
| 974 |  | 
| 975 |     /*! | 
| 976 |      * \internal | 
| 977 |      * | 
| 978 |      * Steps to access this value as a list of C++ statements, to be used in | 
| 979 |      * conjunction with \c {epilogue}. | 
| 980 |      */ | 
| 981 |     QStringList prologue; | 
| 982 |  | 
| 983 |     /*! | 
| 984 |      * \internal | 
| 985 |      * | 
| 986 |      * Steps to finish the statements of the \c prologue (e.g. closing brackets). | 
| 987 |      */ | 
| 988 |     QStringList epilogue; | 
| 989 |  | 
| 990 |     /*! | 
| 991 |      * \internal | 
| 992 |      * | 
| 993 |      * Instructions on how to write the property, after it was loaded with the | 
| 994 |      * instructions from \c prologue. Has to happen before \c epilogue. | 
| 995 |      */ | 
| 996 |     QStringList epilogueForWrite; | 
| 997 |  | 
| 998 |     /*! | 
| 999 |      * \internal | 
| 1000 |      * | 
| 1001 |      * Name of the variable holding the result of this resolution step, to be | 
| 1002 |      * used in the following resolution steps. | 
| 1003 |      */ | 
| 1004 |     QString outVar; | 
| 1005 | }; | 
| 1006 | // special string replaced by outVar of the previous frame | 
| 1007 | QString AliasResolutionFrame::inVar = QStringLiteral("__QMLTC_ALIAS_FRAME_INPUT_VAR__" ); | 
| 1008 |  | 
| 1009 | /*! | 
| 1010 |  * \internal | 
| 1011 |  * | 
| 1012 |  * Process the frames by replacing the placeholder \c invar | 
| 1013 |  * used in \c epilogueForWrite and \c prologue with the result | 
| 1014 |  * obtained from the previous frame. | 
| 1015 |  */ | 
| 1016 | static void unpackFrames(QStack<AliasResolutionFrame> &frames) | 
| 1017 | { | 
| 1018 |     if (frames.size() < 2) | 
| 1019 |         return; | 
| 1020 |  | 
| 1021 |     // assume first frame is fine | 
| 1022 |     auto prev = frames.begin(); | 
| 1023 |     for (auto it = std::next(x: prev); it != frames.end(); ++it, ++prev) { | 
| 1024 |         for (QString &line : it->prologue) | 
| 1025 |             line.replace(before: AliasResolutionFrame::inVar, after: prev->outVar); | 
| 1026 |         for (QString &line : it->epilogueForWrite) | 
| 1027 |             line.replace(before: AliasResolutionFrame::inVar, after: prev->outVar); | 
| 1028 |         it->outVar.replace(before: AliasResolutionFrame::inVar, after: prev->outVar); | 
| 1029 |     } | 
| 1030 | } | 
| 1031 |  | 
| 1032 | template<typename Projection> | 
| 1033 | static QStringList joinFrames(const QStack<AliasResolutionFrame> &frames, Projection project) | 
| 1034 | { | 
| 1035 |     QStringList joined; | 
| 1036 |     for (const AliasResolutionFrame &frame : frames) | 
| 1037 |         joined += project(frame); | 
| 1038 |     return joined; | 
| 1039 | } | 
| 1040 |  | 
| 1041 | void QmltcCompiler::compileAlias(QmltcType ¤t, const QQmlJSMetaProperty &alias, | 
| 1042 |                                  const QQmlJSScope::ConstPtr &owner) | 
| 1043 | { | 
| 1044 |     const QString aliasName = alias.propertyName(); | 
| 1045 |     Q_ASSERT(!aliasName.isEmpty()); | 
| 1046 |  | 
| 1047 |     QStringList aliasExprBits = alias.aliasExpression().split(sep: u'.'); | 
| 1048 |     Q_ASSERT(!aliasExprBits.isEmpty()); | 
| 1049 |  | 
| 1050 |     QStack<AliasResolutionFrame> frames; | 
| 1051 |  | 
| 1052 |     QQmlJSUtils::AliasResolutionVisitor aliasVisitor; | 
| 1053 |     qsizetype i = 0; | 
| 1054 |     aliasVisitor.reset = [&]() { | 
| 1055 |         frames.clear(); | 
| 1056 |         i = 0; // we use it in property processing | 
| 1057 |  | 
| 1058 |         // first frame is a dummy one: | 
| 1059 |         frames.push( | 
| 1060 |                 t: AliasResolutionFrame { .prologue: QStringList(), .epilogue: QStringList(), .epilogueForWrite: QStringList(), .outVar: u"this"_s  }); | 
| 1061 |     }; | 
| 1062 |     aliasVisitor.processResolvedId = [&](const QQmlJSScope::ConstPtr &type) { | 
| 1063 |         Q_ASSERT(type); | 
| 1064 |         if (owner != type) { // cannot start at `this`, need to fetch object through context | 
| 1065 |             const int id = m_visitor->runtimeId(type); | 
| 1066 |             Q_ASSERT(id >= 0); // since the type is found by id, it must have an id | 
| 1067 |  | 
| 1068 |             AliasResolutionFrame queryIdFrame {}; | 
| 1069 |             Q_ASSERT(frames.top().outVar == u"this"_s ); // so inVar would be "this" as well | 
| 1070 |             queryIdFrame.prologue << u"auto context = %1::q_qmltc_thisContext;"_s .arg( | 
| 1071 |                     a: owner->internalName()); | 
| 1072 |  | 
| 1073 |             // doing the above allows us to lookup id object by index (fast) | 
| 1074 |             queryIdFrame.outVar = u"alias_objectById_"  + aliasExprBits.front(); // unique enough | 
| 1075 |             const QString cppType = (m_visitor->qmlComponentIndex(type) == -1) | 
| 1076 |                     ? type->internalName() | 
| 1077 |                     : u"QQmlComponent"_s ; | 
| 1078 |             queryIdFrame.prologue << u"auto "  + queryIdFrame.outVar + u" = static_cast<"  + cppType | 
| 1079 |                             + u"*>(context->idValue("  + QString::number(id) + u"));" ; | 
| 1080 |             queryIdFrame.prologue << u"Q_ASSERT("  + queryIdFrame.outVar + u");" ; | 
| 1081 |  | 
| 1082 |             frames.push(t: queryIdFrame); | 
| 1083 |         } | 
| 1084 |     }; | 
| 1085 |     aliasVisitor.processResolvedProperty = [&](const QQmlJSMetaProperty &p, | 
| 1086 |                                                const QQmlJSScope::ConstPtr &owner) { | 
| 1087 |         AliasResolutionFrame queryPropertyFrame {}; | 
| 1088 |  | 
| 1089 |         auto [extensionPrologue, extensionAccessor, extensionEpilogue] = | 
| 1090 |                 QmltcCodeGenerator::wrap_extensionType( | 
| 1091 |                         type: owner, p, | 
| 1092 |                         accessor: QmltcCodeGenerator::wrap_privateClass(accessor: AliasResolutionFrame::inVar, p)); | 
| 1093 |         QString inVar = extensionAccessor; | 
| 1094 |         queryPropertyFrame.prologue += extensionPrologue; | 
| 1095 |         if (p.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Value) { | 
| 1096 |             // we need to read the property to a local variable and then | 
| 1097 |             // write the updated value once the actual operation is done | 
| 1098 |             const QString aliasVar = u"alias_"  + QString::number(i); // should be fairly unique | 
| 1099 |             ++i; | 
| 1100 |             queryPropertyFrame.prologue | 
| 1101 |                     << u"auto "  + aliasVar + u" = "  + inVar + u"->"  + p.read() + u"();" ; | 
| 1102 |             queryPropertyFrame.epilogueForWrite | 
| 1103 |                     << inVar + u"->"  + p.write() + u"("  + aliasVar + u");" ; | 
| 1104 |             // NB: since accessor becomes a value type, wrap it into an | 
| 1105 |             // addressof operator so that we could access it as a pointer | 
| 1106 |             inVar = QmltcCodeGenerator::wrap_addressof(addressed: aliasVar); // reset | 
| 1107 |         } else { | 
| 1108 |             inVar += u"->"  + p.read() + u"()" ; // update | 
| 1109 |         } | 
| 1110 |         queryPropertyFrame.outVar = inVar; | 
| 1111 |         queryPropertyFrame.epilogue += extensionEpilogue; | 
| 1112 |  | 
| 1113 |         frames.push(t: queryPropertyFrame); | 
| 1114 |     }; | 
| 1115 |  | 
| 1116 |     QQmlJSUtils::ResolvedAlias result = | 
| 1117 |             QQmlJSUtils::resolveAlias(typeResolver: m_typeResolver, property: alias, owner, visitor: aliasVisitor); | 
| 1118 |     Q_ASSERT(result.kind != QQmlJSUtils::AliasTarget_Invalid); | 
| 1119 |  | 
| 1120 |     unpackFrames(frames); | 
| 1121 |  | 
| 1122 |     if (result.kind == QQmlJSUtils::AliasTarget_Property) { | 
| 1123 |         // we don't need the last frame here | 
| 1124 |         frames.pop(); | 
| 1125 |  | 
| 1126 |         // instead, add a custom frame | 
| 1127 |         AliasResolutionFrame customFinalFrame {}; | 
| 1128 |         auto [extensionPrologue, extensionAccessor, extensionEpilogue] = | 
| 1129 |                 QmltcCodeGenerator::wrap_extensionType( | 
| 1130 |                         type: result.owner, p: result.property, | 
| 1131 |                         accessor: QmltcCodeGenerator::wrap_privateClass(accessor: frames.top().outVar, | 
| 1132 |                                                               p: result.property)); | 
| 1133 |         customFinalFrame.prologue = extensionPrologue; | 
| 1134 |         customFinalFrame.outVar = extensionAccessor; | 
| 1135 |         customFinalFrame.epilogue = extensionEpilogue; | 
| 1136 |         frames.push(t: customFinalFrame); | 
| 1137 |     } | 
| 1138 |  | 
| 1139 |     const QString latestAccessor = frames.top().outVar; | 
| 1140 |     const QStringList prologue = | 
| 1141 |             joinFrames(frames, project: [](const AliasResolutionFrame &frame) { return frame.prologue; }); | 
| 1142 |     const QStringList epilogue = | 
| 1143 |             joinFrames(frames, project: [](const AliasResolutionFrame &frame) { return frame.epilogue; }); | 
| 1144 |     const QString underlyingType = (result.kind == QQmlJSUtils::AliasTarget_Property) | 
| 1145 |             ? getUnderlyingType(p: result.property) | 
| 1146 |             : result.owner->internalName() + u" *" ; | 
| 1147 |  | 
| 1148 |     QStringList mocLines; | 
| 1149 |     mocLines.reserve(asize: 10); | 
| 1150 |     mocLines << underlyingType << aliasName; | 
| 1151 |  | 
| 1152 |     QmltcPropertyData compilationData(aliasName); | 
| 1153 |     // 1. add setter and getter | 
| 1154 |     QmltcMethod getter {}; | 
| 1155 |     getter.returnType = underlyingType; | 
| 1156 |     getter.name = compilationData.read; | 
| 1157 |     getter.body += prologue; | 
| 1158 |     if (result.kind == QQmlJSUtils::AliasTarget_Property) { | 
| 1159 |         if (QString read = result.property.read(); !read.isEmpty() | 
| 1160 |             && !QQmlJSUtils::bindablePropertyHasDefaultAccessor( | 
| 1161 |                     p: result.property, accessor: QQmlJSUtils::PropertyAccessor_Read)) { | 
| 1162 |             getter.body << u"return %1->%2();"_s .arg(args: latestAccessor, args&: read); | 
| 1163 |         } else { // use QObject::property() as a fallback when read method is unknown | 
| 1164 |             getter.body << u"return qvariant_cast<%1>(%2->property(\"%3\"));"_s .arg( | 
| 1165 |                     args: underlyingType, args: latestAccessor, args: result.property.propertyName()); | 
| 1166 |         } | 
| 1167 |     } else { // AliasTarget_Object | 
| 1168 |         getter.body << u"return "  + latestAccessor + u";" ; | 
| 1169 |     } | 
| 1170 |     getter.body += epilogue; | 
| 1171 |     getter.userVisible = true; | 
| 1172 |     current.functions.emplaceBack(args&: getter); | 
| 1173 |     mocLines << u"READ"_s  << getter.name; | 
| 1174 |  | 
| 1175 |     if (result.property.isWritable()) { | 
| 1176 |         Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise | 
| 1177 |         QmltcMethod setter {}; | 
| 1178 |         setter.returnType = u"void"_s ; | 
| 1179 |         setter.name = compilationData.write; | 
| 1180 |  | 
| 1181 |         const QString setName = result.property.write(); | 
| 1182 |         QList<QQmlJSMetaMethod> methods = result.owner->methods(name: setName); | 
| 1183 |         if (methods.isEmpty()) { // when we are compiling the property as well | 
| 1184 |             // QmltcVariable | 
| 1185 |             setter.parameterList.emplaceBack(args: QQmlJSUtils::constRefify(type: underlyingType), | 
| 1186 |                                              args: aliasName + u"_" , args: u""_s ); | 
| 1187 |         } else { | 
| 1188 |             setter.parameterList = compileMethodParameters(parameterInfos: methods.at(i: 0).parameters(), | 
| 1189 |                                                            /* allow unnamed = */ allowUnnamed: true); | 
| 1190 |         } | 
| 1191 |  | 
| 1192 |         setter.body += prologue; | 
| 1193 |         QStringList parameterNames; | 
| 1194 |         parameterNames.reserve(asize: setter.parameterList.size()); | 
| 1195 |         std::transform(first: setter.parameterList.cbegin(), last: setter.parameterList.cend(), | 
| 1196 |                        result: std::back_inserter(x&: parameterNames), | 
| 1197 |                        unary_op: [](const QmltcVariable &x) { return x.name; }); | 
| 1198 |         QString commaSeparatedParameterNames = parameterNames.join(sep: u", "_s ); | 
| 1199 |         if (!setName.isEmpty() | 
| 1200 |             && !QQmlJSUtils::bindablePropertyHasDefaultAccessor( | 
| 1201 |                     p: result.property, accessor: QQmlJSUtils::PropertyAccessor_Write)) { | 
| 1202 |             setter.body << u"%1->%2(%3);"_s .arg(args: latestAccessor, args: setName, | 
| 1203 |                                                 args&: commaSeparatedParameterNames); | 
| 1204 |         } else { // use QObject::setProperty() as fallback when write method is unknown | 
| 1205 |             Q_ASSERT(parameterNames.size() == 1); | 
| 1206 |             const QString variantName = u"var_"  + aliasName; // fairly unique | 
| 1207 |             setter.body << u"QVariant %1;"_s .arg(a: variantName); | 
| 1208 |             setter.body << u"%1.setValue(%2);"_s .arg(args: variantName, args&: commaSeparatedParameterNames); | 
| 1209 |             setter.body << u"%1->setProperty(\"%2\", std::move(%3));"_s .arg( | 
| 1210 |                     args: latestAccessor, args: result.property.propertyName(), args: variantName); | 
| 1211 |         } | 
| 1212 |         setter.body += joinFrames( | 
| 1213 |                 frames, project: [](const AliasResolutionFrame &frame) { return frame.epilogueForWrite; }); | 
| 1214 |         setter.body += epilogue; // NB: *after* epilogueForWrite - see prologue construction | 
| 1215 |         setter.userVisible = true; | 
| 1216 |         current.functions.emplaceBack(args&: setter); | 
| 1217 |         mocLines << u"WRITE"_s  << setter.name; | 
| 1218 |     } | 
| 1219 |     // 2. add bindable | 
| 1220 |     if (QString bindableName = result.property.bindable(); !bindableName.isEmpty()) { | 
| 1221 |         Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise | 
| 1222 |         QmltcMethod bindable {}; | 
| 1223 |         bindable.returnType = u"QBindable<"  + underlyingType + u">" ; | 
| 1224 |         bindable.name = compilationData.bindable; | 
| 1225 |         bindable.body += prologue; | 
| 1226 |         bindable.body << u"return "  + latestAccessor + u"->"  + bindableName + u"()"  + u";" ; | 
| 1227 |         bindable.body += epilogue; | 
| 1228 |         bindable.userVisible = true; | 
| 1229 |         current.functions.emplaceBack(args&: bindable); | 
| 1230 |         mocLines << u"BINDABLE"_s  << bindable.name; | 
| 1231 |     } | 
| 1232 |  | 
| 1233 |     // 3. add notify - which is pretty special | 
| 1234 |     // step 1: generate the moc instructions | 
| 1235 |     // mimic the engines behavior: do it even if the notify will never be emitted | 
| 1236 |     if (const QString aliasNotifyName = alias.notify(); !aliasNotifyName.isEmpty()) { | 
| 1237 |  | 
| 1238 |         Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise | 
| 1239 |  | 
| 1240 |         mocLines << u"NOTIFY"_s  << aliasNotifyName; | 
| 1241 |     } | 
| 1242 |  | 
| 1243 |     // step 2: connect the notifier to the aliased property notifier, if this latter exists | 
| 1244 |     // otherwise, mimic the engines behavior and generate a useless notify | 
| 1245 |     if (const QString notifyName = result.property.notify(); !notifyName.isEmpty()) { | 
| 1246 |         auto notifyFrames = frames; | 
| 1247 |         notifyFrames.pop(); // we don't need the last frame at all in this case | 
| 1248 |  | 
| 1249 |         const QStringList notifyPrologue = joinFrames( | 
| 1250 |                 frames, project: [](const AliasResolutionFrame &frame) { return frame.prologue; }); | 
| 1251 |         const QStringList notifyEpilogue = joinFrames( | 
| 1252 |                 frames, project: [](const AliasResolutionFrame &frame) { return frame.epilogue; }); | 
| 1253 |  | 
| 1254 |         // notify is very special | 
| 1255 |         current.endInit.body << u"{ // alias notify connection:"_s ; | 
| 1256 |         current.endInit.body += notifyPrologue; | 
| 1257 |         // TODO: use non-private accessor since signals must exist on the public | 
| 1258 |         // type, not on the private one -- otherwise, you can't connect to a | 
| 1259 |         // private property signal in C++ and so it is useless (hence, use | 
| 1260 |         // public type) | 
| 1261 |         const QString cppType = (m_visitor->qmlComponentIndex(type: result.owner) == -1) | 
| 1262 |                 ? result.owner->internalName() | 
| 1263 |                 : u"QQmlComponent"_s ; | 
| 1264 |         const QString latestAccessorNonPrivate = notifyFrames.top().outVar; | 
| 1265 |         current.endInit.body << u"QObject::connect("  + latestAccessorNonPrivate + u", &"  + cppType | 
| 1266 |                         + u"::"  + notifyName + u", this, &"  + current.cppType + u"::"  | 
| 1267 |                         + compilationData.notify + u");" ; | 
| 1268 |         current.endInit.body += notifyEpilogue; | 
| 1269 |         current.endInit.body << u"}"_s ; | 
| 1270 |     } | 
| 1271 |  | 
| 1272 |     if (QString resetName = result.property.reset(); !resetName.isEmpty()) { | 
| 1273 |         Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property); // property is invalid otherwise | 
| 1274 |         QmltcMethod reset {}; | 
| 1275 |         reset.returnType = u"void"_s ; | 
| 1276 |         reset.name = compilationData.reset; | 
| 1277 |         reset.body += prologue; | 
| 1278 |         reset.body << latestAccessor + u"->"  + resetName + u"()"  + u";" ; | 
| 1279 |         reset.body += epilogue; | 
| 1280 |         reset.userVisible = true; | 
| 1281 |         current.functions.emplaceBack(args&: reset); | 
| 1282 |         mocLines << u"RESET"_s  << reset.name; | 
| 1283 |     } | 
| 1284 |  | 
| 1285 |     // mimic the engines behavior: aliases are never constants | 
| 1286 |     // mocLines << u"CONSTANT"_s; | 
| 1287 |     // mimic the engines behavior: aliases are never stored | 
| 1288 |     mocLines << u"STORED"_s  << u"false"_s ; | 
| 1289 |     // mimic the engines behavior: aliases are never designable | 
| 1290 |     mocLines << u"DESIGNABLE"_s  << u"false"_s ; | 
| 1291 |  | 
| 1292 |     // 4. add moc entry | 
| 1293 |     // Q_PROPERTY(QString text READ text WRITE setText BINDABLE bindableText NOTIFY textChanged) | 
| 1294 |     current.mocCode << u"Q_PROPERTY("  + mocLines.join(sep: u" "_s ) + u")" ; | 
| 1295 |  | 
| 1296 |     // 5. add extra moc entry if this alias is default one | 
| 1297 |     if (aliasName == owner->defaultPropertyName()) { | 
| 1298 |         // Q_CLASSINFO("DefaultProperty", propertyName) | 
| 1299 |         current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s .arg(a: aliasName); | 
| 1300 |     } | 
| 1301 | } | 
| 1302 |  | 
| 1303 | static QString generate_callCompilationUnit(const QString &urlMethodName) | 
| 1304 | { | 
| 1305 |     return u"QQmlEnginePrivate::get(engine)->compilationUnitFromUrl(%1())"_s .arg(a: urlMethodName); | 
| 1306 | } | 
| 1307 |  | 
| 1308 | static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope::ConstPtr &scope, | 
| 1309 |                                                                const QString &propertyName); | 
| 1310 |  | 
| 1311 | /*! | 
| 1312 |  * \internal | 
| 1313 |  * Helper method used to keep compileBindingByType() readable. | 
| 1314 |  */ | 
| 1315 | void QmltcCompiler::compileObjectBinding(QmltcType ¤t, | 
| 1316 |                                          const QQmlJSMetaPropertyBinding &binding, | 
| 1317 |                                          const QQmlJSScope::ConstPtr &type, | 
| 1318 |                                          const BindingAccessorData &accessor) | 
| 1319 | { | 
| 1320 |     Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object); | 
| 1321 |  | 
| 1322 |     const QString &propertyName = binding.propertyName(); | 
| 1323 |     const QQmlJSMetaProperty property = type->property(name: propertyName); | 
| 1324 |     QQmlJSScope::ConstPtr propertyType = property.type(); | 
| 1325 |  | 
| 1326 |     // NB: object is compiled with compileType(), here just need to use it | 
| 1327 |     auto object = binding.objectType(); | 
| 1328 |  | 
| 1329 |     // Note: despite a binding being set for `accessor`, we use "this" as a | 
| 1330 |     // parent of a created object. Both attached and grouped properties are | 
| 1331 |     // parented by "this", so lifetime-wise we should be fine | 
| 1332 |     const QString qobjectParent = u"this"_s ; | 
| 1333 |  | 
| 1334 |     if (!propertyType) { | 
| 1335 |         recordError(location: binding.sourceLocation(), | 
| 1336 |                     message: u"Binding on property '"  + propertyName + u"' of unknown type" ); | 
| 1337 |         return; | 
| 1338 |     } | 
| 1339 |  | 
| 1340 |     const auto addObjectBinding = [&](const QString &value) { | 
| 1341 |         if (qIsReferenceTypeList(p: property)) { | 
| 1342 |             Q_ASSERT(unprocessedListProperty == property || unprocessedListBindings.empty()); | 
| 1343 |             unprocessedListBindings.append(t: value); | 
| 1344 |             unprocessedListProperty = property; | 
| 1345 |         } else { | 
| 1346 |             QmltcCodeGenerator::generate_assignToProperty(block: ¤t.endInit.body, type, p: property, | 
| 1347 |                                                           value, accessor: accessor.name, constructFromQObject: true); | 
| 1348 |         } | 
| 1349 |     }; | 
| 1350 |  | 
| 1351 |     // special case of implicit or explicit component: | 
| 1352 |     if (qsizetype index = m_visitor->qmlComponentIndex(type: object); index >= 0) { | 
| 1353 |         const QString objectName = newSymbol(base: u"sc"_s ); | 
| 1354 |  | 
| 1355 |         const qsizetype creationIndex = m_visitor->creationIndex(type: object); | 
| 1356 |  | 
| 1357 |         QStringList *block = (creationIndex == -1) ? ¤t.endInit.body : ¤t.init.body; | 
| 1358 |         *block << u"{"_s ; | 
| 1359 |         *block << QStringLiteral("auto thisContext = QQmlData::get(%1)->outerContext;" ) | 
| 1360 |                           .arg(a: qobjectParent); | 
| 1361 |         *block << QStringLiteral("auto %1 = QQmlObjectCreator::createComponent(engine, "  | 
| 1362 |                                  "%2, %3, %4, thisContext);" ) | 
| 1363 |                           .arg(args: objectName, args: generate_callCompilationUnit(urlMethodName: m_urlMethodName), | 
| 1364 |                                args: QString::number(index), args: qobjectParent); | 
| 1365 |         *block << QStringLiteral("thisContext->installContext(QQmlData::get(%1), "  | 
| 1366 |                                  "QQmlContextData::OrdinaryObject);" ) | 
| 1367 |                           .arg(a: objectName); | 
| 1368 |  | 
| 1369 |         // objects wrapped in implicit components do not have visible ids, | 
| 1370 |         // however, explicit components can have an id and that one is going | 
| 1371 |         // to be visible in the common document context | 
| 1372 |         if (creationIndex != -1) { | 
| 1373 |             // explicit component | 
| 1374 |             Q_ASSERT(object->isComposite()); | 
| 1375 |             Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s ); | 
| 1376 |  | 
| 1377 |             if (int id = m_visitor->runtimeId(type: object); id >= 0) { | 
| 1378 |                 QString idString = m_visitor->addressableScopes().id(scope: object, referrer: object); | 
| 1379 |                 if (idString.isEmpty()) | 
| 1380 |                     idString = u"<unknown>"_s ; | 
| 1381 |                 QmltcCodeGenerator::generate_setIdValue(block, context: u"thisContext"_s , index: id, accessor: objectName, | 
| 1382 |                                                         idString); | 
| 1383 |             } | 
| 1384 |  | 
| 1385 |             const QString creationIndexStr = QString::number(creationIndex); | 
| 1386 |             *block << QStringLiteral("creator->set(%1, %2);" ).arg(args: creationIndexStr, args: objectName); | 
| 1387 |             Q_ASSERT(block == ¤t.init.body); | 
| 1388 |             current.endInit.body << QStringLiteral("auto %1 = creator->get<%2>(%3);" ) | 
| 1389 |                                             .arg(args: objectName, args: u"QQmlComponent"_s , args: creationIndexStr); | 
| 1390 |         } | 
| 1391 |         addObjectBinding(objectName); | 
| 1392 |         *block << u"}"_s ; | 
| 1393 |         return; | 
| 1394 |     } | 
| 1395 |  | 
| 1396 |     const QString objectName = newSymbol(base: u"o"_s ); | 
| 1397 |     current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s .arg( | 
| 1398 |             args: objectName, args: object->internalName(), args: qobjectParent); | 
| 1399 |     current.init.body << u"creator->set(%1, %2);"_s .arg( | 
| 1400 |             args: QString::number(m_visitor->creationIndex(type: object)), args: objectName); | 
| 1401 |  | 
| 1402 |     // refetch the same object during endInit to set the bindings | 
| 1403 |     current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s .arg( | 
| 1404 |             args: objectName, args: object->internalName(), args: QString::number(m_visitor->creationIndex(type: object))); | 
| 1405 |     addObjectBinding(objectName); | 
| 1406 | } | 
| 1407 |  | 
| 1408 | /*! | 
| 1409 |  * \internal | 
| 1410 |  * Helper method used to keep compileBindingByType() readable. | 
| 1411 |  */ | 
| 1412 | void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType ¤t, | 
| 1413 |                                                            const QQmlJSMetaPropertyBinding &binding, | 
| 1414 |                                                            const QQmlJSScope::ConstPtr &type, | 
| 1415 |                                                            const BindingAccessorData &accessor) | 
| 1416 | { | 
| 1417 |     Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource | 
| 1418 |              || binding.bindingType() == QQmlSA::BindingType::Interceptor); | 
| 1419 |  | 
| 1420 |     const QString &propertyName = binding.propertyName(); | 
| 1421 |     const QQmlJSMetaProperty property = type->property(name: propertyName); | 
| 1422 |     QQmlJSScope::ConstPtr propertyType = property.type(); | 
| 1423 |  | 
| 1424 |     // NB: object is compiled with compileType(), here just need to use it | 
| 1425 |     QSharedPointer<const QQmlJSScope> object; | 
| 1426 |     if (binding.bindingType() == QQmlSA::BindingType::Interceptor) | 
| 1427 |         object = binding.interceptorType(); | 
| 1428 |     else | 
| 1429 |         object = binding.valueSourceType(); | 
| 1430 |  | 
| 1431 |     // Note: despite a binding being set for `accessor`, we use "this" as a | 
| 1432 |     // parent of a created object. Both attached and grouped properties are | 
| 1433 |     // parented by "this", so lifetime-wise we should be fine | 
| 1434 |     const QString qobjectParent = u"this"_s ; | 
| 1435 |  | 
| 1436 |     if (!propertyType) { | 
| 1437 |         recordError(location: binding.sourceLocation(), | 
| 1438 |                     message: u"Binding on property '"  + propertyName + u"' of unknown type" ); | 
| 1439 |         return; | 
| 1440 |     } | 
| 1441 |  | 
| 1442 |     auto &objectName = m_uniques[UniqueStringId(current, propertyName)].onAssignmentObjectName; | 
| 1443 |     if (objectName.isEmpty()) { | 
| 1444 |         objectName = u"onAssign_"  + propertyName; | 
| 1445 |  | 
| 1446 |         current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s .arg( | 
| 1447 |                 args&: objectName, args: object->internalName(), args: qobjectParent); | 
| 1448 |         current.init.body << u"creator->set(%1, %2);"_s .arg( | 
| 1449 |                 args: QString::number(m_visitor->creationIndex(type: object)), args&: objectName); | 
| 1450 |  | 
| 1451 |         current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s .arg( | 
| 1452 |                 args&: objectName, args: object->internalName(), | 
| 1453 |                 args: QString::number(m_visitor->creationIndex(type: object))); | 
| 1454 |     } | 
| 1455 |  | 
| 1456 |     // NB: we expect one "on" assignment per property, so creating | 
| 1457 |     // QQmlProperty each time should be fine (unlike QQmlListReference) | 
| 1458 |     current.endInit.body << u"{"_s ; | 
| 1459 |     current.endInit.body << u"QQmlProperty qmlprop(%1, %2);"_s .arg( | 
| 1460 |             args: accessor.name, args: QQmlJSUtils::toLiteral(s: propertyName)); | 
| 1461 |     current.endInit.body << u"QT_PREPEND_NAMESPACE(QQmlCppOnAssignmentHelper)::set(%1, qmlprop);"_s  | 
| 1462 |                                     .arg(a: objectName); | 
| 1463 |     current.endInit.body << u"}"_s ; | 
| 1464 | } | 
| 1465 |  | 
| 1466 | /*! | 
| 1467 |  * \internal | 
| 1468 |  * Helper method used to keep compileBindingByType() readable. | 
| 1469 |  */ | 
| 1470 | void QmltcCompiler::compileAttachedPropertyBinding(QmltcType ¤t, | 
| 1471 |                                                    const QQmlJSMetaPropertyBinding &binding, | 
| 1472 |                                                    const QQmlJSScope::ConstPtr &type, | 
| 1473 |                                                    const BindingAccessorData &accessor) | 
| 1474 | { | 
| 1475 |     Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty); | 
| 1476 |  | 
| 1477 |     const QString &propertyName = binding.propertyName(); | 
| 1478 |     const QQmlJSMetaProperty property = type->property(name: propertyName); | 
| 1479 |     QQmlJSScope::ConstPtr propertyType = property.type(); | 
| 1480 |  | 
| 1481 |     Q_ASSERT(accessor.name == u"this"_s ); // doesn't have to hold, in fact | 
| 1482 |     const auto attachedType = binding.attachingType(); | 
| 1483 |     Q_ASSERT(attachedType); | 
| 1484 |  | 
| 1485 |     const QString attachingTypeName = propertyName; // acts as an identifier | 
| 1486 |     auto attachingType = m_typeResolver->typeForName(name: attachingTypeName); | 
| 1487 |  | 
| 1488 |     QString attachedTypeName = attachedType->baseTypeName(); | 
| 1489 |     Q_ASSERT(!attachedTypeName.isEmpty()); | 
| 1490 |  | 
| 1491 |     auto &attachedMemberName = | 
| 1492 |             m_uniques[UniqueStringId(current, propertyName)].attachedVariableName; | 
| 1493 |     if (attachedMemberName.isEmpty()) { | 
| 1494 |         attachedMemberName = uniqueVariableName(qmlName: attachingTypeName); | 
| 1495 |  | 
| 1496 |         // add attached type as a member variable to allow noop lookup | 
| 1497 |         current.variables.emplaceBack(args: attachedTypeName + u" *" , args&: attachedMemberName, args: u"nullptr"_s ); | 
| 1498 |  | 
| 1499 |         if (propertyName == u"Component"_s ) { // Component attached type is special | 
| 1500 |             current.endInit.body << u"Q_ASSERT(qmlEngine(this));"_s ; | 
| 1501 |             current.endInit.body | 
| 1502 |                     << u"// attached Component must be added to the object's QQmlData"_s ; | 
| 1503 |             current.endInit.body | 
| 1504 |                     << u"Q_ASSERT(!QQmlEnginePrivate::get(qmlEngine(this))->activeObjectCreator);"_s ; | 
| 1505 |         } | 
| 1506 |  | 
| 1507 |         // Note: getting attached property is fairly expensive | 
| 1508 |         const QString getAttachedPropertyLine = u"qobject_cast<"  + attachedTypeName | 
| 1509 |                 + u" *>(qmlAttachedPropertiesObject<"  + attachingType->internalName() | 
| 1510 |                 + u">(this, /* create = */ true))" ; | 
| 1511 |         current.endInit.body << attachedMemberName + u" = "  + getAttachedPropertyLine + u";" ; | 
| 1512 |  | 
| 1513 |         if (propertyName == u"Component"_s ) { | 
| 1514 |             // call completed/destruction signals appropriately | 
| 1515 |             current.handleOnCompleted.body << u"Q_EMIT "  + attachedMemberName + u"->completed();" ; | 
| 1516 |             if (!current.dtor) { | 
| 1517 |                 current.dtor = QmltcDtor{}; | 
| 1518 |                 current.dtor->name = u"~"  + current.cppType; | 
| 1519 |             } | 
| 1520 |             current.dtor->body << u"Q_EMIT "  + attachedMemberName + u"->destruction();" ; | 
| 1521 |         } | 
| 1522 |     } | 
| 1523 |  | 
| 1524 |     auto subbindings = attachedType->ownPropertyBindingsInQmlIROrder(); | 
| 1525 |     // compile bindings of the attached property | 
| 1526 |     partitionBindings(first: subbindings.begin(), last: subbindings.end()); | 
| 1527 |     compileBinding(current, bindingStart: subbindings.begin(), bindingEnd: subbindings.end(), type: attachedType, | 
| 1528 |                    accessor: { .scope: type, .name: attachedMemberName, .propertyName: propertyName, .isValueType: false }); | 
| 1529 | } | 
| 1530 |  | 
| 1531 | /*! | 
| 1532 |  * \internal | 
| 1533 |  * Helper method used to keep compileBindingByType() readable. | 
| 1534 |  */ | 
| 1535 | void QmltcCompiler::compileGroupPropertyBinding(QmltcType ¤t, | 
| 1536 |                                                 const QQmlJSMetaPropertyBinding &binding, | 
| 1537 |                                                 const QQmlJSScope::ConstPtr &type, | 
| 1538 |                                                 const BindingAccessorData &accessor) | 
| 1539 | { | 
| 1540 |     Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty); | 
| 1541 |  | 
| 1542 |     const QString &propertyName = binding.propertyName(); | 
| 1543 |     const QQmlJSMetaProperty property = type->property(name: propertyName); | 
| 1544 |     QQmlJSScope::ConstPtr propertyType = property.type(); | 
| 1545 |  | 
| 1546 |     Q_ASSERT(accessor.name == u"this"_s ); // doesn't have to hold, in fact | 
| 1547 |     if (property.read().isEmpty()) { | 
| 1548 |         recordError(location: binding.sourceLocation(), | 
| 1549 |                     message: u"READ function of group property '"  + propertyName + u"' is unknown" ); | 
| 1550 |         return; | 
| 1551 |     } | 
| 1552 |  | 
| 1553 |     auto groupType = binding.groupType(); | 
| 1554 |     Q_ASSERT(groupType); | 
| 1555 |  | 
| 1556 |     const bool isValueType = propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Value; | 
| 1557 |     if (!isValueType | 
| 1558 |         && propertyType->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) { | 
| 1559 |         recordError(location: binding.sourceLocation(), | 
| 1560 |                     message: u"Group property '"  + propertyName + u"' has unsupported access semantics" ); | 
| 1561 |         return; | 
| 1562 |     } | 
| 1563 |  | 
| 1564 |     auto subbindings = groupType->ownPropertyBindingsInQmlIROrder(); | 
| 1565 |     auto firstScript = partitionBindings(first: subbindings.begin(), last: subbindings.end()); | 
| 1566 |  | 
| 1567 |     // if we have no non-script bindings, we have no bindings that affect | 
| 1568 |     // the value type group, so no reason to generate the wrapping code | 
| 1569 |     const bool generateValueTypeCode = isValueType && (subbindings.begin() != firstScript); | 
| 1570 |  | 
| 1571 |     QString groupAccessor = QmltcCodeGenerator::wrap_privateClass(accessor: accessor.name, p: property) + u"->"  | 
| 1572 |             + property.read() + u"()" ; | 
| 1573 |     // NB: used when isValueType == true | 
| 1574 |     const QString groupPropertyVarName = accessor.name + u"_group_"  + propertyName; | 
| 1575 |     // value types are special | 
| 1576 |     if (generateValueTypeCode) { | 
| 1577 |         if (property.write().isEmpty()) { // just reject this | 
| 1578 |             recordError(location: binding.sourceLocation(), | 
| 1579 |                         message: u"Group property '"  + propertyName + u"' is a value type without a setter" ); | 
| 1580 |             return; | 
| 1581 |         } | 
| 1582 |  | 
| 1583 |         current.endInit.body << u"auto "  + groupPropertyVarName + u" = "  + groupAccessor + u";" ; | 
| 1584 |         // addressof operator is to make the binding logic work, which | 
| 1585 |         // expects that `accessor.name` is a pointer type | 
| 1586 |         groupAccessor = QmltcCodeGenerator::wrap_addressof(addressed: groupPropertyVarName); | 
| 1587 |     } | 
| 1588 |  | 
| 1589 |     // compile bindings of the grouped property | 
| 1590 |     const auto compile = [&](const auto &bStart, const auto &bEnd) { | 
| 1591 |         compileBinding(current, bindingStart: bStart, bindingEnd: bEnd, type: groupType, | 
| 1592 |                        accessor: { type, groupAccessor, propertyName, isValueType }); | 
| 1593 |     }; | 
| 1594 |  | 
| 1595 |     auto it = subbindings.begin(); | 
| 1596 |     Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) { | 
| 1597 |         return x.bindingType() != QQmlSA::BindingType::Script; | 
| 1598 |     })); | 
| 1599 |     compile(it, firstScript); | 
| 1600 |     it = firstScript; | 
| 1601 |  | 
| 1602 |     // NB: script bindings are special on group properties. if our group is | 
| 1603 |     // a value type, the binding would be installed on the *object* that | 
| 1604 |     // holds the value type and not on the value type itself. this may cause | 
| 1605 |     // subtle side issues (esp. when script binding is actually a simple | 
| 1606 |     // enum value assignment - which is not recognized specially): | 
| 1607 |     // | 
| 1608 |     // auto valueTypeGroupProperty = getCopy(); | 
| 1609 |     // installBinding(valueTypeGroupProperty, "subproperty1"); // changes subproperty1 value | 
| 1610 |     // setCopy(valueTypeGroupProperty); // oops, subproperty1 value changed to old again | 
| 1611 |     if (generateValueTypeCode) { // write the value type back | 
| 1612 |         current.endInit.body << QmltcCodeGenerator::wrap_privateClass(accessor: accessor.name, p: property) | 
| 1613 |                         + u"->"  + property.write() + u"("  + groupPropertyVarName + u");" ; | 
| 1614 |     } | 
| 1615 |  | 
| 1616 |     // once the value is written back, process the script bindings | 
| 1617 |     Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) { | 
| 1618 |         return x.bindingType() == QQmlSA::BindingType::Script; | 
| 1619 |     })); | 
| 1620 |     compile(it, subbindings.end()); | 
| 1621 | } | 
| 1622 |  | 
| 1623 | /*! | 
| 1624 |  * \internal | 
| 1625 |  * Helper method used to keep compileBindingByType() readable. | 
| 1626 |  */ | 
| 1627 | void QmltcCompiler::compileTranslationBinding(QmltcType ¤t, | 
| 1628 |                                               const QQmlJSMetaPropertyBinding &binding, | 
| 1629 |                                               const QQmlJSScope::ConstPtr &type, | 
| 1630 |                                               const BindingAccessorData &accessor) | 
| 1631 | { | 
| 1632 |     Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation | 
| 1633 |              || binding.bindingType() == QQmlSA::BindingType::TranslationById); | 
| 1634 |  | 
| 1635 |     const QString &propertyName = binding.propertyName(); | 
| 1636 |  | 
| 1637 |     auto [property, absoluteIndex] = getMetaPropertyIndex(scope: type, propertyName); | 
| 1638 |  | 
| 1639 |     if (absoluteIndex < 0) { | 
| 1640 |         recordError(location: binding.sourceLocation(), | 
| 1641 |                     message: u"Binding on unknown property '"  + propertyName + u"'" ); | 
| 1642 |         return; | 
| 1643 |     } | 
| 1644 |  | 
| 1645 |     QString bindingTarget = accessor.name; | 
| 1646 |  | 
| 1647 |     int valueTypeIndex = -1; | 
| 1648 |     if (accessor.isValueType) { | 
| 1649 |         Q_ASSERT(accessor.scope != type); | 
| 1650 |         bindingTarget = u"this"_s ; // TODO: not necessarily "this"? | 
| 1651 |         auto [groupProperty, groupPropertyIndex] = | 
| 1652 |                 getMetaPropertyIndex(scope: accessor.scope, propertyName: accessor.propertyName); | 
| 1653 |         if (groupPropertyIndex < 0) { | 
| 1654 |             recordError(location: binding.sourceLocation(), | 
| 1655 |                         message: u"Binding on group property '"  + accessor.propertyName | 
| 1656 |                                 + u"' of unknown type" ); | 
| 1657 |             return; | 
| 1658 |         } | 
| 1659 |         valueTypeIndex = absoluteIndex; | 
| 1660 |         absoluteIndex = groupPropertyIndex; // e.g. index of accessor.name | 
| 1661 |     } | 
| 1662 |  | 
| 1663 |     QmltcCodeGenerator::TranslationBindingInfo info; | 
| 1664 |     info.unitVarName = generate_callCompilationUnit(urlMethodName: m_urlMethodName); | 
| 1665 |     info.scope = u"this"_s ; | 
| 1666 |     info.target = u"this"_s ; | 
| 1667 |     info.propertyIndex = absoluteIndex; | 
| 1668 |     info.property = property; | 
| 1669 |     info.data = binding.translationDataValue(qmlFileNameForContext: m_url); | 
| 1670 |     info.valueTypeIndex = valueTypeIndex; | 
| 1671 |     info.line = binding.sourceLocation().startLine; | 
| 1672 |     info.column = binding.sourceLocation().startColumn; | 
| 1673 |  | 
| 1674 |     QmltcCodeGenerator::generate_createTranslationBindingOnProperty(block: ¤t.endInit.body, info); | 
| 1675 | } | 
| 1676 |  | 
| 1677 | void QmltcCompiler::processLastListBindings(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, | 
| 1678 |                                             const BindingAccessorData &accessor) | 
| 1679 | { | 
| 1680 |     if (unprocessedListBindings.empty()) | 
| 1681 |         return; | 
| 1682 |  | 
| 1683 |     QmltcCodeGenerator::generate_assignToListProperty( | 
| 1684 |             block: ¤t.endInit.body, type, p: unprocessedListProperty, value: unprocessedListBindings, | 
| 1685 |             accessor: accessor.name, | 
| 1686 |             qmlListVarName&: m_uniques[UniqueStringId(current, unprocessedListProperty.propertyName())] | 
| 1687 |                     .qmlListVariableName); | 
| 1688 |  | 
| 1689 |     unprocessedListBindings.clear(); | 
| 1690 | } | 
| 1691 |  | 
| 1692 | void QmltcCompiler::compileBinding(QmltcType ¤t, | 
| 1693 |                                    QList<QQmlJSMetaPropertyBinding>::iterator bindingStart, | 
| 1694 |                                    QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd, | 
| 1695 |                                    const QQmlJSScope::ConstPtr &type, | 
| 1696 |                                    const BindingAccessorData &accessor) | 
| 1697 | { | 
| 1698 |     for (auto it = bindingStart; it != bindingEnd; it++) { | 
| 1699 |         const QQmlJSMetaPropertyBinding &binding = *it; | 
| 1700 |         const QString &propertyName = binding.propertyName(); | 
| 1701 |         Q_ASSERT(!propertyName.isEmpty()); | 
| 1702 |  | 
| 1703 |         // Note: unlike QQmlObjectCreator, we don't have to do a complicated | 
| 1704 |         // deferral logic for bindings: if a binding is deferred, it is not compiled | 
| 1705 |         // (potentially, with all the bindings inside of it), period. | 
| 1706 |         if (type->isNameDeferred(name: propertyName)) { | 
| 1707 |             const auto location = binding.sourceLocation(); | 
| 1708 |             // make sure group property is not generalized by checking if type really has a property | 
| 1709 |             // called propertyName. If not, it is probably an id. | 
| 1710 |             if (binding.bindingType() == QQmlSA::BindingType::GroupProperty | 
| 1711 |                 && type->hasProperty(name: propertyName)) { | 
| 1712 |                 qCWarning(lcQmltcCompiler) | 
| 1713 |                         << QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "  | 
| 1714 |                                           "binding on a group property." ) | 
| 1715 |                                    .arg(args: QString::number(location.startLine), | 
| 1716 |                                         args: QString::number(location.startColumn)); | 
| 1717 |                 // we do not support PropertyChanges and other types with similar | 
| 1718 |                 // behavior yet, so this binding is compiled | 
| 1719 |             } else { | 
| 1720 |                 qCDebug(lcQmltcCompiler) | 
| 1721 |                         << QStringLiteral( | 
| 1722 |                                    "Binding at line %1 column %2 is deferred and thus not compiled" ) | 
| 1723 |                                    .arg(args: QString::number(location.startLine), | 
| 1724 |                                         args: QString::number(location.startColumn)); | 
| 1725 |                 continue; | 
| 1726 |             } | 
| 1727 |         } | 
| 1728 |  | 
| 1729 |         const QQmlJSMetaProperty metaProperty = type->property(name: propertyName); | 
| 1730 |         const QQmlJSScope::ConstPtr propertyType = metaProperty.type(); | 
| 1731 |  | 
| 1732 |         if (!(qIsReferenceTypeList(p: metaProperty) && unprocessedListProperty == metaProperty)) { | 
| 1733 |             processLastListBindings(current, type, accessor); | 
| 1734 |         } | 
| 1735 |  | 
| 1736 |         compileBindingByType(current, binding, type, accessor); | 
| 1737 |     } | 
| 1738 |  | 
| 1739 |     processLastListBindings(current, type, accessor); | 
| 1740 | } | 
| 1741 |  | 
| 1742 | void QmltcCompiler::compileBindingByType(QmltcType ¤t, | 
| 1743 |                                          const QQmlJSMetaPropertyBinding &binding, | 
| 1744 |                                          const QQmlJSScope::ConstPtr &type, | 
| 1745 |                                          const BindingAccessorData &accessor) | 
| 1746 | { | 
| 1747 |     const QString &propertyName = binding.propertyName(); | 
| 1748 |     const QQmlJSMetaProperty metaProperty = type->property(name: propertyName); | 
| 1749 |     const QQmlJSScope::ConstPtr propertyType = metaProperty.type(); | 
| 1750 |  | 
| 1751 |     const auto assignToProperty = [&](const QQmlJSMetaProperty &p, const QString &value, | 
| 1752 |                                       bool constructFromQObject = false) { | 
| 1753 |         QmltcCodeGenerator::generate_assignToProperty(block: ¤t.endInit.body, type, p, value, | 
| 1754 |                                                       accessor: accessor.name, constructFromQObject); | 
| 1755 |     }; | 
| 1756 |     switch (binding.bindingType()) { | 
| 1757 |     case QQmlSA::BindingType::BoolLiteral: { | 
| 1758 |         const bool value = binding.boolValue(); | 
| 1759 |         assignToProperty(metaProperty, value ? u"true"_s  : u"false"_s ); | 
| 1760 |         break; | 
| 1761 |     } | 
| 1762 |     case QQmlSA::BindingType::NumberLiteral: { | 
| 1763 |         assignToProperty(metaProperty, QString::number(binding.numberValue())); | 
| 1764 |         break; | 
| 1765 |     } | 
| 1766 |     case QQmlSA::BindingType::StringLiteral: { | 
| 1767 |         QString value = QQmlJSUtils::toLiteral(s: binding.stringValue()); | 
| 1768 |         if (auto type = metaProperty.type()) { | 
| 1769 |             if (type->internalName() == u"QUrl"_s ) { | 
| 1770 |                 value = u"QUrl(%1)"_s .arg(a: value); | 
| 1771 |             } | 
| 1772 |         } | 
| 1773 |         assignToProperty(metaProperty, value); | 
| 1774 |         break; | 
| 1775 |     } | 
| 1776 |     case QQmlSA::BindingType::RegExpLiteral: { | 
| 1777 |         const QString value = | 
| 1778 |                 u"QRegularExpression(%1)"_s .arg(a: QQmlJSUtils::toLiteral(s: binding.regExpValue())); | 
| 1779 |         assignToProperty(metaProperty, value); | 
| 1780 |         break; | 
| 1781 |     } | 
| 1782 |     case QQmlSA::BindingType::Null: { | 
| 1783 |         // poor check: null bindings are only supported for var and objects | 
| 1784 |         Q_ASSERT(propertyType->isSameType(m_typeResolver->varType()) | 
| 1785 |                  || propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference); | 
| 1786 |         if (propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) | 
| 1787 |             assignToProperty(metaProperty, u"nullptr"_s ); | 
| 1788 |         else | 
| 1789 |             assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s ); | 
| 1790 |         break; | 
| 1791 |     } | 
| 1792 |     case QQmlSA::BindingType::Script: { | 
| 1793 |         QString bindingSymbolName | 
| 1794 |                 = uniqueVariableName(qmlName: type->internalName() + u'_' + propertyName + u"_binding" ); | 
| 1795 |         compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType, | 
| 1796 |                              accessor); | 
| 1797 |         break; | 
| 1798 |     } | 
| 1799 |     case QQmlSA::BindingType::Object: { | 
| 1800 |         compileObjectBinding(current, binding, type, accessor); | 
| 1801 |         break; | 
| 1802 |     } | 
| 1803 |     case QQmlSA::BindingType::Interceptor: | 
| 1804 |         Q_FALLTHROUGH(); | 
| 1805 |     case QQmlSA::BindingType::ValueSource: { | 
| 1806 |         compileValueSourceOrInterceptorBinding(current, binding, type, accessor); | 
| 1807 |         break; | 
| 1808 |     } | 
| 1809 |     case QQmlSA::BindingType::AttachedProperty: { | 
| 1810 |         compileAttachedPropertyBinding(current, binding, type, accessor); | 
| 1811 |         break; | 
| 1812 |     } | 
| 1813 |     case QQmlSA::BindingType::GroupProperty: { | 
| 1814 |         compileGroupPropertyBinding(current, binding, type, accessor); | 
| 1815 |         break; | 
| 1816 |     } | 
| 1817 |  | 
| 1818 |     case QQmlSA::BindingType::TranslationById: | 
| 1819 |     case QQmlSA::BindingType::Translation: { | 
| 1820 |         compileTranslationBinding(current, binding, type, accessor); | 
| 1821 |         break; | 
| 1822 |     } | 
| 1823 |     case QQmlSA::BindingType::Invalid: { | 
| 1824 |         recordError(location: binding.sourceLocation(), message: u"This binding is invalid"_s ); | 
| 1825 |         break; | 
| 1826 |     } | 
| 1827 |     default: { | 
| 1828 |         recordError(location: binding.sourceLocation(), message: u"Binding is not supported"_s ); | 
| 1829 |         break; | 
| 1830 |     } | 
| 1831 |     } | 
| 1832 | } | 
| 1833 |  | 
| 1834 | // returns compiled script binding for "property changed" handler in a form of object type | 
| 1835 | static QmltcType compileScriptBindingPropertyChangeHandler(const QQmlJSMetaPropertyBinding &binding, | 
| 1836 |                                                            const QQmlJSScope::ConstPtr &objectType, | 
| 1837 |                                                            const QString &urlMethodName, | 
| 1838 |                                                            const QString &functorCppType, | 
| 1839 |                                                            const QString &objectCppType) | 
| 1840 | { | 
| 1841 |     QmltcType bindingFunctor {}; | 
| 1842 |     bindingFunctor.cppType = functorCppType; | 
| 1843 |     bindingFunctor.ignoreInit = true; | 
| 1844 |  | 
| 1845 |     // default member variable and ctor: | 
| 1846 |     const QString pointerToObject = objectCppType + u" *" ; | 
| 1847 |     bindingFunctor.variables.emplaceBack( | 
| 1848 |             args: QmltcVariable { pointerToObject, u"m_self"_s , u"nullptr"_s  }); | 
| 1849 |     bindingFunctor.baselineCtor.name = functorCppType; | 
| 1850 |     bindingFunctor.baselineCtor.parameterList.emplaceBack( | 
| 1851 |             args: QmltcVariable { pointerToObject, u"self"_s , QString() }); | 
| 1852 |     bindingFunctor.baselineCtor.initializerList.emplaceBack(args: u"m_self(self)"_s ); | 
| 1853 |  | 
| 1854 |     // call operator: | 
| 1855 |     QmltcMethod callOperator {}; | 
| 1856 |     callOperator.returnType = u"void"_s ; | 
| 1857 |     callOperator.name = u"operator()"_s ; | 
| 1858 |     callOperator.modifiers << u"const"_s ; | 
| 1859 |     QmltcCodeGenerator::generate_callExecuteRuntimeFunction( | 
| 1860 |             block: &callOperator.body, url: urlMethodName + u"()" , | 
| 1861 |             index: objectType->ownRuntimeFunctionIndex(index: binding.scriptIndex()), accessor: u"m_self"_s , returnType: u"void"_s , parameters: {}); | 
| 1862 |  | 
| 1863 |     bindingFunctor.functions.emplaceBack(args: std::move(callOperator)); | 
| 1864 |  | 
| 1865 |     return bindingFunctor; | 
| 1866 | } | 
| 1867 |  | 
| 1868 | // finds property for given scope and returns it together with the absolute | 
| 1869 | // property index in the property array of the corresponding QMetaObject | 
| 1870 | static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope::ConstPtr &scope, | 
| 1871 |                                                                const QString &propertyName) | 
| 1872 | { | 
| 1873 |     auto owner = QQmlJSScope::ownerOfProperty(self: scope, name: propertyName).scope; | 
| 1874 |     Q_ASSERT(owner); | 
| 1875 |     const QQmlJSMetaProperty p = owner->ownProperty(name: propertyName); | 
| 1876 |     if (!p.isValid()) | 
| 1877 |         return { p, -1 }; | 
| 1878 |     int index = p.index(); | 
| 1879 |     if (index < 0) // this property doesn't have index - comes from QML | 
| 1880 |         return { p, -1 }; | 
| 1881 |  | 
| 1882 |     const auto increment = [&](const QQmlJSScope::ConstPtr &type, QQmlJSScope::ExtensionKind m) { | 
| 1883 |         // owner of property is not included in the offset calculation (relative | 
| 1884 |         // index is already added as p.index()) | 
| 1885 |         if (type->isSameType(otherScope: owner)) | 
| 1886 |             return; | 
| 1887 |  | 
| 1888 |         // extension namespace and JavaScript properties are ignored | 
| 1889 |         if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript) | 
| 1890 |             return; | 
| 1891 |  | 
| 1892 |         index += int(type->ownProperties().size()); | 
| 1893 |     }; | 
| 1894 |     QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, start: owner, act: increment); | 
| 1895 |     return { p, index }; | 
| 1896 | } | 
| 1897 |  | 
| 1898 | void QmltcCompiler::compileScriptBinding(QmltcType ¤t, | 
| 1899 |                                          const QQmlJSMetaPropertyBinding &binding, | 
| 1900 |                                          const QString &bindingSymbolName, | 
| 1901 |                                          const QQmlJSScope::ConstPtr &objectType, | 
| 1902 |                                          const QString &propertyName, | 
| 1903 |                                          const QQmlJSScope::ConstPtr &propertyType, | 
| 1904 |                                          const QmltcCompiler::BindingAccessorData &accessor) | 
| 1905 | { | 
| 1906 |     const auto compileScriptSignal = [&](const QString &name) { | 
| 1907 |         QString This_signal = u"this"_s ; | 
| 1908 |         QString This_slot = u"this"_s ; | 
| 1909 |         QString objectClassName_signal = objectType->internalName(); | 
| 1910 |         QString objectClassName_slot = objectType->internalName(); | 
| 1911 |  | 
| 1912 |         // TODO: ugly crutch to make stuff work | 
| 1913 |         if (accessor.name != u"this"_s ) { // e.g. if attached property | 
| 1914 |             This_signal = accessor.name; | 
| 1915 |             This_slot = u"this"_s ; // still | 
| 1916 |             objectClassName_signal = objectType->baseTypeName(); | 
| 1917 |             objectClassName_slot = current.cppType; // real base class where slot would go | 
| 1918 |         } | 
| 1919 |         Q_ASSERT(!objectClassName_signal.isEmpty()); | 
| 1920 |         Q_ASSERT(!objectClassName_slot.isEmpty()); | 
| 1921 |  | 
| 1922 |         const auto signalMethods = objectType->methods(name, type: QQmlJSMetaMethodType::Signal); | 
| 1923 |         Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else | 
| 1924 |         QQmlJSMetaMethod signal = signalMethods.at(i: 0); | 
| 1925 |         Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal); | 
| 1926 |  | 
| 1927 |         const QString signalName = signal.methodName(); | 
| 1928 |         const QString slotName = newSymbol(base: signalName + u"_slot" ); | 
| 1929 |  | 
| 1930 |         const QString signalReturnType = figureReturnType(m: signal); | 
| 1931 |         const QList<QmltcVariable> slotParameters = | 
| 1932 |                 compileMethodParameters(parameterInfos: signal.parameters(), /* allow unnamed = */ allowUnnamed: true); | 
| 1933 |  | 
| 1934 |         // SignalHander specific: | 
| 1935 |         QmltcMethod slotMethod {}; | 
| 1936 |         slotMethod.returnType = signalReturnType; | 
| 1937 |         slotMethod.name = slotName; | 
| 1938 |         slotMethod.parameterList = slotParameters; | 
| 1939 |  | 
| 1940 |         QmltcCodeGenerator::generate_callExecuteRuntimeFunction( | 
| 1941 |                 block: &slotMethod.body, url: m_urlMethodName + u"()" , | 
| 1942 |                 index: objectType->ownRuntimeFunctionIndex(index: binding.scriptIndex()), | 
| 1943 |                 accessor: u"this"_s , // Note: because script bindings always use current QML object scope | 
| 1944 |                 returnType: signalReturnType, parameters: slotParameters); | 
| 1945 |         slotMethod.type = QQmlJSMetaMethodType::Slot; | 
| 1946 |  | 
| 1947 |         current.functions << std::move(slotMethod); | 
| 1948 |         current.setComplexBindings.body << u"QObject::connect("  + This_signal + u", "  + u"&"  | 
| 1949 |                         + objectClassName_signal + u"::"  + signalName + u", "  + This_slot + u", &"  | 
| 1950 |                         + objectClassName_slot + u"::"  + slotName + u");" ; | 
| 1951 |     }; | 
| 1952 |  | 
| 1953 |     switch (binding.scriptKind()) { | 
| 1954 |     case QQmlSA::ScriptBindingKind::PropertyBinding: { | 
| 1955 |         if (!propertyType) { | 
| 1956 |             recordError(location: binding.sourceLocation(), | 
| 1957 |                         message: u"Binding on property '"  + propertyName + u"' of unknown type" ); | 
| 1958 |             return; | 
| 1959 |         } | 
| 1960 |  | 
| 1961 |         auto [property, absoluteIndex] = getMetaPropertyIndex(scope: objectType, propertyName); | 
| 1962 |         if (absoluteIndex < 0) { | 
| 1963 |             recordError(location: binding.sourceLocation(), | 
| 1964 |                         message: u"Binding on unknown property '"  + propertyName + u"'" ); | 
| 1965 |             return; | 
| 1966 |         } | 
| 1967 |  | 
| 1968 |         QString bindingTarget = accessor.name; | 
| 1969 |  | 
| 1970 |         int valueTypeIndex = -1; | 
| 1971 |         if (accessor.isValueType) { | 
| 1972 |             Q_ASSERT(accessor.scope != objectType); | 
| 1973 |             bindingTarget = u"this"_s ; // TODO: not necessarily "this"? | 
| 1974 |             auto [groupProperty, groupPropertyIndex] = | 
| 1975 |                     getMetaPropertyIndex(scope: accessor.scope, propertyName: accessor.propertyName); | 
| 1976 |             if (groupPropertyIndex < 0) { | 
| 1977 |                 recordError(location: binding.sourceLocation(), | 
| 1978 |                             message: u"Binding on group property '"  + accessor.propertyName | 
| 1979 |                                     + u"' of unknown type" ); | 
| 1980 |                 return; | 
| 1981 |             } | 
| 1982 |             valueTypeIndex = absoluteIndex; | 
| 1983 |             absoluteIndex = groupPropertyIndex; // e.g. index of accessor.name | 
| 1984 |         } | 
| 1985 |  | 
| 1986 |         QmltcCodeGenerator::generate_createBindingOnProperty( | 
| 1987 |                 block: ¤t.setComplexBindings.body, unitVarName: generate_callCompilationUnit(urlMethodName: m_urlMethodName), | 
| 1988 |                 scope: u"this"_s , // NB: always using enclosing object as a scope for the binding | 
| 1989 |                 functionIndex: static_cast<qsizetype>(objectType->ownRuntimeFunctionIndex(index: binding.scriptIndex())), | 
| 1990 |                 target: bindingTarget, // binding target | 
| 1991 |                 // value types are special and are bound through valueTypeIndex | 
| 1992 |                 targetType: accessor.isValueType ? QQmlJSScope::ConstPtr() : objectType, propertyIndex: absoluteIndex, | 
| 1993 |                 p: property, valueTypeIndex, subTarget: accessor.name); | 
| 1994 |         break; | 
| 1995 |     } | 
| 1996 |     case QQmlSA::ScriptBindingKind::SignalHandler: { | 
| 1997 |         const auto name = QQmlSignalNames::handlerNameToSignalName(handler: propertyName); | 
| 1998 |         Q_ASSERT(name.has_value()); | 
| 1999 |         compileScriptSignal(*name); | 
| 2000 |         break; | 
| 2001 |     } | 
| 2002 |     case QQmlSA ::ScriptBindingKind::ChangeHandler: { | 
| 2003 |         const QString objectClassName = objectType->internalName(); | 
| 2004 |         const QString bindingFunctorName = newSymbol(base: bindingSymbolName + u"Functor" ); | 
| 2005 |  | 
| 2006 |         const auto signalName = QQmlSignalNames::handlerNameToSignalName(handler: propertyName); | 
| 2007 |         Q_ASSERT(signalName.has_value()); // an error somewhere else | 
| 2008 |         const auto actualProperty = | 
| 2009 |                 QQmlJSUtils::propertyFromChangedHandler(scope: objectType, changedHandler: propertyName); | 
| 2010 |         Q_ASSERT(actualProperty.has_value()); // an error somewhere else | 
| 2011 |         const auto actualPropertyType = actualProperty->type(); | 
| 2012 |         if (!actualPropertyType) { | 
| 2013 |             recordError(location: binding.sourceLocation(), | 
| 2014 |                         message: u"Binding on property '"  + actualProperty->propertyName() | 
| 2015 |                                 + u"' of unknown type" ); | 
| 2016 |             return; | 
| 2017 |         } | 
| 2018 |  | 
| 2019 |         // due to historical reasons (QQmlObjectCreator), prefer NOTIFY over | 
| 2020 |         // BINDABLE when both are available. thus, test for notify first | 
| 2021 |         const QString notifyString = actualProperty->notify(); | 
| 2022 |         if (!notifyString.isEmpty()) { | 
| 2023 |             compileScriptSignal(notifyString); | 
| 2024 |             break; | 
| 2025 |         } | 
| 2026 |         const QString bindableString = actualProperty->bindable(); | 
| 2027 |         QString typeOfQmlBinding = | 
| 2028 |                 u"std::unique_ptr<QPropertyChangeHandler<"  + bindingFunctorName + u">>" ; | 
| 2029 |  | 
| 2030 |         current.children << compileScriptBindingPropertyChangeHandler( | 
| 2031 |                 binding, objectType, urlMethodName: m_urlMethodName, functorCppType: bindingFunctorName, objectCppType: objectClassName); | 
| 2032 |  | 
| 2033 |         current.setComplexBindings.body << u"if (!%1.contains(QStringLiteral(\"%2\")))"_s .arg( | 
| 2034 |             args&: current.propertyInitializer.initializedCache.name, args: propertyName); | 
| 2035 |  | 
| 2036 |         // TODO: this could be dropped if QQmlEngine::setContextForObject() is | 
| 2037 |         // done before currently generated C++ object is constructed | 
| 2038 |         current.setComplexBindings.body << u"    "_s  + bindingSymbolName + u".reset(new QPropertyChangeHandler<"  | 
| 2039 |                         + bindingFunctorName + u">("  | 
| 2040 |                         + QmltcCodeGenerator::wrap_privateClass(accessor: accessor.name, p: *actualProperty) | 
| 2041 |                         + u"->"  + bindableString + u"().onValueChanged("  + bindingFunctorName + u"("  | 
| 2042 |                         + accessor.name + u"))));" ; | 
| 2043 |  | 
| 2044 |         current.variables.emplaceBack( | 
| 2045 |                 args: QmltcVariable { typeOfQmlBinding, bindingSymbolName, QString() }); | 
| 2046 |         break; | 
| 2047 |     } | 
| 2048 |     default: | 
| 2049 |         recordError(location: binding.sourceLocation(), message: u"Invalid script binding found"_s ); | 
| 2050 |         break; | 
| 2051 |     } | 
| 2052 | } | 
| 2053 |  | 
| 2054 | QT_END_NAMESPACE | 
| 2055 |  |