1/****************************************************************************
2**
3** Copyright (C) 2017 Ford Motor Company
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtRemoteObjects module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qremoteobjectsource.h"
41#include "qremoteobjectsource_p.h"
42#include "qremoteobjectnode.h"
43#include "qremoteobjectdynamicreplica.h"
44
45#include "qconnectionfactories_p.h"
46#include "qremoteobjectsourceio_p.h"
47#include "qremoteobjectabstractitemmodeladapter_p.h"
48
49#include <QtCore/qmetaobject.h>
50#include <QtCore/qvarlengtharray.h>
51#include <QtCore/qabstractitemmodel.h>
52
53#include <algorithm>
54#include <iterator>
55
56QT_BEGIN_NAMESPACE
57
58using namespace QRemoteObjectPackets;
59using namespace QRemoteObjectStringLiterals;
60
61const int QRemoteObjectSourceBase::qobjectPropertyOffset = QObject::staticMetaObject.propertyCount();
62const int QRemoteObjectSourceBase::qobjectMethodOffset = QObject::staticMetaObject.methodCount();
63static const QByteArray s_classinfoRemoteobjectSignature(QCLASSINFO_REMOTEOBJECT_SIGNATURE);
64
65QByteArray QtPrivate::qtro_classinfo_signature(const QMetaObject *metaObject)
66{
67 if (!metaObject)
68 return QByteArray{};
69
70 for (int i = metaObject->classInfoOffset(); i < metaObject->classInfoCount(); ++i) {
71 auto ci = metaObject->classInfo(index: i);
72 if (s_classinfoRemoteobjectSignature == ci.name())
73 return ci.value();
74 }
75 return QByteArray{};
76}
77
78inline bool qtro_is_cloned_method(const QMetaObject *mobj, int index)
79{
80 int local_method_index = index - mobj->methodOffset();
81 if (local_method_index < 0 && mobj->superClass())
82 return qtro_is_cloned_method(mobj: mobj->superClass(), index);
83 const auto priv = reinterpret_cast<const QtPrivate::QMetaObjectPrivate*>(mobj->d.data);
84 Q_ASSERT(local_method_index < priv->methodCount);
85 int handle = priv->methodData + 5 * local_method_index;
86 if (mobj->d.data[handle + 4] & 0x20 /*MethodFlags::MethodCloned*/)
87 return true;
88 return false;
89}
90
91QRemoteObjectSourceBase::QRemoteObjectSourceBase(QObject *obj, Private *d, const SourceApiMap *api,
92 QObject *adapter)
93 : QObject(obj),
94 m_object(obj),
95 m_adapter(adapter),
96 m_api(api),
97 d(d)
98{
99 if (!obj) {
100 qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourceBase: Cannot replicate a NULL object" << m_api->name();
101 return;
102 }
103
104 setConnections();
105
106 const int nChildren = api->m_models.count() + api->m_subclasses.count();
107 if (nChildren > 0) {
108 QVector<int> roles;
109 const int numProperties = api->propertyCount();
110 int modelIndex = 0, subclassIndex = 0;
111 for (int i = 0; i < numProperties; ++i) {
112 if (api->isAdapterProperty(i))
113 continue;
114 const int index = api->sourcePropertyIndex(index: i);
115 const auto property = m_object->metaObject()->property(index);
116 if (QMetaType::typeFlags(type: property.userType()).testFlag(flag: QMetaType::PointerToQObject)) {
117 auto propertyMeta = QMetaType::metaObjectForType(type: property.userType());
118 QObject *child = property.read(obj: m_object).value<QObject *>();
119 const QMetaObject *meta = child ? child->metaObject() : propertyMeta;
120 if (!meta)
121 continue;
122 if (meta->inherits(metaObject: &QAbstractItemModel::staticMetaObject)) {
123 const auto modelInfo = api->m_models.at(i: modelIndex++);
124 QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object: child);
125 QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter> *modelApi =
126 new QAbstractItemAdapterSourceAPI<QAbstractItemModel, QAbstractItemModelSourceAdapter>(modelInfo.name);
127 if (!model)
128 m_children.insert(akey: i, avalue: new QRemoteObjectSource(nullptr, d, modelApi, nullptr));
129 else {
130 roles.clear();
131 const auto knownRoles = model->roleNames();
132 for (auto role : modelInfo.roles.split(sep: '|')) {
133 if (role.isEmpty())
134 continue;
135 const int roleIndex = knownRoles.key(avalue: role, defaultValue: -1);
136 if (roleIndex == -1) {
137 qCWarning(QT_REMOTEOBJECT) << "Invalid role" << role << "for model" << model->metaObject()->className();
138 qCWarning(QT_REMOTEOBJECT) << " known roles:" << knownRoles;
139 } else
140 roles << roleIndex;
141 }
142 auto adapter = new QAbstractItemModelSourceAdapter(model, nullptr,
143 roles.isEmpty() ? knownRoles.keys().toVector() : roles);
144 m_children.insert(akey: i, avalue: new QRemoteObjectSource(model, d, modelApi, adapter));
145 }
146 } else {
147 const auto classApi = api->m_subclasses.at(i: subclassIndex++);
148 m_children.insert(akey: i, avalue: new QRemoteObjectSource(child, d, classApi, nullptr));
149 }
150 }
151 }
152 }
153}
154
155QRemoteObjectSource::QRemoteObjectSource(QObject *obj, Private *d, const SourceApiMap *api, QObject *adapter)
156 : QRemoteObjectSourceBase(obj, d, api, adapter)
157 , m_name(api->typeName() == QLatin1String("QAbstractItemModelAdapter") ? MODEL().arg(a: api->name()) : CLASS().arg(a: api->name()))
158{
159 if (obj)
160 d->m_sourceIo->registerSource(source: this);
161}
162
163QRemoteObjectRootSource::QRemoteObjectRootSource(QObject *obj, const SourceApiMap *api,
164 QObject *adapter, QRemoteObjectSourceIo *sourceIo)
165 : QRemoteObjectSourceBase(obj, new Private(sourceIo, this), api, adapter)
166 , m_name(api->name())
167{
168 d->m_sourceIo->registerSource(source: this);
169}
170
171QRemoteObjectSourceBase::~QRemoteObjectSourceBase()
172{
173 delete m_api;
174}
175
176void QRemoteObjectSourceBase::setConnections()
177{
178 const QMetaObject *meta = m_object->metaObject();
179
180 const int index = meta->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE);
181 if (index != -1) { //We have an object created from repc or at least with QCLASSINFO defined
182 while (true) {
183 Q_ASSERT(meta->superClass()); //This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE
184 if (index != meta->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE)) //At the point we don't find the same QCLASSINFO_REMOTEOBJECT_TYPE,
185 //we have the metaobject we should work from
186 break;
187 meta = meta->superClass();
188 }
189 }
190
191 for (int idx = 0; idx < m_api->signalCount(); ++idx) {
192 const int sourceIndex = m_api->sourceSignalIndex(index: idx);
193 const bool isAdapter = m_api->isAdapterSignal(idx);
194 const auto targetMeta = isAdapter ? m_adapter->metaObject() : meta;
195
196 // don't connect cloned signals, or we end up with multiple emissions
197 if (qtro_is_cloned_method(mobj: targetMeta, index: sourceIndex))
198 continue;
199 // This basically connects the parent Signals (note, all dynamic properties have onChange
200 //notifications, thus signals) to us. Normally each Signal is mapped to a unique index,
201 //but since we are forwarding them all, we keep the offset constant.
202 //
203 //We know no one will inherit from this class, so no need to worry about indices from
204 //derived classes.
205 const auto target = isAdapter ? m_adapter : m_object;
206 if (!QMetaObject::connect(sender: target, signal_index: sourceIndex, receiver: this, method_index: QRemoteObjectSource::qobjectMethodOffset+idx, type: Qt::DirectConnection, types: 0)) {
207 qCWarning(QT_REMOTEOBJECT) << "QRemoteObjectSourceBase: QMetaObject::connect returned false. Unable to connect.";
208 return;
209 }
210
211 qCDebug(QT_REMOTEOBJECT) << "Connection made" << idx << sourceIndex
212 << targetMeta->method(index: sourceIndex).name();
213 }
214}
215
216void QRemoteObjectSourceBase::resetObject(QObject *newObject)
217{
218 if (m_object)
219 m_object->disconnect(receiver: this);
220 if (m_adapter) {
221 m_adapter->disconnect(receiver: this);
222 delete m_adapter;
223 m_adapter = nullptr;
224 }
225 // We need some dynamic replica specific code here, in case an object had null sub-classes that
226 // have been replaced with real objects. In this case, the ApiMap could be wrong and need updating.
227 if (newObject && qobject_cast<QRemoteObjectDynamicReplica *>(object: newObject) && m_api->isDynamic()) {
228 auto api = static_cast<const DynamicApiMap*>(m_api);
229 if (api->m_properties[0] == 0) { // 0 is an index into QObject itself, so this isn't a valid QtRO index
230 const auto rep = qobject_cast<QRemoteObjectDynamicReplica *>(object: newObject);
231 auto tmp = m_api;
232 m_api = new DynamicApiMap(newObject, rep->metaObject(), api->m_name, QLatin1String(rep->metaObject()->className()));
233 qCDebug(QT_REMOTEOBJECT) << " Reset m_api for" << api->m_name << "using new metaObject:" << rep->metaObject()->className();
234 delete tmp;
235 }
236 }
237
238 m_object = newObject;
239 auto model = qobject_cast<QAbstractItemModel *>(object: newObject);
240 if (model) {
241 d->m_sourceIo->registerSource(source: this);
242 m_adapter = new QAbstractItemModelSourceAdapter(model, nullptr, model->roleNames().keys().toVector());
243 }
244
245 setParent(newObject);
246 if (newObject)
247 setConnections();
248
249 const int nChildren = m_api->m_models.count() + m_api->m_subclasses.count();
250 if (nChildren == 0)
251 return;
252
253 if (!newObject) {
254 for (auto child : m_children)
255 child->resetObject(newObject: nullptr);
256 return;
257 }
258
259 for (int i : m_children.keys()) {
260 const int index = m_api->sourcePropertyIndex(index: i);
261 const auto property = m_object->metaObject()->property(index);
262 QObject *child = property.read(obj: m_object).value<QObject *>();
263 m_children[i]->resetObject(newObject: child);
264 }
265}
266
267QRemoteObjectSource::~QRemoteObjectSource()
268{
269 for (auto it : m_children) {
270 // We used QPointers for m_children because we don't control the lifetime of child QObjects
271 // Since the this/source QObject's parent is the referenced QObject, it could have already
272 // been deleted
273 delete it;
274 }
275}
276
277QRemoteObjectRootSource::~QRemoteObjectRootSource()
278{
279 for (auto it : m_children) {
280 // We used QPointers for m_children because we don't control the lifetime of child QObjects
281 // Since the this/source QObject's parent is the referenced QObject, it could have already
282 // been deleted
283 delete it;
284 }
285 d->m_sourceIo->unregisterSource(source: this);
286 // removeListener tries to modify d->m_listeners, this is O(N²),
287 // so clear d->m_listeners prior to calling unregister (consume loop).
288 // We can do this, because we don't care about the return value of removeListener() here.
289 for (IoDeviceBase *io : qExchange(t&: d->m_listeners, newValue: {})) {
290 removeListener(io, shouldSendRemove: true);
291 }
292 delete d;
293}
294
295QVariantList* QRemoteObjectSourceBase::marshalArgs(int index, void **a)
296{
297 QVariantList &list = m_marshalledArgs;
298 int N = m_api->signalParameterCount(index);
299 if (N == 1 && QMetaType::typeFlags(type: m_api->signalParameterType(sigIndex: index, paramIndex: 0)).testFlag(flag: QMetaType::PointerToQObject))
300 N = 0; // Don't try to send pointers, the will be handle by QRO_
301 if (list.size() < N)
302 list.reserve(alloc: N);
303 const int minFill = std::min(a: list.size(), b: N);
304 for (int i = 0; i < minFill; ++i) {
305 const int type = m_api->signalParameterType(sigIndex: index, paramIndex: i);
306 if (type == QMetaType::QVariant)
307 list[i] = *reinterpret_cast<QVariant *>(a[i + 1]);
308 else
309 list[i] = QVariant(type, a[i + 1]);
310 }
311 for (int i = list.size(); i < N; ++i) {
312 const int type = m_api->signalParameterType(sigIndex: index, paramIndex: i);
313 if (type == QMetaType::QVariant)
314 list << *reinterpret_cast<QVariant *>(a[i + 1]);
315 else
316 list << QVariant(type, a[i + 1]);
317 }
318 for (int i = N; i < list.size(); ++i)
319 list.removeLast();
320 return &m_marshalledArgs;
321}
322
323bool QRemoteObjectSourceBase::invoke(QMetaObject::Call c, int index, const QVariantList &args, QVariant* returnValue)
324{
325 int status = -1;
326 int flags = 0;
327 bool forAdapter = (c == QMetaObject::InvokeMetaMethod ? m_api->isAdapterMethod(index) : m_api->isAdapterProperty(index));
328 int resolvedIndex = (c == QMetaObject::InvokeMetaMethod ? m_api->sourceMethodIndex(index) : m_api->sourcePropertyIndex(index));
329 if (resolvedIndex < 0)
330 return false;
331 QVarLengthArray<void*, 10> param(args.size() + 1);
332
333 if (c == QMetaObject::InvokeMetaMethod) {
334 QMetaMethod method;
335 if (!forAdapter)
336 method = parent()->metaObject()->method(index: resolvedIndex);
337
338 if (returnValue) {
339 if (!forAdapter && method.isValid() && method.returnType() == QMetaType::QVariant)
340 param[0] = const_cast<void*>(reinterpret_cast<const void*>(returnValue));
341 else
342 param[0] = returnValue->data();
343 } else {
344 param[0] = nullptr;
345 }
346
347 auto argument = [&](int i) -> void * {
348 if ((forAdapter && m_api->methodParameterType(methodIndex: index, paramIndex: i) == QMetaType::QVariant) ||
349 (method.isValid() && method.parameterType(index: i) == QMetaType::QVariant)) {
350 return const_cast<void*>(reinterpret_cast<const void*>(&args.at(i)));
351 }
352 return const_cast<void*>(args.at(i).data());
353 };
354
355 for (int i = 0; i < args.size(); ++i) {
356 param[i + 1] = argument(i);
357 }
358 } else if (c == QMetaObject::WriteProperty || c == QMetaObject::ReadProperty) {
359 bool isQVariant = !forAdapter && parent()->metaObject()->property(index: resolvedIndex).userType() == QMetaType::QVariant;
360 for (int i = 0; i < args.size(); ++i) {
361 if (isQVariant)
362 param[i] = const_cast<void*>(reinterpret_cast<const void*>(&args.at(i)));
363 else
364 param[i] = const_cast<void*>(args.at(i).data());
365 }
366 if (c == QMetaObject::WriteProperty) {
367 Q_ASSERT(param.size() == 2); // for return-value and setter value
368 // check QMetaProperty::write for an explanation of these
369 param.append(t: &status);
370 param.append(t: &flags);
371 }
372 } else {
373 // Better safe than sorry
374 return false;
375 }
376 int r = -1;
377 if (forAdapter)
378 r = m_adapter->qt_metacall(c, resolvedIndex, param.data());
379 else
380 r = parent()->qt_metacall(c, resolvedIndex, param.data());
381 return r == -1 && status == -1;
382}
383
384void QRemoteObjectSourceBase::handleMetaCall(int index, QMetaObject::Call call, void **a)
385{
386 if (d->m_listeners.empty())
387 return;
388
389 int propertyIndex = m_api->propertyIndexFromSignal(index);
390 if (propertyIndex >= 0) {
391 const int internalIndex = m_api->propertyRawIndexFromSignal(index);
392 const auto target = m_api->isAdapterProperty(internalIndex) ? m_adapter : m_object;
393 const QMetaProperty mp = target->metaObject()->property(index: propertyIndex);
394 qCDebug(QT_REMOTEOBJECT) << "Sending Invoke Property" << (m_api->isAdapterSignal(internalIndex) ? "via adapter" : "") << internalIndex << propertyIndex << mp.name() << mp.read(obj: target);
395
396 serializePropertyChangePacket(source: this, signalIndex: index);
397 d->m_packet.baseAddress = d->m_packet.size;
398 propertyIndex = internalIndex;
399 }
400
401 qCDebug(QT_REMOTEOBJECT) << "# Listeners" << d->m_listeners.length();
402 qCDebug(QT_REMOTEOBJECT) << "Invoke args:" << m_object
403 << (call == 0 ? QLatin1String("InvokeMetaMethod") : QStringLiteral("Non-invoked call: %d").arg(a: call))
404 << m_api->signalSignature(index) << *marshalArgs(index, a);
405
406 serializeInvokePacket(d->m_packet, name: name(), call, index, args: *marshalArgs(index, a), serialId: -1, propertyIndex);
407 d->m_packet.baseAddress = 0;
408
409 for (IoDeviceBase *io : qAsConst(t&: d->m_listeners))
410 io->write(data: d->m_packet.array, d->m_packet.size);
411}
412
413void QRemoteObjectRootSource::addListener(IoDeviceBase *io, bool dynamic)
414{
415 d->m_listeners.append(t: io);
416 d->isDynamic = d->isDynamic || dynamic;
417
418 if (dynamic) {
419 d->sentTypes.clear();
420 serializeInitDynamicPacket(d->m_packet, this);
421 io->write(data: d->m_packet.array, d->m_packet.size);
422 } else {
423 serializeInitPacket(d->m_packet, this);
424 io->write(data: d->m_packet.array, d->m_packet.size);
425 }
426}
427
428int QRemoteObjectRootSource::removeListener(IoDeviceBase *io, bool shouldSendRemove)
429{
430 d->m_listeners.removeAll(t: io);
431 if (shouldSendRemove)
432 {
433 serializeRemoveObjectPacket(d->m_packet, name: m_api->name());
434 io->write(data: d->m_packet.array, d->m_packet.size);
435 }
436 return d->m_listeners.length();
437}
438
439int QRemoteObjectSourceBase::qt_metacall(QMetaObject::Call call, int methodId, void **a)
440{
441 methodId = QObject::qt_metacall(call, methodId, a);
442 if (methodId < 0)
443 return methodId;
444
445 if (call == QMetaObject::InvokeMetaMethod)
446 handleMetaCall(index: methodId, call, a);
447
448 return -1;
449}
450
451DynamicApiMap::DynamicApiMap(QObject *object, const QMetaObject *metaObject, const QString &name, const QString &typeName)
452 : m_name(name),
453 m_typeName(typeName),
454 m_metaObject(metaObject),
455 m_cachedMetamethodIndex(-1)
456{
457 m_enumOffset = metaObject->enumeratorOffset();
458 m_enumCount = metaObject->enumeratorCount() - m_enumOffset;
459
460 const int propCount = metaObject->propertyCount();
461 const int propOffset = metaObject->propertyOffset();
462 m_properties.reserve(asize: propCount-propOffset);
463 QSet<int> invalidSignals;
464 for (int i = propOffset; i < propCount; ++i) {
465 const QMetaProperty property = metaObject->property(index: i);
466 if (QMetaType::typeFlags(type: property.userType()).testFlag(flag: QMetaType::PointerToQObject)) {
467 auto propertyMeta = QMetaType::metaObjectForType(type: property.userType());
468 QObject *child = property.read(obj: object).value<QObject *>();
469 const QMetaObject *meta = child ? child->metaObject() : propertyMeta;
470 if (!meta) {
471 const int notifyIndex = metaObject->property(index: i).notifySignalIndex();
472 if (notifyIndex != -1)
473 invalidSignals << notifyIndex;
474 continue;
475 }
476 if (meta->inherits(metaObject: &QAbstractItemModel::staticMetaObject)) {
477 const QByteArray name = QByteArray::fromRawData(property.name(),
478 size: qstrlen(str: property.name()));
479 const QByteArray infoName = name.toUpper() + QByteArrayLiteral("_ROLES");
480 const int infoIndex = metaObject->indexOfClassInfo(name: infoName.constData());
481 QByteArray roleInfo;
482 if (infoIndex >= 0) {
483 auto ci = metaObject->classInfo(index: infoIndex);
484 roleInfo = QByteArray::fromRawData(ci.value(), size: qstrlen(str: ci.value()));
485 }
486 m_models << ModelInfo({.ptr: qobject_cast<QAbstractItemModel *>(object: child),
487 .name: QString::fromLatin1(str: property.name()),
488 .roles: roleInfo});
489 } else {
490 QString typeName = QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(meta);
491 if (typeName.isNull()) {
492 typeName = QString::fromLatin1(str: meta->className());
493 if (typeName.contains(s: QLatin1String("QQuick")))
494 typeName.remove(s: QLatin1String("QQuick"));
495 else if (int index = typeName.indexOf(s: QLatin1String("_QMLTYPE_")))
496 typeName.truncate(pos: index);
497 // TODO better way to ensure we have consistent typenames between source/replicas?
498 else if (typeName.endsWith(s: QLatin1String("Source")))
499 typeName.chop(n: 6);
500 }
501
502 m_subclasses << new DynamicApiMap(child, meta, QString::fromLatin1(str: property.name()), typeName);
503 }
504 }
505 m_properties << i;
506 const int notifyIndex = metaObject->property(index: i).notifySignalIndex();
507 if (notifyIndex != -1) {
508 m_signals << notifyIndex;
509 m_propertyAssociatedWithSignal.append(t: i-propOffset);
510 //The starting values of _signals will be the notify signals
511 //So if we are processing _signal with index i, api->sourcePropertyIndex(_propertyAssociatedWithSignal.at(i))
512 //will be the property that changed. This is only valid if i < _propertyAssociatedWithSignal.size().
513 }
514 }
515 const int methodCount = metaObject->methodCount();
516 const int methodOffset = metaObject->methodOffset();
517 for (int i = methodOffset; i < methodCount; ++i) {
518 const QMetaMethod mm = metaObject->method(index: i);
519 const QMetaMethod::MethodType m = mm.methodType();
520 if (m == QMetaMethod::Signal) {
521 if (m_signals.indexOf(t: i) >= 0) // Already added as a property notifier
522 continue;
523 if (invalidSignals.contains(value: i)) // QObject with no metatype
524 continue;
525 m_signals << i;
526 } else if (m == QMetaMethod::Slot || m == QMetaMethod::Method)
527 m_methods << i;
528 }
529
530 m_objectSignature = QtPrivate::qtro_classinfo_signature(metaObject);
531}
532
533QList<QByteArray> DynamicApiMap::signalParameterNames(int index) const
534{
535 const int objectIndex = m_signals.at(i: index);
536 checkCache(objectIndex);
537 return m_cachedMetamethod.parameterNames();
538}
539
540int DynamicApiMap::parameterCount(int objectIndex) const
541{
542 checkCache(objectIndex);
543 return m_cachedMetamethod.parameterCount();
544}
545
546int DynamicApiMap::parameterType(int objectIndex, int paramIndex) const
547{
548 checkCache(objectIndex);
549 return m_cachedMetamethod.parameterType(index: paramIndex);
550}
551
552const QByteArray DynamicApiMap::signature(int objectIndex) const
553{
554 checkCache(objectIndex);
555 return m_cachedMetamethod.methodSignature();
556}
557
558QMetaMethod::MethodType DynamicApiMap::methodType(int index) const
559{
560 const int objectIndex = m_methods.at(i: index);
561 checkCache(objectIndex);
562 return m_cachedMetamethod.methodType();
563}
564
565const QByteArray DynamicApiMap::typeName(int index) const
566{
567 const int objectIndex = m_methods.at(i: index);
568 checkCache(objectIndex);
569 return m_cachedMetamethod.typeName();
570}
571
572QList<QByteArray> DynamicApiMap::methodParameterNames(int index) const
573{
574 const int objectIndex = m_methods.at(i: index);
575 checkCache(objectIndex);
576 return m_cachedMetamethod.parameterNames();
577}
578
579QT_END_NAMESPACE
580

source code of qtremoteobjects/src/remoteobjects/qremoteobjectsource.cpp