aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc/qmltccompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qmltc/qmltccompiler.cpp')
-rw-r--r--tools/qmltc/qmltccompiler.cpp397
1 files changed, 335 insertions, 62 deletions
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index 4050136ef2..75bd580e07 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -8,6 +8,7 @@
#include "qmltccompilerpieces.h"
#include <QtCore/qloggingcategory.h>
+#include <QtQml/private/qqmlsignalnames_p.h>
#include <private/qqmljsutils_p.h>
#include <algorithm>
@@ -22,6 +23,137 @@ bool qIsReferenceTypeList(const QQmlJSMetaProperty &p)
return false;
}
+static QList<QQmlJSMetaProperty> unboundRequiredProperties(
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+ QList<QQmlJSMetaProperty> requiredProperties{};
+
+ auto isPropertyRequired = [&type, &resolver](const auto &property) {
+ if (!type->isPropertyRequired(property.propertyName()))
+ return false;
+
+ if (type->hasPropertyBindings(property.propertyName()))
+ return false;
+
+ if (property.isAlias()) {
+ QQmlJSUtils::AliasResolutionVisitor aliasVisitor;
+
+ QQmlJSUtils::ResolvedAlias result =
+ QQmlJSUtils::resolveAlias(resolver, property, type, aliasVisitor);
+
+ if (result.kind != QQmlJSUtils::AliasTarget_Property)
+ return false;
+
+ // If the top level alias targets a property that is in
+ // the top level scope and that property is required, then
+ // we will already pick up the property during one of the
+ // iterations.
+ // Setting the property or the alias is the same so we
+ // discard one of the two, as otherwise we would require
+ // the user to pass two values for the same property ,in
+ // this case the alias.
+ //
+ // For example in:
+ //
+ // ```
+ // Item {
+ // id: self
+ // required property int foo
+ // property alias bar: self.foo
+ // }
+ // ```
+ //
+ // Both foo and bar are required but setting one or the
+ // other is the same operation so that we should choose
+ // only one.
+ if (result.owner == type &&
+ type->isPropertyRequired(result.property.propertyName()))
+ return false;
+
+ if (result.owner->hasPropertyBindings(result.property.propertyName()))
+ return false;
+ }
+
+ return true;
+ };
+
+ const auto properties = type->properties();
+ std::copy_if(properties.cbegin(), properties.cend(),
+ std::back_inserter(requiredProperties), isPropertyRequired);
+ std::sort(requiredProperties.begin(), requiredProperties.end(),
+ [](const auto &left, const auto &right) {
+ return left.propertyName() < right.propertyName();
+ });
+
+ return requiredProperties;
+}
+
+
+// Populates the internal representation for a
+// RequiredPropertiesBundle, a class that acts as a bundle of initial
+// values that should be set for the required properties of a type.
+static void compileRequiredPropertiesBundle(
+ QmltcType &current,
+ const QQmlJSScope::ConstPtr &type,
+ QmltcTypeResolver *resolver
+) {
+
+ QList<QQmlJSMetaProperty> requiredProperties = unboundRequiredProperties(type, resolver);
+
+ if (requiredProperties.isEmpty())
+ return;
+
+ current.requiredPropertiesBundle.emplace();
+ current.requiredPropertiesBundle->name = u"RequiredPropertiesBundle"_s;
+
+ current.requiredPropertiesBundle->members.reserve(requiredProperties.size());
+ std::transform(requiredProperties.cbegin(), requiredProperties.cend(),
+ std::back_inserter(current.requiredPropertiesBundle->members),
+ [](const QQmlJSMetaProperty &property) {
+ QString type = qIsReferenceTypeList(property)
+ ? u"const QList<%1*>&"_s.arg(
+ property.type()->valueType()->internalName())
+ : u"passByConstRefOrValue<%1>"_s.arg(
+ property.type()->augmentedInternalName());
+ return QmltcVariable{ type, property.propertyName() };
+ });
+}
+
+static void compileRootExternalConstructorBody(
+ QmltcType& current,
+ const QQmlJSScope::ConstPtr &type
+) {
+ current.externalCtor.body << u"// document root:"_s;
+ // if it's document root, we want to create our QQmltcObjectCreationBase
+ // that would store all the created objects
+ current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
+ type->internalName());
+ current.externalCtor.body
+ << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
+ current.externalCtor.body << u"creator.set(0, this);"_s; // special case
+
+ QString initializerName = u"initializer"_s;
+ if (current.requiredPropertiesBundle) {
+ // Compose new initializer based on the initial values for required properties.
+ current.externalCtor.body << u"auto newInitializer = [&](auto& propertyInitializer) {"_s;
+ for (const auto& member : current.requiredPropertiesBundle->members) {
+ current.externalCtor.body << u" propertyInitializer.%1(requiredPropertiesBundle.%2);"_s.arg(
+ QmltcPropertyData(member.name).write, member.name
+ );
+ }
+ current.externalCtor.body << u" initializer(propertyInitializer);"_s;
+ current.externalCtor.body << u"};"_s;
+
+ initializerName = u"newInitializer"_s;
+ }
+
+ // now call init
+ current.externalCtor.body << current.init.name
+ + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
+ u"endInit */ true, %1);"_s.arg(initializerName);
+};
+
Q_LOGGING_CATEGORY(lcQmltcCompiler, "qml.qmltc.compiler", QtWarningMsg);
const QString QmltcCodeGenerator::privateEngineName = u"ePriv"_s;
@@ -87,6 +219,9 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
const auto *inlineComponentAName = std::get_if<InlineComponentNameType>(&a);
const auto *inlineComponentBName = std::get_if<InlineComponentNameType>(&b);
+ if (inlineComponentAName == inlineComponentBName)
+ return false;
+
// the root comes at last, so (a < b) == true when b is the root and a is not
if (inlineComponentAName && !inlineComponentBName)
return true;
@@ -127,7 +262,7 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
};
for (const auto &type : pureTypes) {
- Q_ASSERT(type->scopeType() == QQmlJSScope::QMLScope);
+ Q_ASSERT(type->scopeType() == QQmlSA::ScopeType::QMLScope);
compiledTypes.emplaceBack(); // create empty type
compileType(compiledTypes.back(), type, compile);
}
@@ -142,8 +277,11 @@ void QmltcCompiler::compile(const QmltcCompilerInfo &info)
program.cppPath = m_info.outputCppFile;
program.hPath = m_info.outputHFile;
program.outNamespace = m_info.outputNamespace;
+ program.exportMacro = m_info.exportMacro;
program.compiledTypes = compiledTypes;
program.includes = m_visitor->cppIncludeFiles();
+ if (!m_info.exportMacro.isEmpty() && !m_info.exportInclude.isEmpty())
+ program.includes += (m_info.exportInclude);
program.urlMethod = urlMethod;
QmltcOutput out;
@@ -186,7 +324,8 @@ void QmltcCompiler::compileType(
// make document root a friend to allow it to access init and endInit
const QString rootInternalName =
m_visitor->inlineComponent(type->enclosingInlineComponentName())->internalName();
- current.otherCode << u"friend class %1;"_s.arg(rootInternalName);
+ if (rootInternalName != current.cppType) // avoid GCC13 warning on self-befriending
+ current.otherCode << "friend class %1;"_L1.arg(rootInternalName);
}
if (documentRoot || inlineComponent) {
auto name = type->inlineComponentName()
@@ -210,8 +349,11 @@ void QmltcCompiler::compileType(
return scope->parentScope();
return scope;
};
- current.otherCode << u"friend class %1;"_s.arg(
- realQmlScope(type->parentScope())->internalName());
+
+ const auto& realScope = realQmlScope(type->parentScope());
+ if (realScope != rootType) {
+ current.otherCode << u"friend class %1;"_s.arg(realScope->internalName());
+ }
}
// make QQmltcObjectCreationHelper a friend of every type since it provides
@@ -240,6 +382,17 @@ void QmltcCompiler::compileType(
current.finalizeComponent.access = QQmlJSMetaMethod::Protected;
current.handleOnCompleted.access = QQmlJSMetaMethod::Protected;
+ current.propertyInitializer.name = u"PropertyInitializer"_s;
+ current.propertyInitializer.constructor.access = QQmlJSMetaMethod::Public;
+ current.propertyInitializer.constructor.name = current.propertyInitializer.name;
+ current.propertyInitializer.constructor.parameterList = {
+ QmltcVariable(u"%1&"_s.arg(current.cppType), u"component"_s)
+ };
+ current.propertyInitializer.component.cppType = current.cppType + u"&";
+ current.propertyInitializer.component.name = u"component"_s;
+ current.propertyInitializer.initializedCache.cppType = u"QSet<QString>"_s;
+ current.propertyInitializer.initializedCache.name = u"initializedCache"_s;
+
current.baselineCtor.name = current.cppType;
current.externalCtor.name = current.cppType;
current.init.name = u"QML_init"_s;
@@ -259,19 +412,40 @@ void QmltcCompiler::compileType(
QmltcVariable creator(u"QQmltcObjectCreationHelper*"_s, u"creator"_s);
QmltcVariable engine(u"QQmlEngine*"_s, u"engine"_s);
QmltcVariable parent(u"QObject*"_s, u"parent"_s, u"nullptr"_s);
+ QmltcVariable initializedCache(
+ u"[[maybe_unused]] const QSet<QString>&"_s,
+ u"initializedCache"_s,
+ u"{}"_s
+ );
QmltcVariable ctxtdata(u"const QQmlRefPointer<QQmlContextData>&"_s, u"parentContext"_s);
QmltcVariable finalizeFlag(u"bool"_s, u"canFinalize"_s);
current.baselineCtor.parameterList = { parent };
current.endInit.parameterList = { creator, engine };
- current.setComplexBindings.parameterList = { creator, engine };
+ current.setComplexBindings.parameterList = { creator, engine, initializedCache };
current.handleOnCompleted.parameterList = { creator };
if (documentRoot || inlineComponent) {
- current.externalCtor.parameterList = { engine, parent };
- current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag };
+ const QmltcVariable initializer(
+ u"[[maybe_unused]] qxp::function_ref<void(%1&)>"_s.arg(current.propertyInitializer.name),
+ u"initializer"_s,
+ u"[](%1&){}"_s.arg(current.propertyInitializer.name));
+
+ current.init.parameterList = { creator, engine, ctxtdata, finalizeFlag, initializer };
current.beginClass.parameterList = { creator, finalizeFlag };
current.completeComponent.parameterList = { creator, finalizeFlag };
current.finalizeComponent.parameterList = { creator, finalizeFlag };
+
+ compileRequiredPropertiesBundle(current, type, m_typeResolver);
+
+ if (current.requiredPropertiesBundle) {
+ QmltcVariable bundle{
+ u"const %1&"_s.arg(current.requiredPropertiesBundle->name),
+ u"requiredPropertiesBundle"_s,
+ };
+ current.externalCtor.parameterList = { engine, bundle, parent, initializer };
+ } else {
+ current.externalCtor.parameterList = { engine, parent, initializer };
+ }
} else {
current.externalCtor.parameterList = { creator, engine, parent };
current.init.parameterList = { creator, engine, ctxtdata };
@@ -295,18 +469,7 @@ void QmltcCompiler::compileType(
// compilation stub:
current.externalCtor.body << u"Q_UNUSED(engine)"_s;
if (documentRoot || inlineComponent) {
- current.externalCtor.body << u"// document root:"_s;
- // if it's document root, we want to create our QQmltcObjectCreationBase
- // that would store all the created objects
- current.externalCtor.body << u"QQmltcObjectCreationBase<%1> objectHolder;"_s.arg(
- type->internalName());
- current.externalCtor.body
- << u"QQmltcObjectCreationHelper creator = objectHolder.view();"_s;
- current.externalCtor.body << u"creator.set(0, this);"_s; // special case
- // now call init
- current.externalCtor.body << current.init.name
- + u"(&creator, engine, QQmlContextData::get(engine->rootContext()), /* "
- u"endInit */ true);";
+ compileRootExternalConstructorBody(current, type);
} else {
current.externalCtor.body << u"// not document root:"_s;
// just call init, we don't do any setup here otherwise
@@ -321,7 +484,7 @@ void QmltcCompiler::compileType(
staticCreate.comments
<< u"Used by the engine for singleton creation."_s
<< u"See also \\l {https://doc.qt.io/qt-6/qqmlengine.html#QML_SINGLETON}."_s;
- staticCreate.type = QQmlJSMetaMethod::StaticMethod;
+ staticCreate.type = QQmlJSMetaMethodType::StaticMethod;
staticCreate.access = QQmlJSMetaMethod::Public;
staticCreate.name = u"create"_s;
staticCreate.returnType = u"%1 *"_s.arg(current.cppType);
@@ -354,6 +517,102 @@ static Iterator partitionBindings(Iterator first, Iterator last)
});
}
+// Populates the propertyInitializer of the current type based on the
+// available properties.
+//
+// A propertyInitializer is a generated class that provides a
+// restricted interface that only allows setting property values and
+// internally keep tracks of which properties where actually set,
+// intended to be used to allow the user to set up the initial values
+// when creating an instance of a component.
+//
+// For each property of the current type that is known, is not private
+// and is writable, a setter method is generated.
+// Each setter method knows how to set a specific property, so as to
+// provide a strongly typed interface to property setting, as if the
+// relevant C++ type was used directly.
+//
+// Each setter uses the write method for the proprerty when available
+// and otherwise falls back to a the more generic
+// `QObject::setProperty` for properties where a WRITE method is not
+// available or in scope.
+static void compilePropertyInitializer(QmltcType &current, const QQmlJSScope::ConstPtr &type) {
+ static auto isFromExtension = [](const QQmlJSMetaProperty &property, const QQmlJSScope::ConstPtr &scope) {
+ return scope->ownerOfProperty(scope, property.propertyName()).extensionSpecifier != QQmlJSScope::NotExtension;
+ };
+
+ current.propertyInitializer.constructor.initializerList << u"component{component}"_s;
+
+ auto properties = type->properties().values();
+ for (auto& property: properties) {
+ if (property.index() == -1) continue;
+ if (property.isPrivate()) continue;
+ if (!property.isWritable() && !qIsReferenceTypeList(property)) continue;
+
+ const QString name = property.propertyName();
+
+ current.propertyInitializer.propertySetters.emplace_back();
+ auto& compiledSetter = current.propertyInitializer.propertySetters.back();
+
+ compiledSetter.userVisible = true;
+ compiledSetter.returnType = u"void"_s;
+ compiledSetter.name = QmltcPropertyData(property).write;
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(u"QList<%1*>"_s.arg(property.type()->valueType()->internalName())),
+ name + u"_", QString()
+ );
+ } else {
+ compiledSetter.parameterList.emplaceBack(
+ QQmlJSUtils::constRefify(getUnderlyingType(property)), name + u"_", QString()
+ );
+ }
+
+ if (qIsReferenceTypeList(property)) {
+ compiledSetter.body << u"QQmlListReference list_ref_(&%1, \"%2\");"_s.arg(
+ current.propertyInitializer.component.name, name
+ );
+ compiledSetter.body << u"list_ref_.clear();"_s;
+ compiledSetter.body << u"for (const auto& list_item_ : %1_)"_s.arg(name);
+ compiledSetter.body << u" list_ref_.append(list_item_);"_s;
+ } else if (
+ QQmlJSUtils::bindablePropertyHasDefaultAccessor(property, QQmlJSUtils::PropertyAccessor_Write)
+ ) {
+ compiledSetter.body << u"%1.%2().setValue(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.bindable(), name);
+ } else if (type->hasOwnProperty(name)) {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, QmltcPropertyData(property).write, name);
+ } else if (property.write().isEmpty() || isFromExtension(property, type)) {
+ // We can end here if a WRITE method is not available or
+ // if the method is available but not in this scope, so
+ // that we fallback to the string-based setters..
+ //
+ // For example, types that makes use of QML_EXTENDED
+ // types, will have the extension types properties
+ // available and with a WRITE method, but the WRITE method
+ // will not be available to the extended type, from C++,
+ // as the type does not directly inherit from the
+ // extension type.
+ //
+ // We specifically scope `setProperty` to `QObject` as
+ // certain types might have shadowed the method.
+ // For example, in QtQuick, some types have a property
+ // called `property` with a `setProperty` WRITE method
+ // that will produce the shadowing.
+ compiledSetter.body << u"%1.QObject::setProperty(\"%2\", QVariant::fromValue(%2_));"_s.arg(
+ current.propertyInitializer.component.name, name);
+ } else {
+ compiledSetter.body << u"%1.%2(%3_);"_s.arg(
+ current.propertyInitializer.component.name, property.write(), name);
+ }
+
+ compiledSetter.body << u"%1.insert(\"%2\");"_s.arg(
+ current.propertyInitializer.initializedCache.name, name);
+ }
+}
+
void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::ConstPtr &type)
{
// compile components of a type:
@@ -396,6 +655,7 @@ void QmltcCompiler::compileTypeElements(QmltcType &current, const QQmlJSScope::C
auto bindings = type->ownPropertyBindingsInQmlIROrder();
partitionBindings(bindings.begin(), bindings.end());
+ compilePropertyInitializer(current, type);
compileBinding(current, bindings.begin(), bindings.end(), type, { type });
}
@@ -450,7 +710,7 @@ compileMethodParameters(const QList<QQmlJSMetaParameter> &parameterInfos, bool a
static QString figureReturnType(const QQmlJSMetaMethod &m)
{
const bool isVoidMethod =
- m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethod::Signal;
+ m.returnTypeName() == u"void" || m.methodType() == QQmlJSMetaMethodType::Signal;
Q_ASSERT(isVoidMethod || m.returnType());
QString type;
if (isVoidMethod) {
@@ -467,10 +727,10 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
const auto returnType = figureReturnType(m);
const QList<QmltcVariable> compiledParams = compileMethodParameters(m.parameters());
- const auto methodType = QQmlJSMetaMethod::Type(m.methodType());
+ const auto methodType = m.methodType();
QStringList code;
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
QmltcCodeGenerator urlGenerator { m_url, m_visitor };
QmltcCodeGenerator::generate_callExecuteRuntimeFunction(
&code, urlGenerator.urlMethodName() + u"()",
@@ -485,7 +745,7 @@ void QmltcCompiler::compileMethod(QmltcType &current, const QQmlJSMetaMethod &m,
compiled.body = std::move(code);
compiled.type = methodType;
compiled.access = m.access();
- if (methodType != QQmlJSMetaMethod::Signal) {
+ if (methodType != QQmlJSMetaMethodType::Signal) {
compiled.declarationPrefixes << u"Q_INVOKABLE"_s;
compiled.userVisible = m.access() == QQmlJSMetaMethod::Public;
} else {
@@ -1057,7 +1317,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Object);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Object);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1115,7 +1375,7 @@ void QmltcCompiler::compileObjectBinding(QmltcType &current,
Q_ASSERT(object->baseType()->internalName() == u"QQmlComponent"_s);
if (int id = m_visitor->runtimeId(object); id >= 0) {
- QString idString = m_visitor->addressableScopes().id(object);
+ QString idString = m_visitor->addressableScopes().id(object, object);
if (idString.isEmpty())
idString = u"<unknown>"_s;
QmltcCodeGenerator::generate_setIdValue(block, u"thisContext"_s, id, objectName,
@@ -1154,8 +1414,8 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::ValueSource
- || binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::ValueSource
+ || binding.bindingType() == QQmlSA::BindingType::Interceptor);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1163,7 +1423,7 @@ void QmltcCompiler::compileValueSourceOrInterceptorBinding(QmltcType &current,
// NB: object is compiled with compileType(), here just need to use it
QSharedPointer<const QQmlJSScope> object;
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::Interceptor)
+ if (binding.bindingType() == QQmlSA::BindingType::Interceptor)
object = binding.interceptorType();
else
object = binding.valueSourceType();
@@ -1212,7 +1472,7 @@ void QmltcCompiler::compileAttachedPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::AttachedProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::AttachedProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1276,7 +1536,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::GroupProperty);
const QString &propertyName = binding.propertyName();
const QQmlJSMetaProperty property = type->property(propertyName);
@@ -1333,7 +1593,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
auto it = subbindings.begin();
Q_ASSERT(std::all_of(it, firstScript, [](const auto &x) {
- return x.bindingType() != QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() != QQmlSA::BindingType::Script;
}));
compile(it, firstScript);
it = firstScript;
@@ -1354,7 +1614,7 @@ void QmltcCompiler::compileGroupPropertyBinding(QmltcType &current,
// once the value is written back, process the script bindings
Q_ASSERT(std::all_of(it, subbindings.end(), [](const auto &x) {
- return x.bindingType() == QQmlJSMetaPropertyBinding::Script;
+ return x.bindingType() == QQmlSA::BindingType::Script;
}));
compile(it, subbindings.end());
}
@@ -1368,8 +1628,8 @@ void QmltcCompiler::compileTranslationBinding(QmltcType &current,
const QQmlJSScope::ConstPtr &type,
const BindingAccessorData &accessor)
{
- Q_ASSERT(binding.bindingType() == QQmlJSMetaPropertyBinding::Translation
- || binding.bindingType() == QQmlJSMetaPropertyBinding::TranslationById);
+ Q_ASSERT(binding.bindingType() == QQmlSA::BindingType::Translation
+ || binding.bindingType() == QQmlSA::BindingType::TranslationById);
const QString &propertyName = binding.propertyName();
@@ -1446,7 +1706,7 @@ void QmltcCompiler::compileBinding(QmltcType &current,
const auto location = binding.sourceLocation();
// make sure group property is not generalized by checking if type really has a property
// called propertyName. If not, it is probably an id.
- if (binding.bindingType() == QQmlJSMetaPropertyBinding::GroupProperty
+ if (binding.bindingType() == QQmlSA::BindingType::GroupProperty
&& type->hasProperty(propertyName)) {
qCWarning(lcQmltcCompiler)
<< QStringLiteral("Binding at line %1 column %2 is not deferred as it is a "
@@ -1493,26 +1753,32 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
accessor.name, constructFromQObject);
};
switch (binding.bindingType()) {
- case QQmlJSMetaPropertyBinding::BoolLiteral: {
+ case QQmlSA::BindingType::BoolLiteral: {
const bool value = binding.boolValue();
assignToProperty(metaProperty, value ? u"true"_s : u"false"_s);
break;
}
- case QQmlJSMetaPropertyBinding::NumberLiteral: {
+ case QQmlSA::BindingType::NumberLiteral: {
assignToProperty(metaProperty, QString::number(binding.numberValue()));
break;
}
- case QQmlJSMetaPropertyBinding::StringLiteral: {
- assignToProperty(metaProperty, QQmlJSUtils::toLiteral(binding.stringValue()));
+ case QQmlSA::BindingType::StringLiteral: {
+ QString value = QQmlJSUtils::toLiteral(binding.stringValue());
+ if (auto type = metaProperty.type()) {
+ if (type->internalName() == u"QUrl"_s) {
+ value = u"QUrl(%1)"_s.arg(value);
+ }
+ }
+ assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::RegExpLiteral: {
+ case QQmlSA::BindingType::RegExpLiteral: {
const QString value =
u"QRegularExpression(%1)"_s.arg(QQmlJSUtils::toLiteral(binding.regExpValue()));
assignToProperty(metaProperty, value);
break;
}
- case QQmlJSMetaPropertyBinding::Null: {
+ case QQmlSA::BindingType::Null: {
// poor check: null bindings are only supported for var and objects
Q_ASSERT(propertyType->isSameType(m_typeResolver->varType())
|| propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference);
@@ -1522,38 +1788,38 @@ void QmltcCompiler::compileBindingByType(QmltcType &current,
assignToProperty(metaProperty, u"QVariant::fromValue(nullptr)"_s);
break;
}
- case QQmlJSMetaPropertyBinding::Script: {
+ case QQmlSA::BindingType::Script: {
QString bindingSymbolName = type->internalName() + u'_' + propertyName + u"_binding";
bindingSymbolName.replace(u'.', u'_'); // can happen with group properties
compileScriptBinding(current, binding, bindingSymbolName, type, propertyName, propertyType,
accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Object: {
+ case QQmlSA::BindingType::Object: {
compileObjectBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Interceptor:
+ case QQmlSA::BindingType::Interceptor:
Q_FALLTHROUGH();
- case QQmlJSMetaPropertyBinding::ValueSource: {
+ case QQmlSA::BindingType::ValueSource: {
compileValueSourceOrInterceptorBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::AttachedProperty: {
+ case QQmlSA::BindingType::AttachedProperty: {
compileAttachedPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::GroupProperty: {
+ case QQmlSA::BindingType::GroupProperty: {
compileGroupPropertyBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::TranslationById:
- case QQmlJSMetaPropertyBinding::Translation: {
+ case QQmlSA::BindingType::TranslationById:
+ case QQmlSA::BindingType::Translation: {
compileTranslationBinding(current, binding, type, accessor);
break;
}
- case QQmlJSMetaPropertyBinding::Invalid: {
+ case QQmlSA::BindingType::Invalid: {
recordError(binding.sourceLocation(), u"This binding is invalid"_s);
break;
}
@@ -1617,8 +1883,11 @@ static std::pair<QQmlJSMetaProperty, int> getMetaPropertyIndex(const QQmlJSScope
// index is already added as p.index())
if (type->isSameType(owner))
return;
- if (m == QQmlJSScope::ExtensionNamespace) // extension namespace properties are ignored
+
+ // extension namespace and JavaScript properties are ignored
+ if (m == QQmlJSScope::ExtensionNamespace || m == QQmlJSScope::ExtensionJavaScript)
return;
+
index += int(type->ownProperties().size());
};
QQmlJSUtils::traverseFollowingMetaObjectHierarchy(scope, owner, increment);
@@ -1649,10 +1918,10 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
Q_ASSERT(!objectClassName_signal.isEmpty());
Q_ASSERT(!objectClassName_slot.isEmpty());
- const auto signalMethods = objectType->methods(name, QQmlJSMetaMethod::Signal);
+ const auto signalMethods = objectType->methods(name, QQmlJSMetaMethodType::Signal);
Q_ASSERT(!signalMethods.isEmpty()); // an error somewhere else
QQmlJSMetaMethod signal = signalMethods.at(0);
- Q_ASSERT(signal.methodType() == QQmlJSMetaMethod::Signal);
+ Q_ASSERT(signal.methodType() == QQmlJSMetaMethodType::Signal);
const QString signalName = signal.methodName();
const QString slotName = newSymbol(signalName + u"_slot");
@@ -1672,7 +1941,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
objectType->ownRuntimeFunctionIndex(binding.scriptIndex()),
u"this"_s, // Note: because script bindings always use current QML object scope
signalReturnType, slotParameters);
- slotMethod.type = QQmlJSMetaMethod::Slot;
+ slotMethod.type = QQmlJSMetaMethodType::Slot;
current.functions << std::move(slotMethod);
current.setComplexBindings.body << u"QObject::connect(" + This_signal + u", " + u"&"
@@ -1681,7 +1950,7 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
};
switch (binding.scriptKind()) {
- case QQmlJSMetaPropertyBinding::Script_PropertyBinding: {
+ case QQmlSA::ScriptBindingKind::PropertyBinding: {
if (!propertyType) {
recordError(binding.sourceLocation(),
u"Binding on property '" + propertyName + u"' of unknown type");
@@ -1723,19 +1992,20 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
property, valueTypeIndex, accessor.name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_SignalHandler: {
- const auto name = QQmlJSUtils::signalName(propertyName);
+ case QQmlSA::ScriptBindingKind::SignalHandler: {
+ const auto name = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(name.has_value());
compileScriptSignal(*name);
break;
}
- case QQmlJSMetaPropertyBinding::Script_ChangeHandler: {
+ case QQmlSA ::ScriptBindingKind::ChangeHandler: {
const QString objectClassName = objectType->internalName();
const QString bindingFunctorName = newSymbol(bindingSymbolName + u"Functor");
- const auto signalName = QQmlJSUtils::signalName(propertyName);
+ const auto signalName = QQmlSignalNames::handlerNameToSignalName(propertyName);
Q_ASSERT(signalName.has_value()); // an error somewhere else
- const auto actualProperty = QQmlJSUtils::changeHandlerProperty(objectType, *signalName);
+ const auto actualProperty =
+ QQmlJSUtils::propertyFromChangedHandler(objectType, propertyName);
Q_ASSERT(actualProperty.has_value()); // an error somewhere else
const auto actualPropertyType = actualProperty->type();
if (!actualPropertyType) {
@@ -1759,9 +2029,12 @@ void QmltcCompiler::compileScriptBinding(QmltcType &current,
current.children << compileScriptBindingPropertyChangeHandler(
binding, objectType, m_urlMethodName, bindingFunctorName, objectClassName);
+ current.setComplexBindings.body << u"if (!%1.contains(\"%2\"))"_s.arg(
+ current.propertyInitializer.initializedCache.name, propertyName);
+
// TODO: this could be dropped if QQmlEngine::setContextForObject() is
// done before currently generated C++ object is constructed
- current.setComplexBindings.body << bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ current.setComplexBindings.body << u" "_s + bindingSymbolName + u".reset(new QPropertyChangeHandler<"
+ bindingFunctorName + u">("
+ QmltcCodeGenerator::wrap_privateClass(accessor.name, *actualProperty)
+ u"->" + bindableString + u"().onValueChanged(" + bindingFunctorName + u"("