1 /***************************************************************************
2 * Copyright (C) 2005-2018 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
24 #include <QCoreApplication>
25 #include <QHostAddress>
26 #include <QMetaMethod>
27 #include <QMetaProperty>
31 # include <QSslSocket>
36 #include "signalproxy.h"
37 #include "syncableobject.h"
41 using namespace Protocol;
43 class RemovePeerEvent : public QEvent
46 RemovePeerEvent(Peer* peer)
47 : QEvent(QEvent::Type(SignalProxy::RemovePeerEvent))
53 // ==================================================
55 // ==================================================
58 thread_local SignalProxy* _current{nullptr};
61 SignalProxy::SignalProxy(QObject* parent)
68 SignalProxy::SignalProxy(ProxyMode mode, QObject* parent)
75 SignalProxy::~SignalProxy()
77 QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
78 while (classIter != _syncSlave.end()) {
79 ObjectId::iterator objIter = classIter->begin();
80 while (objIter != classIter->end()) {
81 SyncableObject* obj = objIter.value();
82 objIter = classIter->erase(objIter);
83 obj->stopSynchronize(this);
91 // Ensure that we don't try to clean up while destroying ourselves
92 disconnect(this, &QObject::destroyed, this, &SignalProxy::detachSlotObjects);
97 SignalProxy* SignalProxy::current()
102 void SignalProxy::setProxyMode(ProxyMode mode)
104 if (!_peerMap.empty()) {
105 qWarning() << Q_FUNC_INFO << "Cannot change proxy mode while connected";
116 void SignalProxy::init()
118 _heartBeatInterval = 0;
119 _maxHeartBeatCount = 0;
120 setHeartBeatInterval(30);
121 setMaxHeartBeatCount(2);
127 void SignalProxy::initServer() {}
129 void SignalProxy::initClient()
131 attachSlot("__objectRenamed__", this, &SignalProxy::objectRenamed);
134 void SignalProxy::setHeartBeatInterval(int secs)
136 if (_heartBeatInterval != secs) {
137 _heartBeatInterval = secs;
138 emit heartBeatIntervalChanged(secs);
142 void SignalProxy::setMaxHeartBeatCount(int max)
144 if (_maxHeartBeatCount != max) {
145 _maxHeartBeatCount = max;
146 emit maxHeartBeatCountChanged(max);
150 bool SignalProxy::addPeer(Peer* peer)
155 if (_peerMap.values().contains(peer))
158 if (!peer->isOpen()) {
159 qWarning("SignalProxy: peer needs to be open!");
163 if (proxyMode() == Client) {
164 if (!_peerMap.isEmpty()) {
165 qWarning("SignalProxy: only one peer allowed in client mode!");
168 connect(peer, &Peer::lagUpdated, this, &SignalProxy::lagUpdated);
171 connect(peer, &Peer::disconnected, this, &SignalProxy::removePeerBySender);
172 connect(peer, &Peer::secureStateChanged, this, &SignalProxy::updateSecureState);
175 peer->setParent(this);
177 if (peer->id() < 0) {
178 peer->setId(nextPeerId());
179 peer->setConnectedSince(QDateTime::currentDateTimeUtc());
182 _peerMap[peer->id()] = peer;
184 peer->setSignalProxy(this);
186 if (peerCount() == 1)
193 void SignalProxy::removeAllPeers()
195 Q_ASSERT(proxyMode() == Server || peerCount() <= 1);
196 // wee need to copy that list since we modify it in the loop
197 QList<Peer*> peers = _peerMap.values();
198 for (auto peer : peers) {
203 void SignalProxy::removePeer(Peer* peer)
206 qWarning() << Q_FUNC_INFO << "Trying to remove a null peer!";
210 if (_peerMap.isEmpty()) {
211 qWarning() << "SignalProxy::removePeer(): No peers in use!";
215 if (!_peerMap.values().contains(peer)) {
216 qWarning() << "SignalProxy: unknown Peer" << peer;
220 disconnect(peer, nullptr, this, nullptr);
221 peer->setSignalProxy(nullptr);
223 _peerMap.remove(peer->id());
224 emit peerRemoved(peer);
226 if (peer->parent() == this)
231 if (_peerMap.isEmpty())
235 void SignalProxy::removePeerBySender()
237 removePeer(qobject_cast<Peer*>(sender()));
240 void SignalProxy::renameObject(const SyncableObject* obj, const QString& newname, const QString& oldname)
242 if (proxyMode() == Client)
245 const QMetaObject* meta = obj->syncMetaObject();
246 const QByteArray className(meta->className());
247 objectRenamed(className, newname, oldname);
249 dispatch(RpcCall("__objectRenamed__", QVariantList() << className << newname << oldname));
252 void SignalProxy::objectRenamed(const QByteArray& classname, const QString& newname, const QString& oldname)
254 if (newname != oldname) {
255 if (_syncSlave.contains(classname) && _syncSlave[classname].contains(oldname)) {
256 SyncableObject* obj = _syncSlave[classname][newname] = _syncSlave[classname].take(oldname);
257 obj->setObjectName(newname);
263 const QMetaObject* SignalProxy::metaObject(const QObject* obj)
265 if (const auto* syncObject = qobject_cast<const SyncableObject*>(obj))
266 return syncObject->syncMetaObject();
268 return obj->metaObject();
271 SignalProxy::ExtendedMetaObject* SignalProxy::extendedMetaObject(const QMetaObject* meta) const
273 if (_extendedMetaObjects.contains(meta))
274 return _extendedMetaObjects[meta];
279 SignalProxy::ExtendedMetaObject* SignalProxy::createExtendedMetaObject(const QMetaObject* meta, bool checkConflicts)
281 if (!_extendedMetaObjects.contains(meta)) {
282 _extendedMetaObjects[meta] = new ExtendedMetaObject(meta, checkConflicts);
284 return _extendedMetaObjects[meta];
287 void SignalProxy::attachSlotObject(const QByteArray& signalName, std::unique_ptr<SlotObjectBase> slotObject)
289 // Remove all attached slots related to the context upon its destruction
290 connect(slotObject->context(), &QObject::destroyed, this, &SignalProxy::detachSlotObjects, Qt::UniqueConnection);
292 _attachedSlots.emplace(QMetaObject::normalizedSignature(signalName.constData()), std::move(slotObject));
295 void SignalProxy::detachSlotObjects(const QObject *context)
297 for (auto&& it = _attachedSlots.begin(); it != _attachedSlots.end(); ) {
298 if (it->second->context() == context) {
299 it = _attachedSlots.erase(it);
307 void SignalProxy::synchronize(SyncableObject* obj)
309 createExtendedMetaObject(obj, true);
311 // attaching as slave to receive sync Calls
312 QByteArray className(obj->syncMetaObject()->className());
313 _syncSlave[className][obj->objectName()] = obj;
315 if (proxyMode() == Server) {
316 obj->setInitialized();
317 emit objectInitialized(obj);
320 if (obj->isInitialized())
321 emit objectInitialized(obj);
326 obj->synchronize(this);
329 void SignalProxy::stopSynchronize(SyncableObject* obj)
331 // we can't use a className here, since it might be effed up, if we receive the call as a result of a decon
332 // gladly the objectName() is still valid. So we have only to iterate over the classes not each instance! *sigh*
333 QHash<QByteArray, ObjectId>::iterator classIter = _syncSlave.begin();
334 while (classIter != _syncSlave.end()) {
335 if (classIter->contains(obj->objectName()) && classIter.value()[obj->objectName()] == obj) {
336 classIter->remove(obj->objectName());
341 obj->stopSynchronize(this);
344 void SignalProxy::dispatchSignal(QByteArray sigName, QVariantList params)
346 RpcCall rpcCall{std::move(sigName), std::move(params)};
347 if (_restrictMessageTarget) {
348 for (auto&& peer : _restrictedTargets) {
349 dispatch(peer, rpcCall);
358 void SignalProxy::dispatch(const T& protoMessage)
360 for (auto&& peer : _peerMap.values()) {
361 dispatch(peer, protoMessage);
366 void SignalProxy::dispatch(Peer* peer, const T& protoMessage)
370 if (peer && peer->isOpen())
371 peer->dispatch(protoMessage);
373 QCoreApplication::postEvent(this, new ::RemovePeerEvent(peer));
375 _targetPeer = nullptr;
378 void SignalProxy::handle(Peer* peer, const SyncMessage& syncMessage)
380 if (!_syncSlave.contains(syncMessage.className) || !_syncSlave[syncMessage.className].contains(syncMessage.objectName)) {
381 qWarning() << QString("no registered receiver for sync call: %1::%2 (objectName=\"%3\"). Params are:")
382 .arg(syncMessage.className, syncMessage.slotName, syncMessage.objectName)
383 << syncMessage.params;
387 SyncableObject* receiver = _syncSlave[syncMessage.className][syncMessage.objectName];
388 ExtendedMetaObject* eMeta = extendedMetaObject(receiver);
389 if (!eMeta->slotMap().contains(syncMessage.slotName)) {
390 qWarning() << QString("no matching slot for sync call: %1::%2 (objectName=\"%3\"). Params are:")
391 .arg(syncMessage.className, syncMessage.slotName, syncMessage.objectName)
392 << syncMessage.params;
396 int slotId = eMeta->slotMap()[syncMessage.slotName];
397 if (proxyMode() != eMeta->receiverMode(slotId)) {
398 qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed. Wrong ProxyMode!", eMeta->methodName(slotId).constData());
402 // We can no longer construct a QVariant from QMetaType::Void
403 QVariant returnValue;
404 int returnType = eMeta->returnType(slotId);
405 if (returnType != QMetaType::Void)
406 returnValue = QVariant(static_cast<QVariant::Type>(returnType));
408 if (!invokeSlot(receiver, slotId, syncMessage.params, returnValue, peer)) {
409 qWarning("SignalProxy::handleSync(): invokeMethod for \"%s\" failed ", eMeta->methodName(slotId).constData());
413 if (returnValue.type() != QVariant::Invalid && eMeta->receiveMap().contains(slotId)) {
414 int receiverId = eMeta->receiveMap()[slotId];
415 QVariantList returnParams;
416 if (eMeta->argTypes(receiverId).count() > 1)
417 returnParams << syncMessage.params;
418 returnParams << returnValue;
420 peer->dispatch(SyncMessage(syncMessage.className, syncMessage.objectName, eMeta->methodName(receiverId), returnParams));
421 _targetPeer = nullptr;
424 // send emit update signal
425 invokeSlot(receiver, eMeta->updatedRemotelyId());
428 void SignalProxy::handle(Peer* peer, const RpcCall& rpcCall)
432 auto range = _attachedSlots.equal_range(rpcCall.signalName);
433 std::for_each(range.first, range.second, [&rpcCall](const auto& p) {
434 if (!p.second->invoke(rpcCall.params)) {
435 qWarning() << "Could not invoke slot for remote signal" << rpcCall.signalName;
440 void SignalProxy::handle(Peer* peer, const InitRequest& initRequest)
442 if (!_syncSlave.contains(initRequest.className)) {
443 qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Class:" << initRequest.className;
447 if (!_syncSlave[initRequest.className].contains(initRequest.objectName)) {
448 qWarning() << "SignalProxy::handleInitRequest() received initRequest for unregistered Object:" << initRequest.className
449 << initRequest.objectName;
453 SyncableObject* obj = _syncSlave[initRequest.className][initRequest.objectName];
455 peer->dispatch(InitData(initRequest.className, initRequest.objectName, initData(obj)));
456 _targetPeer = nullptr;
459 void SignalProxy::handle(Peer* peer, const InitData& initData)
463 if (!_syncSlave.contains(initData.className)) {
464 qWarning() << "SignalProxy::handleInitData() received initData for unregistered Class:" << initData.className;
468 if (!_syncSlave[initData.className].contains(initData.objectName)) {
469 qWarning() << "SignalProxy::handleInitData() received initData for unregistered Object:" << initData.className << initData.objectName;
473 SyncableObject* obj = _syncSlave[initData.className][initData.objectName];
474 setInitData(obj, initData.initData);
477 bool SignalProxy::invokeSlot(QObject* receiver, int methodId, const QVariantList& params, QVariant& returnValue, Peer* peer)
479 ExtendedMetaObject* eMeta = extendedMetaObject(receiver);
480 const QList<int> args = eMeta->argTypes(methodId);
481 const int numArgs = params.count() < args.count() ? params.count() : args.count();
483 if (eMeta->minArgCount(methodId) > params.count()) {
484 qWarning() << "SignalProxy::invokeSlot(): not enough params to invoke" << eMeta->methodName(methodId);
488 void* _a[] = {nullptr, // return type...
493 nullptr, // and 10 args - that's the max size qt can handle with signals and slots
500 // check for argument compatibility and build params array
501 for (int i = 0; i < numArgs; i++) {
502 if (!params[i].isValid()) {
503 qWarning() << "SignalProxy::invokeSlot(): received invalid data for argument number" << i << "of method"
504 << QString("%1::%2()")
505 .arg(receiver->metaObject()->className())
506 .arg(receiver->metaObject()->method(methodId).methodSignature().constData());
507 qWarning() << " - make sure all your data types are known by the Qt MetaSystem";
510 if (args[i] != QMetaType::type(params[i].typeName())) {
511 qWarning() << "SignalProxy::invokeSlot(): incompatible param types to invoke" << eMeta->methodName(methodId);
515 _a[i + 1] = const_cast<void*>(params[i].constData());
518 if (returnValue.type() != QVariant::Invalid)
519 _a[0] = const_cast<void*>(returnValue.constData());
521 Qt::ConnectionType type = QThread::currentThread() == receiver->thread() ? Qt::DirectConnection : Qt::QueuedConnection;
523 if (type == Qt::DirectConnection) {
525 auto result = receiver->qt_metacall(QMetaObject::InvokeMetaMethod, methodId, _a) < 0;
526 _sourcePeer = nullptr;
530 qWarning() << "Queued Connections are not implemented yet";
531 // note to self: qmetaobject.cpp:990 ff
536 bool SignalProxy::invokeSlot(QObject* receiver, int methodId, const QVariantList& params, Peer* peer)
539 return invokeSlot(receiver, methodId, params, ret, peer);
542 void SignalProxy::requestInit(SyncableObject* obj)
544 if (proxyMode() == Server || obj->isInitialized())
547 dispatch(InitRequest(obj->syncMetaObject()->className(), obj->objectName()));
550 QVariantMap SignalProxy::initData(SyncableObject* obj) const
552 return obj->toVariantMap();
555 void SignalProxy::setInitData(SyncableObject* obj, const QVariantMap& properties)
557 if (obj->isInitialized())
559 obj->fromVariantMap(properties);
560 obj->setInitialized();
561 emit objectInitialized(obj);
562 invokeSlot(obj, extendedMetaObject(obj)->updatedRemotelyId());
565 void SignalProxy::customEvent(QEvent* event)
567 switch ((int)event->type()) {
568 case RemovePeerEvent: {
569 auto* e = static_cast<::RemovePeerEvent*>(event);
576 qWarning() << Q_FUNC_INFO << "Received unknown custom event:" << event->type();
581 void SignalProxy::sync_call__(const SyncableObject* obj, SignalProxy::ProxyMode modeType, const char* funcname, va_list ap)
583 // qDebug() << obj << modeType << "(" << _proxyMode << ")" << funcname;
584 if (modeType != _proxyMode)
587 ExtendedMetaObject* eMeta = extendedMetaObject(obj);
591 const QList<int>& argTypes = eMeta->argTypes(eMeta->methodId(QByteArray(funcname)));
593 for (int i = 0; i < argTypes.size(); i++) {
594 if (argTypes[i] == 0) {
595 qWarning() << Q_FUNC_INFO << "received invalid data for argument number" << i << "of signal"
596 << QString("%1::%2").arg(eMeta->metaObject()->className()).arg(funcname);
597 qWarning() << " - make sure all your data types are known by the Qt MetaSystem";
600 params << QVariant(argTypes[i], va_arg(ap, void*));
603 if (_restrictMessageTarget) {
604 for (auto peer : _restrictedTargets) {
606 dispatch(peer, SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
610 dispatch(SyncMessage(eMeta->metaObject()->className(), obj->objectName(), QByteArray(funcname), params));
613 void SignalProxy::disconnectDevice(QIODevice* dev, const QString& reason)
615 if (!reason.isEmpty())
616 qWarning() << qPrintable(reason);
617 auto* sock = qobject_cast<QAbstractSocket*>(dev);
619 qWarning() << qPrintable(tr("Disconnecting")) << qPrintable(sock->peerAddress().toString());
623 void SignalProxy::dumpProxyStats()
626 if (proxyMode() == Server)
632 foreach (ObjectId oid, _syncSlave.values())
633 slaveCount += oid.count();
636 qDebug() << " Proxy Mode:" << mode;
637 qDebug() << " attached Slots:" << _attachedSlots.size();
638 qDebug() << " number of synced Slaves:" << slaveCount;
639 qDebug() << "number of Classes cached:" << _extendedMetaObjects.count();
642 void SignalProxy::updateSecureState()
644 bool wasSecure = _secure;
646 _secure = !_peerMap.isEmpty();
647 for (auto peer : _peerMap.values()) {
648 _secure &= peer->isSecure();
651 if (wasSecure != _secure)
652 emit secureStateChanged(_secure);
655 QVariantList SignalProxy::peerData()
658 for (auto&& peer : _peerMap.values()) {
660 data["id"] = peer->id();
661 data["clientVersion"] = peer->clientVersion();
662 // We explicitly rename this, as, due to the Debian reproducability changes, buildDate isn’t actually the build
663 // date anymore, but on newer clients the date of the last git commit
664 data["clientVersionDate"] = peer->buildDate();
665 data["remoteAddress"] = peer->address();
666 data["connectedSince"] = peer->connectedSince();
667 data["secure"] = peer->isSecure();
668 data["features"] = static_cast<quint32>(peer->features().toLegacyFeatures());
669 data["featureList"] = peer->features().toStringList();
675 Peer* SignalProxy::peerById(int peerId)
677 // We use ::value() here instead of the [] operator because the latter has the side-effect
678 // of automatically inserting a null value with the passed key into the map. See
679 // https://doc.qt.io/qt-5/qhash.html#operator-5b-5d and https://doc.qt.io/qt-5/qhash.html#value.
680 return _peerMap.value(peerId);
683 void SignalProxy::restrictTargetPeers(QSet<Peer*> peers, std::function<void()> closure)
685 auto previousRestrictMessageTarget = _restrictMessageTarget;
686 auto previousRestrictedTargets = _restrictedTargets;
687 _restrictMessageTarget = true;
688 _restrictedTargets = peers;
692 _restrictMessageTarget = previousRestrictMessageTarget;
693 _restrictedTargets = previousRestrictedTargets;
696 Peer* SignalProxy::sourcePeer()
701 void SignalProxy::setSourcePeer(Peer* sourcePeer)
703 _sourcePeer = sourcePeer;
706 Peer* SignalProxy::targetPeer()
711 void SignalProxy::setTargetPeer(Peer* targetPeer)
713 _targetPeer = targetPeer;
716 // ---- SlotObjectBase ---------------------------------------------------------------------------------------------------------------------
718 SignalProxy::SlotObjectBase::SlotObjectBase(const QObject* context)
722 const QObject* SignalProxy::SlotObjectBase::context() const
727 // ---- ExtendedMetaObject ----------------------------------------------------------------------------------------------------------------
729 SignalProxy::ExtendedMetaObject::ExtendedMetaObject(const QMetaObject* meta, bool checkConflicts)
731 , _updatedRemotelyId(_meta->indexOfSignal("updatedRemotely()"))
733 for (int i = 0; i < _meta->methodCount(); i++) {
734 if (_meta->method(i).methodType() != QMetaMethod::Slot)
737 if (_meta->method(i).methodSignature().contains('*'))
738 continue; // skip methods with ptr params
740 QByteArray method = methodName(_meta->method(i));
741 if (method.startsWith("init"))
742 continue; // skip initializers
744 if (_methodIds.contains(method)) {
745 /* funny... moc creates for methods containing default parameters multiple metaMethod with separate methodIds.
746 we don't care... we just need the full fledged version
748 const QMetaMethod& current = _meta->method(_methodIds[method]);
749 const QMetaMethod& candidate = _meta->method(i);
750 if (current.parameterTypes().count() > candidate.parameterTypes().count()) {
751 int minCount = candidate.parameterTypes().count();
752 QList<QByteArray> commonParams = current.parameterTypes().mid(0, minCount);
753 if (commonParams == candidate.parameterTypes())
754 continue; // we already got the full featured version
757 int minCount = current.parameterTypes().count();
758 QList<QByteArray> commonParams = candidate.parameterTypes().mid(0, minCount);
759 if (commonParams == current.parameterTypes()) {
760 _methodIds[method] = i; // use the new one
764 if (checkConflicts) {
765 qWarning() << "class" << meta->className() << "contains overloaded methods which is currently not supported!";
766 qWarning() << " - " << _meta->method(i).methodSignature() << "conflicts with"
767 << _meta->method(_methodIds[method]).methodSignature();
771 _methodIds[method] = i;
775 const SignalProxy::ExtendedMetaObject::MethodDescriptor& SignalProxy::ExtendedMetaObject::methodDescriptor(int methodId)
777 if (!_methods.contains(methodId)) {
778 _methods[methodId] = MethodDescriptor(_meta->method(methodId));
780 return _methods[methodId];
783 const QHash<int, int>& SignalProxy::ExtendedMetaObject::receiveMap()
785 if (_receiveMap.isEmpty()) {
786 QHash<int, int> receiveMap;
788 QMetaMethod requestSlot;
789 QByteArray returnTypeName;
790 QByteArray signature;
791 QByteArray methodName;
795 const int methodCount = _meta->methodCount();
796 for (int i = 0; i < methodCount; i++) {
797 requestSlot = _meta->method(i);
798 if (requestSlot.methodType() != QMetaMethod::Slot)
801 returnTypeName = requestSlot.typeName();
802 if (QMetaType::Void == (QMetaType::Type)returnType(i))
805 signature = requestSlot.methodSignature();
806 if (!signature.startsWith("request"))
809 paramsPos = signature.indexOf('(');
813 methodName = signature.left(paramsPos);
814 params = signature.mid(paramsPos);
816 methodName = methodName.replace("request", "receive");
817 params = params.left(params.count() - 1) + ", " + returnTypeName + ")";
819 signature = QMetaObject::normalizedSignature(methodName + params);
820 receiverId = _meta->indexOfSlot(signature);
822 if (receiverId == -1) {
823 signature = QMetaObject::normalizedSignature(methodName + "(" + returnTypeName + ")");
824 receiverId = _meta->indexOfSlot(signature);
827 if (receiverId != -1) {
828 receiveMap[i] = receiverId;
831 _receiveMap = receiveMap;
836 QByteArray SignalProxy::ExtendedMetaObject::methodName(const QMetaMethod& method)
838 QByteArray sig(method.methodSignature());
839 return sig.left(sig.indexOf("("));
842 QString SignalProxy::ExtendedMetaObject::methodBaseName(const QMetaMethod& method)
844 QString methodname = QString(method.methodSignature()).section("(", 0, 0);
846 // determine where we have to chop:
848 if (method.methodType() == QMetaMethod::Slot) {
849 // we take evertyhing from the first uppercase char if it's slot
850 upperCharPos = methodname.indexOf(QRegExp("[A-Z]"));
851 if (upperCharPos == -1)
853 methodname = methodname.mid(upperCharPos);
856 // and if it's a signal we discard everything from the last uppercase char
857 upperCharPos = methodname.lastIndexOf(QRegExp("[A-Z]"));
858 if (upperCharPos == -1)
860 methodname = methodname.left(upperCharPos);
863 methodname[0] = methodname[0].toUpper();
868 SignalProxy::ExtendedMetaObject::MethodDescriptor::MethodDescriptor(const QMetaMethod& method)
869 : _methodName(SignalProxy::ExtendedMetaObject::methodName(method))
870 , _returnType(QMetaType::type(method.typeName()))
872 // determine argTypes
873 QList<QByteArray> paramTypes = method.parameterTypes();
875 for (int i = 0; i < paramTypes.count(); i++) {
876 argTypes.append(QMetaType::type(paramTypes[i]));
878 _argTypes = argTypes;
880 // determine minArgCount
881 QString signature(method.methodSignature());
882 _minArgCount = method.parameterTypes().count() - signature.count("=");
884 _receiverMode = (_methodName.startsWith("request")) ? SignalProxy::Server : SignalProxy::Client;