diff options
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 98 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/testtypes.h | 20 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 41 |
3 files changed, 120 insertions, 39 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 517fbab6f1..40398cb648 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -1585,17 +1585,17 @@ static int MatchScore(const QV4::Value &actual, QMetaType conversionMetaType) else if (v.canConvert(conversionMetaType)) return 5; return 10; - } else if (conversionType == QMetaType::QJsonObject) { - return 5; - } else if (conversionType == qMetaTypeId<QJSValue>()) { - return 0; - } else { - return 10; } - } else { - return 10; + if (conversionType == QMetaType::QJsonObject) + return 5; + if (conversionType == qMetaTypeId<QJSValue>()) + return 0; + if (conversionType == QMetaType::QVariantMap) + return 5; } + + return 10; } static int numDefinedArguments(QV4::CallData *callArgs) @@ -1708,46 +1708,66 @@ static const QQmlPropertyData *ResolveOverloaded( const QQmlPropertyData *best = nullptr; int bestParameterScore = INT_MAX; - int bestMatchScore = INT_MAX; + int bestMaxMatchScore = INT_MAX; + int bestSumMatchScore = INT_MAX; QV4::Scope scope(engine); QV4::ScopedValue v(scope); for (int i = 0; i < methodCount; ++i) { const QQmlPropertyData *attempt = methods + i; - QQmlMetaObject::ArgTypeStorage storage; - int methodArgumentCount = 0; - if (attempt->hasArguments()) { - if (attempt->isConstructor()) { - if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) - continue; - } else { - if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) - continue; - } - methodArgumentCount = storage.size(); - } - if (methodArgumentCount > argumentCount) - continue; // We don't have sufficient arguments to call this method + // QQmlV4Function overrides anything that doesn't provide the exact number of arguments + int methodParameterScore = 1; + // QQmlV4Function overrides the "no idea" option, which is 10 + int maxMethodMatchScore = 9; + // QQmlV4Function cannot provide a best sum of match scores as we don't match the arguments + int sumMethodMatchScore = bestSumMatchScore; - int methodParameterScore = definedArgumentCount - methodArgumentCount; - if (methodParameterScore > bestParameterScore) - continue; // We already have a better option + if (!attempt->isV4Function()) { + QQmlMetaObject::ArgTypeStorage storage; + int methodArgumentCount = 0; + if (attempt->hasArguments()) { + if (attempt->isConstructor()) { + if (!object.constructorParameterTypes(attempt->coreIndex(), &storage, nullptr)) + continue; + } else { + if (!object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr)) + continue; + } + methodArgumentCount = storage.size(); + } - int methodMatchScore = 0; - for (int ii = 0; ii < methodArgumentCount; ++ii) { - methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])), - storage[ii]); + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + methodParameterScore = (definedArgumentCount == methodArgumentCount) + ? 0 + : (definedArgumentCount - methodArgumentCount + 1); + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + maxMethodMatchScore = 0; + sumMethodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) { + const int score = MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])), + storage[ii]); + maxMethodMatchScore = qMax(maxMethodMatchScore, score); + sumMethodMatchScore += score; + } } - if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + if (bestParameterScore > methodParameterScore || bestMaxMatchScore > maxMethodMatchScore + || (bestParameterScore == methodParameterScore + && bestMaxMatchScore == maxMethodMatchScore + && bestSumMatchScore > sumMethodMatchScore)) { best = attempt; bestParameterScore = methodParameterScore; - bestMatchScore = methodMatchScore; + bestMaxMatchScore = maxMethodMatchScore; + bestSumMatchScore = sumMethodMatchScore; } - if (bestParameterScore == 0 && bestMatchScore == 0) + if (bestParameterScore == 0 && bestMaxMatchScore == 0) break; // We can't get better than that }; @@ -2259,6 +2279,12 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * return call(); }; + if (d()->methodCount != 1) { + method = ResolveOverloaded(object, d()->methods, d()->methodCount, v4, callData); + if (method == nullptr) + return Encode::undefined(); + } + if (method->isV4Function()) { return doCall([&]() { QV4::ScopedValue rv(scope, QV4::Value::undefinedValue()); @@ -2272,12 +2298,6 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * }); } - if (d()->methodCount != 1) { - method = ResolveOverloaded(object, d()->methods, d()->methodCount, v4, callData); - if (method == nullptr) - return Encode::undefined(); - } - return doCall([&]() { return CallPrecise(object, *method, v4, callData); }); } diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index 59e0a9cb65..99fc6c96ab 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -891,6 +891,26 @@ public: Q_INVOKABLE void method_unknown(NonRegisteredType) { invoke(28); } + Q_INVOKABLE void method_overload2(QQmlV4Function *v) + { + invoke(31); + QV4::Scope scope(v->v4engine()); + for (int i = 0, end = v->length(); i != end; ++i) { + QV4::ScopedValue v4Value(scope, (*v)[i]); + m_actuals.append(v->v4engine()->toVariant(v4Value, QMetaType())); + } + } + Q_INVOKABLE void method_overload2(const QVariantList &list) + { + invoke(32); + m_actuals << QVariant(list); + } + Q_INVOKABLE void method_overload2(const QVariantMap &map) { invoke(33); m_actuals << map; } + Q_INVOKABLE void method_overload2(int a) { invoke(34); m_actuals << a; } + Q_INVOKABLE void method_overload2(int a, int b) { invoke(35); m_actuals << a << b; } + Q_INVOKABLE void method_overload2(QString a) { invoke(36); m_actuals << a; } + Q_INVOKABLE void method_overload2() { invoke(37); } + private: friend class MyInvokableBaseObject; void invoke(int idx) { if (m_invoked != -1) m_invokedError = true; m_invoked = idx;} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 98b08a0e23..47d3aee5da 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -3345,6 +3345,47 @@ void tst_qqmlecmascript::callQtInvokables() QJSValue callback = qvariant_cast<QJSValue>(o->actuals().at(1)); QVERIFY(!callback.isNull()); QVERIFY(callback.isCallable()); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload2('foo', 12, [1, 2, 3])", QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 31); + QCOMPARE(o->actuals().count(), 3); + QCOMPARE(qvariant_cast<QString>(o->actuals().at(0)), QStringLiteral("foo")); + QCOMPARE(qvariant_cast<int>(o->actuals().at(1)), 12); + QCOMPARE(qvariant_cast<QVariantList>(o->actuals().at(2)), (QVariantList {1.0, 2.0, 3.0})); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload2(11, 12, {a: 1, b: 2})", QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 31); + QCOMPARE(o->actuals().count(), 3); + QCOMPARE(qvariant_cast<int>(o->actuals().at(0)), 11); + QCOMPARE(qvariant_cast<int>(o->actuals().at(1)), 12); + QCOMPARE(qvariant_cast<QVariantMap>(o->actuals().at(2)), + (QVariantMap { {QStringLiteral("a"), 1.0}, {QStringLiteral("b"), 2.0}, })); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload2([1, 'bar', 0.2])", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 32); + QCOMPARE(o->actuals().count(), 1); + QCOMPARE(qvariant_cast<QVariantList>(o->actuals().at(0)), + (QVariantList {1.0, QStringLiteral("bar"), 0.2})); + + o->reset(); + QVERIFY(EVALUATE_VALUE("object.method_overload2({one: 1, two: 'bar', three: 0.2})", + QV4::Primitive::undefinedValue())); + QCOMPARE(o->error(), false); + QCOMPARE(o->invoked(), 33); + QCOMPARE(o->actuals().count(), 1); + QCOMPARE(qvariant_cast<QVariantMap>(o->actuals().at(0)), + (QVariantMap { + {QStringLiteral("one"), 1.0}, + {QStringLiteral("two"), QStringLiteral("bar")}, + {QStringLiteral("three"), 0.2} + })); } void tst_qqmlecmascript::resolveClashingProperties() |