Added scoring system for determining overloaded function to call

This commit is contained in:
ksuprynowicz 2022-08-25 00:25:34 +02:00
parent f926e8d30c
commit def8555b99
3 changed files with 125 additions and 13 deletions

View file

@ -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 QVariant convert(const ScriptValue& value, int typeId) override;
virtual void registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc, virtual void registerCustomType(int type, ScriptEngine::MarshalFunction marshalFunc,
ScriptEngine::DemarshalFunction demarshalFunc) override; ScriptEngine::DemarshalFunction demarshalFunc) override;
int computeCastPenalty(QScriptValue& val, int destTypeId);
bool castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId); bool castValueToVariant(const QScriptValue& val, QVariant& dest, int destTypeId);
QScriptValue castVariantToValue(const QVariant& val); QScriptValue castVariantToValue(const QVariant& val);
static QString valueType(const QScriptValue& val); static QString valueType(const QScriptValue& val);

View file

@ -195,6 +195,87 @@ void ScriptEngineQtScript::registerSystemTypes() {
scriptRegisterMetaType<QJsonArray, JsonArrayToScriptValue, JsonArrayFromScriptValue>(this); scriptRegisterMetaType<QJsonArray, JsonArrayToScriptValue, JsonArrayFromScriptValue>(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) { 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 // if we're not particularly interested in a specific type, try to detect if we're dealing with a registered type

View file

@ -462,38 +462,51 @@ QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& arg
int parameterConversionFailureId = 0; int parameterConversionFailureId = 0;
int parameterConversionFailureCount = 0; int parameterConversionFailureCount = 0;
for (auto iter = _metas.cbegin(); iter != _metas.end(); ++iter) { int num_metas = _metas.size();
const QMetaMethod& meta = *iter; QVector< QList<ScriptValue> > qScriptArgLists;
QVector< QVector <QGenericArgument> > qGenArgsVectors;
QVector< QList<QVariant> > 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(); int methodNumArgs = meta.parameterCount();
if (methodNumArgs != numArgs) { if (methodNumArgs != numArgs) {
isMetaRejected[i] = true;
continue; continue;
} }
QList<ScriptValue> qScriptArgList; qGenArgsVectors[i].resize(10);
QList<QVariant> qVarArgList; conversionPenaltyScore[i] = 0;
QGenericArgument qGenArgs[10];
int conversionFailures = 0; int conversionFailures = 0;
for (int arg = 0; arg < numArgs; ++arg) { for (int arg = 0; arg < numArgs; ++arg) {
int methodArgTypeId = meta.parameterType(arg); int methodArgTypeId = meta.parameterType(arg);
Q_ASSERT(methodArgTypeId != QMetaType::UnknownType); Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);
QScriptValue argVal = context->argument(arg); QScriptValue argVal = context->argument(arg);
if (methodArgTypeId == scriptValueTypeId) { if (methodArgTypeId == scriptValueTypeId) {
qScriptArgList.append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal))); qScriptArgLists[i].append(ScriptValue(new ScriptValueQtWrapper(_engine, argVal)));
qGenArgs[arg] = Q_ARG(ScriptValue, qScriptArgList.back()); qGenArgsVectors[i][arg] = Q_ARG(ScriptValue, qScriptArgLists[i].back());
} else if (methodArgTypeId == QMetaType::QVariant) { } else if (methodArgTypeId == QMetaType::QVariant) {
qVarArgList.append(argVal.toVariant()); qVarArgLists[i].append(argVal.toVariant());
qGenArgs[arg] = Q_ARG(QVariant, qVarArgList.back()); qGenArgsVectors[i][arg] = Q_ARG(QVariant, qVarArgLists[i].back());
} else { } else {
QVariant varArgVal; QVariant varArgVal;
if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) { if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) {
conversionFailures++; conversionFailures++;
} else { } else {
qVarArgList.append(varArgVal); qVarArgLists[i].append(varArgVal);
const QVariant& converted = qVarArgList.back(); 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 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. // A const_cast is needed because calling data() would detach the QVariant.
qGenArgs[arg] = qGenArgsVectors[i][arg] =
QGenericArgument(QMetaType::typeName(converted.userType()), const_cast<void*>(converted.constData())); QGenericArgument(QMetaType::typeName(converted.userType()), const_cast<void*>(converted.constData()));
} }
} }
@ -503,13 +516,30 @@ QVariant ScriptMethodQtProxy::extension(Extension extension, const QVariant& arg
parameterConversionFailureCount = conversionFailures; parameterConversionFailureCount = conversionFailures;
parameterConversionFailureId = meta.methodIndex(); parameterConversionFailureId = meta.methodIndex();
} }
isMetaRejected[i] = true;
continue; continue;
} }
ScriptContextQtWrapper ourContext(_engine, context); ScriptContextQtWrapper ourContext(_engine, context);
ScriptContextGuard guard(&ourContext); 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(); int returnTypeId = meta.returnType();
QVector <QGenericArgument> &qGenArgs = qGenArgsVectors[bestMeta];
// The Qt MOC engine will automatically call qRegisterMetaType on invokable parameters and properties, but there's // 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! // 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? Q_ASSERT(false); // really shouldn't have gotten here -- it didn't work before and it's working now?
return QVariant(); 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 { QString ScriptSignalQtProxy::fullName() const {