aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4jscall_p.h
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2023-09-19 16:43:11 +0200
committerUlf Hermann <ulf.hermann@qt.io>2023-09-28 16:23:04 +0200
commit4f1b9156a48e44cf1f127a4563d0ac69ab436f12 (patch)
treee4092d3574468a0af89efc39145e6fd0348230ae /src/qml/jsruntime/qv4jscall_p.h
parentf9271771cf6179a8b51ca90a2f863d6983c8fe0c (diff)
QML: Implement QObjectMethod::virtualCallWithMetaTypes
We can use the same mechanism we have in place when calling typed JavaScript functions. The type coercion is generalized and moved to qv4jscall_p.h. We also use the correct JavaScript coercion in the rare fallback case where the types are actually different. Fixes: QTBUG-113258 Change-Id: I30404ee0122433b47227b2fc0dc4b0e3862a99c7 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/jsruntime/qv4jscall_p.h')
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h159
1 files changed, 157 insertions, 2 deletions
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c11a0be6f3..f49b5a4a47 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -390,7 +390,7 @@ template<typename Callable>
ReturnedValue coerceAndCall(
ExecutionEngine *engine,
const Function::JSTypedFunction *typedFunction, const CompiledData::Function *compiledFunction,
- const Value *thisObject, const Value *argv, int argc, Callable call)
+ const Value *argv, int argc, Callable call)
{
Scope scope(engine);
@@ -402,10 +402,165 @@ ReturnedValue coerceAndCall(
typedFunction->argumentTypes[i], formals[i].type.isList());
}
- ScopedValue result(scope, call(thisObject, jsCallData.args, jsCallData.argc));
+ ScopedValue result(scope, call(jsCallData.args, jsCallData.argc));
return coerce(engine, result, typedFunction->returnType, compiledFunction->returnType.isList());
}
+// Note: \a to is unininitialized here! This is in contrast to most other related functions.
+inline void coerce(
+ ExecutionEngine *engine, QMetaType fromType, const void *from, QMetaType toType, void *to)
+{
+ if ((fromType.flags() & QMetaType::PointerToQObject)
+ && (toType.flags() & QMetaType::PointerToQObject)) {
+ QObject *fromObj = *static_cast<QObject * const*>(from);
+ *static_cast<QObject **>(to)
+ = (fromObj && fromObj->metaObject()->inherits(toType.metaObject()))
+ ? fromObj
+ : nullptr;
+ return;
+ }
+
+ if (toType == QMetaType::fromType<QVariant>()) {
+ new (to) QVariant(fromType, from);
+ return;
+ }
+
+ if (toType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ new (to) QJSPrimitiveValue(fromType, from);
+ return;
+ }
+
+ if (fromType == QMetaType::fromType<QVariant>()) {
+ const QVariant *fromVariant = static_cast<const QVariant *>(from);
+ if (fromVariant->metaType() == toType)
+ toType.construct(to, fromVariant->data());
+ else
+ coerce(engine, fromVariant->metaType(), fromVariant->data(), toType, to);
+ return;
+ }
+
+ if (fromType == QMetaType::fromType<QJSPrimitiveValue>()) {
+ const QJSPrimitiveValue *fromPrimitive = static_cast<const QJSPrimitiveValue *>(from);
+ if (fromPrimitive->metaType() == toType)
+ toType.construct(to, fromPrimitive->data());
+ else
+ coerce(engine, fromPrimitive->metaType(), fromPrimitive->data(), toType, to);
+ return;
+ }
+
+ // TODO: This is expensive. We might establish a direct C++-to-C++ type coercion, like we have
+ // for JS-to-JS. However, we shouldn't need this very often. Most of the time the compiler
+ // will generate code that passes the right arguments.
+ if (toType.flags() & QMetaType::NeedsConstruction)
+ toType.construct(to);
+ QV4::Scope scope(engine);
+ QV4::ScopedValue value(scope, engine->fromData(fromType, from));
+ if (!ExecutionEngine::metaTypeFromJS(value, toType, to))
+ QMetaType::convert(fromType, from, toType, to);
+}
+
+template<typename TypedFunction, typename Callable>
+void coerceAndCall(
+ ExecutionEngine *engine, const TypedFunction *typedFunction,
+ void **argv, const QMetaType *types, int argc, Callable call)
+{
+ const qsizetype numFunctionArguments = typedFunction->parameterCount();
+
+ Q_ALLOCA_DECLARE(void *, transformedArguments);
+ Q_ALLOCA_DECLARE(void, transformedResult);
+
+ const QMetaType returnType = typedFunction->returnMetaType();
+ const QMetaType frameReturn = types[0];
+ bool returnsQVariantWrapper = false;
+ if (argv[0] && returnType != frameReturn) {
+ Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *));
+ memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *));
+
+ if (frameReturn == QMetaType::fromType<QVariant>()) {
+ void *returnValue = argv[0];
+ new (returnValue) QVariant(returnType);
+ transformedResult = transformedArguments[0]
+ = static_cast<QVariant *>(returnValue)->data();
+ returnsQVariantWrapper = true;
+ } else if (returnType.sizeOf() > 0) {
+ Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
+ transformedArguments[0] = transformedResult;
+ } else {
+ transformedResult = transformedArguments[0] = &argc; // Some non-null marker value
+ }
+ }
+
+ for (qsizetype i = 0; i < numFunctionArguments; ++i) {
+ const bool isValid = argc > i;
+ const QMetaType frameType = isValid ? types[i + 1] : QMetaType();
+
+ const QMetaType argumentType = typedFunction->parameterMetaType(i);
+ if (isValid && argumentType == frameType)
+ continue;
+
+ if (transformedArguments == nullptr) {
+ Q_ALLOCA_ASSIGN(void *, transformedArguments, (numFunctionArguments + 1) * sizeof(void *));
+ memcpy(transformedArguments, argv, (argc + 1) * sizeof(void *));
+ }
+
+ if (argumentType.sizeOf() == 0) {
+ transformedArguments[i + 1] = nullptr;
+ continue;
+ }
+
+ void *frameVal = isValid ? argv[i + 1] : nullptr;
+ if (isValid && frameType == QMetaType::fromType<QVariant>()) {
+ QVariant *variant = static_cast<QVariant *>(frameVal);
+
+ const QMetaType variantType = variant->metaType();
+ if (variantType == argumentType) {
+ // Slightly nasty, but we're allowed to do this.
+ // We don't want to destruct() the QVariant's data() below.
+ transformedArguments[i + 1] = argv[i + 1] = variant->data();
+ } else {
+ Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
+ coerce(engine, variantType, variant->constData(), argumentType, arg);
+ transformedArguments[i + 1] = arg;
+ }
+ continue;
+ }
+
+ Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
+
+ if (isValid)
+ coerce(engine, frameType, frameVal, argumentType, arg);
+ else
+ argumentType.construct(arg);
+
+ transformedArguments[i + 1] = arg;
+ }
+
+ if (!transformedArguments) {
+ call(argv, numFunctionArguments);
+ return;
+ }
+
+ call(transformedArguments, numFunctionArguments);
+
+ if (transformedResult && !returnsQVariantWrapper) {
+ if (frameReturn.sizeOf() > 0)
+ coerce(engine, returnType, transformedResult, frameReturn, argv[0]);
+ if (returnType.flags() & QMetaType::NeedsDestruction)
+ returnType.destruct(transformedResult);
+ }
+
+ for (qsizetype i = 0; i < numFunctionArguments; ++i) {
+ void *arg = transformedArguments[i + 1];
+ if (arg == nullptr)
+ continue;
+ if (i >= argc || arg != argv[i + 1]) {
+ const QMetaType argumentType = typedFunction->parameterMetaType(i);
+ if (argumentType.flags() & QMetaType::NeedsDestruction)
+ argumentType.destruct(arg);
+ }
+ }
+}
+
} // namespace QV4
QT_END_NAMESPACE