aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4qobjectwrapper.cpp
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2024-11-08 17:00:38 +0100
committerUlf Hermann <[email protected]>2024-11-13 11:58:50 +0100
commit12fb7d27431a7db2a19e1954c85d5dc03264b356 (patch)
tree5e998ab686c48c3c2d49084cb1eafd5e26c28e97 /src/qml/jsruntime/qv4qobjectwrapper.cpp
parent9c2fc83be55647e0519cdbd6b19701fa022743e7 (diff)
QtQml: Fix calling of method with QML_USING types
On the engine side, types made available with QML_USING are recognizable by their QMetaType conversions. In order to actually call the right methods, we need to slightly prefer methods to whose arguments we can convert over ones we have not idea about. This, however, adds the problem of converting to QString, double, QVariant and QJSValue, which is possible for any type. Since such conversions are less specific than manually added converters, we de-prioritize them a bit. On the compiler side we need to transmit the knowledge about the overload to be called from the compiler (which has already done its own overload selection) to the lookup methods. This is easily done using the relative method index. This way we do not need to do any run time overload selection at all and we do not need to pass any types to the lookup methods. We can do this without further compatibility adaptations because the current version of those lookup methods was only introduced in 6.9. Excluded, of course, are shadowable calls, which are performed with only QVariant arguments. These carry the arguments themselves and trigger the engine's overload selection. Internally nothing changes about them. Passing a list of QMetaType::fromType<QVariant>() to the lookup methods for them has always been a waste. Only the engine changes are relevant for 6.8. In 6.8, the lookup methods defer the overload selection to the engine and take the types on every call. We still cannot separate the engine changes from the compiler changes in dev because the same test is run once in interpreted and once in compiled mode. Pick-to: 6.8 Task-number: QTBUG-127174 Change-Id: I6ab52ddf3be65dcc94547266c5dcc5ac1050c93c Reviewed-by: Olivier De Cannière <[email protected]>
Diffstat (limited to 'src/qml/jsruntime/qv4qobjectwrapper.cpp')
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp103
1 files changed, 83 insertions, 20 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index abe7a96512..b771a938e4 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1711,8 +1711,19 @@ int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
return 1;
}
- if (QMetaType::canConvert(type, conversionMetaType))
+ if (QMetaType::canConvert(type, conversionMetaType)) {
+ if (conversionMetaType == QMetaType::fromType<QJSValue>()
+ || conversionMetaType == QMetaType::fromType<double>()
+ || conversionMetaType == QMetaType::fromType<QString>()) {
+ // Unspecific conversions receive lower score. You can convert anything
+ // to QString or double via toString() and valueOf(), respectively.
+ // And anything can be wrapped into QJSValue, but that's inefficient.
+ return 6;
+ }
+
+ // We have an explicitly defined conversion method to a non-boring type.
return 5;
+ }
return 10;
};
@@ -1726,6 +1737,33 @@ int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
static int MatchScore(const Value &actual, QMetaType conversionMetaType)
{
const int conversionType = conversionMetaType.id();
+ const auto convertibleScore = [&](QMetaType actualType) {
+ // There are a number of things we can do in JavaScript to subvert this, but
+ // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
+ if (!QMetaType::canConvert(actualType, conversionMetaType))
+ return 10;
+
+ // You can convert anything to QJSValue, but that's inefficient.
+ // If we have a better option, we should use it.
+ if (conversionMetaType == QMetaType::fromType<QJSValue>())
+ return 9;
+
+ // You can also convert anything to QVariant, but that's also suboptimal.
+ // You can convert anything to string or double via toString() and valueOf().
+ // Those are also rather unspecific.
+ switch (conversionType) {
+ case QMetaType::QVariant:
+ case QMetaType::Double:
+ case QMetaType::QString:
+ return 9;
+ default:
+ break;
+ }
+
+ // We have an explicitly defined conversion method to a non-boring type.
+ return 8;
+ };
+
if (actual.isNumber()) {
switch (conversionType) {
case QMetaType::Double:
@@ -1751,7 +1789,9 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QJsonValue:
return 5;
default:
- return 10;
+ return convertibleScore(actual.isInteger()
+ ? QMetaType::fromType<int>()
+ : QMetaType::fromType<double>());
}
} else if (actual.isString()) {
switch (conversionType) {
@@ -1761,8 +1801,21 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 5;
case QMetaType::QUrl:
return 6; // we like to convert strings to URLs in QML
- default:
+ case QMetaType::Double:
+ case QMetaType::Float:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::Char:
+ case QMetaType::UChar:
+ // QMetaType can natively convert strings to numbers.
+ // However, in the general case it's of course extremely lossy.
return 10;
+ default:
+ return convertibleScore(QMetaType::fromType<QString>());
}
} else if (actual.isBoolean()) {
switch (conversionType) {
@@ -1771,7 +1824,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QJsonValue:
return 5;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<bool>());
}
} else if (actual.as<DateObject>()) {
switch (conversionType) {
@@ -1782,23 +1835,26 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QTime:
return 2;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QDateTime>());
}
} else if (actual.as<RegExpObject>()) {
switch (conversionType) {
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
return 0;
-#endif
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QRegularExpression>());
+#else
+ default:
+ return convertibleScore(QMetaType());
+#endif
}
} else if (actual.as<ArrayBuffer>()) {
switch (conversionType) {
case QMetaType::QByteArray:
return 0;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QByteArray>());
}
} else if (actual.as<ArrayObject>()) {
switch (conversionType) {
@@ -1813,7 +1869,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QVector3D:
return 7;
default:
- return 10;
+ return convertibleScore(QMetaType());
}
} else if (actual.isNull()) {
switch (conversionType) {
@@ -1826,7 +1882,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
return 0;
else
- return 10;
+ return convertibleScore(QMetaType());
}
}
} else if (const Object *obj = actual.as<Object>()) {
@@ -1849,7 +1905,8 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 0;
}
}
- return 10;
+
+ return convertibleScore(QMetaType::fromType<QObject *>());
}
if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
@@ -1871,14 +1928,15 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
}
}
- return 10;
+ return convertibleScore(QMetaType());
}
if (const Sequence *sequence = obj->as<Sequence>()) {
- if (SequencePrototype::metaTypeForSequence(sequence) == conversionMetaType)
+ const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
+ if (sequenceType == conversionMetaType)
return 1;
- else
- return 10;
+
+ return convertibleScore(sequenceType);
}
if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
@@ -1889,15 +1947,20 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
});
}
- if (conversionType == QMetaType::QJsonObject)
- return 5;
- if (conversionType == qMetaTypeId<QJSValue>())
+ if (conversionMetaType == QMetaType::fromType<QJSValue>())
return 0;
- if (conversionType == QMetaType::QVariantMap)
+
+ switch (conversionType) {
+ case QMetaType::QJsonObject:
+ case QMetaType::QVariantMap:
return 5;
+ default:
+ break;
+ }
+
}
- return 10;
+ return convertibleScore(QMetaType());
}
static int numDefinedArguments(CallData *callArgs)