aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
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
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')
-rw-r--r--src/qml/jsruntime/qv4function.cpp4
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp5
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h159
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp189
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h16
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h3
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp159
7 files changed, 396 insertions, 139 deletions
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index b0d937de90..44e05fbfcc 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -68,8 +68,8 @@ ReturnedValue Function::call(
});
case JsTyped:
return QV4::coerceAndCall(
- context->engine(), jsTypedFunction, compiledFunction, thisObject, argv, argc,
- [this, context](const Value *thisObject, const Value *argv, int argc) {
+ context->engine(), jsTypedFunction, compiledFunction, argv, argc,
+ [this, context, thisObject](const Value *argv, int argc) {
return doCall(this, thisObject, argv, argc, context);
});
default:
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 45ca75008c..0b11cdfe20 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -545,9 +545,8 @@ ReturnedValue ArrowFunction::virtualCall(const QV4::FunctionObject *fo, const Va
});
case Function::JsTyped:
return QV4::coerceAndCall(
- fo->engine(), function->jsTypedFunction, function->compiledFunction,
- thisObject, argv, argc,
- [fo](const Value *thisObject, const Value *argv, int argc) {
+ fo->engine(), function->jsTypedFunction, function->compiledFunction, argv, argc,
+ [fo, thisObject](const Value *argv, int argc) {
return qfoDoCall(fo, thisObject, argv, argc);
});
default:
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
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index 2b2a8c2ea4..176d696e9c 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -33,6 +33,7 @@
#include <private/qqmlscriptstring_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qqmlpropertybinding_p.h>
+#include <private/qqmlpropertycachemethodarguments_p.h>
#include <QtQml/qjsvalue.h>
#include <QtCore/qjsonarray.h>
@@ -1969,7 +1970,69 @@ static const QQmlPropertyData *ResolveOverloaded(
}
}
+static bool ExactMatch(QMetaType passed, QMetaType required, const void *data)
+{
+ if (required == QMetaType::fromType<QVariant>()
+ || required == QMetaType::fromType<QJSValue>()
+ || required == QMetaType::fromType<QJSManagedValue>()) {
+ return true;
+ }
+
+ if (data) {
+ if (passed == QMetaType::fromType<QVariant>())
+ passed = static_cast<const QVariant *>(data)->metaType();
+ else if (passed == QMetaType::fromType<QJSPrimitiveValue>())
+ passed = static_cast<const QJSPrimitiveValue *>(data)->metaType();
+ }
+
+ if (passed == required)
+ return true;
+
+ if (required == QMetaType::fromType<QJSPrimitiveValue>()) {
+ switch (passed.id()) {
+ case QMetaType::UnknownType:
+ case QMetaType::Nullptr:
+ case QMetaType::Bool:
+ case QMetaType::Int:
+ case QMetaType::Double:
+ case QMetaType::QString:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+static const QQmlPropertyData *ResolveOverloaded(
+ const QQmlPropertyData *methods, int methodCount,
+ void **argv, int argc, const QMetaType *types)
+{
+ // We only accept exact matches here. Everything else goes through the JavaScript conversion.
+ for (int i = 0; i < methodCount; ++i) {
+ const QQmlPropertyData *attempt = methods + i;
+ if (types[0].isValid() && !ExactMatch(attempt->propType(), types[0], nullptr))
+ continue;
+
+ const QMetaMethod method = attempt->metaMethod();
+ if (method.parameterCount() != argc)
+ continue;
+
+ bool valid = true;
+ for (int i = 0; i < argc; ++i) {
+ if (!ExactMatch(types[i + 1], method.parameterMetaType(i), argv[i + 1])) {
+ valid = false;
+ break;
+ }
+ }
+ if (valid)
+ return attempt;
+ }
+
+ return nullptr;
+}
void CallArgument::cleanup()
{
@@ -2565,6 +2628,7 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
QQmlPropertyData dummy;
QMetaMethod method = mo->method(index);
dummy.load(method);
+ dummy.setMetaObject(mo);
resolvedMethods.append(dummy);
// Look for overloaded methods
QByteArray methodName = method.name();
@@ -2616,12 +2680,20 @@ ReturnedValue QObjectMethod::method_destroy(
return Encode::undefined();
}
-ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue QObjectMethod::virtualCall(
+ const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
return This->callInternal(thisObject, argv, argc);
}
+void QObjectMethod::virtualCallWithMetaTypes(
+ const FunctionObject *m, QObject *thisObject, void **argv, const QMetaType *types, int argc)
+{
+ const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
+ This->callInternalWithMetaTypes(thisObject, argv, types, argc);
+}
+
ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const
{
ExecutionEngine *v4 = engine();
@@ -2733,6 +2805,121 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
return doCall([&]() { return CallPrecise(object, *method, v4, callData); });
}
+struct ToStringMetaMethod
+{
+ constexpr int parameterCount() const { return 0; }
+ constexpr QMetaType returnMetaType() const { return QMetaType::fromType<QString>(); }
+ constexpr QMetaType parameterMetaType(int) const { return QMetaType(); }
+};
+
+void QObjectMethod::callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const
+{
+ ExecutionEngine *v4 = engine();
+
+ const QMetaObject *thisMeta = nullptr;
+ Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
+
+ if (thisObject) {
+ thisMeta = thisObject->metaObject();
+ } else {
+ Q_ASSERT(Value::fromHeapObject(d()->wrapper).as<QQmlValueTypeWrapper>());
+ valueWrapper = d()->wrapper.cast<Heap::QQmlValueTypeWrapper>();
+ thisMeta = valueWrapper->metaObject();
+ }
+
+ QQmlObjectOrGadget object = [&](){
+ if (thisObject)
+ return QQmlObjectOrGadget(thisObject);
+
+ Scope scope(v4);
+ Scoped<QQmlValueTypeWrapper> wrapper(scope, d()->wrapper);
+ Q_ASSERT(wrapper);
+
+ Heap::QQmlValueTypeWrapper *valueWrapper = wrapper->d();
+ if (!valueWrapper->enforcesLocation())
+ QV4::ReferenceObject::readReference(valueWrapper);
+ return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
+ }();
+
+ if (object.isNull())
+ return;
+
+ if (d()->index == DestroyMethod) {
+ // method_destroy will use at most one argument
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, std::min(argc, 1),
+ [this, v4, object](const Value *thisObject, const Value *argv, int argc) {
+ Q_UNUSED(thisObject);
+ return method_destroy(v4, object.qObject(), argv, argc);
+ });
+ return;
+ }
+
+ if (d()->index == ToStringMethod) {
+ const ToStringMetaMethod metaMethod;
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, thisMeta, object](void **argv, int) {
+ *static_cast<QString *>(argv[0])
+ = QObjectWrapper::objectToString(v4, thisMeta, object.qObject());
+ });
+ return;
+ }
+
+ d()->ensureMethodsCache(thisMeta);
+
+ const QQmlPropertyData *method = d()->methods;
+ if (d()->methodCount != 1) {
+ Q_ASSERT(d()->methodCount > 0);
+ method = ResolveOverloaded(d()->methods, d()->methodCount, argv, argc, types);
+ }
+
+ if (!method || method->isV4Function()) {
+ QV4::convertAndCall(
+ v4, thisObject, argv, types, argc,
+ [this](const Value *thisObject, const Value *argv, int argc) {
+ return callInternal(thisObject, argv, argc);
+ });
+ } else {
+ const QMetaMethod metaMethod = method->metaMethod();
+ QV4::coerceAndCall(
+ v4, &metaMethod, argv, types, argc,
+ [v4, object, valueWrapper, method](void **argv, int argc) {
+ Q_UNUSED(argc);
+
+ // If we call the method, we have to write back any value type references afterwards.
+ // The method might change the value.
+ object.metacall(QMetaObject::InvokeMetaMethod, method->coreIndex(), argv);
+ if (!method->isConstant()) {
+ if (valueWrapper && valueWrapper->isReference())
+ valueWrapper->writeBack();
+ }
+
+ // If the method returns a QObject* we need to track it on the JS heap
+ // (if it's destructible).
+ QObject *qobjectPtr = nullptr;
+ const QMetaType resultType = method->propType();
+ if (resultType.flags() & QMetaType::PointerToQObject) {
+ qobjectPtr = *static_cast<QObject **>(argv[0]);
+ } else if (resultType == QMetaType::fromType<QVariant>()) {
+ const QVariant *result = static_cast<const QVariant *>(argv[0]);
+ const QMetaType variantType = result->metaType();
+ if (variantType.flags() & QMetaType::PointerToQObject)
+ qobjectPtr = *static_cast<QObject *const *>(result->data());
+ }
+
+ if (qobjectPtr) {
+ QQmlData *ddata = QQmlData::get(qobjectPtr, true);
+ if (!ddata->explicitIndestructibleSet) {
+ ddata->indestructible = false;
+ QObjectWrapper::wrap(v4, qobjectPtr);
+ }
+ }
+ });
+ }
+}
+
DEFINE_OBJECT_VTABLE(QObjectMethod);
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index bec8b955e5..7a2dbe7745 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -366,10 +366,20 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine, QObject *o) const;
QV4::ReturnedValue method_destroy(
QV4::ExecutionEngine *ctx, QObject *o, const Value *args, int argc) const;
+ void method_destroy(
+ QV4::ExecutionEngine *engine, QObject *o,
+ void **argv, const QMetaType *types, int argc) const;
- static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
-
- ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
+ static ReturnedValue virtualCall(
+ const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static void virtualCallWithMetaTypes(
+ const FunctionObject *m, QObject *thisObject,
+ void **argv, const QMetaType *types, int argc);
+
+ ReturnedValue callInternal(
+ const Value *thisObject, const Value *argv, int argc) const;
+ void callInternalWithMetaTypes(
+ QObject *thisObject, void **argv, const QMetaType *types, int argc) const;
static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function);
};
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
index 3effd3c236..449d4f095d 100644
--- a/src/qml/jsruntime/qv4stackframe_p.h
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -144,6 +144,9 @@ struct Q_QML_PRIVATE_EXPORT MetaTypesStackFrame : public CppStackFrame
const QMetaType *argTypes() const { return metaTypes + 1; }
void **argv() const { return returnAndArgs + 1; }
+ const QMetaType *returnAndArgTypes() const { return metaTypes; }
+ void **returnAndArgValues() const { return returnAndArgs; }
+
QObject *thisObject() const { return CppStackFrameBase::thisObject; }
ExecutionContext *context() const { return CppStackFrameBase::context; }
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index 55f2261b4d..8c063fdcf5 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -395,6 +395,21 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
} \
} while (false)
+struct AOTCompiledMetaMethod
+{
+public:
+ AOTCompiledMetaMethod(const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction)
+ : aotCompiledFunction(aotCompiledFunction)
+ {}
+
+ int parameterCount() const { return aotCompiledFunction->argumentTypes.size(); }
+ QMetaType returnMetaType() const { return aotCompiledFunction->returnType; }
+ QMetaType parameterMetaType(int i) const { return aotCompiledFunction->argumentTypes[i]; }
+
+private:
+ const QQmlPrivate::AOTCompiledFunction *aotCompiledFunction = nullptr;
+};
+
void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
{
qt_v4ResolvePendingBreakpointsHook();
@@ -412,136 +427,24 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
function->compiledFunction->location.column());
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
- const qsizetype numFunctionArguments = function->aotCompiledFunction->argumentTypes.size();
-
- Q_ALLOCA_DECLARE(void *, transformedArguments);
- for (qsizetype i = 0; i < numFunctionArguments; ++i) {
- const bool isValid = frame->argc() > i;
- const QMetaType frameType = isValid ? frame->argTypes()[i] : QMetaType();
-
- const QMetaType argumentType = function->aotCompiledFunction->argumentTypes[i];
- if (isValid && argumentType == frameType)
- continue;
-
- if (transformedArguments == nullptr) {
- Q_ALLOCA_ASSIGN(void *, transformedArguments, numFunctionArguments * sizeof(void *));
- memcpy(transformedArguments, frame->argv(), frame->argc() * sizeof(void *));
- }
-
- if (argumentType.sizeOf() == 0) {
- transformedArguments[i] = nullptr;
- continue;
- }
-
- void *frameVal = isValid ? frame->argv()[i] : 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] = frame->argv()[i] = variant->data();
- } else {
- Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
- argumentType.construct(arg);
- QMetaType::convert(variantType, variant->data(), argumentType, arg);
- transformedArguments[i] = arg;
- }
- continue;
- }
-
- Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
-
- if (argumentType == QMetaType::fromType<QVariant>()) {
- if (isValid)
- new (arg) QVariant(frameType, frameVal);
- else
- new (arg) QVariant();
- } else if (argumentType == QMetaType::fromType<QJSPrimitiveValue>()) {
- if (isValid)
- new (arg) QJSPrimitiveValue(frameType, frameVal);
- else
- new (arg) QJSPrimitiveValue();
- } else {
-
- argumentType.construct(arg);
- if (isValid)
- QMetaType::convert(frameType, frameVal, argumentType, arg);
+ const AOTCompiledMetaMethod method(function->aotCompiledFunction);
+ QV4::coerceAndCall(
+ engine, &method, frame->returnAndArgValues(),
+ frame->returnAndArgTypes(), frame->argc(),
+ [frame, engine, function](void **argv, int argc) {
+ Q_UNUSED(argc);
+
+ QQmlPrivate::AOTCompiledContext aotContext;
+ if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) {
+ QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
+ aotContext.qmlScopeObject = wrapper->scopeObject;
+ aotContext.qmlContext = wrapper->context;
}
- transformedArguments[i] = arg;
- }
-
- const QMetaType returnType = function->aotCompiledFunction->returnType;
- const QMetaType frameReturn = frame->returnType();
- bool returnsQVariantWrapper = false;
- Q_ALLOCA_DECLARE(void, transformedResult);
- if (frame->returnValue() && returnType != frameReturn) {
- if (frameReturn == QMetaType::fromType<QVariant>()) {
- void *returnValue = frame->returnValue();
- new (returnValue) QVariant(returnType);
- transformedResult = static_cast<QVariant *>(returnValue)->data();
- returnsQVariantWrapper = true;
- } else if (returnType.sizeOf() > 0) {
- Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
- } else {
- transformedResult = frame; // Some non-null marker value
- }
- }
-
- QQmlPrivate::AOTCompiledContext aotContext;
- if (auto context = QV4::ExecutionEngine::qmlContext(frame->context()->d())) {
- QV4::Heap::QQmlContextWrapper *wrapper = static_cast<Heap::QmlContext *>(context)->qml();
- aotContext.qmlScopeObject = wrapper->scopeObject;
- aotContext.qmlContext = wrapper->context;
- }
-
- aotContext.engine = engine->jsEngine();
- aotContext.compilationUnit = function->executableCompilationUnit();
-
- function->aotCompiledFunction->functionPtr(
- &aotContext, transformedResult ? transformedResult : frame->returnValue(),
- transformedArguments ? transformedArguments : frame->argv());
-
- if (transformedResult && !returnsQVariantWrapper) {
- // Shortcut the common case of the AOT function returning a more generic QObject pointer
- // that we need to QObject-cast. No need to construct or destruct anything in that case.
- if ((frameReturn.flags() & QMetaType::PointerToQObject)
- && (returnType.flags() & QMetaType::PointerToQObject)) {
- QObject *resultObj = *static_cast<QObject **>(transformedResult);
- *static_cast<QObject **>(frame->returnValue())
- = (resultObj && resultObj->metaObject()->inherits(frameReturn.metaObject()))
- ? resultObj
- : nullptr;
- } else if (returnType == QMetaType::fromType<QVariant>()) {
- const QVariant *resultVariant = static_cast<QVariant *>(transformedResult);
- if (resultVariant->metaType() == frameReturn) {
- frameReturn.construct(frame->returnValue(), resultVariant->data());
- } else {
- // Convert needs a pre-constructed target.
- frameReturn.construct(frame->returnValue());
- QMetaType::convert(resultVariant->metaType(), resultVariant->data(),
- frameReturn, frame->returnValue());
- }
- resultVariant->~QVariant();
- } else {
- // Convert needs a pre-constructed target.
- frameReturn.construct(frame->returnValue());
- QMetaType::convert(returnType, transformedResult, frameReturn, frame->returnValue());
- returnType.destruct(transformedResult);
- }
- }
-
- if (transformedArguments) {
- for (int i = 0; i < numFunctionArguments; ++i) {
- void *arg = transformedArguments[i];
- if (arg == nullptr)
- continue;
- if (i >= frame->argc() || arg != frame->argv()[i])
- function->aotCompiledFunction->argumentTypes[i].destruct(arg);
- }
- }
+ aotContext.engine = engine->jsEngine();
+ aotContext.compilationUnit = function->executableCompilationUnit();
+ function->aotCompiledFunction->functionPtr(&aotContext, argv[0], argv + 1);
+ });
}
ReturnedValue VME::exec(JSTypesStackFrame *frame, ExecutionEngine *engine)