QtQml: Disallow multi-step construction of value types

If we coerce the argument for a constructor, we should not call another
constructor in the process. Such chaining leads to rather confusing
effects and easily ends in infinite recursion.

Pick-to: 6.5 6.6
Change-Id: Ia6d5063641170d2cd3bee3fdcbb8b97837d5c700
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2023-06-01 11:02:56 +02:00
parent e0235bca55
commit b4ce8051af
2 changed files with 17 additions and 15 deletions

View File

@ -120,12 +120,11 @@ static bool fromMatchingType(
return true;
}
QVariant converted = QQmlValueTypeProvider::createValueType(s, parameterType);
if (converted.isValid()) {
callConstructor(mo, i, converted.data(), allocate());
return true;
}
// Do not recursively try to create parameters here. This may end up in infinite recursion.
// At this point, s should be a builtin type. For builtin types
// the QMetaType converters are good enough.
QVariant converted(parameterType);
if (QMetaType::convert(parameter.metaType(), parameter.constData(),
parameterType, converted.data())) {
callConstructor(mo, i, converted.data(), allocate());
@ -157,14 +156,11 @@ static bool fromMatchingType(const QMetaObject *mo, QVariant s, Allocate &&alloc
return true;
}
QVariant parameter = QQmlValueTypeProvider::createValueType(s, parameterType);
if (parameter.isValid()) {
callConstructor(mo, i, parameter.data(), allocate());
return true;
}
// Do not recursively try to create parameters here. This may end up in infinite recursion.
// At this point, s should be a builtin type. For builtin types
// the QMetaType converters are good enough.
QVariant parameter(parameterType);
if (QMetaType::convert(sourceMetaType, s.constData(), parameterType, parameter.data())) {
callConstructor(mo, i, parameter.data(), allocate());
return true;

View File

@ -485,8 +485,10 @@ bool QQmlJSTypeResolver::adjustTrackedType(
// If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
// we better not change the type.
if (!canPrimitivelyConvertFromTo(tracked, conversion))
if (!canPrimitivelyConvertFromTo(tracked, conversion)
&& !selectConstructor(conversion, tracked, nullptr).isValid()) {
return false;
}
it->replacement = comparableType(conversion);
*it->clone = std::move(*QQmlJSScope::clone(conversion));
@ -508,8 +510,10 @@ bool QQmlJSTypeResolver::adjustTrackedType(
// If we cannot convert to the new type without the help of e.g. lookupResultMetaType(),
// we better not change the type.
if (!canPrimitivelyConvertFromTo(tracked, result))
if (!canPrimitivelyConvertFromTo(tracked, result)
&& !selectConstructor(result, tracked, nullptr).isValid()) {
return false;
}
it->replacement = comparableType(result);
*mutableTracked = std::move(*QQmlJSScope::clone(result));
@ -569,7 +573,7 @@ QString QQmlJSTypeResolver::containedTypeName(const QQmlJSRegisterContent &conta
bool QQmlJSTypeResolver::canConvertFromTo(const QQmlJSScope::ConstPtr &from,
const QQmlJSScope::ConstPtr &to) const
{
if (canPrimitivelyConvertFromTo(from, to))
if (canPrimitivelyConvertFromTo(from, to) || selectConstructor(to, from, nullptr).isValid())
return true;
// ### need a generic solution for custom cpp types:
@ -1005,7 +1009,7 @@ QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
{
// If the "from" type can hold the target type, we should not try to coerce
// it to any constructor argument.
if (canHold(passedArgumentType, type))
if (type.isNull() || canHold(passedArgumentType, type))
return QQmlJSMetaMethod();
if (QQmlJSScope::ConstPtr extension = type->extensionType().scope) {
@ -1041,6 +1045,8 @@ QQmlJSMetaMethod QQmlJSTypeResolver::selectConstructor(
if (equals(passedArgumentType, methodArgumentType))
return method;
// Do not select further ctors here. We don't want to do multi-step construction as that
// is confusing and easily leads to infinite recursion.
if (!candidate.isValid()
&& canPrimitivelyConvertFromTo(passedArgumentType, methodArgumentType)) {
candidate = method;
@ -1165,7 +1171,7 @@ bool QQmlJSTypeResolver::canPrimitivelyConvertFromTo(
return true;
}
return selectConstructor(to, from, nullptr).isValid();
return false;
}
QQmlJSRegisterContent QQmlJSTypeResolver::lengthProperty(