diff options
author | Ulf Hermann <ulf.hermann@qt.io> | 2023-09-19 16:43:11 +0200 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2023-09-28 16:23:04 +0200 |
commit | 4f1b9156a48e44cf1f127a4563d0ac69ab436f12 (patch) | |
tree | e4092d3574468a0af89efc39145e6fd0348230ae /src | |
parent | f9271771cf6179a8b51ca90a2f863d6983c8fe0c (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')
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4functionobject.cpp | 5 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4jscall_p.h | 159 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper.cpp | 189 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4qobjectwrapper_p.h | 16 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4stackframe_p.h | 3 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 159 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertydata_p.h | 44 |
9 files changed, 435 insertions, 146 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) diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index ae181b23dc..c4d233f9cc 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -80,8 +80,6 @@ void QQmlPropertyData::load(const QMetaProperty &p) void QQmlPropertyData::load(const QMetaMethod &m) { setCoreIndex(m.methodIndex()); - setArguments(nullptr); - setPropType(m.returnMetaType()); m_flags.setType(Flags::FunctionType); diff --git a/src/qml/qml/qqmlpropertydata_p.h b/src/qml/qml/qqmlpropertydata_p.h index 74f8afc281..760e577ae1 100644 --- a/src/qml/qml/qqmlpropertydata_p.h +++ b/src/qml/qml/qqmlpropertydata_p.h @@ -80,7 +80,7 @@ public: unsigned isRequiredORisCloned : 1; // Has REQUIRED flag OR The function was marked as cloned unsigned isConstructorORisBindable : 1; // The function was marked is a constructor OR property is backed by QProperty<T> unsigned isOverridden : 1; // Is overridden by a extension property - unsigned reserved : 1; + unsigned hasMetaObject : 1; unsigned type : 3; // stores an entry of Types // Internal QQmlPropertyCache flags @@ -173,6 +173,10 @@ public: isConstructorORisBindable = b; } + void setHasMetaObject(bool b) { + hasMetaObject = b; + } + void setType(Type newType) { type = newType; } @@ -212,6 +216,7 @@ public: bool isVMESignal() const { return isFunction() && m_flags.isAliasORisVMESignal; } bool isV4Function() const { return isFunction() && m_flags.isFinalORisV4Function; } bool isSignalHandler() const { return m_flags.isSignalHandler; } + bool hasMetaObject() const { return m_flags.hasMetaObject; } // TODO: Remove this once we can. Signals should not be overridable. bool isOverridableSignal() const { return m_flags.isOverridableSignal; } @@ -280,8 +285,36 @@ public: QTypeRevision typeVersion() const { return m_typeVersion; } void setTypeVersion(QTypeRevision typeVersion) { m_typeVersion = typeVersion; } - QQmlPropertyCacheMethodArguments *arguments() const { return m_arguments; } - void setArguments(QQmlPropertyCacheMethodArguments *args) { m_arguments = args; } + QQmlPropertyCacheMethodArguments *arguments() const + { + Q_ASSERT(!hasMetaObject()); + return m_arguments; + } + void setArguments(QQmlPropertyCacheMethodArguments *args) + { + Q_ASSERT(!hasMetaObject()); + m_arguments = args; + } + + const QMetaObject *metaObject() const + { + Q_ASSERT(hasMetaObject()); + return m_metaObject; + } + + void setMetaObject(const QMetaObject *metaObject) + { + Q_ASSERT(!hasArguments() || !m_arguments); + m_flags.setHasMetaObject(true); + m_metaObject = metaObject; + } + + QMetaMethod metaMethod() const + { + Q_ASSERT(hasMetaObject()); + Q_ASSERT(isFunction()); + return m_metaObject->method(m_coreIndex); + } int metaObjectOffset() const { return m_metaObjectOffset; } void setMetaObjectOffset(int off) @@ -393,6 +426,7 @@ private: union { QQmlPropertyCacheMethodArguments *m_arguments = nullptr; StaticMetaCallFunction m_staticMetaCallFunction; + const QMetaObject *m_metaObject; }; }; @@ -426,11 +460,10 @@ QQmlPropertyData::Flags::Flags() , isRequiredORisCloned(false) , isConstructorORisBindable(false) , isOverridden(false) - , reserved(false) + , hasMetaObject(false) , type(OtherType) , overrideIndexIsProperty(false) { - Q_UNUSED(reserved) } bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) const @@ -444,6 +477,7 @@ bool QQmlPropertyData::Flags::operator==(const QQmlPropertyData::Flags &other) c isOverridden == other.isOverridden && isSignalHandler == other.isSignalHandler && isRequiredORisCloned == other.isRequiredORisCloned && + hasMetaObject == other.hasMetaObject && type == other.type && isConstructorORisBindable == other.isConstructorORisBindable && overrideIndexIsProperty == other.overrideIndexIsProperty; |