aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc/prototype/codegenerator.cpp
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2021-12-17 11:49:46 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2021-12-24 12:17:49 +0000
commitefc6f61149d2eb6cdfbbba80df1ac51a618227b1 (patch)
treef0e5c104c300e455d93210eee9a3c14e95c437a1 /tools/qmltc/prototype/codegenerator.cpp
parent55b9515af6d714b6ce6fb557189c8a401b3f939b (diff)
qmltc: Support different property change handlers
qmltc must be able to compile property change handlers: - for bindable properties (with no notify) - for notifiable properties (with no bindable) - for both bindable and notifiable properties (preferring notify signal) Test the aforementioned cases and some signal handling cases along the way Task-number: QTBUG-84368 Change-Id: I2cd2d0ad6407889942c806e03831dec4c7ce265a Reviewed-by: Fawzi Mohamed <fawzi.mohamed@qt.io> (cherry picked from commit 71084db1df9453aa9c657b91f2dab6766a56903b) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'tools/qmltc/prototype/codegenerator.cpp')
-rw-r--r--tools/qmltc/prototype/codegenerator.cpp75
1 files changed, 51 insertions, 24 deletions
diff --git a/tools/qmltc/prototype/codegenerator.cpp b/tools/qmltc/prototype/codegenerator.cpp
index e28754ac4d..8a304d7d72 100644
--- a/tools/qmltc/prototype/codegenerator.cpp
+++ b/tools/qmltc/prototype/codegenerator.cpp
@@ -106,7 +106,7 @@ compileMethodParameters(const QStringList &names,
if (name.isEmpty() && allowUnnamed)
name = u"unnamed_" + QString::number(i);
paramList.emplaceBack(
- QQmlJSAotVariable { types[i]->augmentedInternalName(), names[i], QString() });
+ QQmlJSAotVariable { types[i]->augmentedInternalName(), name, QString() });
}
return paramList;
}
@@ -1513,6 +1513,21 @@ static QQmlJSAotObject compileScriptBindingPropertyChangeHandler(
return bindingFunctor;
}
+static std::optional<QQmlJSMetaProperty>
+propertyForChangeHandler(const QQmlJSScope::ConstPtr &scope, QString name)
+{
+ if (!name.endsWith(QLatin1String("Changed")))
+ return {};
+ constexpr int length = int(sizeof("Changed") / sizeof(char)) - 1;
+ name.chop(length);
+ auto p = scope->property(name);
+ const bool isBindable = !p.bindable().isEmpty();
+ const bool canNotify = !p.notify().isEmpty();
+ if (p.isValid() && (isBindable || canNotify))
+ return p;
+ return {};
+}
+
void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::Binding &binding,
const QString &bindingSymbolName,
const CodeGenObject &object, const QString &propertyName,
@@ -1523,25 +1538,32 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
// returns signal by name. signal exists if std::optional<> has value
const auto signalByName = [&](const QString &name) -> std::optional<QQmlJSMetaMethod> {
- const QList<QQmlJSMetaMethod> signalMethods = objectType->methods(name);
+ const auto signalMethods = objectType->methods(name, QQmlJSMetaMethod::Signal);
if (signalMethods.isEmpty())
return {};
- // TODO: no clue how to handle redefinition, so just record an error
- if (signalMethods.size() != 1) {
- recordError(binding.location, u"Binding on redefined signal '" + name + u"'");
- return {};
- }
+ // if (signalMethods.size() != 1) return {}; // error somewhere else
QQmlJSMetaMethod s = signalMethods.at(0);
- if (s.methodType() != QQmlJSMetaMethod::Signal)
- return {};
+ Q_ASSERT(s.methodType() == QQmlJSMetaMethod::Signal);
return s;
};
+ const auto resolveSignal = [&](const QString &name) -> std::optional<QQmlJSMetaMethod> {
+ auto signal = signalByName(name);
+ if (signal) // found signal
+ return signal;
+ auto property = propertyForChangeHandler(objectType, name);
+ if (!property) // nor signal nor property change handler
+ return {}; // error somewhere else
+ if (auto notify = property->notify(); !notify.isEmpty())
+ return signalByName(notify);
+ return {};
+ };
+
// TODO: add Invalid case which would reject garbage handlers
enum BindingKind {
JustProperty, // is a binding on property
- SignalHandler, // is a slot related to custom signal
- PropertyChangeHandler, // is a slot related to property's "changed" signal
+ SignalHandler, // is a slot related to some signal
+ BindablePropertyChangeHandler, // is a slot related to property's bindable
};
// these only make sense when binding is on signal handler
@@ -1556,17 +1578,22 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
if (auto name = QQmlJSUtils::signalName(propertyName); name.has_value())
signalName = *name;
- std::optional<QQmlJSMetaMethod> possibleSignal = signalByName(signalName);
+ std::optional<QQmlJSMetaMethod> possibleSignal = resolveSignal(signalName);
if (possibleSignal) { // signal with signalName exists
QQmlJSMetaMethod s = possibleSignal.value();
+
+ // Note: update signal name since it may be different from the one
+ // used to resolve a signal
+ signalName = s.methodName();
+
const auto paramNames = s.parameterNames();
const auto paramTypes = s.parameterTypes();
Q_ASSERT(paramNames.size() == paramTypes.size());
- slotParameters = compileMethodParameters(paramNames, paramTypes);
+ slotParameters = compileMethodParameters(paramNames, paramTypes, true);
signalReturnType = figureReturnType(s);
return BindingKind::SignalHandler;
} else if (propertyName.endsWith(u"Changed"_qs)) {
- return BindingKind::PropertyChangeHandler;
+ return BindingKind::BindablePropertyChangeHandler;
}
return BindingKind::JustProperty;
}();
@@ -1657,9 +1684,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
+ u");";
break;
}
- // TODO: fix code generation for property change handlers - it is broken for
- // many cases. see QTBUG-91956
- case BindingKind::PropertyChangeHandler: {
+ case BindingKind::BindablePropertyChangeHandler: {
const QString objectClassName = objectType->internalName();
const QString bindingFunctorName = makeGensym(bindingSymbolName + u"Functor");
@@ -1670,7 +1695,7 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
actualPropertyName = match.captured(1);
actualPropertyName[0] = actualPropertyName.at(0).toLower();
} else {
- recordError(binding.location, u"Missing parameter name in on*Changed handler"_qs);
+ // an error somewhere else
return;
}
if (!objectType->hasProperty(actualPropertyName)) {
@@ -1684,12 +1709,6 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
u"Binding on property '" + actualPropertyName + u"' of unknown type");
return;
}
- QString typeOfQmlBinding =
- u"std::unique_ptr<QPropertyChangeHandler<" + bindingFunctorName + u">>";
-
- current.children << compileScriptBindingPropertyChangeHandler(
- m_doc, binding, object.irObject, m_urlMethod, bindingFunctorName, objectClassName,
- slotParameters);
QString bindableString = actualProperty.bindable();
if (bindableString.isEmpty()) { // TODO: always should come from prop.bindalbe()
@@ -1699,6 +1718,14 @@ void CodeGenerator::compileScriptBinding(QQmlJSAotObject &current, const QmlIR::
u"supported");
break;
}
+
+ QString typeOfQmlBinding =
+ u"std::unique_ptr<QPropertyChangeHandler<" + bindingFunctorName + u">>";
+
+ current.children << compileScriptBindingPropertyChangeHandler(
+ m_doc, binding, object.irObject, m_urlMethod, bindingFunctorName, objectClassName,
+ slotParameters);
+
// TODO: the finalize.lastLines has to be used here -- somehow if property gets a binding,
// the change handler is not updated?!