diff options
author | Andrei Golubev <andrei.golubev@qt.io> | 2021-11-15 17:10:31 +0100 |
---|---|---|
committer | Andrei Golubev <andrei.golubev@qt.io> | 2021-11-23 18:01:09 +0100 |
commit | 147b659a9427718c30bf3774aa1ef33804e97465 (patch) | |
tree | 18d6f50a16c05a038f47dc8077a774227f6e396a /tools/qmltc | |
parent | a12d59bfdfbd9e96849887a3a77ab195c6645d9a (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.cpp | 1 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.cpp | 87 | ||||
-rw-r--r-- | tools/qmltc/qmltccompiler.h | 21 | ||||
-rw-r--r-- | tools/qmltc/qmltccompilerpieces.h | 42 | ||||
-rw-r--r-- | tools/qmltc/qmltcvisitor.cpp | 3 |
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 ¤t, 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 ¤t, const QQmlJSMetaEnum &e) @@ -405,4 +409,87 @@ void QmltcCompiler::compileProperty(QmltcType ¤t, const QQmlJSMetaProperty current.properties.emplaceBack(underlyingType, variableName, current.cppType, p.notify()); } +void QmltcCompiler::compileBinding(QmltcType ¤t, 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 ¤t, 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 ¤t, 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 ¤t, const QQmlJSScope::ConstPtr &type); + + inline void generate_assignToProperty(QmltcType ¤t, const QQmlJSScope::ConstPtr &type, + const QQmlJSMetaProperty &p, const QString &value, + const QString &accessor); }; inline decltype(auto) @@ -152,6 +168,32 @@ QmltcCodeGenerator::generate_qmlContextSetup(QmltcType ¤t, const QQmlJSSco return QScopeGuard(generateFinalLines); } +inline void QmltcCodeGenerator::generate_assignToProperty(QmltcType ¤t, + 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 |