7#include <private/qqmltcoutputir_p.h>
8#include <private/qqmltccodewriter_p.h>
9#include <private/qqmltcpropertyutils_p.h>
10#include <private/qqmltccompilerpieces_p.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtQml/private/qqmlsignalnames_p.h>
14#include <private/qqmljsutils_p.h>
20using namespace Qt::StringLiterals;
26 if (QQmlJSScope::ConstPtr type = p.type())
27 return type->isListProperty();
32 const QQmlJSScope::ConstPtr &type, TypeResolver *resolver)
34 QList<QQmlJSMetaProperty> requiredProperties{};
36 auto isPropertyRequired = [&type, &resolver](
const auto &property) {
37 if (type->hasPropertyBindings(property.propertyName()))
40 if (type->isPropertyRequired(property.propertyName()))
48 if (property.isAlias()) {
49 QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
51 QQmlJSUtils::ResolvedAlias result =
52 QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
54 if (result.kind != QQmlJSUtils::AliasTarget_Property)
57 if (!result.owner->isPropertyRequired(result.property.propertyName()))
60 if (result.owner == type)
63 if (result.owner->hasPropertyBindings(result.property.propertyName()))
72 const auto properties = type->properties();
73 std::copy_if(properties.cbegin(), properties.cend(),
74 std::back_inserter(requiredProperties), isPropertyRequired);
75 std::sort(requiredProperties.begin(), requiredProperties.end(),
76 [](
const auto &left,
const auto &right) {
77 return left.propertyName() < right.propertyName();
80 return requiredProperties;
88 Type ¤t,
const QQmlJSScope::ConstPtr &type, TypeResolver *resolver)
91 QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
93 if (requiredProperties.isEmpty())
96 current.requiredPropertiesBundle.emplace();
97 current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
99 current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
100 std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
101 std::back_inserter(current.requiredPropertiesBundle->members),
102 [](
const QQmlJSMetaProperty &property) {
103 QString type = qIsReferenceTypeList(property)
104 ? u"const QList<%1*>&"_s.arg(
105 property.type()->elementType()->internalName())
106 : u"passByConstRefOrValue<%1>"_s.arg(
107 property.type()->augmentedInternalName());
108 return Variable{ type, property.propertyName() };
114 current.externalCtor.body << u"// document root:"_s;
117 current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
118 type->internalName());
119 current.externalCtor.body
120 << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
121 current.externalCtor.body << u"creator.set(0, this);"_s;
123 QString initializerName = u"initializer"_s;
124 if (current.requiredPropertiesBundle) {
126 current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
127 for (
const auto& member : current.requiredPropertiesBundle->members) {
128 current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
129 PropertyData(member.name).write, member.name
132 current.externalCtor.body << u" initializer(propertyInitializer);"_s;
133 current.externalCtor.body << u"};"_s;
135 initializerName = u"newInitializer"_s;
139 current.externalCtor.body << current.init.name
140 + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
141 u"endInit */ true, %1);"_s.arg(initializerName);
148 QQmlJSLogger *logger)
151 Q_UNUSED(m_typeResolver);
152 Q_ASSERT(!hasErrors());
159QString
Compiler::newSymbol(
const QString &base)
161 QString symbol = base;
162 symbol.replace(QLatin1String(
"."), QLatin1String(
"_"));
163 while (symbol.startsWith(QLatin1Char(
'_')) && symbol.size() >= 2
164 && (symbol[1].isUpper() || symbol[1] == QLatin1Char(
'_'))) {
167 if (!m_symbols.contains(symbol)) {
168 m_symbols.insert(symbol, 1);
170 symbol += u"_" + QString::number(m_symbols[symbol]++);
178 Q_ASSERT(!m_info.outputCppFile.isEmpty());
179 Q_ASSERT(!m_info.outputHFile.isEmpty());
180 Q_ASSERT(!m_info.resourcePath.isEmpty());
185 const auto isComponent = [](
const QQmlJSScope::ConstPtr &type) {
186 auto base = type->baseType();
187 return base && base->internalName() == u"QQmlComponent"_s;
190 CodeGenerator generator { m_url, m_visitor };
193 compileUrlMethod(urlMethod, generator.urlMethodName());
194 m_urlMethodName = urlMethod.name;
200 auto sortedInlineComponentNames = m_visitor->inlineComponentNames();
201 std::sort(sortedInlineComponentNames.begin(), sortedInlineComponentNames.end(),
202 [&](
const InlineComponentOrDocumentRootName &a,
203 const InlineComponentOrDocumentRootName &b) {
204 const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
205 const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
207 if (inlineComponentAName == inlineComponentBName)
211 if (inlineComponentAName && !inlineComponentBName)
216 if (inlineComponentAName && inlineComponentBName) {
217 QQmlJSScope::ConstPtr inlineComponentA = m_visitor->inlineComponent(a);
218 QQmlJSScope::ConstPtr inlineComponentB = m_visitor->inlineComponent(b);
219 if (inlineComponentB->inherits(inlineComponentA)) {
221 }
else if (inlineComponentA->inherits(inlineComponentB)) {
225 return *inlineComponentAName < *inlineComponentBName;
228 Q_ASSERT(!inlineComponentAName || !inlineComponentBName);
233 QList<Type> compiledTypes;
234 for (
const auto &inlineComponent : sortedInlineComponentNames) {
235 const QList<QQmlJSScope::ConstPtr> &pureTypes = m_visitor->pureQmlTypes(inlineComponent);
236 Q_ASSERT(!pureTypes.empty());
237 const QQmlJSScope::ConstPtr &root = pureTypes.front();
238 if (isComponent(root)) {
239 compiledTypes.emplaceBack();
240 const auto compile = [&](Type ¤t,
const QQmlJSScope::ConstPtr &type) {
241 generator.generate_initCodeForTopLevelComponent(current, type);
243 compileType(compiledTypes.back(), root, compile);
245 const auto compile = [
this](Type ¤t,
const QQmlJSScope::ConstPtr &type) {
246 compileTypeElements(current, type);
249 for (
const auto &type : pureTypes) {
250 Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
251 compiledTypes.emplaceBack();
252 compileType(compiledTypes.back(), type, compile);
262 program.cppPath = m_info.outputCppFile;
263 program.hPath = m_info.outputHFile;
264 program.outNamespace = m_info.outputNamespace;
265 program.exportMacro = m_info.exportMacro;
266 program.compiledTypes = compiledTypes;
267 program.includes = m_visitor->cppIncludeFiles();
268 if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
269 program.includes += (m_info.exportInclude);
270 program.urlMethod = urlMethod;
273 OutputWrapper code(out);
274 CodeWriter::write(code, program);
277void Compiler::compileUrlMethod(Method &urlMethod,
const QString &urlMethodName)
279 urlMethod.name = urlMethodName;
280 urlMethod.returnType = u"const QUrl&"_s;
281 urlMethod.body << u"static QUrl url {QStringLiteral(\"qrc:%1\")};"_s.arg(m_info.resourcePath);
282 urlMethod.body << u"return url;"_s;
283 urlMethod.declarationPrefixes << u"static"_s;
284 urlMethod.modifiers << u"noexcept"_s;
288 Type ¤t,
const QQmlJSScope::ConstPtr &type,
289 std::function<
void(Type &,
const QQmlJSScope::ConstPtr &)> compileElements)
291 Q_ASSERT(!type->internalName().isEmpty());
292 current.cppType = type->internalName();
293 Q_ASSERT(!type->baseType()->internalName().isEmpty());
294 const QString baseClass = type->baseType()->internalName();
296 const auto rootType = m_visitor->result();
297 const InlineComponentOrDocumentRootName name = type->enclosingInlineComponentName();
298 QQmlJSScope::ConstPtr inlineComponentType = m_visitor->inlineComponent(name);
299 Q_ASSERT(inlineComponentType);
300 const bool documentRoot = (type == rootType);
301 const bool inlineComponent = type->isInlineComponent();
302 const bool isAnonymous = !documentRoot || type->internalName().at(0).isLower();
303 const bool isSingleton = type->isSingleton();
305 CodeGenerator generator { m_url, m_visitor };
307 current.baseClasses = { baseClass };
310 const QString rootInternalName =
311 m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
312 if (rootInternalName != current.cppType)
313 current.otherCode <<
"friend class %1;"_L1.arg(rootInternalName);
315 if (documentRoot || inlineComponent) {
316 auto name = type->inlineComponentName()
317 ? InlineComponentOrDocumentRootName(*type->inlineComponentName())
318 : InlineComponentOrDocumentRootName(RootDocumentNameType());
321 current.otherCode << u"friend class QQmltcObjectCreationBase<%1>;"_s.arg(
322 inlineComponentType->internalName());
324 Method typeCountMethod;
325 typeCountMethod.name = CodeGenerator::typeCountName;
326 typeCountMethod.returnType = u"uint"_s;
327 typeCountMethod.body << u"return " + generator.generate_typeCount(name) + u";";
328 current.typeCount = typeCountMethod;
332 const auto realQmlScope = [](
const QQmlJSScope::ConstPtr &scope) {
333 if (scope->isArrayScope())
334 return scope->parentScope();
338 const auto& realScope = realQmlScope(type->parentScope());
339 if (realScope != rootType) {
340 current.otherCode << u"friend class %1;"_s.arg(realScope->internalName());
346 current.otherCode << u"friend class QT_PREPEND_NAMESPACE(QQmltcObjectCreationHelper);"_s;
351 type->isInlineComponent() ? (u"QML_NAMED_ELEMENT(%1)"_s.arg(*type->inlineComponentName()))
352 : (isAnonymous ? u"QML_ANONYMOUS"_s : u"QML_ELEMENT"_s),
356 current.baselineCtor.access = QQmlJSMetaMethod::Protected;
357 if (documentRoot || inlineComponent || isSingleton) {
358 current.externalCtor.access = QQmlJSMetaMethod::Public;
360 current.externalCtor.access = QQmlJSMetaMethod::Protected;
362 current.init.access = QQmlJSMetaMethod::Protected;
363 current.beginClass.access = QQmlJSMetaMethod::Protected;
364 current.endInit.access = QQmlJSMetaMethod::Protected;
365 current.setComplexBindings.access = QQmlJSMetaMethod::Protected;
366 current.completeComponent.access = QQmlJSMetaMethod::Protected;
367 current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
368 current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
370 current.propertyInitializer.name = u"PropertyInitializer"_s;
371 current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public;
372 current.propertyInitializer.constructor.name = current.propertyInitializer.name;
373 current.propertyInitializer.constructor.parameterList = {
374 Variable(u"%1&"_s.arg(current.cppType), u"component"_s)
376 current.propertyInitializer.component.cppType = current.cppType + u"&";
377 current.propertyInitializer.component.name = u"component"_s;
378 current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s;
379 current.propertyInitializer.initializedCache.name = u"initializedCache"_s;
381 current.baselineCtor.name = current.cppType;
382 current.externalCtor.name = current.cppType;
383 current.init.name = u"QML_init"_s;
384 current.init.returnType = u"QQmlRefPointer<QQmlContextData>"_s;
385 current.beginClass.name = u"QML_beginClass"_s;
386 current.beginClass.returnType = u"void"_s;
387 current.endInit.name = u"QML_endInit"_s;
388 current.endInit.returnType = u"void"_s;
389 current.setComplexBindings.name = u"QML_setComplexBindings"_s;
390 current.setComplexBindings.returnType = u"void"_s;
391 current.completeComponent.name = u"QML_completeComponent"_s;
392 current.completeComponent.returnType = u"void"_s;
393 current.finalizeComponent.name = u"QML_finalizeComponent"_s;
394 current.finalizeComponent.returnType = u"void"_s;
395 current.handleOnCompleted.name = u"QML_handleOnCompleted"_s;
396 current.handleOnCompleted.returnType = u"void"_s;
397 Variable creator(u"QQmltcObjectCreationHelper*"_s, u"creator"_s);
398 Variable engine(u"QQmlEngine*"_s, u"engine"_s);
399 Variable parent(u"QObject*"_s, u"parent"_s, u"nullptr"_s);
400 Variable initializedCache(
401 u"[[maybe_unused]] const QSet<QString>&"_s,
402 u"initializedCache"_s,
405 Variable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
406 Variable finalizeFlag(u"bool"_s, u"canFinalize"_s);
407 current.baselineCtor.parameterList = { parent };
408 current.endInit.parameterList = { creator, engine };
409 current.setComplexBindings.parameterList = { creator, engine, initializedCache };
410 current.handleOnCompleted.parameterList = { creator };
412 if (documentRoot || inlineComponent) {
413 const Variable initializer(
414 u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s.arg(current.propertyInitializer.name),
416 u"[](%1&){}"_s.arg(current.propertyInitializer.name));
418 current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
419 current.beginClass.parameterList = { creator, finalizeFlag };
420 current.completeComponent.parameterList = { creator, finalizeFlag };
421 current.finalizeComponent.parameterList = { creator, finalizeFlag };
423 compileRequiredPropertiesBundle(current, type, m_typeResolver);
425 if (current.requiredPropertiesBundle) {
427 u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
428 u"requiredPropertiesBundle"_s,
430 current.externalCtor.parameterList = { engine, bundle, parent, initializer };
432 current.externalCtor.parameterList = { engine, parent, initializer };
435 current.externalCtor.parameterList = { creator, engine, parent };
436 current.init.parameterList = { creator, engine, ctxtdata };
437 current.beginClass.parameterList = { creator };
438 current.completeComponent.parameterList = { creator };
439 current.finalizeComponent.parameterList = { creator };
442 current.externalCtor.initializerList = { current.baselineCtor.name + u"(" + parent.name
444 if (QQmlJSUtils::hasCompositeBase(type)) {
447 current.baselineCtor.initializerList = { baseClass + u"(" + parent.name + u")" };
451 current.baselineCtor.body << u"QQml_setParent_noEvent(this, " + parent.name + u");";
455 current.externalCtor.body << u"Q_UNUSED(engine)"_s;
456 if (documentRoot || inlineComponent) {
457 compileRootExternalConstructorBody(current, type);
459 current.externalCtor.body << u"// not document root:"_s;
461 current.externalCtor.body << current.init.name
462 + u"(creator, engine, QQmlData::get(parent)->outerContext);";
467 current.mocCode.append(u"QML_SINGLETON"_s);
468 auto &staticCreate = current.staticCreate.emplace();
469 staticCreate.comments
470 << u"Used by the engine for singleton creation."_s
471 << u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s;
472 staticCreate.type = QQmlJSMetaMethodType::StaticMethod;
473 staticCreate.access = QQmlJSMetaMethod::Public;
474 staticCreate.name = u"create"_s;
475 staticCreate.returnType = u"%1 *"_s.arg(current.cppType);
476 Variable jsEngine(u"QJSEngine*"_s, u"jsEngine"_s);
477 staticCreate.parameterList = { engine, jsEngine };
478 staticCreate.body << u"Q_UNUSED(jsEngine);"_s
479 << u"%1 *result = new %1(engine, nullptr);"_s.arg(current.cppType)
480 << u"return result;"_s;
482 auto postponedQmlContextSetup = generator.generate_initCode(current, type);
483 generator.generate_endInitCode(current, type);
484 generator.generate_setComplexBindingsCode(current, type);
485 generator.generate_beginClassCode(current, type);
486 generator.generate_completeComponentCode(current, type);
487 generator.generate_finalizeComponentCode(current, type);
488 generator.generate_handleOnCompletedCode(current, type);
490 compileElements(current, type);
493template<
typename Iterator>
499 return std::stable_partition(first, last, [](
const QQmlJSMetaPropertyBinding &b) {
501 return !Compiler::isComplexBinding(b);
524void Compiler::compilePropertyInitializer(Type ¤t,
const QQmlJSScope::ConstPtr &type)
526 static auto isFromExtension
527 = [](
const QQmlJSMetaProperty &property,
const QQmlJSScope::ConstPtr &scope) {
528 return scope->ownerOfProperty(scope, property.propertyName()).extensionSpecifier
529 != QQmlJSScope::NotExtension;
532 current.propertyInitializer.constructor.initializerList << u"component{component}"_s;
534 const auto properties = type->properties().values();
535 for (
const auto &property: properties) {
536 if (property.index() == -1)
continue;
537 if (property.isPrivate())
continue;
538 if (!property.isWritable() && !qIsReferenceTypeList(property))
continue;
540 const QString name = property.propertyName();
541 const auto propertyType = property.type();
542 if (propertyType.isNull() || propertyType->isOpaqueType()) {
543 recordError(type->sourceLocation(), u"Type of property '%1' is unknown"_s.arg(name));
547 current.propertyInitializer.propertySetters.emplace_back();
548 auto& compiledSetter = current.propertyInitializer.propertySetters.back();
550 compiledSetter.userVisible =
true;
551 compiledSetter.returnType = u"void"_s;
552 compiledSetter.name = PropertyData(property).write;
554 if (qIsReferenceTypeList(property)) {
555 compiledSetter.parameterList.emplaceBack(
556 QQmlJSUtils::constRefify(
557 u"QList<%1*>"_s.arg(propertyType->elementType()->internalName())),
558 name + u"_", QString()
561 compiledSetter.parameterList.emplaceBack(
562 QQmlJSUtils::constRefify(getUnderlyingType(property)), name + u"_", QString()
566 if (qIsReferenceTypeList(property)) {
567 compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s.arg(
568 current.propertyInitializer.component.name, name
570 compiledSetter.body << u"list_ref_.clear();"_s;
571 compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s.arg(name);
572 compiledSetter.body << u" list_ref_.append(list_item_);"_s;
574 QQmlJSUtils::bindablePropertyHasDefaultAccessor(property, QQmlJSUtils::PropertyAccessor_Write)
576 compiledSetter.body << u"%1.%2().setValue(%3_);"_s.arg(
577 current.propertyInitializer.component.name, property.bindable(), name);
578 }
else if (type->hasOwnProperty(name)) {
579 compiledSetter.body << u"%1.%2(%3_);"_s.arg(
580 current.propertyInitializer.component.name, PropertyData(property).write, name);
581 }
else if (property.write().isEmpty() || isFromExtension(property, type)) {
598 compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s.arg(
599 current.propertyInitializer.component.name, name);
601 compiledSetter.body << u"%1.%2(%3_);"_s.arg(
602 current.propertyInitializer.component.name, property.write(), name);
605 compiledSetter.body << u"%1.insert(QStringLiteral(\"%2\"));"_s.arg(
606 current.propertyInitializer.initializedCache.name, name);
610void Compiler::compileTypeElements(Type ¤t,
const QQmlJSScope::ConstPtr &type)
618 const auto enums = type->ownEnumerations();
619 current.enums.reserve(enums.size());
620 for (
auto it = enums.begin(); it != enums.end(); ++it)
621 compileEnum(current, it.value());
623 auto properties = type->ownProperties().values();
624 current.properties.reserve(properties.size());
627 std::sort(properties.begin(), properties.end(),
628 [](
const QQmlJSMetaProperty &x,
const QQmlJSMetaProperty &y) {
629 return x.index() < y.index();
631 for (
const QQmlJSMetaProperty &p : std::as_const(properties)) {
632 if (p.index() == -1) {
633 recordError(type->sourceLocation(),
634 u"Internal error: property '%1' has incomplete information"_s.arg(
639 compileAlias(current, p, type);
641 compileProperty(current, p, type);
645 const auto methods = type->ownMethods();
646 for (
const QQmlJSMetaMethod &m : methods)
647 compileMethod(current, m, type);
649 auto bindings = type->ownPropertyBindingsInQmlIROrder();
650 partitionBindings(bindings.begin(), bindings.end());
652 compilePropertyInitializer(current, type);
653 compileBinding(current, bindings.begin(), bindings.end(), type, { type });
656void Compiler::compileEnum(Type ¤t,
const QQmlJSMetaEnum &e)
658 const auto intValues = e.values();
660 values.reserve(intValues.size());
661 std::transform(intValues.cbegin(), intValues.cend(),
std::back_inserter(values),
662 [](
int x) {
return QString::number(x); });
665 current.enums.emplaceBack(e.name(), e.keys(),
std::move(values),
666 u"Q_ENUM(%1)"_s.arg(e.name()));
672 QList<Variable> parameters;
673 const auto size = parameterInfos.size();
674 parameters.reserve(size);
675 for (qsizetype i = 0; i < size; ++i) {
676 const auto &p = parameterInfos[i];
678 QString name = p.name();
679 Q_ASSERT(allowUnnamed || !name.isEmpty());
680 if (name.isEmpty() && allowUnnamed)
681 name = u"unnamed_" + QString::number(i);
683 QString internalName;
684 const QQmlJSScope::AccessSemantics semantics = p.type()->accessSemantics();
687 case QQmlJSScope::AccessSemantics::Reference:
688 if (p.typeQualifier() == QQmlJSMetaParameter::Const)
689 internalName = u"const "_s;
690 internalName += u"%1*"_s.arg(p.type()->internalName());
692 case QQmlJSScope::AccessSemantics::Value:
693 case QQmlJSScope::AccessSemantics::Sequence:
694 internalName = u"passByConstRefOrValue<%1>"_s.arg(p.type()->internalName());
696 case QQmlJSScope::AccessSemantics::None:
699 parameters.emplaceBack(internalName, name, QString());
704void Compiler::compileMethod(Type ¤t,
const QQmlJSMetaMethod &m,
705 const QQmlJSScope::ConstPtr &owner)
707 const QString returnType = m.returnType()->augmentedInternalName();
709 const QList<Variable> compiledParams = compileMethodParameters(m.parameters());
710 const auto methodType = m.methodType();
713 if (methodType != QQmlJSMetaMethodType::Signal) {
714 CodeGenerator urlGenerator { m_url, m_visitor };
715 CodeGenerator::generate_callExecuteRuntimeFunction(
716 &code, urlGenerator.urlMethodName() + u"()",
717 owner->ownRuntimeFunctionIndex(m.jsFunctionIndex()), u"this"_s, returnType,
722 compiled.returnType = returnType;
723 compiled.name = m.methodName();
724 compiled.parameterList =
std::move(compiledParams);
725 compiled.body =
std::move(code);
726 compiled.type = methodType;
727 compiled.access = m.access();
728 if (methodType != QQmlJSMetaMethodType::Signal) {
729 compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
730 compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
732 compiled.userVisible = !m.isImplicitQmlPropertyChangeSignal();
734 current.functions.emplaceBack(compiled);
738
739
740
741void Compiler::compileExtraListMethods(Type ¤t,
const QQmlJSMetaProperty &p)
743 PropertyData data(p);
744 const QString elementType = p.type()->elementType()->internalName() + u'*';
745 const QString variableName = data.read + u"()"_s;
746 const QStringList ownershipWarning = {
747 u"\\note {This method does not change the ownership of its argument."_s,
748 u"The caller is responsible for setting the argument's \\c {QObject::parent} or"_s,
749 u"for ensuring that the argument lives long enough."_s,
750 u"For example, an argument created with \\c {createObject()} that has no parent"_s,
751 u"will eventually be garbage-collected, leaving a dangling pointer.}"_s
757 append.comments.emplaceBack(u"\\brief Append an element to %1."_s.arg(data.read));
758 append.comments << ownershipWarning;
759 append.returnType = u"void"_s;
760 append.name = u"%1Append"_s.arg(data.read);
761 append.parameterList.emplaceBack(elementType, u"toBeAppended"_s);
763 append.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
765 << u"q_qmltc_localList.append(std::addressof(q_qmltc_localList), toBeAppended);"_s;
768 append.userVisible =
true;
769 current.functions.emplaceBack(append);
775 count.comments.emplaceBack(u"\\brief Number of elements in %1."_s.arg(data.read));
776 count.returnType = u"int"_s;
777 count.name = u"%1Count"_s.arg(data.read);
779 count.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
780 count.body << u"int result = q_qmltc_localList.count(std::addressof(q_qmltc_localList));"_s;
781 count.body << u"return result;"_s;
782 count.userVisible =
true;
783 current.functions.emplaceBack(count);
789 at.comments.emplaceBack(u"\\brief Access an element in %1."_s.arg(data.read));
790 at.returnType = elementType;
791 at.name = u"%1At"_s.arg(data.read);
792 at.parameterList.emplaceBack(u"qsizetype"_s, u"position"_s, QString());
794 at.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
795 at.body << u"auto result = q_qmltc_localList.at(std::addressof(q_qmltc_localList), position);"_s;
796 at.body << u"return result;"_s;
797 at.userVisible =
true;
798 current.functions.emplaceBack(at);
804 clear.comments.emplaceBack(u"\\brief Clear %1."_s.arg(data.read));
805 clear.returnType = u"void"_s;
806 clear.name = u"%1Clear"_s.arg(data.read);
808 clear.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
809 clear.body << u"q_qmltc_localList.clear(std::addressof(q_qmltc_localList));"_s;
812 clear.userVisible =
true;
813 current.functions.emplaceBack(clear);
819 replace.comments.emplaceBack(u"\\brief Replace an element in %1."_s.arg(data.read));
820 replace.comments << ownershipWarning;
821 replace.returnType = u"void"_s;
822 replace.name = u"%1Replace"_s.arg(data.read);
823 replace.parameterList.emplaceBack(u"qsizetype"_s, u"position"_s, QString());
824 replace.parameterList.emplaceBack(elementType, u"element"_s,
827 replace.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
829 << u"q_qmltc_localList.replace(std::addressof(q_qmltc_localList), position, element);"_s;
832 replace.userVisible =
true;
833 current.functions.emplaceBack(replace);
839 removeLast.comments.emplaceBack(u"\\brief Remove the last element in %1."_s.arg(data.read));
840 removeLast.returnType = u"void"_s;
841 removeLast.name = u"%1RemoveLast"_s.arg(data.read);
843 removeLast.body << u"auto q_qmltc_localList = %1;"_s.arg(variableName);
844 removeLast.body << u"q_qmltc_localList.removeLast(std::addressof(q_qmltc_localList));"_s;
848 removeLast.userVisible =
true;
849 current.functions.emplaceBack(removeLast);
853void Compiler::compileProperty(Type ¤t,
const QQmlJSMetaProperty &p,
854 const QQmlJSScope::ConstPtr &owner)
856 Q_ASSERT(!p.isAlias());
859 const QString name = p.propertyName();
860 const QString variableName = u"m_" + name;
861 const QString underlyingType = getUnderlyingType(p);
862 if (qIsReferenceTypeList(p)) {
863 const QString storageName = variableName + u"_storage";
864 current.variables.emplaceBack(
865 u"QList<" + p.type()->elementType()->internalName() + u"*>", storageName,
867 current.baselineCtor.initializerList.emplaceBack(variableName + u"(" + underlyingType
868 + u"(this, std::addressof(" + storageName
870 compileExtraListMethods(current, p);
875 QStringList mocPieces;
876 mocPieces.reserve(10);
877 mocPieces << underlyingType << name;
879 PropertyData compilationData(p);
885 getter.returnType = underlyingType;
886 getter.name = compilationData.read;
887 getter.body << u"return " + variableName + u".value();";
888 getter.userVisible =
true;
889 current.functions.emplaceBack(getter);
890 mocPieces << u"READ"_s << getter.name;
892 if (p.isWritable() && !qIsReferenceTypeList(p)) {
894 setter.returnType = u"void"_s;
895 setter.name = compilationData.write;
897 setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType), name + u"_",
899 setter.body << variableName + u".setValue(" + name + u"_);";
900 setter.body << u"Q_EMIT " + compilationData.notify + u"();";
901 setter.userVisible =
true;
902 current.functions.emplaceBack(setter);
903 mocPieces << u"WRITE"_s << setter.name;
907 if (!qIsReferenceTypeList(p)) {
909 bindable.returnType = u"QBindable<" + underlyingType + u">";
910 bindable.name = compilationData.bindable;
911 bindable.body << u"return QBindable<" + underlyingType + u">(std::addressof(" + variableName
913 bindable.userVisible =
true;
914 current.functions.emplaceBack(bindable);
915 mocPieces << u"BINDABLE"_s << bindable.name;
920 if (owner->isPropertyRequired(name))
921 mocPieces << u"REQUIRED"_s;
925 current.mocCode << u"Q_PROPERTY(" + mocPieces.join(u" "_s) + u")";
928 if (name == owner->defaultPropertyName())
929 current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(name);
932 current.properties.emplaceBack(underlyingType, variableName, current.cppType,
933 compilationData.notify);
937
938
939
940
941
942
946
947
948
949
950
951
952
956
957
958
959
960
964
965
966
967
971
972
973
974
975
979
980
981
982
983
990
991
992
993
994
995
998 if (frames.size() < 2)
1002 auto prev = frames.begin();
1003 for (
auto it =
std::next(prev); it != frames.end(); ++it, ++prev) {
1004 for (QString &line : it->prologue)
1005 line.replace(AliasResolutionFrame::inVar, prev->outVar);
1006 for (QString &line : it->epilogueForWrite)
1007 line.replace(AliasResolutionFrame::inVar, prev->outVar);
1008 it->outVar.replace(AliasResolutionFrame::inVar, prev->outVar);
1012template<
typename Projection>
1016 for (
const AliasResolutionFrame &frame : frames)
1017 joined += project(frame);
1021void Compiler::compileAlias(Type ¤t,
const QQmlJSMetaProperty &alias,
1022 const QQmlJSScope::ConstPtr &owner)
1024 const QString aliasName = alias.propertyName();
1025 Q_ASSERT(!aliasName.isEmpty());
1027 QStringList aliasExprBits = alias.aliasExpression().split(u'.');
1028 Q_ASSERT(!aliasExprBits.isEmpty());
1030 QStack<AliasResolutionFrame> frames;
1032 QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
1034 aliasVisitor.reset = [&]() {
1040 AliasResolutionFrame { QStringList(), QStringList(), QStringList(), u"this"_s });
1042 aliasVisitor.processResolvedId = [&](
const QQmlJSScope::ConstPtr &type) {
1044 if (owner != type) {
1045 const int id = m_visitor->runtimeId(type);
1049 Q_ASSERT(frames.top().outVar == u"this"_s);
1050 queryIdFrame.prologue << u"auto context = %1::q_qmltc_thisContext;"_s.arg(
1051 owner->internalName());
1054 queryIdFrame.outVar = u"alias_objectById_" + aliasExprBits.front();
1055 const QString cppType = (m_visitor->qmlComponentIndex(type) == -1)
1056 ? type->internalName()
1057 : u"QQmlComponent"_s;
1058 queryIdFrame.prologue << u"auto " + queryIdFrame.outVar + u" = static_cast<" + cppType
1059 + u"*>(context->idValue(" + QString::number(id) + u"));";
1060 queryIdFrame.prologue << u"Q_ASSERT(" + queryIdFrame.outVar + u");";
1062 frames.push(queryIdFrame);
1065 aliasVisitor.processResolvedProperty = [&](
const QQmlJSMetaProperty &p,
1066 const QQmlJSScope::ConstPtr &owner) {
1069 auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
1070 CodeGenerator::wrap_extensionType(
1071 owner, p, CodeGenerator::wrap_privateClass(AliasResolutionFrame::inVar, p));
1072 QString inVar = extensionAccessor;
1073 queryPropertyFrame.prologue += extensionPrologue;
1074 if (p.type()->accessSemantics() == QQmlJSScope::AccessSemantics::Value) {
1077 const QString aliasVar = u"alias_" + QString::number(i);
1079 queryPropertyFrame.prologue
1080 << u"auto " + aliasVar + u" = " + inVar + u"->" + p.read() + u"();";
1081 queryPropertyFrame.epilogueForWrite
1082 << inVar + u"->" + p.write() + u"(" + aliasVar + u");";
1085 inVar = CodeGenerator::wrap_addressof(aliasVar);
1087 inVar += u"->" + p.read() + u"()";
1089 queryPropertyFrame.outVar = inVar;
1090 queryPropertyFrame.epilogue += extensionEpilogue;
1092 frames.push(queryPropertyFrame);
1095 QQmlJSUtils::ResolvedAlias result =
1096 QQmlJSUtils::resolveAlias(m_typeResolver, alias, owner, aliasVisitor);
1097 Q_ASSERT(result.kind != QQmlJSUtils::AliasTarget_Invalid);
1099 unpackFrames(frames);
1101 if (result.kind == QQmlJSUtils::AliasTarget_Property) {
1107 auto [extensionPrologue, extensionAccessor, extensionEpilogue] =
1108 CodeGenerator::wrap_extensionType(
1109 result.owner, result.property,
1110 CodeGenerator::wrap_privateClass(frames.top().outVar,
1112 customFinalFrame.prologue = extensionPrologue;
1113 customFinalFrame.outVar = extensionAccessor;
1114 customFinalFrame.epilogue = extensionEpilogue;
1115 frames.push(customFinalFrame);
1118 const QString latestAccessor = frames.top().outVar;
1119 const QStringList prologue =
1121 const QStringList epilogue =
1123 const QString underlyingType = (result.kind == QQmlJSUtils::AliasTarget_Property)
1124 ? getUnderlyingType(result.property)
1125 : result.owner->internalName() + u" *";
1127 QStringList mocLines;
1128 mocLines.reserve(10);
1129 mocLines << underlyingType << aliasName;
1131 PropertyData compilationData(aliasName);
1134 getter.returnType = underlyingType;
1135 getter.name = compilationData.read;
1136 getter.body += prologue;
1137 if (result.kind == QQmlJSUtils::AliasTarget_Property) {
1138 if (QString read = result.property.read(); !read.isEmpty()
1139 && !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
1140 result.property, QQmlJSUtils::PropertyAccessor_Read)) {
1141 getter.body << u"return %1->%2();"_s.arg(latestAccessor, read);
1143 getter.body << u"return qvariant_cast<%1>(%2->property(\"%3\"));"_s.arg(
1144 underlyingType, latestAccessor, result.property.propertyName());
1147 getter.body << u"return " + latestAccessor + u";";
1149 getter.body += epilogue;
1150 getter.userVisible =
true;
1151 current.functions.emplaceBack(getter);
1152 mocLines << u"READ"_s << getter.name;
1154 if (result.property.isWritable()) {
1155 Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property);
1157 setter.returnType = u"void"_s;
1158 setter.name = compilationData.write;
1160 const QString setName = result.property.write();
1161 QList<QQmlJSMetaMethod> methods = result.owner->methods(setName);
1162 if (methods.isEmpty()) {
1164 setter.parameterList.emplaceBack(QQmlJSUtils::constRefify(underlyingType),
1165 aliasName + u"_", u""_s);
1167 setter.parameterList = compileMethodParameters(methods.at(0).parameters(),
1171 setter.body += prologue;
1172 QStringList parameterNames;
1173 parameterNames.reserve(setter.parameterList.size());
1174 std::transform(setter.parameterList.cbegin(), setter.parameterList.cend(),
1175 std::back_inserter(parameterNames),
1176 [](
const Variable &x) {
return x.name; });
1177 QString commaSeparatedParameterNames = parameterNames.join(u", "_s);
1178 if (!setName.isEmpty()
1179 && !QQmlJSUtils::bindablePropertyHasDefaultAccessor(
1180 result.property, QQmlJSUtils::PropertyAccessor_Write)) {
1181 setter.body << u"%1->%2(%3);"_s.arg(latestAccessor, setName,
1182 commaSeparatedParameterNames);
1184 Q_ASSERT(parameterNames.size() == 1);
1185 const QString variantName = u"var_" + aliasName;
1186 setter.body << u"QVariant %1;"_s.arg(variantName);
1187 setter.body << u"%1.setValue(%2);"_s.arg(variantName, commaSeparatedParameterNames);
1188 setter.body << u"%1->setProperty(\"%2\", std::move(%3));"_s.arg(
1189 latestAccessor, result.property.propertyName(), variantName);
1191 setter.body += joinFrames(
1193 setter.body += epilogue;
1194 setter.userVisible =
true;
1195 current.functions.emplaceBack(setter);
1196 mocLines << u"WRITE"_s << setter.name;
1199 if (QString bindableName = result.property.bindable(); !bindableName.isEmpty()) {
1200 Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property);
1202 bindable.returnType = u"QBindable<" + underlyingType + u">";
1203 bindable.name = compilationData.bindable;
1204 bindable.body += prologue;
1205 bindable.body << u"return " + latestAccessor + u"->" + bindableName + u"()" + u";";
1206 bindable.body += epilogue;
1207 bindable.userVisible =
true;
1208 current.functions.emplaceBack(bindable);
1209 mocLines << u"BINDABLE"_s << bindable.name;
1215 if (
const QString aliasNotifyName = alias.notify(); !aliasNotifyName.isEmpty()) {
1217 Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property);
1219 mocLines << u"NOTIFY"_s << aliasNotifyName;
1224 if (
const QString notifyName = result.property.notify(); !notifyName.isEmpty()) {
1225 auto notifyFrames = frames;
1228 const QStringList notifyPrologue = joinFrames(
1230 const QStringList notifyEpilogue = joinFrames(
1234 current.endInit.body << u"{ // alias notify connection:"_s;
1235 current.endInit.body += notifyPrologue;
1240 const QString cppType = (m_visitor->qmlComponentIndex(result.owner) == -1)
1241 ? result.owner->internalName()
1242 : u"QQmlComponent"_s;
1243 const QString latestAccessorNonPrivate = notifyFrames.top().outVar;
1244 current.endInit.body << u"QObject::connect(" + latestAccessorNonPrivate + u", &" + cppType
1245 + u"::" + notifyName + u", this, &" + current.cppType + u"::"
1246 + compilationData.notify + u");";
1247 current.endInit.body += notifyEpilogue;
1248 current.endInit.body << u"}"_s;
1251 if (QString resetName = result.property.reset(); !resetName.isEmpty()) {
1252 Q_ASSERT(result.kind == QQmlJSUtils::AliasTarget_Property);
1254 reset.returnType = u"void"_s;
1255 reset.name = compilationData.reset;
1256 reset.body += prologue;
1257 reset.body << latestAccessor + u"->" + resetName + u"()" + u";";
1258 reset.body += epilogue;
1259 reset.userVisible =
true;
1260 current.functions.emplaceBack(reset);
1261 mocLines << u"RESET"_s << reset.name;
1267 mocLines << u"STORED"_s << u"false"_s;
1269 mocLines << u"DESIGNABLE"_s << u"false"_s;
1273 current.mocCode << u"Q_PROPERTY(" + mocLines.join(u" "_s) + u")";
1276 if (aliasName == owner->defaultPropertyName()) {
1278 current.mocCode << u"Q_CLASSINFO(\"DefaultProperty\", \"%1\")"_s.arg(aliasName);
1284 return u"QQmlEnginePrivate::get(engine)->compilationUnitFromUrl(%1())"_s.arg(urlMethodName);
1288 const QString &propertyName);
1291
1292
1293
1294void Compiler::compileObjectBinding(Type ¤t,
const QQmlJSMetaPropertyBinding &binding,
1295 const QQmlJSScope::ConstPtr &type,
1296 const BindingAccessorData &accessor)
1298 Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object);
1300 const QString &propertyName = binding.propertyName();
1301 const QQmlJSMetaProperty property = type->property(propertyName);
1302 QQmlJSScope::ConstPtr propertyType = property.type();
1305 auto object = binding.objectType();
1310 const QString qobjectParent = u"this"_s;
1312 if (!propertyType) {
1313 recordError(binding.sourceLocation(),
1314 u"Binding on property '" + propertyName + u"' of unknown type");
1318 const auto addObjectBinding = [&](
const QString &value) {
1319 if (qIsReferenceTypeList(property)) {
1320 Q_ASSERT(unprocessedListProperty == property || unprocessedListBindings.empty());
1321 unprocessedListBindings.append(value);
1322 unprocessedListProperty = property;
1324 CodeGenerator::generate_assignToProperty(¤t.endInit.body, type, property, value,
1325 accessor.name,
true);
1330 if (qsizetype index = m_visitor->qmlComponentIndex(object); index >= 0) {
1331 const QString objectName = newSymbol(u"sc"_s);
1333 const qsizetype creationIndex = m_visitor->creationIndex(object);
1335 QStringList *block = (creationIndex == -1) ? ¤t.endInit.body : ¤t.init.body;
1337 *block << QStringLiteral(
"auto thisContext = QQmlData::get(%1)->outerContext;")
1338 .arg(qobjectParent);
1339 *block << QStringLiteral(
"auto %1 = QQmlObjectCreator::createComponent(engine, "
1340 "%2, %3, %4, thisContext);")
1341 .arg(objectName, generate_callCompilationUnit(m_urlMethodName),
1342 QString::number(index), qobjectParent);
1343 *block << QStringLiteral(
"thisContext->installContext(QQmlData::get(%1), "
1344 "QQmlContextData::OrdinaryObject);")
1350 if (creationIndex != -1) {
1352 Q_ASSERT(object->isComposite());
1353 Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s);
1355 if (
int id = m_visitor->runtimeId(object); id >= 0) {
1356 QString idString = m_visitor->addressableScopes().id(object, object);
1357 if (idString.isEmpty())
1358 idString = u"<unknown>"_s;
1359 CodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName,
1363 const QString creationIndexStr = QString::number(creationIndex);
1364 *block << QStringLiteral(
"creator->set(%1, %2);").arg(creationIndexStr, objectName);
1365 Q_ASSERT(block == ¤t.init.body);
1366 current.endInit.body << QStringLiteral(
"auto %1 = creator->get<%2>(%3);")
1367 .arg(objectName, u"QQmlComponent"_s, creationIndexStr);
1369 addObjectBinding(objectName);
1374 const QString objectName = newSymbol(u"o"_s);
1375 current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s.arg(
1376 objectName, object->internalName(), qobjectParent);
1377 current.init.body << u"creator->set(%1, %2);"_s.arg(
1378 QString::number(m_visitor->creationIndex(object)), objectName);
1381 current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s.arg(
1382 objectName, object->internalName(), QString::number(m_visitor->creationIndex(object)));
1383 addObjectBinding(objectName);
1387
1388
1389
1390void Compiler::compileValueSourceOrInterceptorBinding(Type ¤t,
1391 const QQmlJSMetaPropertyBinding &binding,
1392 const QQmlJSScope::ConstPtr &type,
1393 const BindingAccessorData &accessor)
1395 Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource
1396 || binding.bindingType() == QQmlSA::BindingType::Interceptor);
1398 const QString &propertyName = binding.propertyName();
1399 const QQmlJSMetaProperty property = type->property(propertyName);
1400 QQmlJSScope::ConstPtr propertyType = property.type();
1403 QSharedPointer<
const QQmlJSScope> object;
1404 if (binding.bindingType() == QQmlSA::BindingType::Interceptor)
1405 object = binding.interceptorType();
1407 object = binding.valueSourceType();
1412 const QString qobjectParent = u"this"_s;
1414 if (!propertyType) {
1415 recordError(binding.sourceLocation(),
1416 u"Binding on property '" + propertyName + u"' of unknown type");
1420 auto &objectName = m_uniques[UniqueStringId(current, propertyName)].onAssignmentObjectName;
1421 if (objectName.isEmpty()) {
1422 objectName = u"onAssign_" + propertyName;
1424 current.init.body << u"auto %1 = new %2(creator, engine, %3);"_s.arg(
1425 objectName, object->internalName(), qobjectParent);
1426 current.init.body << u"creator->set(%1, %2);"_s.arg(
1427 QString::number(m_visitor->creationIndex(object)), objectName);
1429 current.endInit.body << u"auto %1 = creator->get<%2>(%3);"_s.arg(
1430 objectName, object->internalName(),
1431 QString::number(m_visitor->creationIndex(object)));
1436 current.endInit.body << u"{"_s;
1437 current.endInit.body << u"QQmlProperty qmlprop(%1, %2);"_s.arg(
1438 accessor.name, QQmlJSUtils::toLiteral(propertyName));
1439 current.endInit.body << u"QT_PREPEND_NAMESPACE(QQmlCppOnAssignmentHelper)::set(%1, qmlprop);"_s
1441 current.endInit.body << u"}"_s;
1445
1446
1447
1448void Compiler::compileAttachedPropertyBinding(Type ¤t,
1449 const QQmlJSMetaPropertyBinding &binding,
1450 const QQmlJSScope::ConstPtr &type,
1451 const BindingAccessorData &accessor)
1453 Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty);
1455 const QString &propertyName = binding.propertyName();
1456 const QQmlJSMetaProperty property = type->property(propertyName);
1457 QQmlJSScope::ConstPtr propertyType = property.type();
1459 Q_ASSERT(accessor.name == u"this"_s);
1460 const auto attachedType = binding.attachedType();
1461 Q_ASSERT(attachedType);
1463 const QString attachingTypeName = propertyName;
1464 auto attachingType = m_typeResolver->typeForName(attachingTypeName);
1466 QString attachedTypeName = attachedType->baseTypeName();
1467 Q_ASSERT(!attachedTypeName.isEmpty());
1469 auto &attachedMemberName =
1470 m_uniques[UniqueStringId(current, propertyName)].attachedVariableName;
1471 if (attachedMemberName.isEmpty()) {
1472 attachedMemberName = uniqueVariableName(attachingTypeName);
1475 current.variables.emplaceBack(attachedTypeName + u" *", attachedMemberName, u"nullptr"_s);
1477 if (propertyName == u"Component"_s) {
1478 current.endInit.body << u"Q_ASSERT(qmlEngine(this));"_s;
1479 current.endInit.body
1480 << u"// attached Component must be added to the object's QQmlData"_s;
1481 current.endInit.body
1482 << u"Q_ASSERT(!QQmlEnginePrivate::get(qmlEngine(this))->activeObjectCreator);"_s;
1486 const QString getAttachedPropertyLine = u"qobject_cast<" + attachedTypeName
1487 + u" *>(qmlAttachedPropertiesObject<" + attachingType->internalName()
1488 + u">(this, /* create = */ true))";
1489 current.endInit.body << attachedMemberName + u" = " + getAttachedPropertyLine + u";";
1491 if (propertyName == u"Component"_s) {
1493 current.handleOnCompleted.body << u"Q_EMIT " + attachedMemberName + u"->completed();";
1494 if (!current.dtor) {
1495 current.dtor = Dtor{};
1496 current.dtor->name = u"~" + current.cppType;
1498 current.dtor->body << u"Q_EMIT " + attachedMemberName + u"->destruction();";
1502 auto subbindings = attachedType->ownPropertyBindingsInQmlIROrder();
1504 partitionBindings(subbindings.begin(), subbindings.end());
1505 compileBinding(current, subbindings.begin(), subbindings.end(), attachedType,
1506 { type, attachedMemberName, propertyName,
false });
1510
1511
1512
1513void Compiler::compileGroupPropertyBinding(Type ¤t,
1514 const QQmlJSMetaPropertyBinding &binding,
1515 const QQmlJSScope::ConstPtr &type,
1516 const BindingAccessorData &accessor)
1518 Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty);
1520 const QString &propertyName = binding.propertyName();
1521 const QQmlJSMetaProperty property = type->property(propertyName);
1522 QQmlJSScope::ConstPtr propertyType = property.type();
1524 Q_ASSERT(accessor.name == u"this"_s);
1525 if (property.read().isEmpty()) {
1526 recordError(binding.sourceLocation(),
1527 u"READ function of group property '" + propertyName + u"' is unknown");
1531 auto groupType = binding.groupType();
1532 Q_ASSERT(groupType);
1534 const bool isValueType = propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Value;
1536 && propertyType->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
1537 recordError(binding.sourceLocation(),
1538 u"Group property '" + propertyName + u"' has unsupported access semantics");
1542 auto subbindings = groupType->ownPropertyBindingsInQmlIROrder();
1543 auto firstScript = partitionBindings(subbindings.begin(), subbindings.end());
1547 const bool generateValueTypeCode = isValueType && (subbindings.begin() != firstScript);
1549 QString groupAccessor = CodeGenerator::wrap_privateClass(accessor.name, property) + u"->"
1550 + property.read() + u"()";
1552 const QString groupPropertyVarName = accessor.name + u"_group_" + propertyName;
1554 if (generateValueTypeCode) {
1555 if (property.write().isEmpty()) {
1556 recordError(binding.sourceLocation(),
1557 u"Group property '" + propertyName + u"' is a value type without a setter");
1561 current.endInit.body << u"auto " + groupPropertyVarName + u" = " + groupAccessor + u";";
1564 groupAccessor = CodeGenerator::wrap_addressof(groupPropertyVarName);
1568 const auto compile = [&](
const auto &bStart,
const auto &bEnd) {
1569 compileBinding(current, bStart, bEnd, groupType,
1570 { type, groupAccessor, propertyName, isValueType });
1573 auto it = subbindings.begin();
1574 Q_ASSERT(
std::all_of(it, firstScript, [](
const auto &x) {
1575 return x.bindingType() != QQmlSA::BindingType::Script;
1577 compile(it, firstScript);
1589 if (generateValueTypeCode) {
1590 current.endInit.body << CodeGenerator::wrap_privateClass(accessor.name, property)
1591 + u"->" + property.write() + u"(" + groupPropertyVarName + u");";
1595 Q_ASSERT(
std::all_of(it, subbindings.end(), [](
const auto &x) {
1596 return x.bindingType() == QQmlSA::BindingType::Script;
1598 compile(it, subbindings.end());
1602
1603
1604
1605void Compiler::compileTranslationBinding(Type ¤t,
1606 const QQmlJSMetaPropertyBinding &binding,
1607 const QQmlJSScope::ConstPtr &type,
1608 const BindingAccessorData &accessor)
1610 Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation
1611 || binding.bindingType() == QQmlSA::BindingType::TranslationById);
1613 const QString &propertyName = binding.propertyName();
1615 auto [property, absoluteIndex] = getMetaPropertyIndex(type, propertyName);
1617 if (absoluteIndex < 0) {
1618 recordError(binding.sourceLocation(),
1619 u"Binding on unknown property '" + propertyName + u"'");
1623 QString bindingTarget = accessor.name;
1625 int valueTypeIndex = -1;
1626 if (accessor.isValueType) {
1627 Q_ASSERT(accessor.scope != type);
1628 bindingTarget = u"this"_s;
1629 auto [groupProperty, groupPropertyIndex] =
1630 getMetaPropertyIndex(accessor.scope, accessor.propertyName);
1631 if (groupPropertyIndex < 0) {
1632 recordError(binding.sourceLocation(),
1633 u"Binding on group property '" + accessor.propertyName
1634 + u"' of unknown type");
1637 valueTypeIndex = absoluteIndex;
1638 absoluteIndex = groupPropertyIndex;
1641 CodeGenerator::TranslationBindingInfo info;
1642 info.unitVarName = generate_callCompilationUnit(m_urlMethodName);
1643 info.scope = u"this"_s;
1644 info.target = u"this"_s;
1645 info.propertyIndex = absoluteIndex;
1646 info.property = property;
1647 info.data = binding.translationDataValue(m_url);
1648 info.valueTypeIndex = valueTypeIndex;
1649 info.line = binding.sourceLocation().startLine;
1650 info.column = binding.sourceLocation().startColumn;
1652 CodeGenerator::generate_createTranslationBindingOnProperty(¤t.endInit.body, info);
1655void Compiler::processLastListBindings(Type ¤t,
const QQmlJSScope::ConstPtr &type,
1656 const BindingAccessorData &accessor)
1658 if (unprocessedListBindings.empty())
1661 CodeGenerator::generate_assignToListProperty(
1662 ¤t.endInit.body, type, unprocessedListProperty, unprocessedListBindings,
1664 m_uniques[UniqueStringId(current, unprocessedListProperty.propertyName())]
1665 .qmlListVariableName);
1667 unprocessedListBindings.clear();
1670void Compiler::compileBinding(Type ¤t,
1671 QList<QQmlJSMetaPropertyBinding>::iterator bindingStart,
1672 QList<QQmlJSMetaPropertyBinding>::iterator bindingEnd,
1673 const QQmlJSScope::ConstPtr &type,
1674 const BindingAccessorData &accessor)
1676 for (
auto it = bindingStart; it != bindingEnd; it++) {
1677 const QQmlJSMetaPropertyBinding &binding = *it;
1678 const QString &propertyName = binding.propertyName();
1679 Q_ASSERT(!propertyName.isEmpty());
1684 if (type->isNameDeferred(propertyName)) {
1685 const auto location = binding.sourceLocation();
1688 if (binding.bindingType() == QQmlSA::BindingType::GroupProperty
1689 && type->hasProperty(propertyName)) {
1690 qCWarning(lcQmltcCompiler)
1691 << QStringLiteral(
"Binding at line %1 column %2 is not deferred as it is a "
1692 "binding on a group property.")
1693 .arg(QString::number(location.startLine),
1694 QString::number(location.startColumn));
1698 qCDebug(lcQmltcCompiler)
1700 "Binding at line %1 column %2 is deferred and thus not compiled")
1701 .arg(QString::number(location.startLine),
1702 QString::number(location.startColumn));
1707 const QQmlJSMetaProperty metaProperty = type->property(propertyName);
1708 const QQmlJSScope::ConstPtr propertyType = metaProperty.type();
1710 if (!(qIsReferenceTypeList(metaProperty) && unprocessedListProperty == metaProperty)) {
1711 processLastListBindings(current, type, accessor);
1714 compileBindingByType(current, binding, type, accessor);
1717 processLastListBindings(current, type, accessor);
1720void Compiler::compileBindingByType(Type ¤t,
const QQmlJSMetaPropertyBinding &binding,
1721 const QQmlJSScope::ConstPtr &type,
1722 const BindingAccessorData &accessor)
1724 const QString &propertyName = binding.propertyName();
1725 const QQmlJSMetaProperty metaProperty = type->property(propertyName);
1726 const QQmlJSScope::ConstPtr propertyType = metaProperty.type();
1728 const auto assignToProperty = [&](
const QQmlJSMetaProperty &p,
const QString &value,
1729 bool constructFromQObject =
false) {
1730 CodeGenerator::generate_assignToProperty(¤t.endInit.body, type, p, value,
1731 accessor.name, constructFromQObject);
1733 switch (binding.bindingType()) {
1734 case QQmlSA::BindingType::BoolLiteral: {
1735 const bool value = binding.boolValue();
1736 assignToProperty(metaProperty, value ? u"true"_s : u"false"_s);
1739 case QQmlSA::BindingType::NumberLiteral: {
1740 assignToProperty(metaProperty, QString::number(binding.numberValue()));
1743 case QQmlSA::BindingType::StringLiteral: {
1744 QString value = QQmlJSUtils::toLiteral(binding.stringValue());
1745 if (
auto type = metaProperty.type()) {
1746 if (type->internalName() == u"QUrl"_s) {
1747 value = u"QUrl(%1)"_s.arg(value);
1750 assignToProperty(metaProperty, value);
1753 case QQmlSA::BindingType::RegExpLiteral: {
1754 const QString value =
1755 u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue()));
1756 assignToProperty(metaProperty, value);
1759 case QQmlSA::BindingType::Null: {
1761 Q_ASSERT(propertyType->isSameType(m_typeResolver->varType())
1762 || propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
1763 if (propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference)
1764 assignToProperty(metaProperty, u"nullptr"_s);
1766 assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s);
1769 case QQmlSA::BindingType::Script: {
1770 QString bindingSymbolName
1771 = uniqueVariableName(type->internalName() + u'_' + propertyName + u"_binding");
1772 compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType,
1776 case QQmlSA::BindingType::Object: {
1777 compileObjectBinding(current, binding, type, accessor);
1780 case QQmlSA::BindingType::Interceptor:
1782 case QQmlSA::BindingType::ValueSource: {
1783 compileValueSourceOrInterceptorBinding(current, binding, type, accessor);
1786 case QQmlSA::BindingType::AttachedProperty: {
1787 compileAttachedPropertyBinding(current, binding, type, accessor);
1790 case QQmlSA::BindingType::GroupProperty: {
1791 compileGroupPropertyBinding(current, binding, type, accessor);
1795 case QQmlSA::BindingType::TranslationById:
1796 case QQmlSA::BindingType::Translation: {
1797 compileTranslationBinding(current, binding, type, accessor);
1800 case QQmlSA::BindingType::Invalid: {
1801 recordError(binding.sourceLocation(), u"This binding is invalid"_s);
1805 recordError(binding.sourceLocation(), u"Binding is not supported"_s);
1813 const QQmlJSScope::ConstPtr &objectType,
1814 const QString &urlMethodName,
1815 const QString &functorCppType,
1816 const QString &objectCppType)
1818 Type bindingFunctor {};
1819 bindingFunctor.cppType = functorCppType;
1820 bindingFunctor.ignoreInit =
true;
1823 const QString pointerToObject = objectCppType + u" *";
1824 bindingFunctor.variables.emplaceBack(Variable { pointerToObject, u"m_self"_s, u"nullptr"_s });
1825 bindingFunctor.baselineCtor.name = functorCppType;
1826 bindingFunctor.baselineCtor.parameterList.emplaceBack(
1827 Variable { pointerToObject, u"self"_s, QString() });
1828 bindingFunctor.baselineCtor.initializerList.emplaceBack(u"m_self(self)"_s);
1831 Method callOperator {};
1832 callOperator.returnType = u"void"_s;
1833 callOperator.name = u"operator()"_s;
1834 callOperator.modifiers << u"const"_s;
1835 CodeGenerator::generate_callExecuteRuntimeFunction(
1836 &callOperator.body, urlMethodName + u"()",
1837 objectType->ownRuntimeFunctionIndex(binding.scriptIndex()), u"m_self"_s, u"void"_s, {});
1839 bindingFunctor.functions.emplaceBack(
std::move(callOperator));
1841 return bindingFunctor;
1847 const QString &propertyName)
1849 auto owner = QQmlJSScope::ownerOfProperty(scope, propertyName).scope;
1851 const QQmlJSMetaProperty p = owner->ownProperty(propertyName);
1854 int index = p.index();
1858 const auto increment = [&](
const QQmlJSScope::ConstPtr &type, QQmlJSScope::ExtensionKind m) {
1861 if (type->isSameType(owner))
1865 if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript)
1868 index +=
int(type->ownProperties().size());
1870 QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, owner, increment);
1871 return { p, index };
1874void Compiler::compileScriptBinding(Type ¤t,
const QQmlJSMetaPropertyBinding &binding,
1875 const QString &bindingSymbolName,
1876 const QQmlJSScope::ConstPtr &objectType,
1877 const QString &propertyName,
1878 const QQmlJSScope::ConstPtr &propertyType,
1879 const Compiler::BindingAccessorData &accessor)
1881 const auto compileScriptSignal = [&](
const QString &name) {
1882 QString This_signal = u"this"_s;
1883 QString This_slot = u"this"_s;
1884 QString objectClassName_signal = objectType->internalName();
1885 QString objectClassName_slot = objectType->internalName();
1888 if (accessor.name != u"this"_s) {
1889 This_signal = accessor.name;
1890 This_slot = u"this"_s;
1891 objectClassName_signal = objectType->baseTypeName();
1892 objectClassName_slot = current.cppType;
1894 Q_ASSERT(!objectClassName_signal.isEmpty());
1895 Q_ASSERT(!objectClassName_slot.isEmpty());
1897 const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal);
1898 Q_ASSERT(!signalMethods.isEmpty());
1899 QQmlJSMetaMethod signal = signalMethods.at(0);
1900 Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal);
1902 const QString signalName = signal.methodName();
1903 const QString slotName = newSymbol(signalName + u"_slot");
1905 const QString signalReturnType = signal.returnType()->augmentedInternalName();
1906 const QList<Variable> slotParameters =
1907 compileMethodParameters(signal.parameters(),
true);
1910 Method slotMethod {};
1911 slotMethod.returnType = signalReturnType;
1912 slotMethod.name = slotName;
1913 slotMethod.parameterList = slotParameters;
1915 CodeGenerator::generate_callExecuteRuntimeFunction(
1916 &slotMethod.body, m_urlMethodName + u"()",
1917 objectType->ownRuntimeFunctionIndex(binding.scriptIndex()),
1919 signalReturnType, slotParameters);
1920 slotMethod.type = QQmlJSMetaMethodType::Slot;
1922 current.functions <<
std::move(slotMethod);
1923 current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&"
1924 + objectClassName_signal + u"::" + signalName + u", " + This_slot + u", &"
1925 + objectClassName_slot + u"::" + slotName + u");";
1928 switch (binding.scriptKind()) {
1929 case QQmlSA::ScriptBindingKind::PropertyBinding: {
1930 if (!propertyType) {
1931 recordError(binding.sourceLocation(),
1932 u"Binding on property '" + propertyName + u"' of unknown type");
1936 auto [property, absoluteIndex] = getMetaPropertyIndex(objectType, propertyName);
1937 if (absoluteIndex < 0) {
1938 recordError(binding.sourceLocation(),
1939 u"Binding on unknown property '" + propertyName + u"'");
1943 QString bindingTarget = accessor.name;
1945 int valueTypeIndex = -1;
1946 if (accessor.isValueType) {
1947 Q_ASSERT(accessor.scope != objectType);
1948 bindingTarget = u"this"_s;
1949 auto [groupProperty, groupPropertyIndex] =
1950 getMetaPropertyIndex(accessor.scope, accessor.propertyName);
1951 if (groupPropertyIndex < 0) {
1952 recordError(binding.sourceLocation(),
1953 u"Binding on group property '" + accessor.propertyName
1954 + u"' of unknown type");
1957 valueTypeIndex = absoluteIndex;
1958 absoluteIndex = groupPropertyIndex;
1961 CodeGenerator::generate_createBindingOnProperty(
1962 ¤t.setComplexBindings.body, generate_callCompilationUnit(m_urlMethodName),
1964 static_cast<qsizetype>(objectType->ownRuntimeFunctionIndex(binding.scriptIndex())),
1967 accessor.isValueType ? QQmlJSScope::ConstPtr() : objectType, absoluteIndex,
1968 property, valueTypeIndex, accessor.name);
1971 case QQmlSA::ScriptBindingKind::SignalHandler: {
1972 const auto name = QQmlSignalNames::handlerNameToSignalName(propertyName);
1973 Q_ASSERT(name.has_value());
1974 compileScriptSignal(*name);
1977 case QQmlSA ::ScriptBindingKind::ChangeHandler: {
1978 const QString objectClassName = objectType->internalName();
1979 const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor");
1981 const auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
1982 Q_ASSERT(signalName.has_value());
1983 const auto actualProperty =
1984 QQmlJSUtils::propertyFromChangedHandler(objectType, propertyName);
1985 Q_ASSERT(actualProperty.has_value());
1986 const auto actualPropertyType = actualProperty->type();
1987 if (!actualPropertyType) {
1988 recordError(binding.sourceLocation(),
1989 u"Binding on property '" + actualProperty->propertyName()
1990 + u"' of unknown type");
1996 const QString notifyString = actualProperty->notify();
1997 if (!notifyString.isEmpty()) {
1998 compileScriptSignal(notifyString);
2001 const QString bindableString = actualProperty->bindable();
2002 QString typeOfQmlBinding =
2003 u"std::unique_ptr<QPropertyChangeHandler<" + bindingFunctorName + u">>";
2005 current.children << compileScriptBindingPropertyChangeHandler(
2006 binding, objectType, m_urlMethodName, bindingFunctorName, objectClassName);
2008 current.setComplexBindings.body << u"if (!%1.contains(QStringLiteral(\"%2\")))"_s.arg(
2009 current.propertyInitializer.initializedCache.name, propertyName);
2013 current.setComplexBindings.body << u" "_s + bindingSymbolName + u".reset(new QPropertyChangeHandler<"
2014 + bindingFunctorName + u">("
2015 + CodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
2016 + u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("
2017 + accessor.name + u"))));";
2019 current.variables.emplaceBack(Variable { typeOfQmlBinding, bindingSymbolName, QString() });
2023 recordError(binding.sourceLocation(), u"Invalid script binding found"_s);
void compile(const CompilerInfo &info)
Compiler(const QString &url, TypeResolver *resolver, Visitor *visitor, QQmlJSLogger *logger)
bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
static QList< QQmlJSMetaProperty > unboundRequiredProperties(const QQmlJSScope::ConstPtr &type, TypeResolver *resolver)
static std::pair< QQmlJSMetaProperty, int > getMetaPropertyIndex(const QQmlJSScope::ConstPtr &scope, const QString &propertyName)
static QString generate_callCompilationUnit(const QString &urlMethodName)
static Type compileScriptBindingPropertyChangeHandler(const QQmlJSMetaPropertyBinding &binding, const QQmlJSScope::ConstPtr &objectType, const QString &urlMethodName, const QString &functorCppType, const QString &objectCppType)
static void compileRequiredPropertiesBundle(Type ¤t, const QQmlJSScope::ConstPtr &type, TypeResolver *resolver)
static QStringList joinFrames(const QStack< AliasResolutionFrame > &frames, Projection project)
static QList< Variable > compileMethodParameters(const QList< QQmlJSMetaParameter > ¶meterInfos, bool allowUnnamed=false)
static void unpackFrames(QStack< AliasResolutionFrame > &frames)
static void compileRootExternalConstructorBody(Type ¤t, const QQmlJSScope::ConstPtr &type)
static Iterator partitionBindings(Iterator first, Iterator last)
QStringList epilogueForWrite