From 979e9ff113d64a6a2e32768fefa4549eca3859ad Mon Sep 17 00:00:00 2001 From: ksuprynowicz Date: Thu, 25 Aug 2022 00:25:34 +0200 Subject: [PATCH] Added scoring system for determining overloaded function to call --- .../src/qtscript/ScriptEngineQtScript.h | 1 + .../qtscript/ScriptEngineQtScript_cast.cpp | 81 +++++++++++++++++++ .../src/qtscript/ScriptObjectQtProxy.cpp | 56 ++++++++++--- 3 files changed, 125 insertions(+), 13 deletions(-) diff --git a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h index 7033d4c6c3..283672cb7d 100644 --- a/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h +++ b/libraries/script-engine/src/qtscript/ScriptEngineQtScript.h @@ -152,6 +152,7 @@ public: // not for public use, but I don't like how Qt strings this along with p virtual QVariant convert(const ScriptValue& value, int typeId) override; virtual void registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, ScriptEngine::DemarshalFunction demarshalFunc) override; + int computeCastPenalty(QScriptValue& val, int destTypeId); bool castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId); QScriptValue castVariantToValue(const QVariant& val); static QString valueType(const QScriptValue& val); diff --git a/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp b/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp index 99f2732fa0..6a83e4eddc 100644 --- a/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp +++ b/libraries/script-engine/src/qtscript/ScriptEngineQtScript_cast.cpp @@ -195,6 +195,87 @@ void ScriptEngineQtScript::registerSystemTypes() { scriptRegisterMetaType(this); } +int ScriptEngineQtScript::computeCastPenalty(QScriptValue& val, int destTypeId) { + if (val.isNumber()) { + switch (destTypeId){ + case QMetaType::Bool: + return 5; + break; + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::Int: + case QMetaType::Long: + case QMetaType::Short: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::ULongLong: + case QMetaType::LongLong: + case QMetaType::UShort: + return 0; + break; + case QMetaType::QString: + case QMetaType::QByteArray: + case QMetaType::QDateTime: + case QMetaType::QDate: + return 100; + break; + default: + return 5; + } + } else if (val.isString() || val.isDate() || val.isRegExp()) { + switch (destTypeId){ + case QMetaType::Bool: + return 100; + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::Int: + case QMetaType::Long: + case QMetaType::Short: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::ULongLong: + case QMetaType::LongLong: + case QMetaType::UShort: + return 100; + case QMetaType::QString: + return 0; + case QMetaType::QByteArray: + case QMetaType::QDateTime: + case QMetaType::QDate: + return 5; + default: + return 5; + } + } else if (val.isBool() || val.isBoolean()) { + switch (destTypeId){ + case QMetaType::Bool: + return 0; + break; + case QMetaType::UInt: + case QMetaType::ULong: + case QMetaType::Int: + case QMetaType::Long: + case QMetaType::Short: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::ULongLong: + case QMetaType::LongLong: + case QMetaType::UShort: + return 5; + break; + case QMetaType::QString: + case QMetaType::QByteArray: + case QMetaType::QDateTime: + case QMetaType::QDate: + return 50; + break; + default: + return 5; + } + } + return 0; +} + bool ScriptEngineQtScript::castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId) { // if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type diff --git a/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp index b89825426d..939036ecda 100644 --- a/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp +++ b/libraries/script-engine/src/qtscript/ScriptObjectQtProxy.cpp @@ -462,38 +462,51 @@ QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& arg int parameterConversionFailureId = 0; int parameterConversionFailureCount = 0; - for (auto iter = _metas.cbegin(); iter != _metas.end(); ++iter) { - const QMetaMethod& meta = *iter; + int num_metas = _metas.size(); + QVector< QList > qScriptArgLists; + QVector< QVector > qGenArgsVectors; + QVector< QList > qVarArgLists; + int conversionPenaltyScore[num_metas]; + bool isMetaRejected[num_metas]; + qScriptArgLists.resize(num_metas); + qGenArgsVectors.resize(num_metas); + qVarArgLists.resize(num_metas); + + for (int i = 0; i < num_metas; i++) { + const QMetaMethod& meta = _metas[i]; + isMetaRejected[i] = false; int methodNumArgs = meta.parameterCount(); if (methodNumArgs != numArgs) { + isMetaRejected[i] = true; continue; } - QList qScriptArgList; - QList qVarArgList; - QGenericArgument qGenArgs[10]; + qGenArgsVectors[i].resize(10); + conversionPenaltyScore[i] = 0; int conversionFailures = 0; + for (int arg = 0; arg < numArgs; ++arg) { int methodArgTypeId = meta.parameterType(arg); Q_ASSERT(methodArgTypeId != QMetaType::UnknownType); QScriptValue argVal = context->argument(arg); if (methodArgTypeId == scriptValueTypeId) { - qScriptArgList.append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal))); - qGenArgs[arg] = Q_ARG(ScriptValue, qScriptArgList.back()); + qScriptArgLists[i].append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal))); + qGenArgsVectors[i][arg] = Q_ARG(ScriptValue, qScriptArgLists[i].back()); } else if (methodArgTypeId == QMetaType::QVariant) { - qVarArgList.append(argVal.toVariant()); - qGenArgs[arg] = Q_ARG(QVariant, qVarArgList.back()); + qVarArgLists[i].append(argVal.toVariant()); + qGenArgsVectors[i][arg] = Q_ARG(QVariant, qVarArgLists[i].back()); } else { QVariant varArgVal; if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) { conversionFailures++; } else { - qVarArgList.append(varArgVal); - const QVariant& converted = qVarArgList.back(); + qVarArgLists[i].append(varArgVal); + const QVariant& converted = qVarArgLists[i].back(); + conversionPenaltyScore[i] = _engine->computeCastPenalty(argVal, methodArgTypeId); // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant // A const_cast is needed because calling data() would detach the QVariant. - qGenArgs[arg] = + qGenArgsVectors[i][arg] = QGenericArgument(QMetaType::typeName(converted.userType()), const_cast(converted.constData())); } } @@ -503,13 +516,30 @@ QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& arg parameterConversionFailureCount = conversionFailures; parameterConversionFailureId = meta.methodIndex(); } + isMetaRejected[i] = true; continue; } ScriptContextQtWrapper ourContext(_engine, context); ScriptContextGuard guard(&ourContext); + } + bool isValidMetaSelected = false; + int bestMeta = 0; + for (int i = 0; i < num_metas; i++) { + if (!isValidMetaSelected && !isMetaRejected[i]) { + isValidMetaSelected = true; + bestMeta = i; + } + if (isValidMetaSelected && !isMetaRejected[i] && conversionPenaltyScore[bestMeta] > conversionPenaltyScore[i]) { + bestMeta = i; + } + } + + if (isValidMetaSelected) { + const QMetaMethod& meta = _metas[bestMeta]; int returnTypeId = meta.returnType(); + QVector &qGenArgs = qGenArgsVectors[bestMeta]; // The Qt MOC engine will automatically call qRegisterMetaType on invokable parameters and properties, but there's // nothing in there for return values so these need to be explicitly runtime-registered! @@ -579,9 +609,9 @@ QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& arg } } + context->throwError(QString("Native call of %1 failed: could not locate an overload with the requested arguments").arg(fullName())); Q_ASSERT(false); // really shouldn't have gotten here -- it didn't work before and it's working now? return QVariant(); - context->throwError(QString("Native call of %1 failed: could not locate an overload with the requested arguments").arg(fullName())); } QString ScriptSignalQtProxy::fullName() const {