aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qmltc
diff options
context:
space:
mode:
authorAndrei Golubev <andrei.golubev@qt.io>2021-11-15 17:10:31 +0100
committerAndrei Golubev <andrei.golubev@qt.io>2021-11-23 18:01:09 +0100
commit147b659a9427718c30bf3774aa1ef33804e97465 (patch)
tree18d6f50a16c05a038f47dc8077a774227f6e396a /tools/qmltc
parenta12d59bfdfbd9e96849887a3a77ab195c6645d9a (diff)
qmltc: Support literal bindings
Ignore type conversions for now. Expect C++ to handle them magically for us. The simple binding type dependent conversions that are performed already should suffice Task-number: QTBUG-84368 Change-Id: I62bd36ccf6c60fd62c2a50b6e011c637c5bcfbce Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tools/qmltc')
-rw-r--r--tools/qmltc/main.cpp1
-rw-r--r--tools/qmltc/qmltccompiler.cpp87
-rw-r--r--tools/qmltc/qmltccompiler.h21
-rw-r--r--tools/qmltc/qmltccompilerpieces.h42
-rw-r--r--tools/qmltc/qmltcvisitor.cpp3
5 files changed, 153 insertions, 1 deletions
diff --git a/tools/qmltc/main.cpp b/tools/qmltc/main.cpp
index 1b48623e2d..7167e03962 100644
--- a/tools/qmltc/main.cpp
+++ b/tools/qmltc/main.cpp
@@ -46,6 +46,7 @@
void setupLogger(QQmlJSLogger &logger) // prepare logger to work with compiler
{
+ // TODO: support object bindings and change to setCategoryLevel(QtInfoMsg)
logger.setCategoryError(Log_Compiler, true);
}
diff --git a/tools/qmltc/qmltccompiler.cpp b/tools/qmltc/qmltccompiler.cpp
index a6671423b7..65029dbd32 100644
--- a/tools/qmltc/qmltccompiler.cpp
+++ b/tools/qmltc/qmltccompiler.cpp
@@ -251,6 +251,10 @@ void QmltcCompiler::compileType(QmltcType &current, const QQmlJSScope::ConstPtr
compileProperty(current, p, type);
}
}
+
+ const QMultiHash<QString, QQmlJSMetaPropertyBinding> allBindings = type->ownPropertyBindings();
+ for (auto it = allBindings.begin(); it != allBindings.end(); ++it)
+ compileBinding(current, it.value(), type, BindingAccessorData { type });
}
void QmltcCompiler::compileEnum(QmltcType &current, const QQmlJSMetaEnum &e)
@@ -405,4 +409,87 @@ void QmltcCompiler::compileProperty(QmltcType &current, const QQmlJSMetaProperty
current.properties.emplaceBack(underlyingType, variableName, current.cppType, p.notify());
}
+void QmltcCompiler::compileBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type,
+ const BindingAccessorData &accessor)
+{
+ Q_UNUSED(current);
+ Q_UNUSED(accessor);
+ QString propertyName = binding.propertyName();
+ if (propertyName.isEmpty()) {
+ // if empty, try default property
+ for (QQmlJSScope::ConstPtr t = type->baseType(); t && propertyName.isEmpty();
+ t = t->baseType()) {
+ propertyName = t->defaultPropertyName();
+ }
+ }
+ Q_ASSERT(!propertyName.isEmpty());
+ QQmlJSMetaProperty p = type->property(propertyName);
+ Q_ASSERT(p.isValid());
+ QQmlJSScope::ConstPtr propertyType = p.type();
+ Q_ASSERT(propertyType);
+
+ // NB: we assume here that QmltcVisitor took care of type mismatches and
+ // other errors, so the compiler just needs to add correct instructions,
+ // without if-checking every type
+
+ QmltcCodeGenerator generator {
+ QQmlJSScope::ConstPtr()
+ }; // NB: we don't need document root here
+
+ switch (binding.bindingType()) {
+ case QQmlJSMetaPropertyBinding::BoolLiteral: {
+ const bool value = binding.literalValue().toBool();
+ generator.generate_assignToProperty(current, type, p, value ? u"true"_qs : u"false"_qs,
+ accessor.name);
+ break;
+ }
+ case QQmlJSMetaPropertyBinding::NumberLiteral: {
+ const QString value = QString::number(binding.literalValue().toDouble());
+ generator.generate_assignToProperty(current, type, p, value, accessor.name);
+ break;
+ }
+ case QQmlJSMetaPropertyBinding::StringLiteral: {
+ const QString value = binding.literalValue().toString();
+ generator.generate_assignToProperty(
+ current, type, p, QmltcCodeGenerator::toStringLiteral(value), accessor.name);
+ break;
+ }
+ case QQmlJSMetaPropertyBinding::Null: {
+ // poor check: null bindings are only supported for var and objects
+ if (propertyType != m_typeResolver->varType()
+ && propertyType->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) {
+ // TODO: this should really be done before the compiler here
+ recordError(binding.sourceLocation(),
+ u"Cannot assign null to incompatible property"_qs);
+ } else if (propertyType->accessSemantics() == QQmlJSScope::AccessSemantics::Reference) {
+ generator.generate_assignToProperty(current, type, p, u"nullptr"_qs, accessor.name);
+ } else {
+ generator.generate_assignToProperty(current, type, p,
+ u"QVariant::fromValue(nullptr)"_qs, accessor.name);
+ }
+ break;
+ }
+
+ // case QQmlJSMetaPropertyBinding::RegExpLiteral:
+ // case QQmlJSMetaPropertyBinding::Translation:
+ // case QQmlJSMetaPropertyBinding::TranslationById:
+ // case QQmlJSMetaPropertyBinding::Script:
+ // case QQmlJSMetaPropertyBinding::Object:
+ // case QQmlJSMetaPropertyBinding::Interceptor:
+ // case QQmlJSMetaPropertyBinding::ValueSource:
+ // case QQmlJSMetaPropertyBinding::AttachedProperty:
+ // case QQmlJSMetaPropertyBinding::GroupProperty:
+ case QQmlJSMetaPropertyBinding::Invalid: {
+ Q_UNREACHABLE(); // this is truly something that must not happen here
+ break;
+ }
+ default: {
+ m_logger->logWarning(u"Binding type is not supported (yet)"_qs, Log_Compiler,
+ binding.sourceLocation());
+ break;
+ }
+ }
+}
+
QT_END_NAMESPACE
diff --git a/tools/qmltc/qmltccompiler.h b/tools/qmltc/qmltccompiler.h
index c934dfffac..0574b03088 100644
--- a/tools/qmltc/qmltccompiler.h
+++ b/tools/qmltc/qmltccompiler.h
@@ -70,6 +70,27 @@ private:
void compileProperty(QmltcType &current, const QQmlJSMetaProperty &p,
const QQmlJSScope::ConstPtr &owner);
+ /*!
+ \internal
+
+ Helper structure that holds the information necessary for most bindings,
+ such as accessor name, which is used to reference the properties. For
+ example:
+ > (accessor.name)->(propertyName) results in "this->myProperty"
+
+ This data is also used in more advanced scenarios by attached and
+ grouped properties
+ */
+ struct BindingAccessorData
+ {
+ QQmlJSScope::ConstPtr scope; // usually the current type
+ QString name = u"this"_qs;
+ QString propertyName = QString();
+ bool isValueType = false;
+ };
+ void compileBinding(QmltcType &current, const QQmlJSMetaPropertyBinding &binding,
+ const QQmlJSScope::ConstPtr &type, const BindingAccessorData &accessor);
+
bool hasErrors() const { return m_logger->hasErrors() || m_logger->hasWarnings(); }
void recordError(const QQmlJS::SourceLocation &location, const QString &message,
QQmlJSLoggerCategory category = Log_Compiler)
diff --git a/tools/qmltc/qmltccompilerpieces.h b/tools/qmltc/qmltccompilerpieces.h
index 94d14a3abd..bdb732feb5 100644
--- a/tools/qmltc/qmltccompilerpieces.h
+++ b/tools/qmltc/qmltccompilerpieces.h
@@ -51,6 +51,18 @@ struct QmltcCodeGenerator
QQmlJSScope::ConstPtr documentRoot;
+ static QString escapeString(QString s)
+ {
+ return s.replace(u"\\"_qs, u"\\\\"_qs)
+ .replace(u"\""_qs, u"\\\""_qs)
+ .replace(u"\n"_qs, u"\\n"_qs);
+ }
+
+ static QString toStringLiteral(const QString &s)
+ {
+ return u"QStringLiteral(\"" + escapeString(s) + u"\")";
+ }
+
/*!
\internal
@@ -61,6 +73,10 @@ struct QmltcCodeGenerator
*/
[[nodiscard]] inline decltype(auto) generate_qmlContextSetup(QmltcType &current,
const QQmlJSScope::ConstPtr &type);
+
+ inline void generate_assignToProperty(QmltcType &current, const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p, const QString &value,
+ const QString &accessor);
};
inline decltype(auto)
@@ -152,6 +168,32 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType &current, const QQmlJSSco
return QScopeGuard(generateFinalLines);
}
+inline void QmltcCodeGenerator::generate_assignToProperty(QmltcType &current,
+ const QQmlJSScope::ConstPtr &type,
+ const QQmlJSMetaProperty &p,
+ const QString &value,
+ const QString &accessor)
+{
+ Q_ASSERT(p.isValid());
+ Q_ASSERT(!p.isList()); // TODO: check this one
+ Q_ASSERT(!p.isPrivate());
+ Q_UNUSED(type);
+
+ QStringList code;
+ code.reserve(6); // always should be enough
+
+ if (!p.isWritable()) {
+ Q_ASSERT(type->hasOwnProperty(p.propertyName())); // otherwise we cannot set it
+ code << u"%1->m_%2 = %3;"_qs.arg(accessor, p.propertyName(), value);
+ } else {
+ const QString setter = p.write();
+ Q_ASSERT(!setter.isEmpty()); // in practice could be empty, but ignore it for now
+ code << u"%1->%2(%3);"_qs.arg(accessor, setter, value);
+ }
+
+ current.init.body += code;
+}
+
QT_END_NAMESPACE
#endif // QMLTCCOMPILERPIECES_H
diff --git a/tools/qmltc/qmltcvisitor.cpp b/tools/qmltc/qmltcvisitor.cpp
index 30b28653c1..7d9062a4db 100644
--- a/tools/qmltc/qmltcvisitor.cpp
+++ b/tools/qmltc/qmltcvisitor.cpp
@@ -190,7 +190,8 @@ bool QmltcVisitor::visit(QQmlJS::AST::UiPublicMember *publicMember)
QQmlJSMetaProperty prop = m_currentScope->ownProperty(name);
const QString nameWithUppercase = name[0].toUpper() + name.sliced(1);
prop.setRead(name);
- prop.setWrite(u"set" + nameWithUppercase);
+ if (prop.isWritable())
+ prop.setWrite(u"set" + nameWithUppercase);
prop.setBindable(u"bindable" + nameWithUppercase);
prop.setNotify(name + u"Changed");
// also check that notify is already a method of m_currentScope