#include "qt_provider_object.h" #include "../core/interface.h" #include "logos_api.h" #include "token_manager.h" #include "logos_types.h" #include #include #include #include #include #include #include #include // ── QMetaObject dispatch helpers (moved from module_proxy.cpp) ────────────── #define INVOKE_METHOD_WITH_RETURN(returnType, castType) \ do { \ castType* result = static_cast(returnValue); \ switch (args.size()) { \ case 0: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result)); \ case 1: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg); \ case 2: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg); \ case 3: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg); \ case 4: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg); \ case 5: \ return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg); \ default: \ qWarning() << "QtProviderObject: Currently supports 0-5 arguments. Got:" << args.size(); \ return false; \ } \ } while(0) namespace { class ScopedQArg { public: ScopedQArg(QMetaMethodArgument a, std::function d) : arg(a), deleter(std::move(d)) {} ~ScopedQArg() { if (deleter) { deleter(arg.data); } } ScopedQArg(ScopedQArg&& other) : arg(std::move(other.arg)), deleter(std::move(other.deleter)) { other.deleter = nullptr; } ScopedQArg& operator=(ScopedQArg&&) = delete; ScopedQArg(const ScopedQArg&) = delete; ScopedQArg& operator=(const ScopedQArg&) = delete; QMetaMethodArgument arg; private: std::function deleter; }; auto toScopedQArgs(const QVariantList& args) { auto scopedArgs = std::vector{}; for (const auto& arg : args) { switch (arg.typeId()) { case QMetaType::Int: { auto value = new int{arg.toInt()}; scopedArgs.emplace_back( Q_ARG(int, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::QStringList: { auto value = new QStringList{arg.toStringList()}; scopedArgs.emplace_back( Q_ARG(QStringList, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::QByteArray: { auto value = new QByteArray{arg.toByteArray()}; scopedArgs.emplace_back( Q_ARG(QByteArray, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::QUrl: { auto value = new QUrl{arg.toUrl()}; scopedArgs.emplace_back( Q_ARG(QUrl, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::Bool: { auto value = new bool{arg.toBool()}; scopedArgs.emplace_back( Q_ARG(bool, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::Double: { auto value = new double{arg.toDouble()}; scopedArgs.emplace_back( Q_ARG(double, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::Float: { auto value = new float{arg.toFloat()}; scopedArgs.emplace_back( Q_ARG(float, *value), [](const void* data) { delete static_cast(data); } ); break; } case QMetaType::QString: default: { auto value = new QString{arg.toString()}; scopedArgs.emplace_back( Q_ARG(QString, *value), [](const void* data) { delete static_cast(data); } ); break; } } } return scopedArgs; } bool invokeMethodByArgCount(QObject *module, const QString& methodName, const QVariantList& args, void* returnValue, const char* returnTypeName) { QByteArray methodNameBytes = methodName.toUtf8(); const char* methodNameCStr = methodNameBytes.constData(); auto scopedArgs = toScopedQArgs(args); if (returnValue == nullptr) { switch (args.size()) { case 0: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection); case 1: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg); case 2: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg); case 3: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg); case 4: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg); case 5: return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg); default: qWarning() << "QtProviderObject: Currently supports 0-5 arguments. Got:" << args.size(); return false; } } else if (strcmp(returnTypeName, "bool") == 0) { INVOKE_METHOD_WITH_RETURN(bool, bool); } else if (strcmp(returnTypeName, "int") == 0) { INVOKE_METHOD_WITH_RETURN(int, int); } else if (strcmp(returnTypeName, "double") == 0) { INVOKE_METHOD_WITH_RETURN(double, double); } else if (strcmp(returnTypeName, "float") == 0) { INVOKE_METHOD_WITH_RETURN(float, float); } else if (strcmp(returnTypeName, "QString") == 0) { INVOKE_METHOD_WITH_RETURN(QString, QString); } else if (strcmp(returnTypeName, "LogosResult") == 0) { INVOKE_METHOD_WITH_RETURN(LogosResult, LogosResult); } else if (strcmp(returnTypeName, "QVariant") == 0) { INVOKE_METHOD_WITH_RETURN(QVariant, QVariant); } else if (strcmp(returnTypeName, "QJsonArray") == 0) { INVOKE_METHOD_WITH_RETURN(QJsonArray, QJsonArray); } else if (strcmp(returnTypeName, "QVariantList") == 0) { INVOKE_METHOD_WITH_RETURN(QVariantList, QVariantList); } else if (strcmp(returnTypeName, "QVariantMap") == 0) { INVOKE_METHOD_WITH_RETURN(QVariantMap, QVariantMap); } else if (strcmp(returnTypeName, "QStringList") == 0) { INVOKE_METHOD_WITH_RETURN(QStringList, QStringList); } else { qWarning() << "QtProviderObject: Unsupported return type:" << returnTypeName; return false; } } } // ── QtProviderObject implementation ───────────────────────────────────────── QtProviderObject::QtProviderObject(QObject* module, QObject* parent) : QObject(parent) , m_module(module) { if (m_module) { connect(m_module, SIGNAL(eventResponse(QString, QVariantList)), this, SLOT(onWrappedEventResponse(QString, QVariantList))); qDebug() << "[LogosProviderObject] QtProviderObject: connected to QObject eventResponse signal"; } } QtProviderObject::~QtProviderObject() { qDebug() << "[LogosProviderObject] QtProviderObject: destroyed"; } void QtProviderObject::onWrappedEventResponse(const QString& eventName, const QVariantList& data) { if (m_eventCallback) { m_eventCallback(eventName, data); } } void QtProviderObject::init(void* apiInstance) { if (!m_module) return; LogosAPI* api = static_cast(apiInstance); int methodIndex = m_module->metaObject()->indexOfMethod("initLogos(LogosAPI*)"); if (methodIndex != -1) { qDebug() << "[LogosProviderObject] QtProviderObject: calling initLogos on wrapped QObject"; QMetaObject::invokeMethod(m_module, "initLogos", Qt::DirectConnection, Q_ARG(LogosAPI*, api)); } else { qDebug() << "[LogosProviderObject] QtProviderObject: wrapped QObject has no initLogos, skipping"; } } QString QtProviderObject::providerName() const { PluginInterface* pi = qobject_cast(m_module); return pi ? pi->name() : QString(); } QString QtProviderObject::providerVersion() const { PluginInterface* pi = qobject_cast(m_module); return pi ? pi->version() : QString(); } void QtProviderObject::setEventListener(EventCallback callback) { m_eventCallback = std::move(callback); } QVariant QtProviderObject::callMethod(const QString& methodName, const QVariantList& args) { if (!m_module) { qWarning() << "[LogosProviderObject] QtProviderObject::callMethod: null module"; return QVariant(); } if (methodName.isEmpty()) { qWarning() << "[LogosProviderObject] QtProviderObject::callMethod: empty method name"; return QVariant(); } // Special-case getPluginMethods (framework-level, not on the wrapped plugin) if (methodName == "getPluginMethods" && args.isEmpty()) { return QVariant(getMethods()); } // Special-case getPluginEvents / getPluginInterface. Legacy Qt modules have // no logos_events: section, so getMethods() (built here from QMetaObject) // only ever contains methods: events are always empty and the interface is // just the methods list. if (methodName == "getPluginEvents" && args.isEmpty()) { return QVariant(QJsonArray()); } if (methodName == "getPluginInterface" && args.isEmpty()) { return QVariant(getMethods()); } // No auth check here by design: this adapter has no token parameter and is // only ever reached through ModuleProxy::callRemoteMethod, which authorizes // the caller's token before dispatching (see ModuleProxy::isAuthorized). // The checks below are sanity guards on the wrapped plugin, not authz. PluginInterface* pluginInterface = qobject_cast(m_module); if (!pluginInterface) { qWarning() << "[LogosProviderObject] QtProviderObject::callMethod: module is not a PluginInterface"; return QVariant(); } LogosAPI* api = pluginInterface->logosAPI; if (!api) { qWarning() << "[LogosProviderObject] QtProviderObject::callMethod: LogosAPI not available"; return QVariant(); } // Find method via QMetaObject const QMetaObject* metaObject = m_module->metaObject(); int methodIndex = -1; for (int i = 0; i < metaObject->methodCount(); ++i) { QMetaMethod method = metaObject->method(i); if (method.name() == methodName && method.parameterCount() == args.size()) { methodIndex = i; break; } } if (methodIndex == -1) { qWarning() << "[LogosProviderObject] QtProviderObject: method not found:" << methodName << "with" << args.size() << "arguments"; return QVariant(); } QMetaMethod method = metaObject->method(methodIndex); QMetaType returnType = method.returnMetaType(); // Coerce args to the types the method actually declares. QVariantList coercedArgs = args; for (int i = 0; i < method.parameterCount() && i < coercedArgs.size(); ++i) { QMetaType paramType = method.parameterMetaType(i); if (coercedArgs[i].metaType() != paramType) { QVariant converted = coercedArgs[i]; if (converted.convert(paramType)) { coercedArgs[i] = converted; } else { qWarning() << "[LogosProviderObject] QtProviderObject: could not convert arg" << i << "from" << coercedArgs[i].typeName() << "to" << paramType.name() << "for method" << methodName; } } } bool success = false; QVariant result; if (returnType == QMetaType::fromType()) { success = invokeMethodByArgCount(m_module, methodName, coercedArgs, nullptr, nullptr); if (success) result = QVariant(true); } else if (returnType == QMetaType::fromType()) { bool v = false; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "bool"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { int v = 0; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "int"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { double v = 0.0; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "double"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { float v = 0.0f; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "float"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { QString v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QString"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { LogosResult v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "LogosResult"); if (success) result = QVariant::fromValue(v); } else if (returnType == QMetaType::fromType()) { QVariant v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QVariant"); if (success) result = v; } else if (returnType == QMetaType::fromType()) { QJsonArray v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QJsonArray"); if (success) result = QVariant(v); } else if (returnType == QMetaType::fromType()) { QVariantList v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QVariantList"); if (success) result = QVariant::fromValue(v); } else if (returnType == QMetaType::fromType()) { QVariantMap v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QVariantMap"); if (success) result = QVariant::fromValue(v); } else if (returnType == QMetaType::fromType()) { QStringList v; success = invokeMethodByArgCount(m_module, methodName, coercedArgs, &v, "QStringList"); if (success) result = QVariant(v); } else { qWarning() << "[LogosProviderObject] QtProviderObject: unsupported return type:" << returnType.name() << "for method:" << methodName; return QVariant(); } if (!success) { qWarning() << "[LogosProviderObject] QtProviderObject: failed to invoke" << methodName; } return result; } bool QtProviderObject::informModuleToken(const QString& moduleName, const QString& token) { PluginInterface* pluginInterface = qobject_cast(m_module); if (!pluginInterface) { qWarning() << "[LogosProviderObject] QtProviderObject::informModuleToken: not a PluginInterface"; return false; } LogosAPI* api = pluginInterface->logosAPI; if (!api) { qWarning() << "[LogosProviderObject] QtProviderObject::informModuleToken: LogosAPI not available"; return false; } TokenManager* tokenManager = api->getTokenManager(); if (!tokenManager) { qWarning() << "[LogosProviderObject] QtProviderObject::informModuleToken: TokenManager not available"; return false; } qDebug() << "[LogosProviderObject] QtProviderObject: saving token for module:" << moduleName; tokenManager->saveToken(moduleName, token); return true; } QJsonArray QtProviderObject::getMethods() { if (!m_module) return QJsonArray(); QJsonArray methodsArray; const QMetaObject* metaObject = m_module->metaObject(); for (int i = 0; i < metaObject->methodCount(); ++i) { QMetaMethod method = metaObject->method(i); if (method.enclosingMetaObject() != metaObject) { continue; } QJsonObject methodObj; methodObj["signature"] = QString::fromUtf8(method.methodSignature()); methodObj["name"] = QString::fromUtf8(method.name()); methodObj["returnType"] = QString::fromUtf8(method.typeName()); methodObj["isInvokable"] = method.isValid() && (method.methodType() == QMetaMethod::Method || method.methodType() == QMetaMethod::Slot); if (method.parameterCount() > 0) { QJsonArray params; for (int p = 0; p < method.parameterCount(); ++p) { QJsonObject paramObj; paramObj["type"] = QString::fromUtf8(method.parameterTypeName(p)); QByteArrayList paramNames = method.parameterNames(); if (p < paramNames.size() && !paramNames.at(p).isEmpty()) { paramObj["name"] = QString::fromUtf8(paramNames.at(p)); } else { paramObj["name"] = QString("param%1").arg(p); } params.append(paramObj); } methodObj["parameters"] = params; } methodsArray.append(methodObj); } return methodsArray; } #include "moc_qt_provider_object.cpp"