class QMetaMethodPrivate : public QMetaMethodInvoker
{
public:
static const QMetaMethodPrivate *get(const QMetaMethod *q)
{ return static_cast<const QMetaMethodPrivate *>(q); }
inline QByteArray signature() const;
inline QByteArray name() const;
inline int typesDataIndex() const;
inline const char *rawReturnTypeName() const;
inline int returnType() const;
inline int parameterCount() const;
inline int parametersDataIndex() const;
inline uint parameterTypeInfo(int index) const;
inline int parameterType(int index) const;
inline void getParameterTypes(int *types) const;
inline const QtPrivate::QMetaTypeInterface *returnMetaTypeInterface() const;
inline const QtPrivate::QMetaTypeInterface *const *parameterMetaTypeInterfaces() const;
inline QByteArray parameterTypeName(int index) const;
inline QList<QByteArray> parameterTypes() const;
inline QList<QByteArray> parameterNames() const;
inline QByteArray tag() const;
inline int ownMethodIndex() const;
inline int ownConstructorMethodIndex() const;
private:
void checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface, int index) const;
QMetaMethodPrivate();
};
} // unnamed namespace
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 {};
}