aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorFabian Kosmale <fabian.kosmale@qt.io>2020-11-20 16:50:54 +0100
committerFabian Kosmale <fabian.kosmale@qt.io>2020-12-02 12:54:51 +0100
commite5f06d60245b75dd31daa2f212619236e6808cd3 (patch)
tree391e4acc7168bc0abbab60c9fe406fe4823ddf18 /src/qml/jsruntime
parent9e81149dd88848b3dc9b6c66d77b41f7ff0e1cf6 (diff)
Unify overload resolution code and introduce a cache
With this approach we can also avoid passing the property cache around. Original-patch-by: Lars Knoll <lars.knoll@qt.io> Task-number: QTBUG-82931 Change-Id: I88f770d5d9a31b2f5071ad457d9a830900130a85 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp238
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h11
2 files changed, 76 insertions, 173 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 22c4863bde..02f9dee3d3 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1496,53 +1496,6 @@ static int MatchScore(const QV4::Value &actual, int conversionType)
}
}
-/*
-Returns the next related method, if one, or 0.
-*/
-static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
- const QQmlPropertyData *current,
- QQmlPropertyData &dummy,
- const QQmlPropertyCache *propertyCache)
-{
- if (!current->isOverload())
- return nullptr;
-
- Q_ASSERT(!current->overrideIndexIsProperty());
-
- if (propertyCache) {
- return propertyCache->method(current->overrideIndex());
- } else {
- const QMetaObject *mo = object.metaObject();
- int methodOffset = mo->methodOffset();
-
- while (methodOffset > current->overrideIndex()) {
- mo = mo->superClass();
- methodOffset -= QMetaObjectPrivate::get(mo)->methodCount;
- }
-
- // If we've been called before with the same override index, then
- // we can't go any further...
- if (&dummy == current && dummy.coreIndex() == current->overrideIndex())
- return nullptr;
-
- QMetaMethod method = mo->method(current->overrideIndex());
- dummy.load(method);
-
- // Look for overloaded methods
- QByteArray methodName = method.name();
- for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) {
- if (methodName == mo->method(ii).name()) {
- dummy.setOverload(true);
- dummy.setOverrideIndexIsProperty(false);
- dummy.setOverrideIndex(ii);
- return &dummy;
- }
- }
-
- return &dummy;
- }
-}
-
static int numDefinedArguments(QV4::CallData *callArgs)
{
int numDefinedArguments = callArgs->argc();
@@ -1644,8 +1597,8 @@ Resolve the overloaded method to call. The algorithm works conceptually like th
If two or more overloads have the same match score, call the last one. The match
score is constructed by adding the matchScore() result for each of the parameters.
*/
-static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
- QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
+static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData *methods, int methodCount,
+ QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
{
const int argumentCount = callArgs->argc();
@@ -1655,18 +1608,21 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
int bestParameterScore = INT_MAX;
int bestMatchScore = INT_MAX;
- QQmlPropertyData dummy;
- const QQmlPropertyData *attempt = &data;
-
QV4::Scope scope(engine);
QV4::ScopedValue v(scope);
- do {
+ for (int i = 0; i < methodCount; ++i) {
+ const QQmlPropertyData &attempt = methods[i];
QQmlMetaObject::ArgTypeStorage storage;
int methodArgumentCount = 0;
int *methodArgTypes = nullptr;
- if (attempt->hasArguments()) {
- int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, nullptr);
+ if (attempt.hasArguments()) {
+ int *args = [&]() {
+ if (attempt.isConstructor())
+ return object.constructorParameterTypes(attempt.coreIndex(), &storage, nullptr);
+ else
+ return object.methodParameterTypes(attempt.coreIndex(), &storage, nullptr);
+ }();
if (!args) // Must be an unknown argument
continue;
@@ -1688,7 +1644,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
}
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
- best = *attempt;
+ best = attempt;
bestParameterScore = methodParameterScore;
bestMatchScore = methodMatchScore;
}
@@ -1696,18 +1652,20 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const
if (bestParameterScore == 0 && bestMatchScore == 0)
break; // We can't get better than that
- } while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != nullptr);
+ };
if (best.isValid()) {
return CallPrecise(object, best, engine, callArgs, callType);
} else {
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
- const QQmlPropertyData *candidate = &data;
- while (candidate) {
- error += QLatin1String("\n ") +
- QString::fromUtf8(object.metaObject()->method(candidate->coreIndex())
- .methodSignature());
- candidate = RelatedMethod(object, candidate, dummy, propertyCache);
+ for (int i = 0; i < methodCount; ++i) {
+ for (int i = 0; i < methodCount; ++i) {
+ const QQmlPropertyData &candidate = methods[i];
+ const QMetaMethod m = candidate.isConstructor()
+ ? object.metaObject()->constructor(candidate.coreIndex())
+ : object.metaObject()->method(candidate.coreIndex());
+ error += u"\n " + QString::fromUtf8(m.methodSignature());
+ }
}
return engine->throwError(error);
@@ -2082,6 +2040,41 @@ const QMetaObject *Heap::QObjectMethod::metaObject()
return object()->metaObject();
}
+void Heap::QObjectMethod::ensureMethodsCache()
+{
+ if (methods)
+ return;
+ const QMetaObject *mo = metaObject();
+ int methodOffset = mo->methodOffset();
+ while (methodOffset > index) {
+ mo = mo->superClass();
+ methodOffset -= QMetaObjectPrivate::get(mo)->methodCount;
+ }
+ QVarLengthArray<QQmlPropertyData, 9> resolvedMethods;
+ QQmlPropertyData dummy;
+ QMetaMethod method = mo->method(index);
+ dummy.load(method);
+ resolvedMethods.append(dummy);
+ // Look for overloaded methods
+ QByteArray methodName = method.name();
+ for (int ii = index - 1; ii >= methodOffset; --ii) {
+ if (methodName == mo->method(ii).name()) {
+ method = mo->method(ii);
+ dummy.load(method);
+ resolvedMethods.append(dummy);
+ }
+ }
+ if (resolvedMethods.size() > 1) {
+ methods = new QQmlPropertyData[resolvedMethods.size()];
+ memcpy(methods, resolvedMethods.data(), resolvedMethods.size()*sizeof(QQmlPropertyData));
+ methodCount = resolvedMethods.size();
+ } else {
+ methods = reinterpret_cast<QQmlPropertyData *>(&_singleMethod);
+ *methods = resolvedMethods.at(0);
+ methodCount = 1;
+ }
+}
+
QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const
{
QString result;
@@ -2137,46 +2130,22 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
else if (d()->index == ToStringMethod)
return method_toString(v4);
+ d()->ensureMethodsCache();
+
QQmlObjectOrGadget object(d()->object());
if (!d()->object()) {
if (!d()->valueTypeWrapper)
return Encode::undefined();
- object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr());
- }
-
- QQmlPropertyData method;
-
- if (d()->propertyCache()) {
- QQmlPropertyData *data = d()->propertyCache()->method(d()->index);
- if (!data)
- return QV4::Encode::undefined();
- method = *data;
- } else {
- const QMetaObject *mo = d()->object()->metaObject();
- const QMetaMethod moMethod = mo->method(d()->index);
- method.load(moMethod);
-
- if (method.coreIndex() == -1)
- return QV4::Encode::undefined();
-
- // Look for overloaded methods
- QByteArray methodName = moMethod.name();
- const int methodOffset = mo->methodOffset();
- for (int ii = d()->index - 1; ii >= methodOffset; --ii) {
- if (methodName == mo->method(ii).name()) {
- method.setOverload(true);
- method.setOverrideIndexIsProperty(false);
- method.setOverrideIndex(ii);
- break;
- }
- }
+ object = QQmlObjectOrGadget(d()->metaObject(), d()->valueTypeWrapper->gadgetPtr());
}
Scope scope(v4);
JSCallData cData(scope, argc, argv, thisObject);
CallData *callData = cData.callData();
+ auto method = d()->methods[0];
+
if (method.isV4Function()) {
QV4::ScopedValue rv(scope, QV4::Value::undefinedValue());
QQmlV4Function func(callData, rv, v4);
@@ -2188,10 +2157,10 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
return rv->asReturnedValue();
}
- if (!method.isOverload()) {
+ if (d()->methodCount == 1) {
return CallPrecise(object, method, v4, callData);
} else {
- return CallOverloaded(object, method, v4, callData, d()->propertyCache());
+ return CallOverloaded(object, d()->methods, d()->methodCount, v4, callData);
}
}
@@ -2277,11 +2246,12 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
JSCallData cData(scope, argc, argv);
CallData *callData = cData.callData();
+ const QQmlObjectOrGadget objectOrGadget(mo);
+
if (d()->constructorCount == 1) {
- object = callConstructor(d()->constructors[0], v4, callData);
- }
- else {
- object = callOverloadedConstructor(v4, callData);
+ object = CallPrecise(objectOrGadget, d()->constructors[0], v4, callData, QMetaObject::CreateInstance);
+ } else {
+ object = CallOverloaded(objectOrGadget, d()->constructors, d()->constructorCount, v4, callData, QMetaObject::CreateInstance);
}
Scoped<QMetaObjectWrapper> metaObject(scope, this);
object->defineDefaultProperty(v4->id_constructor(), metaObject);
@@ -2290,78 +2260,6 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
}
-ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
-
- const QMetaObject* mo = d()->metaObject;
- const QQmlObjectOrGadget object(mo);
- return CallPrecise(object, data, engine, callArgs, QMetaObject::CreateInstance);
-}
-
-
-ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
- const int numberOfConstructors = d()->constructorCount;
- const int argumentCount = callArgs->argc();
- const QQmlObjectOrGadget object(d()->metaObject);
-
- QQmlPropertyData best;
- int bestParameterScore = INT_MAX;
- int bestMatchScore = INT_MAX;
-
- QV4::Scope scope(engine);
- QV4::ScopedValue v(scope);
-
- for (int i = 0; i < numberOfConstructors; i++) {
- const QQmlPropertyData & attempt = d()->constructors[i];
- QQmlMetaObject::ArgTypeStorage storage;
- int methodArgumentCount = 0;
- int *methodArgTypes = nullptr;
- if (attempt.hasArguments()) {
- int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, nullptr);
- if (!args) // Must be an unknown argument
- continue;
-
- methodArgumentCount = args[0];
- methodArgTypes = args + 1;
- }
-
- if (methodArgumentCount > argumentCount)
- continue; // We don't have sufficient arguments to call this method
-
- int methodParameterScore = argumentCount - methodArgumentCount;
- if (methodParameterScore > bestParameterScore)
- continue; // We already have a better option
-
- int methodMatchScore = 0;
- for (int ii = 0; ii < methodArgumentCount; ++ii) {
- methodMatchScore += MatchScore((v = QV4::Value::fromStaticValue(callArgs->args[ii])),
- methodArgTypes[ii]);
- }
-
- if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
- best = attempt;
- bestParameterScore = methodParameterScore;
- bestMatchScore = methodMatchScore;
- }
-
- if (bestParameterScore == 0 && bestMatchScore == 0)
- break; // We can't get better than that
- };
-
- if (best.isValid()) {
- return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance);
- } else {
- QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
- for (int i = 0; i < numberOfConstructors; i++) {
- const QQmlPropertyData & candidate = d()->constructors[i];
- error += QLatin1String("\n ") +
- QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex())
- .methodSignature());
- }
-
- return engine->throwError(error);
- }
-}
-
bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QMetaObjectWrapper>());
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index bc6fa09d4f..896de97c56 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -104,9 +104,15 @@ private:
DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
DECLARE_MARKOBJECTS(QObjectMethod);
+ QQmlPropertyData *methods;
+ int methodCount;
+ alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
+
void init(QV4::ExecutionContext *scope);
void destroy()
{
+ if (methods != reinterpret_cast<QQmlPropertyData *>(&_singleMethod))
+ delete[] methods;
setPropertyCache(nullptr);
qObj.destroy();
FunctionObject::destroy();
@@ -121,6 +127,8 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
_propertyCache = c;
}
+ void ensureMethodsCache();
+
const QMetaObject *metaObject();
QObject *object() const { return qObj.data(); }
void setObject(QObject *o) { qObj = o; }
@@ -317,9 +325,6 @@ protected:
private:
void init(ExecutionEngine *engine);
ReturnedValue constructInternal(const Value *argv, int argc) const;
- ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
- ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const;
-
};
struct Q_QML_EXPORT QmlSignalHandler : public QV4::Object