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
|
qqmljsimportvisitor.cpp qqmljsimportvisitor_p.h
|
||||||
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
|
qqmljsloadergenerator.cpp qqmljsloadergenerator_p.h
|
||||||
qqmljslogger_p.h qqmljslogger.cpp
|
qqmljslogger_p.h qqmljslogger.cpp
|
||||||
qqmljsmetatypes_p.h
|
qqmljsmetatypes_p.h qqmljsmetatypes.cpp
|
||||||
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
|
qqmljsregistercontent.cpp qqmljsregistercontent_p.h
|
||||||
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
|
qqmljsresourcefilemapper.cpp qqmljsresourcefilemapper_p.h
|
||||||
qqmljsscope.cpp qqmljsscope_p.h
|
qqmljsscope.cpp qqmljsscope_p.h
|
||||||
|
|
|
@ -1261,7 +1261,7 @@ bool QQmlJSImportVisitor::visit(UiPublicMember *publicMember)
|
||||||
if (publicMember->isRequired())
|
if (publicMember->isRequired())
|
||||||
m_currentScope->setPropertyLocallyRequired(prop.propertyName(), true);
|
m_currentScope->setPropertyLocallyRequired(prop.propertyName(), true);
|
||||||
|
|
||||||
parseLiteralBinding(publicMember->name.toString(), publicMember->statement);
|
parseLiteralOrScriptBinding(publicMember->name.toString(), publicMember->statement);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1385,12 +1385,9 @@ void QQmlJSImportVisitor::endVisit(QQmlJS::AST::ClassExpression *)
|
||||||
|
|
||||||
|
|
||||||
// ### TODO: add warning about suspicious translation binding when returning false?
|
// ### TODO: add warning about suspicious translation binding when returning false?
|
||||||
static std::optional<QQmlJSMetaPropertyBinding>
|
void handleTranslationBinding(QQmlJSMetaPropertyBinding &binding, QStringView base,
|
||||||
handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
|
QQmlJS::AST::ArgumentList *args)
|
||||||
const QQmlJSImporter::ImportedTypes &rootScopeImports,
|
|
||||||
const QQmlJS::SourceLocation &location)
|
|
||||||
{
|
{
|
||||||
std::optional<QQmlJSMetaPropertyBinding> maybeBinding = std::nullopt;
|
|
||||||
QStringView mainString;
|
QStringView mainString;
|
||||||
auto registerMainString = [&](QStringView string) {
|
auto registerMainString = [&](QStringView string) {
|
||||||
mainString = string;
|
mainString = string;
|
||||||
|
@ -1399,116 +1396,75 @@ handleTranslationBinding(QStringView base, QQmlJS::AST::ArgumentList *args,
|
||||||
auto discardCommentString = [](QStringView) {return -1;};
|
auto discardCommentString = [](QStringView) {return -1;};
|
||||||
auto finalizeBinding = [&](QV4::CompiledData::Binding::ValueType type,
|
auto finalizeBinding = [&](QV4::CompiledData::Binding::ValueType type,
|
||||||
QV4::CompiledData::TranslationData) {
|
QV4::CompiledData::TranslationData) {
|
||||||
QQmlJSMetaPropertyBinding binding(location);
|
|
||||||
if (type == QV4::CompiledData::Binding::Type_Translation) {
|
if (type == QV4::CompiledData::Binding::Type_Translation) {
|
||||||
binding.setTranslation(mainString);
|
binding.setTranslation(mainString);
|
||||||
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
|
} else if (type == QV4::CompiledData::Binding::Type_TranslationById) {
|
||||||
binding.setTranslationId(mainString);
|
binding.setTranslationId(mainString);
|
||||||
} else {
|
} else {
|
||||||
binding.setLiteral(
|
binding.setStringLiteral(mainString);
|
||||||
QQmlJSMetaPropertyBinding::StringLiteral, u"string"_qs,
|
|
||||||
mainString.toString(), rootScopeImports[u"string"_qs].scope);
|
|
||||||
}
|
}
|
||||||
maybeBinding = binding;
|
|
||||||
};
|
};
|
||||||
QmlIR::tryGeneratingTranslationBindingBase(base, args, registerMainString, discardCommentString, finalizeBinding);
|
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 QQmlJS::AST::Statement *statement)
|
||||||
{
|
{
|
||||||
const auto *exprStatement = cast<const ExpressionStatement *>(statement);
|
const auto *exprStatement = cast<const ExpressionStatement *>(statement);
|
||||||
|
|
||||||
if (exprStatement == nullptr)
|
if (exprStatement == nullptr)
|
||||||
return false;
|
return LiteralOrScriptParseResult::Invalid;
|
||||||
|
|
||||||
QVariant value;
|
|
||||||
QString literalType;
|
|
||||||
QQmlJSMetaPropertyBinding::BindingType bindingType = QQmlJSMetaPropertyBinding::Invalid;
|
|
||||||
|
|
||||||
auto expr = exprStatement->expression;
|
auto expr = exprStatement->expression;
|
||||||
|
QQmlJSMetaPropertyBinding binding(expr->firstSourceLocation(), name);
|
||||||
|
|
||||||
switch (expr->kind) {
|
switch (expr->kind) {
|
||||||
case Node::Kind_TrueLiteral:
|
case Node::Kind_TrueLiteral:
|
||||||
value = true;
|
binding.setBoolLiteral(true);
|
||||||
literalType = u"bool"_qs;
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_FalseLiteral:
|
case Node::Kind_FalseLiteral:
|
||||||
value = false;
|
binding.setBoolLiteral(false);
|
||||||
literalType = u"bool"_qs;
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::BoolLiteral;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_NullExpression:
|
case Node::Kind_NullExpression:
|
||||||
// Note: because we set value to nullptr, Null binding returns false in
|
binding.setNullLiteral();
|
||||||
// QQmlJSMetaPropertyBinding::hasLiteral()
|
|
||||||
value = QVariant::fromValue(nullptr);
|
|
||||||
Q_ASSERT(value.isNull());
|
|
||||||
literalType = u"$internal$.std::nullptr_t"_qs;
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::Null;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_NumericLiteral:
|
case Node::Kind_NumericLiteral:
|
||||||
literalType = u"double"_qs;
|
binding.setNumberLiteral(cast<NumericLiteral *>(expr)->value);
|
||||||
value = cast<NumericLiteral *>(expr)->value;
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_StringLiteral:
|
case Node::Kind_StringLiteral:
|
||||||
literalType = u"string"_qs;
|
binding.setStringLiteral(cast<StringLiteral *>(expr)->value);
|
||||||
value = cast<StringLiteral *>(expr)->value.toString();
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_RegExpLiteral:
|
case Node::Kind_RegExpLiteral:
|
||||||
literalType = u"regexp"_qs;
|
binding.setRegexpLiteral(cast<RegExpLiteral *>(expr)->pattern);
|
||||||
value = cast<RegExpLiteral *>(expr)->pattern.toString();
|
|
||||||
bindingType = QQmlJSMetaPropertyBinding::RegExpLiteral;
|
|
||||||
break;
|
break;
|
||||||
case Node::Kind_TemplateLiteral: {
|
case Node::Kind_TemplateLiteral: {
|
||||||
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
|
auto templateLit = QQmlJS::AST::cast<QQmlJS::AST::TemplateLiteral *>(expr);
|
||||||
Q_ASSERT(templateLit);
|
Q_ASSERT(templateLit);
|
||||||
value = templateLit->value.toString();
|
|
||||||
if (templateLit->hasNoSubstitution) {
|
if (templateLit->hasNoSubstitution) {
|
||||||
literalType = u"string"_qs;
|
binding.setStringLiteral(templateLit->value);
|
||||||
bindingType = QQmlJSMetaPropertyBinding::StringLiteral;
|
|
||||||
} else {
|
} else {
|
||||||
bindingType = QQmlJSMetaPropertyBinding::Script;
|
binding.setScriptBinding();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
|
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)) {
|
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression))
|
||||||
literalType = u"double"_qs;
|
binding.setNumberLiteral(-lit->value);
|
||||||
bindingType = QQmlJSMetaPropertyBinding::NumberLiteral;
|
|
||||||
value = -lit->value;
|
|
||||||
}
|
|
||||||
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
|
} 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 (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base))
|
||||||
if (auto translationBindingOpt = handleTranslationBinding(
|
handleTranslationBinding(binding, base->name, call->arguments);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (binding.isValid()) // always add the binding to the scope, even if it's not a literal one
|
||||||
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);
|
m_currentScope->addOwnPropertyBinding(binding);
|
||||||
|
else
|
||||||
|
return LiteralOrScriptParseResult::Invalid;
|
||||||
|
if (!QQmlJSMetaPropertyBinding::isLiteralBinding(binding.bindingType()))
|
||||||
|
return LiteralOrScriptParseResult::Script;
|
||||||
m_literalScopesToCheck << m_currentScope;
|
m_literalScopesToCheck << m_currentScope;
|
||||||
return true;
|
return LiteralOrScriptParseResult::Literal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
|
void QQmlJSImportVisitor::handleIdDeclaration(QQmlJS::AST::UiScriptBinding *scriptBinding)
|
||||||
|
@ -1578,11 +1534,7 @@ bool QQmlJSImportVisitor::visit(UiScriptBinding *scriptBinding)
|
||||||
if (!signal.has_value()) {
|
if (!signal.has_value()) {
|
||||||
m_propertyBindings[m_currentScope].append(
|
m_propertyBindings[m_currentScope].append(
|
||||||
{ m_savedBindingOuterScope, group->firstSourceLocation(), name.toString() });
|
{ m_savedBindingOuterScope, group->firstSourceLocation(), name.toString() });
|
||||||
if (!parseLiteralBinding(name.toString(), scriptBinding->statement)) {
|
parseLiteralOrScriptBinding(name.toString(), scriptBinding->statement);
|
||||||
QQmlJSMetaPropertyBinding binding(group->firstSourceLocation(), name.toString());
|
|
||||||
// TODO: Actually store the value
|
|
||||||
m_savedBindingOuterScope->addOwnPropertyBinding(binding);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const auto statement = scriptBinding->statement;
|
const auto statement = scriptBinding->statement;
|
||||||
QStringList signalParameters;
|
QStringList signalParameters;
|
||||||
|
|
|
@ -203,7 +203,8 @@ protected:
|
||||||
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
|
void checkGroupedAndAttachedScopes(QQmlJSScope::ConstPtr scope);
|
||||||
|
|
||||||
QQmlJSLogger *m_logger;
|
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
|
// Used to temporarily store annotations for functions and generators wrapped in UiSourceElements
|
||||||
QVector<QQmlJSAnnotation> m_pendingMethodAnnotations;
|
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
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QQmlJSTypeResolver;
|
||||||
class QQmlJSScope;
|
class QQmlJSScope;
|
||||||
class QQmlJSMetaEnum
|
class QQmlJSMetaEnum
|
||||||
{
|
{
|
||||||
|
@ -390,38 +391,98 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
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;
|
QQmlJS::SourceLocation m_sourceLocation;
|
||||||
QString m_propertyName; // TODO: this is a debug-only information
|
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
|
void ensureSetBindingTypeOnce()
|
||||||
// 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)
|
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_bindingType == BindingType::Invalid);
|
Q_ASSERT(bindingType() == BindingType::Invalid);
|
||||||
m_bindingType = type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isLiteralBinding() const { return isLiteralBinding(m_bindingType); }
|
bool isLiteralBinding() const { return isLiteralBinding(bindingType()); }
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -448,90 +509,124 @@ public:
|
||||||
|
|
||||||
const QQmlJS::SourceLocation &sourceLocation() const { return m_sourceLocation; }
|
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,
|
void setStringLiteral(QAnyStringView value)
|
||||||
const QSharedPointer<const QQmlJSScope> &type)
|
|
||||||
{
|
{
|
||||||
Q_ASSERT(isLiteralBinding(kind));
|
ensureSetBindingTypeOnce();
|
||||||
setBindingTypeOnce(kind);
|
m_bindingContent = Content::StringLiteral { value.toString() };
|
||||||
m_value = type;
|
}
|
||||||
m_valueTypeName = typeName;
|
|
||||||
m_literalValue = value;
|
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
|
// ### TODO: we might need comment and translation number at some point
|
||||||
void setTranslation(QStringView translation)
|
void setTranslation(QStringView translation)
|
||||||
{
|
{
|
||||||
setBindingTypeOnce(BindingType::Translation);
|
ensureSetBindingTypeOnce();
|
||||||
m_translationString = translation.toString();
|
m_bindingContent = Content::TranslationString { translation.toString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTranslationId(QStringView id)
|
void setTranslationId(QStringView id)
|
||||||
{
|
{
|
||||||
setBindingTypeOnce(BindingType::TranslationById);
|
ensureSetBindingTypeOnce();
|
||||||
m_translationId = id.toString();
|
m_bindingContent = Content::TranslationById { id.toString() };
|
||||||
}
|
}
|
||||||
|
|
||||||
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
void setObject(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||||
{
|
{
|
||||||
setBindingTypeOnce(BindingType::Object);
|
ensureSetBindingTypeOnce();
|
||||||
m_value = type;
|
m_bindingContent = Content::Object { typeName, type };
|
||||||
m_valueTypeName = typeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInterceptor(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
void setInterceptor(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||||
{
|
{
|
||||||
setBindingTypeOnce(BindingType::Interceptor);
|
ensureSetBindingTypeOnce();
|
||||||
m_interceptor = type;
|
m_bindingContent = Content::Interceptor { typeName, type };
|
||||||
m_interceptorTypeName = typeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setValueSource(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
void setValueSource(const QString &typeName, const QSharedPointer<const QQmlJSScope> &type)
|
||||||
{
|
{
|
||||||
setBindingTypeOnce(BindingType::ValueSource);
|
ensureSetBindingTypeOnce();
|
||||||
m_valueSource = type;
|
m_bindingContent = Content::ValueSource { typeName, type };
|
||||||
m_valueSourceTypeName = typeName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString literalTypeName() const { return m_valueTypeName; }
|
QString literalTypeName() const;
|
||||||
const QVariant &literalValue() const { return m_literalValue; }
|
|
||||||
QSharedPointer<const QQmlJSScope> literalType() const { return m_value; }
|
|
||||||
|
|
||||||
QString objectTypeName() const { return m_valueTypeName; }
|
// ### TODO: here and below: Introduce an allowConversion parameter, if yes, enable conversions e.g. bool -> number?
|
||||||
QSharedPointer<const QQmlJSScope> objectType() const { return m_value; }
|
bool boolValue() const;
|
||||||
|
|
||||||
QString interceptorTypeName() const { return m_interceptorTypeName; }
|
double numberValue() const;
|
||||||
QSharedPointer<const QQmlJSScope> interceptorType() const { return m_interceptor; }
|
|
||||||
|
|
||||||
QString valueSourceTypeName() const { return m_valueSourceTypeName; }
|
QString stringValue() const;
|
||||||
QSharedPointer<const QQmlJSScope> valueSourceType() const { return m_valueSource; }
|
|
||||||
|
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
|
bool hasLiteral() const
|
||||||
{
|
{
|
||||||
return isLiteralBinding()
|
// TODO: Assumption: if the type is literal, we must have one
|
||||||
&& (!m_literalValue.isNull() || m_bindingType == QQmlJSMetaPropertyBinding::Null);
|
return isLiteralBinding();
|
||||||
}
|
}
|
||||||
bool hasObject() const { return m_bindingType == BindingType::Object && !m_value.isNull(); }
|
bool hasObject() const { return bindingType() == BindingType::Object; }
|
||||||
bool hasInterceptor() const
|
bool hasInterceptor() const
|
||||||
{
|
{
|
||||||
return m_bindingType == BindingType::Interceptor && !m_interceptor.isNull();
|
return bindingType() == BindingType::Interceptor;
|
||||||
}
|
}
|
||||||
bool hasValueSource() const
|
bool hasValueSource() const
|
||||||
{
|
{
|
||||||
return m_bindingType == BindingType::ValueSource && !m_valueSource.isNull();
|
return bindingType() == BindingType::ValueSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
|
friend bool operator==(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
|
||||||
{
|
{
|
||||||
return a.m_bindingType == b.m_bindingType && a.m_propertyName == b.m_propertyName
|
return a.m_propertyName == b.m_propertyName
|
||||||
&& a.m_valueTypeName == b.m_valueTypeName
|
&& a.m_bindingContent == b.m_bindingContent
|
||||||
&& a.m_interceptorTypeName == b.m_interceptorTypeName
|
&& a.m_sourceLocation == b.m_sourceLocation;
|
||||||
&& 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator!=(const QQmlJSMetaPropertyBinding &a, const QQmlJSMetaPropertyBinding &b)
|
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)
|
friend size_t qHash(const QQmlJSMetaPropertyBinding &binding, size_t seed = 0)
|
||||||
{
|
{
|
||||||
return qHashMulti(seed, binding.m_propertyName, binding.m_valueTypeName,
|
// we don't need to care about the actual binding content when hashing
|
||||||
binding.m_interceptorTypeName, binding.m_valueSourceTypeName,
|
return qHashMulti(seed, binding.m_propertyName, binding.m_sourceLocation,
|
||||||
binding.m_literalValue.toString(), binding.m_value.toStrongRef().data(),
|
binding.bindingType());
|
||||||
binding.m_interceptor.toStrongRef().data(),
|
|
||||||
binding.m_valueSource.toStrongRef().data(), binding.m_sourceLocation,
|
|
||||||
binding.m_bindingType);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -149,12 +149,12 @@ void QQmlJSTypeResolver::init(QQmlJSImportVisitor *visitor, QQmlJS::AST::Node *p
|
||||||
Log_Type, binding.sourceLocation());
|
Log_Type, binding.sourceLocation());
|
||||||
continue;
|
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
|
m_logger->log(u"Cannot assign binding of type %1 to %2"_qs
|
||||||
.arg(binding.literalTypeName(), property.typeName()),
|
.arg(binding.literalTypeName(), property.typeName()),
|
||||||
Log_Type, binding.sourceLocation());
|
Log_Type, binding.sourceLocation());
|
||||||
} else if (equals(property.type(), m_stringType)
|
} 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,
|
m_logger->log(u"Cannot assign a numeric constant to a string property"_qs,
|
||||||
Log_Type, binding.sourceLocation());
|
Log_Type, binding.sourceLocation());
|
||||||
}
|
}
|
||||||
|
|
|
@ -440,18 +440,18 @@ void QmltcCompiler::compileBinding(QmltcType ¤t, const QQmlJSMetaPropertyB
|
||||||
|
|
||||||
switch (binding.bindingType()) {
|
switch (binding.bindingType()) {
|
||||||
case QQmlJSMetaPropertyBinding::BoolLiteral: {
|
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,
|
generator.generate_assignToProperty(current, type, p, value ? u"true"_qs : u"false"_qs,
|
||||||
accessor.name);
|
accessor.name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QQmlJSMetaPropertyBinding::NumberLiteral: {
|
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);
|
generator.generate_assignToProperty(current, type, p, value, accessor.name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QQmlJSMetaPropertyBinding::StringLiteral: {
|
case QQmlJSMetaPropertyBinding::StringLiteral: {
|
||||||
const QString value = binding.literalValue().toString();
|
const QString value = binding.stringValue();
|
||||||
generator.generate_assignToProperty(current, type, p, QQmlJSUtils::toLiteral(value),
|
generator.generate_assignToProperty(current, type, p, QQmlJSUtils::toLiteral(value),
|
||||||
accessor.name);
|
accessor.name);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue