Refactor QQmlJSMetaPropertyBinding
- Store the "binding content", i.e. the literal, object, interceptor or value source in a std::variant. This saves space compared to the previous approach (where we had individual fields), and also helps with type-safety. We also get rid of m_bindingType, which can now be obtained via BindingType(m_bindingContent.index()), as long as we keep the types in the variant in the correct order. - Remove a few methods that were not used anywhere. Those can be brought back once we actually need them. - Removed the setLiteral method in lieu of type-safe setX methods where X is one of the literal types. QQmlJSImportVisitor has been refactored to make use of this, and its parseLiteralBinding method has been changed into a parseLiteralOrScriptBinding method. This simplifies the control flow, and ensures that we always add the parsed binding. - Literals no longer store the literal type (as in, the actual QQmlJSScope pointer) themselves. Instead, literalType takes a pointer to a QQmlJSTypeResolver, and we use that one to resolve the type on demand. Change-Id: I0612d49f2f46fec0fa90f0f5047d8c9f831214ef Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
This commit is contained in:
parent
9575b54a4e
commit
e88318802d
|
@ -17,7 +17,7 @@ qt_internal_add_module(QmlCompilerPrivate
|
|||
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
|
||||
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
|
||||
qqmljslogger_p.h qqmljslogger.cpp
|
||||
qqmljsmetatypes_p.h
|
||||
qqmljsmetatypes_p.h qqmljsmetatypes.cpp
|
||||
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
|
||||
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
|
||||
qqmljsscope.cpp qqmljsscope_p.h
|
||||
|
|
|
@ -1261,7 +1261,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
|
|||
if (publicMember->isRequired())
|
||||
m_currentScope->setPropertyLocallyRequired(prop.propertyName(), true);
|
||||
|
||||
parseLiteralBinding(publicMember->name.toString(), publicMember->statement);
|
||||
parseLiteralOrScriptBinding(publicMember->name.toString(), publicMember->statement);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -1385,12 +1385,9 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
|
|||
|
||||
|
||||
// ### TODO: add warning about suspicious translation binding when returning false?
|
||||
static std::optional<QQmlJSMetaPropertyBinding>
|
||||
handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
|
||||
const QQmlJSImporter::ImportedTypes &rootScopeImports,
|
||||
const QQmlJS::SourceLocation &location)
|
||||
void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base,
|
||||
QQmlJS::AST::ArgumentList *args)
|
||||
{
|
||||
std::optional<QQmlJSMetaPropertyBinding> maybeBinding = std::nullopt;
|
||||
QStringView mainString;
|
||||
auto registerMainString = [&](QStringView string) {
|
||||
mainString = string;
|
||||
|
@ -1399,116 +1396,75 @@ handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
|
|||
auto discardCommentString = [](QStringView) {return -1;};
|
||||
auto finalizeBinding = [&](QV4::CompiledData::Binding::ValueType type,
|
||||
QV4::CompiledData::TranslationData) {
|
||||
QQmlJSMetaPropertyBinding binding(location);
|
||||
if (type == QV4::CompiledData::Binding::Type_Translation) {
|
||||
binding.setTranslation(mainString);
|
||||
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
|
||||
binding.setTranslationId(mainString);
|
||||
} else {
|
||||
binding.setLiteral(
|
||||
QQmlJSMetaPropertyBinding::StringLiteral, u"string"_qs,
|
||||
mainString.toString(), rootScopeImports[u"string"_qs].scope);
|
||||
binding.setStringLiteral(mainString);
|
||||
}
|
||||
maybeBinding = binding;
|
||||
};
|
||||
QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString, discardCommentString, finalizeBinding);
|
||||
return maybeBinding;
|
||||
}
|
||||
|
||||
bool QQmlJSImportVisitor::parseLiteralBinding(const QString name,
|
||||
QQmlJSImportVisitor::LiteralOrScriptParseResult QQmlJSImportVisitor::parseLiteralOrScriptBinding(const QString name,
|
||||
const QQmlJS::AST::Statement *statement)
|
||||
{
|
||||
const auto *exprStatement = cast<const ExpressionStatement *>(statement);
|
||||
|
||||
if (exprStatement == nullptr)
|
||||
return false;
|
||||
|
||||
QVariant value;
|
||||
QString literalType;
|
||||
QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid;
|
||||
return LiteralOrScriptParseResult::Invalid;
|
||||
|
||||
auto expr = exprStatement->expression;
|
||||
QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
|
||||
|
||||
switch (expr->kind) {
|
||||
case Node::Kind_TrueLiteral:
|
||||
value = true;
|
||||
literalType = u"bool"_qs;
|
||||
bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
|
||||
binding.setBoolLiteral(true);
|
||||
break;
|
||||
case Node::Kind_FalseLiteral:
|
||||
value = false;
|
||||
literalType = u"bool"_qs;
|
||||
bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
|
||||
binding.setBoolLiteral(false);
|
||||
break;
|
||||
case Node::Kind_NullExpression:
|
||||
// Note: because we set value to nullptr, Null binding returns false in
|
||||
// QQmlJSMetaPropertyBinding::hasLiteral()
|
||||
value = QVariant::fromValue(nullptr);
|
||||
Q_ASSERT(value.isNull());
|
||||
literalType = u"$internal$.std::nullptr_t"_qs;
|
||||
bindingType = QQmlJSMetaPropertyBinding::Null;
|
||||
binding.setNullLiteral();
|
||||
break;
|
||||
case Node::Kind_NumericLiteral:
|
||||
literalType = u"double"_qs;
|
||||
value = cast<NumericLiteral *>(expr)->value;
|
||||
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
|
||||
binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
|
||||
break;
|
||||
case Node::Kind_StringLiteral:
|
||||
literalType = u"string"_qs;
|
||||
value = cast<StringLiteral *>(expr)->value.toString();
|
||||
bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
|
||||
binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
|
||||
break;
|
||||
case Node::Kind_RegExpLiteral:
|
||||
literalType = u"regexp"_qs;
|
||||
value = cast<RegExpLiteral *>(expr)->pattern.toString();
|
||||
bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral;
|
||||
binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
|
||||
break;
|
||||
case Node::Kind_TemplateLiteral: {
|
||||
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
|
||||
Q_ASSERT(templateLit);
|
||||
value = templateLit->value.toString();
|
||||
if (templateLit->hasNoSubstitution) {
|
||||
literalType = u"string"_qs;
|
||||
bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
|
||||
binding.setStringLiteral(templateLit->value);
|
||||
} else {
|
||||
bindingType = QQmlJSMetaPropertyBinding::Script;
|
||||
binding.setScriptBinding();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
|
||||
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
|
||||
literalType = u"double"_qs;
|
||||
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
|
||||
value = -lit->value;
|
||||
}
|
||||
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
|
||||
binding.setNumberLiteral(-lit->value);
|
||||
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
|
||||
if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
|
||||
if (auto translationBindingOpt = handleTranslationBinding(
|
||||
base->name, call->arguments, m_rootScopeImports,
|
||||
expr->firstSourceLocation())) {
|
||||
auto translationBinding = translationBindingOpt.value();
|
||||
translationBinding.setPropertyName(name);
|
||||
m_currentScope->addOwnPropertyBinding(translationBinding);
|
||||
if (translationBinding.bindingType() == QQmlJSMetaPropertyBinding::BindingType::StringLiteral)
|
||||
m_literalScopesToCheck << m_currentScope;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
|
||||
handleTranslationBinding(binding, base->name, call->arguments);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!QQmlJSMetaPropertyBinding::isLiteralBinding(bindingType))
|
||||
return false;
|
||||
Q_ASSERT(m_rootScopeImports.contains(literalType)); // built-ins must contain support for all literal bindings
|
||||
|
||||
QQmlJSMetaPropertyBinding binding(exprStatement->expression->firstSourceLocation(), name);
|
||||
binding.setLiteral(bindingType, literalType, value, m_rootScopeImports[literalType].scope);
|
||||
|
||||
m_currentScope->addOwnPropertyBinding(binding);
|
||||
|
||||
if (binding.isValid()) // always add the binding to the scope, even if it's not a literal one
|
||||
m_currentScope->addOwnPropertyBinding(binding);
|
||||
else
|
||||
return LiteralOrScriptParseResult::Invalid;
|
||||
if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
|
||||
return LiteralOrScriptParseResult::Script;
|
||||
m_literalScopesToCheck << m_currentScope;
|
||||
return true;
|
||||
return LiteralOrScriptParseResult::Literal;
|
||||
}
|
||||
|
||||
void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
|
||||
|
@ -1578,11 +1534,7 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
|
|||
if (!signal.has_value()) {
|
||||
m_propertyBindings[m_currentScope].append(
|
||||
{ m_savedBindingOuterScope, group->firstSourceLocation(), name.toString() });
|
||||
if (!parseLiteralBinding(name.toString(), scriptBinding->statement)) {
|
||||
QQmlJSMetaPropertyBinding binding(group->firstSourceLocation(), name.toString());
|
||||
// TODO: Actually store the value
|
||||
m_savedBindingOuterScope->addOwnPropertyBinding(binding);
|
||||
}
|
||||
parseLiteralOrScriptBinding(name.toString(), scriptBinding->statement);
|
||||
} else {
|
||||
const auto statement = scriptBinding->statement;
|
||||
QStringList signalParameters;
|
||||
|
|
|
@ -203,7 +203,8 @@ protected:
|
|||
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
|
||||
|
||||
QQmlJSLogger *m_logger;
|
||||
bool parseLiteralBinding(const QString name, const QQmlJS::AST::Statement *statement);
|
||||
enum class LiteralOrScriptParseResult { Invalid, Script, Literal };
|
||||
LiteralOrScriptParseResult parseLiteralOrScriptBinding(const QString name, const QQmlJS::AST::Statement *statement);
|
||||
|
||||
// Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
|
||||
QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the tools applications of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <private/qqmljsmetatypes_p.h>
|
||||
#include <private/qqmljstyperesolver_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
A binding is valid when it has both a target (m_propertyName is set)
|
||||
and some content set (m_bindingType != Invalid).
|
||||
*/
|
||||
bool QQmlJSMetaPropertyBinding::isValid() const { return !m_propertyName.isEmpty() && bindingType() != Invalid; }
|
||||
|
||||
QString QQmlJSMetaPropertyBinding::literalTypeName() const
|
||||
{
|
||||
if (std::holds_alternative<Content::BoolLiteral>(m_bindingContent))
|
||||
return QLatin1String("bool");
|
||||
else if (std::holds_alternative<Content::NumberLiteral>(m_bindingContent))
|
||||
return QLatin1String("double");
|
||||
else if (std::holds_alternative<Content::StringLiteral>(m_bindingContent))
|
||||
return QLatin1String("string");
|
||||
else if (std::holds_alternative<Content::RegexpLiteral>(m_bindingContent))
|
||||
return QLatin1String("regexp");
|
||||
else if (std::holds_alternative<Content::Null>(m_bindingContent))
|
||||
return QLatin1String("$internal$.std::nullptr_t");
|
||||
return {};
|
||||
}
|
||||
|
||||
bool QQmlJSMetaPropertyBinding::boolValue() const
|
||||
{
|
||||
if (auto boolLit = std::get_if<Content::BoolLiteral>(&m_bindingContent))
|
||||
return boolLit->value;
|
||||
// warn
|
||||
return false;
|
||||
}
|
||||
|
||||
double QQmlJSMetaPropertyBinding::numberValue() const
|
||||
{
|
||||
if (auto numberLit = std::get_if<Content::NumberLiteral>(&m_bindingContent))
|
||||
return numberLit->value;
|
||||
// warn
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString QQmlJSMetaPropertyBinding::stringValue() const
|
||||
{
|
||||
if (auto stringLiteral = std::get_if<Content::StringLiteral>(&m_bindingContent))
|
||||
return stringLiteral->value;
|
||||
// warn
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Uses \a resolver to return the correct type for the stored literal
|
||||
and a null scope pointer if the binding does not contain a literal
|
||||
*/
|
||||
QSharedPointer<const QQmlJSScope> QQmlJSMetaPropertyBinding::literalType(const QQmlJSTypeResolver *resolver) const
|
||||
{
|
||||
Q_ASSERT(resolver);
|
||||
switch (bindingType()) {
|
||||
case QQmlJSMetaPropertyBinding::BoolLiteral:
|
||||
return resolver->boolType();
|
||||
case QQmlJSMetaPropertyBinding::NumberLiteral:
|
||||
return resolver->typeForName(QLatin1String("double"));
|
||||
case QQmlJSMetaPropertyBinding::Translation: // translations are strings
|
||||
case QQmlJSMetaPropertyBinding::TranslationById:
|
||||
case QQmlJSMetaPropertyBinding::StringLiteral:
|
||||
return resolver->stringType();
|
||||
case QQmlJSMetaPropertyBinding::RegExpLiteral:
|
||||
return resolver->typeForName(QLatin1String("regexp"));
|
||||
case QQmlJSMetaPropertyBinding::Null:
|
||||
return resolver->nullType();
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
return {}; // needed on some compilers which do not see that every case in the switch returns
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -59,6 +59,7 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQmlJSTypeResolver;
|
||||
class QQmlJSScope;
|
||||
class QQmlJSMetaEnum
|
||||
{
|
||||
|
@ -390,38 +391,98 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
|
||||
// needs to be kept in sync with the BindingType enum
|
||||
struct Content {
|
||||
using Invalid = std::monostate;
|
||||
struct BoolLiteral {
|
||||
bool value;
|
||||
friend bool operator==(BoolLiteral a, BoolLiteral b) { return a.value == b.value; }
|
||||
friend bool operator!=(BoolLiteral a, BoolLiteral b) { return !(a == b); }
|
||||
};
|
||||
struct NumberLiteral {
|
||||
friend bool operator==(NumberLiteral a, NumberLiteral b) { return a.value == b.value; }
|
||||
friend bool operator!=(NumberLiteral a, NumberLiteral b) { return !(a == b); }
|
||||
double value; // ### TODO: int?
|
||||
};
|
||||
struct StringLiteral {
|
||||
friend bool operator==(StringLiteral a, StringLiteral b) { return a.value == b.value; }
|
||||
friend bool operator!=(StringLiteral a, StringLiteral b) { return !(a == b); }
|
||||
QString value;
|
||||
};
|
||||
struct RegexpLiteral {
|
||||
friend bool operator==(RegexpLiteral a, RegexpLiteral b) { return a.value == b.value; }
|
||||
friend bool operator!=(RegexpLiteral a, RegexpLiteral b) { return !(a == b); }
|
||||
QString value;
|
||||
};
|
||||
struct Null {
|
||||
friend bool operator==(Null , Null ) { return true; }
|
||||
friend bool operator!=(Null a, Null b) { return !(a == b); }
|
||||
};
|
||||
struct TranslationString {
|
||||
friend bool operator==(TranslationString a, TranslationString b) { return a.value == b.value; }
|
||||
friend bool operator!=(TranslationString a, TranslationString b) { return !(a == b); }
|
||||
QString value;
|
||||
};
|
||||
struct TranslationById {
|
||||
friend bool operator==(TranslationById a, TranslationById b) { return a.value == b.value; }
|
||||
friend bool operator!=(TranslationById a, TranslationById b) { return !(a == b); }
|
||||
QString value;
|
||||
};
|
||||
struct Script {
|
||||
friend bool operator==(Script , Script ) { return true; }
|
||||
friend bool operator!=(Script a, Script b) { return !(a == b); }
|
||||
};
|
||||
struct Object {
|
||||
friend bool operator==(Object a, Object b) { return a.value == b.value && a.typeName == b.typeName; }
|
||||
friend bool operator!=(Object a, Object b) { return !(a == b); }
|
||||
QString typeName;
|
||||
QWeakPointer<const QQmlJSScope> value;
|
||||
};
|
||||
struct Interceptor {
|
||||
friend bool operator==(Interceptor a, Interceptor b)
|
||||
{
|
||||
return a.interceptor == b.interceptor && a.interceptorTypeName == b.interceptorTypeName;
|
||||
}
|
||||
friend bool operator!=(Interceptor a, Interceptor b) { return !(a == b); }
|
||||
QString interceptorTypeName;
|
||||
QWeakPointer<const QQmlJSScope> interceptor;
|
||||
};
|
||||
struct ValueSource {
|
||||
friend bool operator==(ValueSource a, ValueSource b)
|
||||
{
|
||||
return a.valueSource == b.valueSource && a.valueSourceTypeName == b.valueSourceTypeName;
|
||||
}
|
||||
friend bool operator!=(ValueSource a, ValueSource b) { return !(a == b); }
|
||||
QString valueSourceTypeName;
|
||||
QWeakPointer<const QQmlJSScope> valueSource;
|
||||
};
|
||||
struct AttachedProperty {
|
||||
friend bool operator==(AttachedProperty , AttachedProperty ) { return true; }
|
||||
friend bool operator!=(AttachedProperty a, AttachedProperty b) { return !(a == b); }
|
||||
};
|
||||
struct GroupProperty {
|
||||
friend bool operator==(GroupProperty , GroupProperty ) { return true; }
|
||||
friend bool operator!=(GroupProperty a, GroupProperty b) { return !(a == b); }
|
||||
};
|
||||
using type = std::variant<Invalid, BoolLiteral, NumberLiteral, StringLiteral,
|
||||
RegexpLiteral, Null, TranslationString,
|
||||
TranslationById, Script, Object, Interceptor,
|
||||
ValueSource, AttachedProperty, GroupProperty
|
||||
>;
|
||||
};
|
||||
using BindingContent = Content::type;
|
||||
|
||||
QQmlJS::SourceLocation m_sourceLocation;
|
||||
QString m_propertyName; // TODO: this is a debug-only information
|
||||
BindingType m_bindingType = BindingType::Invalid;
|
||||
BindingContent m_bindingContent;
|
||||
|
||||
// TODO: the below should really be put into a union (of sorts). despite the
|
||||
// data being overlapping for different things (which for now is just
|
||||
// ignored), we would still only have one kind of information stored in a
|
||||
// binding. separating the non-overlapping bits would indicate what we
|
||||
// require for each binding type more clearly
|
||||
|
||||
//union {
|
||||
QString m_translationString;
|
||||
QString m_translationId;
|
||||
QVariant m_literalValue; // constant in literal (or null) expression
|
||||
//};
|
||||
|
||||
QWeakPointer<const QQmlJSScope> m_value; // object type of Object binding *OR* a literal type
|
||||
QString m_valueTypeName;
|
||||
|
||||
QWeakPointer<const QQmlJSScope> m_interceptor; // QQmlPropertyValueInterceptor derived type
|
||||
QString m_interceptorTypeName;
|
||||
|
||||
QWeakPointer<const QQmlJSScope> m_valueSource; // QQmlPropertyValueSource derived type
|
||||
QString m_valueSourceTypeName;
|
||||
|
||||
void setBindingTypeOnce(BindingType type)
|
||||
void ensureSetBindingTypeOnce()
|
||||
{
|
||||
Q_ASSERT(m_bindingType == BindingType::Invalid);
|
||||
m_bindingType = type;
|
||||
Q_ASSERT(bindingType() == BindingType::Invalid);
|
||||
}
|
||||
|
||||
bool isLiteralBinding() const { return isLiteralBinding(m_bindingType); }
|
||||
bool isLiteralBinding() const { return isLiteralBinding(bindingType()); }
|
||||
|
||||
|
||||
public:
|
||||
|
@ -448,90 +509,124 @@ public:
|
|||
|
||||
const QQmlJS::SourceLocation &sourceLocation() const { return m_sourceLocation; }
|
||||
|
||||
BindingType bindingType() const { return m_bindingType; }
|
||||
BindingType bindingType() const { return BindingType(m_bindingContent.index()); }
|
||||
|
||||
bool isValid() const { return !m_propertyName.isEmpty(); }
|
||||
bool isValid() const;
|
||||
|
||||
void setLiteral(BindingType kind, const QString &typeName, const QVariant &value,
|
||||
const QSharedPointer<const QQmlJSScope> &type)
|
||||
void setStringLiteral(QAnyStringView value)
|
||||
{
|
||||
Q_ASSERT(isLiteralBinding(kind));
|
||||
setBindingTypeOnce(kind);
|
||||
m_value = type;
|
||||
m_valueTypeName = typeName;
|
||||
m_literalValue = value;
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::StringLiteral { value.toString() };
|
||||
}
|
||||
|
||||
void setScriptBinding()
|
||||
{
|
||||
// ### TODO: this does not allow us to do anything interesting with the binding
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::Script {};
|
||||
}
|
||||
|
||||
void setBoolLiteral(bool value)
|
||||
{
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::BoolLiteral { value };
|
||||
}
|
||||
|
||||
void setNullLiteral()
|
||||
{
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::Null {};
|
||||
}
|
||||
|
||||
void setNumberLiteral(double value)
|
||||
{
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::NumberLiteral { value };
|
||||
}
|
||||
|
||||
void setRegexpLiteral(QAnyStringView value)
|
||||
{
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::RegexpLiteral { value.toString() };
|
||||
}
|
||||
|
||||
// ### TODO: we might need comment and translation number at some point
|
||||
void setTranslation(QStringView translation)
|
||||
{
|
||||
setBindingTypeOnce(BindingType::Translation);
|
||||
m_translationString = translation.toString();
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::TranslationString { translation.toString() };
|
||||
}
|
||||
|
||||
void setTranslationId(QStringView id)
|
||||
{
|
||||
setBindingTypeOnce(BindingType::TranslationById);
|
||||
m_translationId = id.toString();
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::TranslationById { id.toString() };
|
||||
}
|
||||
|
||||
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||
{
|
||||
setBindingTypeOnce(BindingType::Object);
|
||||
m_value = type;
|
||||
m_valueTypeName = typeName;
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::Object { typeName, type };
|
||||
}
|
||||
|
||||
void setInterceptor(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||
{
|
||||
setBindingTypeOnce(BindingType::Interceptor);
|
||||
m_interceptor = type;
|
||||
m_interceptorTypeName = typeName;
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::Interceptor { typeName, type };
|
||||
}
|
||||
|
||||
void setValueSource(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||
{
|
||||
setBindingTypeOnce(BindingType::ValueSource);
|
||||
m_valueSource = type;
|
||||
m_valueSourceTypeName = typeName;
|
||||
ensureSetBindingTypeOnce();
|
||||
m_bindingContent = Content::ValueSource { typeName, type };
|
||||
}
|
||||
|
||||
QString literalTypeName() const { return m_valueTypeName; }
|
||||
const QVariant &literalValue() const { return m_literalValue; }
|
||||
QSharedPointer<const QQmlJSScope> literalType() const { return m_value; }
|
||||
QString literalTypeName() const;
|
||||
|
||||
QString objectTypeName() const { return m_valueTypeName; }
|
||||
QSharedPointer<const QQmlJSScope> objectType() const { return m_value; }
|
||||
// ### TODO: here and below: Introduce an allowConversion parameter, if yes, enable conversions e.g. bool -> number?
|
||||
bool boolValue() const;
|
||||
|
||||
QString interceptorTypeName() const { return m_interceptorTypeName; }
|
||||
QSharedPointer<const QQmlJSScope> interceptorType() const { return m_interceptor; }
|
||||
double numberValue() const;
|
||||
|
||||
QString valueSourceTypeName() const { return m_valueSourceTypeName; }
|
||||
QSharedPointer<const QQmlJSScope> valueSourceType() const { return m_valueSource; }
|
||||
QString stringValue() const;
|
||||
|
||||
QSharedPointer<const QQmlJSScope> literalType(const QQmlJSTypeResolver *resolver) const;
|
||||
|
||||
QString objectTypeName() const
|
||||
{
|
||||
if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
|
||||
return object->typeName;
|
||||
// warn
|
||||
return {};
|
||||
}
|
||||
QSharedPointer<const QQmlJSScope> objectType() const
|
||||
{
|
||||
if (auto *object = std::get_if<Content::Object>(&m_bindingContent))
|
||||
return object->value.lock();
|
||||
// warn
|
||||
return {};
|
||||
}
|
||||
|
||||
bool hasLiteral() const
|
||||
{
|
||||
return isLiteralBinding()
|
||||
&& (!m_literalValue.isNull() || m_bindingType == QQmlJSMetaPropertyBinding::Null);
|
||||
// TODO: Assumption: if the type is literal, we must have one
|
||||
return isLiteralBinding();
|
||||
}
|
||||
bool hasObject() const { return m_bindingType == BindingType::Object && !m_value.isNull(); }
|
||||
bool hasObject() const { return bindingType() == BindingType::Object; }
|
||||
bool hasInterceptor() const
|
||||
{
|
||||
return m_bindingType == BindingType::Interceptor && !m_interceptor.isNull();
|
||||
return bindingType() == BindingType::Interceptor;
|
||||
}
|
||||
bool hasValueSource() const
|
||||
{
|
||||
return m_bindingType == BindingType::ValueSource && !m_valueSource.isNull();
|
||||
return bindingType() == BindingType::ValueSource;
|
||||
}
|
||||
|
||||
friend bool operator==(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
|
||||
{
|
||||
return a.m_bindingType == b.m_bindingType && a.m_propertyName == b.m_propertyName
|
||||
&& a.m_valueTypeName == b.m_valueTypeName
|
||||
&& a.m_interceptorTypeName == b.m_interceptorTypeName
|
||||
&& a.m_valueSourceTypeName == b.m_valueSourceTypeName
|
||||
&& a.m_literalValue == b.m_literalValue && a.m_value == b.m_value
|
||||
&& a.m_interceptor == b.m_interceptor && a.m_sourceLocation == b.m_sourceLocation;
|
||||
return a.m_propertyName == b.m_propertyName
|
||||
&& a.m_bindingContent == b.m_bindingContent
|
||||
&& a.m_sourceLocation == b.m_sourceLocation;
|
||||
}
|
||||
|
||||
friend bool operator!=(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
|
||||
|
@ -541,12 +636,9 @@ public:
|
|||
|
||||
friend size_t qHash(const QQmlJSMetaPropertyBinding &binding, size_t seed = 0)
|
||||
{
|
||||
return qHashMulti(seed, binding.m_propertyName, binding.m_valueTypeName,
|
||||
binding.m_interceptorTypeName, binding.m_valueSourceTypeName,
|
||||
binding.m_literalValue.toString(), binding.m_value.toStrongRef().data(),
|
||||
binding.m_interceptor.toStrongRef().data(),
|
||||
binding.m_valueSource.toStrongRef().data(), binding.m_sourceLocation,
|
||||
binding.m_bindingType);
|
||||
// we don't need to care about the actual binding content when hashing
|
||||
return qHashMulti(seed, binding.m_propertyName, binding.m_sourceLocation,
|
||||
binding.bindingType());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -149,12 +149,12 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
|
|||
Log_Type, binding.sourceLocation());
|
||||
continue;
|
||||
}
|
||||
if (!canConvertFromTo(binding.literalType(), property.type())) {
|
||||
if (!canConvertFromTo(binding.literalType(this), property.type())) {
|
||||
m_logger->log(u"Cannot assign binding of type %1 to %2"_qs
|
||||
.arg(binding.literalTypeName(), property.typeName()),
|
||||
Log_Type, binding.sourceLocation());
|
||||
} else if (equals(property.type(), m_stringType)
|
||||
&& isNumeric(binding.literalType())) {
|
||||
&& isNumeric(binding.literalType(this))) {
|
||||
m_logger->log(u"Cannot assign a numeric constant to a string property"_qs,
|
||||
Log_Type, binding.sourceLocation());
|
||||
}
|
||||
|
|
|
@ -440,18 +440,18 @@ void QmltcCompiler::compileBinding(QmltcType ¤t, const QQmlJSMetaPropertyB
|
|||
|
||||
switch (binding.bindingType()) {
|
||||
case QQmlJSMetaPropertyBinding::BoolLiteral: {
|
||||
const bool value = binding.literalValue().toBool();
|
||||
const bool value = binding.boolValue();
|
||||
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());
|
||||
const QString value = QString::number(binding.numberValue());
|
||||
generator.generate_assignToProperty(current, type, p, value, accessor.name);
|
||||
break;
|
||||
}
|
||||
case QQmlJSMetaPropertyBinding::StringLiteral: {
|
||||
const QString value = binding.literalValue().toString();
|
||||
const QString value = binding.stringValue();
|
||||
generator.generate_assignToProperty(current, type, p, QQmlJSUtils::toLiteral(value),
|
||||
accessor.name);
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue