bool QMetaMethod::invoke(QObject *object,
Qt::ConnectionType connectionType,
QGenericReturnArgument returnValue,
QGenericArgument val0,
QGenericArgument val1,
QGenericArgument val2,
QGenericArgument val3,
QGenericArgument val4,
QGenericArgument val5,
QGenericArgument val6,
QGenericArgument val7,
QGenericArgument val8,
QGenericArgument val9) const
{
if (!object || !mobj)
return false;
// check argument count (we don't allow invoking a method if given too few arguments)
const char *typeNames[] = {
returnValue.name(),
val0.name(),
val1.name(),
val2.name(),
val3.name(),
val4.name(),
val5.name(),
val6.name(),
val7.name(),
val8.name(),
val9.name()
};
void *param[] = {
returnValue.data(),
val0.data(),
val1.data(),
val2.data(),
val3.data(),
val4.data(),
val5.data(),
val6.data(),
val7.data(),
val8.data(),
val9.data()
};
int paramCount;
for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
if (qstrlen(typeNames[paramCount]) <= 0)
break;
}
return invokeImpl(*this, object, connectionType, paramCount, param, typeNames, nullptr);
}
bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType connectionType,
qsizetype paramCount, const void *const *parameters,
const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes)
{
if (!target || !self.mobj)
return false;
QMetaMethodPrivate::InvokeFailReason r =
QMetaMethodPrivate::invokeImpl(self, target, connectionType, paramCount, parameters,
typeNames, metaTypes);
if (Q_LIKELY(r == QMetaMethodPrivate::InvokeFailReason::None))
return true;
if (int(r) >= int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch)) {
int n = int(r) - int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch);
qWarning("QMetaMethod::invoke: cannot convert formal parameter %d from %s in call to %s::%s",
n, typeNames[n + 1] ? typeNames[n + 1] : metaTypes[n + 1]->name,
self.mobj->className(), self.methodSignature().constData());
}
if (r == QMetaMethodPrivate::InvokeFailReason::TooFewArguments) {
qWarning("QMetaMethod::invoke: too few arguments (%d) in call to %s::%s",
int(paramCount), self.mobj->className(), self.methodSignature().constData());
}
return false;
}
auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
Qt::ConnectionType connectionType,
qsizetype paramCount, const void *const *parameters,
const char *const *typeNames,
const QtPrivate::QMetaTypeInterface *const *metaTypes) -> InvokeFailReason
{
auto object = static_cast<QObject *>(target);
auto priv = QMetaMethodPrivate::get(&self);
constexpr bool MetaTypesAreOptional = QT_VERSION < QT_VERSION_CHECK(7, 0, 0);
auto methodMetaTypes = priv->parameterMetaTypeInterfaces();
auto param = const_cast<void **>(parameters);
Q_ASSERT(priv->mobj);
Q_ASSERT(self.methodType() == Constructor || object);
Q_ASSERT(self.methodType() == Constructor || connectionType == Qt::ConnectionType(-1) ||
priv->mobj->cast(object));
Q_ASSERT(paramCount >= 1); // includes the return type
Q_ASSERT(parameters);
Q_ASSERT(typeNames);
Q_ASSERT(MetaTypesAreOptional || metaTypes);
if ((paramCount - 1) < qsizetype(priv->data.argc()))
return InvokeFailReason::TooFewArguments;
// 0 is the return type, 1 is the first formal parameter
auto checkTypesAreCompatible = [=](int idx) {
uint typeInfo = priv->parameterTypeInfo(idx - 1);
QLatin1StringView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
if ((typeInfo & IsUnresolvedType) == 0) {
// this is a built-in type
if (MetaTypesAreOptional && !metaTypes)
return int(typeInfo) == QMetaType::fromName(userTypeName).id();
return int(typeInfo) == metaTypes[idx]->typeId;
}
QLatin1StringView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
if ((MetaTypesAreOptional && !metaTypes) || !metaTypes[idx]) {
// compatibility call, compare strings
if (methodTypeName == userTypeName)
return true;
// maybe the user type needs normalization
QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
return methodTypeName == QLatin1StringView(normalized);
}
QMetaType userType(metaTypes[idx]);
Q_ASSERT(userType.isValid());
if (QMetaType(methodMetaTypes[idx - 1]) == userType)
return true;
// if the parameter type was NOT only forward-declared, it MUST have
// matched
if (methodMetaTypes[idx - 1])
return false;
// resolve from the name moc stored for us
QMetaType resolved = QMetaType::fromName(methodTypeName);
return resolved == userType;
};
// force all types to be registered, just in case
for (qsizetype i = 0; metaTypes && i < paramCount; ++i)
QMetaType(metaTypes[i]).registerType();
// check formal parameters first (overload set)
for (qsizetype i = 1; i < paramCount; ++i) {
if (!checkTypesAreCompatible(i))
return InvokeFailReason(int(InvokeFailReason::FormalParameterMismatch) + i - 1);
}
// handle constructors first
if (self.methodType() == Constructor) {
if (object) {
qWarning("QMetaMethod::invokeMethod: cannot call constructor %s on object %p",
self.methodSignature().constData(), object);
return InvokeFailReason::ConstructorCallOnObject;
}
if (!parameters[0]) {
qWarning("QMetaMethod::invokeMethod: constructor call to %s must assign a return type",
self.methodSignature().constData());
return InvokeFailReason::ConstructorCallWithoutResult;
}
if (!MetaTypesAreOptional || metaTypes) {
if (metaTypes[0]->typeId != QMetaType::QObjectStar) {
qWarning("QMetaMethod::invokeMethod: cannot convert QObject* to %s on constructor call %s",
metaTypes[0]->name, self.methodSignature().constData());
return InvokeFailReason::ReturnTypeMismatch;
}
}
int idx = priv->ownConstructorMethodIndex();
if (priv->mobj->static_metacall(QMetaObject::CreateInstance, idx, param) >= 0)
return InvokeFailReason::ConstructorCallFailed;
return {};
}
// regular type - check return type
if (parameters[0]) {
if (!checkTypesAreCompatible(0)) {
const char *retType = typeNames[0] ? typeNames[0] : metaTypes[0]->name;
qWarning("QMetaMethod::invokeMethod: return type mismatch for method %s::%s:"
" cannot convert from %s to %s during invocation",
priv->mobj->className(), priv->methodSignature().constData(),
priv->rawReturnTypeName(), retType);
return InvokeFailReason::ReturnTypeMismatch;
}
}
Qt::HANDLE currentThreadId = nullptr;
QThread *objectThread = nullptr;
auto receiverInSameThread = [&]() {
if (!currentThreadId) {
currentThreadId = QThread::currentThreadId();
objectThread = object->thread();
}
if (objectThread)
return currentThreadId == QThreadData::get2(objectThread)->threadId.loadRelaxed();
return false;
};
// check connection type
if (connectionType == Qt::AutoConnection)
connectionType = receiverInSameThread() ? Qt::DirectConnection : Qt::QueuedConnection;
else if (connectionType == Qt::ConnectionType(-1))
connectionType = Qt::DirectConnection;
#if !QT_CONFIG(thread)
if (connectionType == Qt::BlockingQueuedConnection) {
connectionType = Qt::DirectConnection;
}
#endif
// invoke!
int idx_relative = priv->ownMethodIndex();
int idx_offset = priv->mobj->methodOffset();
QObjectPrivate::StaticMetaCallFunction callFunction = priv->mobj->d.static_metacall;
if (connectionType == Qt::DirectConnection) {
if (callFunction)
callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
else if (QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) >= 0)
return InvokeFailReason::CallViaVirtualFailed;
} else if (connectionType == Qt::QueuedConnection) {
if (parameters[0]) {
qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
"queued connections");
return InvokeFailReason::CouldNotQueueParameter;
}
auto event = std::make_unique<QMetaCallEvent>(idx_offset, idx_relative, callFunction, nullptr, -1, paramCount);
QMetaType *types = event->types();
void **args = event->args();
// fill in the meta types first
for (int i = 1; i < paramCount; ++i) {
types[i] = QMetaType(methodMetaTypes[i - 1]);
if (!types[i].iface() && (!MetaTypesAreOptional || metaTypes))
types[i] = QMetaType(metaTypes[i]);
if (!types[i].iface())
types[i] = priv->parameterMetaType(i - 1);
if (!types[i].iface() && typeNames[i])
types[i] = QMetaType::fromName(typeNames[i]);
if (!types[i].iface()) {
qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
typeNames[i]);
return InvokeFailReason(int(InvokeFailReason::CouldNotQueueParameter) - i);
}
}
// now create copies of our parameters using those meta types
for (int i = 1; i < paramCount; ++i)
args[i] = types[i].create(parameters[i]);
QCoreApplication::postEvent(object, event.release());
} else { // blocking queued connection
#if QT_CONFIG(thread)
if (receiverInSameThread()) {
qWarning("QMetaMethod::invoke: Dead lock detected in BlockingQueuedConnection: "
"Receiver is %s(%p)", priv->mobj->className(), object);
return InvokeFailReason::DeadLockDetected;
}
QSemaphore semaphore;
QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
nullptr, -1, param, &semaphore));
semaphore.acquire();
#endif // QT_CONFIG(thread)
}
return {};
}