| 1 | // Copyright (C) 2017 Ford Motor Company |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | |
| 4 | #include "qremoteobjectreplica.h" |
| 5 | #include "qremoteobjectreplica_p.h" |
| 6 | |
| 7 | #include "qremoteobjectnode.h" |
| 8 | #include "qremoteobjectnode_p.h" |
| 9 | #include "qremoteobjectdynamicreplica.h" |
| 10 | #include "qremoteobjectpacket_p.h" |
| 11 | #include "qremoteobjectpendingcall_p.h" |
| 12 | #include "qconnectionfactories_p.h" |
| 13 | #include "qremoteobjectsource_p.h" |
| 14 | |
| 15 | #include <QtCore/qcoreapplication.h> |
| 16 | #include <QtCore/qdatastream.h> |
| 17 | #include <QtCore/qelapsedtimer.h> |
| 18 | #include <QtCore/qvariant.h> |
| 19 | #include <QtCore/qthread.h> |
| 20 | |
| 21 | #include <limits> |
| 22 | |
| 23 | QT_BEGIN_NAMESPACE |
| 24 | |
| 25 | using namespace QRemoteObjectPackets; |
| 26 | |
| 27 | QT_WARNING_PUSH |
| 28 | QT_WARNING_DISABLE_GCC("-Wtautological-compare" ) |
| 29 | |
| 30 | #if !defined(Q_OS_WIN) && !defined(Q_OS_INTEGRITY) |
| 31 | Q_STATIC_ASSERT_X(&QRemoteObjectReplica::staticMetaObject == &QRemoteObjectDynamicReplica::staticMetaObject, |
| 32 | "m_signalOffset initializer expects QRemoteObjectDynamicReplica to not have a unique staticMetaObject" ); |
| 33 | #endif |
| 34 | |
| 35 | QT_WARNING_POP |
| 36 | |
| 37 | // If QRemoteObjectDynamicReplica ever gets its own staticMetaObject, some commented out code will need to be |
| 38 | // used. It was changed to avoid a Coverity complaint. We use the above static assert to detect if this changes |
| 39 | // in the future. See FIX #1, #2, #3 in this file. |
| 40 | |
| 41 | QRemoteObjectReplicaImplementation::QRemoteObjectReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode *_node) |
| 42 | : QObject(nullptr), m_objectName(name), m_metaObject(meta), m_numSignals(0), m_methodOffset(0) |
| 43 | // Uncomment the following two lines if QRemoteObjectDynamicReplica gets a unique staticMetaObject (FIX #1, #2) |
| 44 | //, m_signalOffset(meta ? QRemoteObjectReplica::staticMetaObject.methodCount() : QRemoteObjectDynamicReplica::staticMetaObject.methodCount()) |
| 45 | //, m_propertyOffset(meta ? QRemoteObjectReplica::staticMetaObject.propertyCount() : QRemoteObjectDynamicReplica::staticMetaObject.propertyCount()) |
| 46 | , m_signalOffset(QRemoteObjectReplica::staticMetaObject.methodCount()) |
| 47 | , m_propertyOffset(QRemoteObjectReplica::staticMetaObject.propertyCount()) |
| 48 | , m_node(_node) |
| 49 | , m_objectSignature(QtPrivate::qtro_classinfo_signature(metaObject: m_metaObject)) |
| 50 | , m_state(meta ? QRemoteObjectReplica::Default : QRemoteObjectReplica::Uninitialized) |
| 51 | { |
| 52 | } |
| 53 | |
| 54 | QRemoteObjectReplicaImplementation::~QRemoteObjectReplicaImplementation() |
| 55 | { |
| 56 | } |
| 57 | |
| 58 | QConnectedReplicaImplementation::QConnectedReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode *node) |
| 59 | : QRemoteObjectReplicaImplementation(name, meta, node), connectionToSource(nullptr) |
| 60 | { |
| 61 | m_heartbeatTimer.setTimerType(Qt::CoarseTimer); |
| 62 | m_heartbeatTimer.setSingleShot(true); |
| 63 | m_heartbeatTimer.setInterval(node->heartbeatInterval()); |
| 64 | |
| 65 | connect(sender: node, signal: &QRemoteObjectNode::heartbeatIntervalChanged, context: this, slot: [this](int interval) { |
| 66 | m_heartbeatTimer.stop(); |
| 67 | m_heartbeatTimer.setInterval(interval); |
| 68 | if (interval) |
| 69 | m_heartbeatTimer.start(); |
| 70 | }); |
| 71 | connect(sender: &m_heartbeatTimer, signal: &QTimer::timeout, context: this, slot: [this] { |
| 72 | // TODO: Revisit if a baseclass method can be used to avoid specialized cast |
| 73 | // conditional logic. |
| 74 | |
| 75 | if (m_pendingCalls.contains(key: 0)) { |
| 76 | m_pendingCalls.take(key: 0); |
| 77 | // The source didn't respond in time, disconnect the connection |
| 78 | if (connectionToSource) { |
| 79 | auto clientIo = qobject_cast<QtROClientIoDevice *>(object: connectionToSource); |
| 80 | if (clientIo) |
| 81 | clientIo->disconnectFromServer(); |
| 82 | else |
| 83 | connectionToSource->close(); |
| 84 | } |
| 85 | } else { |
| 86 | if (connectionToSource.isNull()) { |
| 87 | qCDebug(QT_REMOTEOBJECT) << "Ignoring heartbeat as there is no source connected." ; |
| 88 | return; |
| 89 | } |
| 90 | connectionToSource->d_func()->m_codec->serializePingPacket(name: m_objectName); |
| 91 | if (sendCommandWithReply(serialId: 0).d->serialId == -1) { |
| 92 | m_heartbeatTimer.stop(); |
| 93 | auto clientIo = qobject_cast<QtROClientIoDevice *>(object: connectionToSource); |
| 94 | if (clientIo) |
| 95 | clientIo->disconnectFromServer(); |
| 96 | else |
| 97 | connectionToSource->close(); |
| 98 | } |
| 99 | } |
| 100 | }); |
| 101 | |
| 102 | if (!meta) |
| 103 | return; |
| 104 | |
| 105 | auto offsetMeta = m_metaObject; |
| 106 | QtRemoteObjects::getTypeNameAndMetaobjectFromClassInfo(meta&: offsetMeta); |
| 107 | for (int index = offsetMeta->propertyOffset(); index < offsetMeta->propertyCount(); ++index) { |
| 108 | const QMetaProperty property = offsetMeta->property(index); |
| 109 | if (property.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) |
| 110 | m_childIndices << index - offsetMeta->propertyOffset(); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | QConnectedReplicaImplementation::~QConnectedReplicaImplementation() |
| 115 | { |
| 116 | if (!connectionToSource.isNull()) { |
| 117 | qCDebug(QT_REMOTEOBJECT) << "Replica deleted: sending RemoveObject to RemoteObjectSource" << m_objectName; |
| 118 | connectionToSource->d_func()->m_codec->serializeRemoveObjectPacket(name: m_objectName); |
| 119 | sendCommand(); |
| 120 | } |
| 121 | for (auto prop : m_propertyStorage) { |
| 122 | if (prop.canConvert<QObject*>()) { |
| 123 | if (auto o = prop.value<QObject*>()) |
| 124 | o->deleteLater(); |
| 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | bool QRemoteObjectReplicaImplementation::needsDynamicInitialization() const |
| 130 | { |
| 131 | return m_metaObject == nullptr; |
| 132 | } |
| 133 | |
| 134 | void QRemoteObjectReplicaImplementation::setState(QRemoteObjectReplica::State state) |
| 135 | { |
| 136 | if (m_state.loadAcquire() != QRemoteObjectReplica::Suspect && m_state.loadAcquire() >= state) |
| 137 | return; |
| 138 | |
| 139 | int oldState = m_state.loadAcquire(); |
| 140 | m_state.storeRelease(newValue: state); |
| 141 | |
| 142 | // We should emit initialized before emitting any changed signals in case connections are made in a |
| 143 | // Slot responding to initialized/validChanged. |
| 144 | if (m_state.loadAcquire() == QRemoteObjectReplica::Valid) { |
| 145 | // we're initialized now, emit signal |
| 146 | emitInitialized(); |
| 147 | } |
| 148 | |
| 149 | const static int stateChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod(method: "stateChanged(State,State)" ); |
| 150 | Q_ASSERT(stateChangedIndex != -1); |
| 151 | void *args[] = {nullptr, &state, &oldState}; |
| 152 | QMetaObject::activate(sender: this, metaObject(), local_signal_index: stateChangedIndex, argv: args); |
| 153 | } |
| 154 | |
| 155 | void QRemoteObjectReplicaImplementation::emitNotified() |
| 156 | { |
| 157 | const static int notifiedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod(method: "notified()" ); |
| 158 | Q_ASSERT(notifiedIndex != -1); |
| 159 | void *args[] = {nullptr}; |
| 160 | QMetaObject::activate(sender: this, metaObject(), local_signal_index: notifiedIndex, argv: args); |
| 161 | } |
| 162 | |
| 163 | bool QConnectedReplicaImplementation::sendCommand() |
| 164 | { |
| 165 | Q_ASSERT(connectionToSource); |
| 166 | if (!connectionToSource->isOpen()) |
| 167 | return false; |
| 168 | |
| 169 | connectionToSource->d_func()->m_codec->send(connection: connectionToSource); |
| 170 | if (m_heartbeatTimer.interval()) |
| 171 | m_heartbeatTimer.start(); |
| 172 | return true; |
| 173 | } |
| 174 | |
| 175 | QList<int> QConnectedReplicaImplementation::childIndices() const |
| 176 | { |
| 177 | return m_childIndices; |
| 178 | } |
| 179 | |
| 180 | void QConnectedReplicaImplementation::initialize(QVariantList &&values) |
| 181 | { |
| 182 | qCDebug(QT_REMOTEOBJECT) << "initialize()" << m_propertyStorage.size(); |
| 183 | const int nParam = int(values.size()); |
| 184 | QVarLengthArray<int> changedProperties(nParam); |
| 185 | const int offset = m_propertyOffset; |
| 186 | for (int i = 0; i < nParam; ++i) { |
| 187 | qCDebug(QT_REMOTEOBJECT) << " in loop" << i << m_propertyStorage.size(); |
| 188 | changedProperties[i] = -1; |
| 189 | if (m_propertyStorage[i] != values.at(i)) { |
| 190 | const QMetaProperty property = m_metaObject->property(index: i+offset); |
| 191 | m_propertyStorage[i] = QRemoteObjectPackets::decodeVariant(value: std::move(values[i]), metaType: property.metaType()); |
| 192 | changedProperties[i] = i; |
| 193 | } |
| 194 | qCDebug(QT_REMOTEOBJECT) << "SETPROPERTY" << i << m_metaObject->property(index: i+offset).name() |
| 195 | << m_propertyStorage[i].typeName() |
| 196 | << m_propertyStorage[i].toString(); |
| 197 | } |
| 198 | |
| 199 | Q_ASSERT(m_state.loadAcquire() < QRemoteObjectReplica::Valid || m_state.loadAcquire() == QRemoteObjectReplica::Suspect); |
| 200 | setState(QRemoteObjectReplica::Valid); |
| 201 | |
| 202 | void *args[] = {nullptr, nullptr}; |
| 203 | for (int i = 0; i < nParam; ++i) { |
| 204 | if (changedProperties[i] < 0) |
| 205 | continue; |
| 206 | const int notifyIndex = m_metaObject->property(index: changedProperties[i]+offset).notifySignalIndex(); |
| 207 | if (notifyIndex < 0) |
| 208 | continue; |
| 209 | qCDebug(QT_REMOTEOBJECT) << " Before activate" << notifyIndex << m_metaObject->property(index: notifyIndex).name(); |
| 210 | args[1] = m_propertyStorage[i].data(); |
| 211 | QMetaObject::activate(sender: this, metaObject(), local_signal_index: notifyIndex, argv: args); |
| 212 | } |
| 213 | emitNotified(); |
| 214 | |
| 215 | qCDebug(QT_REMOTEOBJECT) << "isSet = true for" << m_objectName; |
| 216 | if (node()->heartbeatInterval()) |
| 217 | m_heartbeatTimer.start(); |
| 218 | } |
| 219 | |
| 220 | void QRemoteObjectReplicaImplementation::emitInitialized() |
| 221 | { |
| 222 | const static int initializedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod(method: "initialized()" ); |
| 223 | Q_ASSERT(initializedIndex != -1); |
| 224 | void *noArgs[] = {nullptr}; |
| 225 | QMetaObject::activate(sender: this, metaObject(), local_signal_index: initializedIndex, argv: noArgs); |
| 226 | } |
| 227 | |
| 228 | /*! |
| 229 | \internal |
| 230 | */ |
| 231 | void QRemoteObjectReplica::persistProperties(const QString &repName, const QByteArray &repSig, const QVariantList &props) const |
| 232 | { |
| 233 | if (!node()) { |
| 234 | qWarning(msg: "Tried calling persistProperties on a replica (%s) that hasn't been initialized with a node" , qPrintable(repName)); |
| 235 | return; |
| 236 | } |
| 237 | node()->persistProperties(repName, repSig, props); |
| 238 | } |
| 239 | |
| 240 | /*! |
| 241 | \internal |
| 242 | */ |
| 243 | QVariantList QRemoteObjectReplica::retrieveProperties(const QString &repName, const QByteArray &repSig) const |
| 244 | { |
| 245 | if (!node()) { |
| 246 | qWarning(msg: "Tried calling retrieveProperties on a replica (%s) that hasn't been initialized with a node" , qPrintable(repName)); |
| 247 | return QVariantList(); |
| 248 | } |
| 249 | return node()->retrieveProperties(repName, repSig); |
| 250 | } |
| 251 | |
| 252 | void QRemoteObjectReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta) |
| 253 | { |
| 254 | Q_ASSERT(!m_metaObject); |
| 255 | |
| 256 | m_metaObject = meta; |
| 257 | } |
| 258 | |
| 259 | void QConnectedReplicaImplementation::setDynamicMetaObject(const QMetaObject *meta) |
| 260 | { |
| 261 | QRemoteObjectReplicaImplementation::setDynamicMetaObject(meta); |
| 262 | |
| 263 | for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) { |
| 264 | const QMetaProperty property = m_metaObject->property(index); |
| 265 | if (property.metaType().flags().testFlag(flag: QMetaType::PointerToQObject)) |
| 266 | m_childIndices << index - m_metaObject->propertyOffset(); |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | void QRemoteObjectReplicaImplementation::setDynamicProperties(QVariantList &&values) |
| 271 | { |
| 272 | const int offset = m_propertyOffset; |
| 273 | int propertyIndex = -1; |
| 274 | for (auto &prop : values) { |
| 275 | propertyIndex++; |
| 276 | const QMetaProperty property = m_metaObject->property(index: propertyIndex+offset); |
| 277 | prop = QRemoteObjectPackets::decodeVariant(value: std::move(prop), metaType: property.metaType()); |
| 278 | } |
| 279 | //rely on order of properties; |
| 280 | setProperties(std::move(values)); |
| 281 | } |
| 282 | |
| 283 | void QConnectedReplicaImplementation::setDynamicProperties(QVariantList &&values) |
| 284 | { |
| 285 | QRemoteObjectReplicaImplementation::setDynamicProperties(std::move(values)); |
| 286 | for (QRemoteObjectReplica *obj : std::exchange(obj&: m_parentsNeedingConnect, new_val: {})) |
| 287 | configurePrivate(obj); |
| 288 | |
| 289 | Q_ASSERT(m_state.loadAcquire() < QRemoteObjectReplica::Valid); |
| 290 | setState(QRemoteObjectReplica::Valid); |
| 291 | |
| 292 | void *args[] = {nullptr, nullptr}; |
| 293 | for (int index = m_metaObject->propertyOffset(); index < m_metaObject->propertyCount(); ++index) { |
| 294 | const QMetaProperty mp = m_metaObject->property(index); |
| 295 | if (mp.hasNotifySignal()) { |
| 296 | qCDebug(QT_REMOTEOBJECT) << " Before activate" << index << m_metaObject->property(index).name(); |
| 297 | args[1] = this->m_propertyStorage[index-m_propertyOffset].data(); |
| 298 | QMetaObject::activate(sender: this, metaObject(), local_signal_index: mp.notifySignalIndex(), argv: args); |
| 299 | } |
| 300 | } |
| 301 | emitNotified(); |
| 302 | |
| 303 | qCDebug(QT_REMOTEOBJECT) << "isSet = true for" << m_objectName; |
| 304 | } |
| 305 | |
| 306 | bool QConnectedReplicaImplementation::isInitialized() const |
| 307 | { |
| 308 | return m_state.loadAcquire() > QRemoteObjectReplica::Default && m_state.loadAcquire() != QRemoteObjectReplica::SignatureMismatch; |
| 309 | } |
| 310 | |
| 311 | bool QConnectedReplicaImplementation::waitForSource(int timeout) |
| 312 | { |
| 313 | switch (state()) { |
| 314 | case QRemoteObjectReplica::State::Valid: |
| 315 | return true; |
| 316 | case QRemoteObjectReplica::State::SignatureMismatch: |
| 317 | return false; |
| 318 | default: |
| 319 | break; |
| 320 | } |
| 321 | |
| 322 | const static int stateChangedIndex = QRemoteObjectReplica::staticMetaObject.indexOfMethod(method: "stateChanged(State,State)" ); |
| 323 | Q_ASSERT(stateChangedIndex != -1); |
| 324 | |
| 325 | QEventLoop loop; |
| 326 | QMetaObject::connect(sender: this, signal_index: stateChangedIndex, |
| 327 | receiver: &loop, method_index: QEventLoop::staticMetaObject.indexOfMethod(method: "quit()" ), |
| 328 | type: Qt::DirectConnection, types: nullptr); |
| 329 | |
| 330 | QTimer t; // NB: Related to QTBUG-94570 - don't use QTimer::singleShot here. |
| 331 | if (timeout >= 0) { |
| 332 | t.setSingleShot(true); |
| 333 | connect(sender: &t, signal: &QTimer::timeout, context: &loop, slot: &QEventLoop::quit); |
| 334 | t.start(msec: timeout); |
| 335 | } |
| 336 | |
| 337 | // enter the event loop and wait for a reply |
| 338 | loop.exec(flags: QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); |
| 339 | |
| 340 | return state() == QRemoteObjectReplica::State::Valid; |
| 341 | } |
| 342 | |
| 343 | void QConnectedReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args) |
| 344 | { |
| 345 | static const bool debugArgs = qEnvironmentVariableIsSet(varName: "QT_REMOTEOBJECT_DEBUG_ARGUMENTS" ); |
| 346 | |
| 347 | Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty); |
| 348 | if (connectionToSource.isNull()) { |
| 349 | qCWarning(QT_REMOTEOBJECT) << "connectionToSource is null" ; |
| 350 | return; |
| 351 | } |
| 352 | |
| 353 | if (call == QMetaObject::InvokeMetaMethod) { |
| 354 | if (debugArgs) { |
| 355 | qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource; |
| 356 | } else { |
| 357 | qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << connectionToSource; |
| 358 | } |
| 359 | if (index < m_methodOffset) //index - m_methodOffset < 0 is invalid, and can't be resolved on the Source side |
| 360 | qCWarning(QT_REMOTEOBJECT) << "Skipping invalid method invocation. Index not found:" << index << "( offset =" << m_methodOffset << ") object:" << m_objectName << this->m_metaObject->method(index).name(); |
| 361 | else { |
| 362 | connectionToSource->d_func()->m_codec->serializeInvokePacket(name: m_objectName, call, index: index - m_methodOffset, args); |
| 363 | sendCommand(); |
| 364 | } |
| 365 | } else { |
| 366 | qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->property(index).name() << index << args << connectionToSource; |
| 367 | if (index < m_propertyOffset) //index - m_propertyOffset < 0 is invalid, and can't be resolved on the Source side |
| 368 | qCWarning(QT_REMOTEOBJECT) << "Skipping invalid property invocation. Index not found:" << index << "( offset =" << m_propertyOffset << ") object:" << m_objectName << this->m_metaObject->property(index).name(); |
| 369 | else { |
| 370 | connectionToSource->d_func()->m_codec->serializeInvokePacket(name: m_objectName, call, index: index - m_propertyOffset, args); |
| 371 | sendCommand(); |
| 372 | } |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | QRemoteObjectPendingCall QConnectedReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) |
| 377 | { |
| 378 | Q_ASSERT(call == QMetaObject::InvokeMetaMethod); |
| 379 | if (connectionToSource.isNull()) { |
| 380 | qCWarning(QT_REMOTEOBJECT) << "connectionToSource is null" ; |
| 381 | return QRemoteObjectPendingCall(); |
| 382 | } |
| 383 | |
| 384 | qCDebug(QT_REMOTEOBJECT) << "Send" << call << this->m_metaObject->method(index).name() << index << args << connectionToSource; |
| 385 | int serialId = (m_curSerialId == std::numeric_limits<int>::max() ? 1 : m_curSerialId++); |
| 386 | connectionToSource->d_func()->m_codec->serializeInvokePacket(name: m_objectName, call, index: index - m_methodOffset, args, serialId); |
| 387 | return sendCommandWithReply(serialId); |
| 388 | } |
| 389 | |
| 390 | QRemoteObjectPendingCall QConnectedReplicaImplementation::sendCommandWithReply(int serialId) |
| 391 | { |
| 392 | bool success = sendCommand(); |
| 393 | if (!success) { |
| 394 | return QRemoteObjectPendingCall(); // invalid |
| 395 | } |
| 396 | |
| 397 | qCDebug(QT_REMOTEOBJECT) << "Sent InvokePacket with serial id:" << serialId; |
| 398 | QRemoteObjectPendingCall pendingCall(new QRemoteObjectPendingCallData(serialId, this)); |
| 399 | Q_ASSERT(!m_pendingCalls.contains(serialId)); |
| 400 | m_pendingCalls[serialId] = pendingCall; |
| 401 | return pendingCall; |
| 402 | } |
| 403 | |
| 404 | void QConnectedReplicaImplementation::notifyAboutReply(int ackedSerialId, const QVariant &value) |
| 405 | { |
| 406 | QRemoteObjectPendingCall call = m_pendingCalls.take(key: ackedSerialId); |
| 407 | if (ackedSerialId == 0) { |
| 408 | m_heartbeatTimer.stop(); |
| 409 | if (m_heartbeatTimer.interval()) |
| 410 | m_heartbeatTimer.start(); |
| 411 | return; |
| 412 | } |
| 413 | |
| 414 | QMutexLocker mutex(&call.d->mutex); |
| 415 | |
| 416 | // clear error flag |
| 417 | call.d->error = QRemoteObjectPendingCall::NoError; |
| 418 | call.d->returnValue = value; |
| 419 | |
| 420 | // notify watchers if needed |
| 421 | if (call.d->watcherHelper) |
| 422 | call.d->watcherHelper->emitSignals(); |
| 423 | } |
| 424 | |
| 425 | bool QConnectedReplicaImplementation::waitForFinished(const QRemoteObjectPendingCall& call, int timeout) |
| 426 | { |
| 427 | if (!call.d->watcherHelper) |
| 428 | call.d->watcherHelper.reset(other: new QRemoteObjectPendingCallWatcherHelper); |
| 429 | |
| 430 | call.d->mutex.unlock(); |
| 431 | |
| 432 | QEventLoop loop; |
| 433 | loop.connect(sender: call.d->watcherHelper.data(), signal: &QRemoteObjectPendingCallWatcherHelper::finished, |
| 434 | context: &loop, slot: &QEventLoop::quit); |
| 435 | |
| 436 | QTimer t; // NB: Related to QTBUG-94570 - don't use QTimer::singleShot here. |
| 437 | if (timeout >= 0) { |
| 438 | t.setSingleShot(true); |
| 439 | connect(sender: &t, signal: &QTimer::timeout, context: &loop, slot: &QEventLoop::quit); |
| 440 | t.start(msec: timeout); |
| 441 | } |
| 442 | |
| 443 | // enter the event loop and wait for a reply |
| 444 | loop.exec(flags: QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); |
| 445 | |
| 446 | call.d->mutex.lock(); |
| 447 | |
| 448 | return call.d->error != QRemoteObjectPendingCall::InvalidMessage; |
| 449 | } |
| 450 | |
| 451 | const QVariant QConnectedReplicaImplementation::getProperty(int i) const |
| 452 | { |
| 453 | Q_ASSERT_X(i >= 0 && i < m_propertyStorage.size(), __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2" )).arg(i).arg(m_propertyStorage.size()))); |
| 454 | return m_propertyStorage[i]; |
| 455 | } |
| 456 | |
| 457 | void QConnectedReplicaImplementation::setProperties(QVariantList &&properties) |
| 458 | { |
| 459 | Q_ASSERT(m_propertyStorage.isEmpty()); |
| 460 | m_propertyStorage.reserve(asize: properties.size()); |
| 461 | m_propertyStorage = std::move(properties); |
| 462 | } |
| 463 | |
| 464 | void QConnectedReplicaImplementation::setProperty(int i, const QVariant &prop) |
| 465 | { |
| 466 | m_propertyStorage[i] = prop; |
| 467 | } |
| 468 | |
| 469 | void QConnectedReplicaImplementation::setConnection(QtROIoDeviceBase *conn) |
| 470 | { |
| 471 | if (connectionToSource.isNull()) { |
| 472 | connectionToSource = conn; |
| 473 | qCDebug(QT_REMOTEOBJECT) << "setConnection started" << conn << m_objectName; |
| 474 | } |
| 475 | requestRemoteObjectSource(); |
| 476 | } |
| 477 | |
| 478 | void QConnectedReplicaImplementation::setDisconnected() |
| 479 | { |
| 480 | Q_ASSERT(connectionToSource); |
| 481 | connectionToSource.clear(); |
| 482 | setState(QRemoteObjectReplica::State::Suspect); |
| 483 | for (const int index : childIndices()) { |
| 484 | auto pointerToQObject = qvariant_cast<QObject *>(v: getProperty(i: index)); |
| 485 | auto child = qobject_cast<QRemoteObjectReplica *>(object: pointerToQObject); |
| 486 | if (child) { |
| 487 | QConnectedReplicaImplementation *childReplicaImplData |
| 488 | = static_cast<QConnectedReplicaImplementation *>(child->d_impl.data()); |
| 489 | if (childReplicaImplData && !childReplicaImplData->connectionToSource.isNull()) |
| 490 | childReplicaImplData->setDisconnected(); |
| 491 | } |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | void QConnectedReplicaImplementation::requestRemoteObjectSource() |
| 496 | { |
| 497 | Q_ASSERT(connectionToSource); |
| 498 | connectionToSource->d_func()->m_codec->serializeAddObjectPacket(name: m_objectName, isDynamic: needsDynamicInitialization()); |
| 499 | sendCommand(); |
| 500 | } |
| 501 | |
| 502 | void QRemoteObjectReplicaImplementation::configurePrivate(QRemoteObjectReplica *rep) |
| 503 | { |
| 504 | qCDebug(QT_REMOTEOBJECT) << "configurePrivate starting for" << this->m_objectName; |
| 505 | //We need to connect the Replicant only signals too |
| 506 | // Uncomment the following two lines if QRemoteObjectDynamicReplica gets a unique staticMetaObject (FIX #3) |
| 507 | //const QMetaObject *m = rep->inherits("QRemoteObjectDynamicReplica") ? |
| 508 | // &QRemoteObjectDynamicReplica::staticMetaObject : &QRemoteObjectReplica::staticMetaObject; |
| 509 | const QMetaObject *m = &QRemoteObjectReplica::staticMetaObject; |
| 510 | for (int i = m->methodOffset(); i < m->methodCount(); ++i) |
| 511 | { |
| 512 | const QMetaMethod mm = m->method(index: i); |
| 513 | if (mm.methodType() == QMetaMethod::Signal) { |
| 514 | const bool res = QMetaObject::connect(sender: this, signal_index: i, receiver: rep, method_index: i, type: Qt::DirectConnection, types: nullptr); |
| 515 | qCDebug(QT_REMOTEOBJECT) << " Rep connect" <<i<<res<<mm.name(); |
| 516 | Q_UNUSED(res) |
| 517 | } |
| 518 | } |
| 519 | if (m_methodOffset == 0) //We haven't initialized the offsets yet |
| 520 | { |
| 521 | const int index = m_metaObject->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE); |
| 522 | const QMetaObject *metaObject = m_metaObject; |
| 523 | if (index != -1) { //We have an object created from repc or at least with QCLASSINFO defined |
| 524 | while (true) { |
| 525 | Q_ASSERT(metaObject->superClass()); //This recurses to QObject, which doesn't have QCLASSINFO_REMOTEOBJECT_TYPE |
| 526 | if (index != metaObject->superClass()->indexOfClassInfo(QCLASSINFO_REMOTEOBJECT_TYPE)) //At the point we don't find the same QCLASSINFO_REMOTEOBJECT_TYPE, |
| 527 | //we have the metaobject we should work from |
| 528 | break; |
| 529 | metaObject = metaObject->superClass(); |
| 530 | } |
| 531 | } |
| 532 | |
| 533 | for (int i = m_signalOffset; i < metaObject->methodCount(); ++i) { |
| 534 | const QMetaMethod mm = metaObject->method(index: i); |
| 535 | if (mm.methodType() == QMetaMethod::Signal) { |
| 536 | ++m_numSignals; |
| 537 | const bool res = QMetaObject::connect(sender: this, signal_index: i, receiver: rep, method_index: i, type: Qt::DirectConnection, types: nullptr); |
| 538 | qCDebug(QT_REMOTEOBJECT) << " Connect" <<i<<res<<mm.name(); |
| 539 | Q_UNUSED(res) |
| 540 | } |
| 541 | } |
| 542 | m_methodOffset = m_signalOffset + m_numSignals; |
| 543 | qCDebug(QT_REMOTEOBJECT) << QStringLiteral("configurePrivate finished, signalOffset = %1, methodOffset = %2, #Signals = %3" ).arg(a: m_signalOffset).arg(a: m_methodOffset).arg(a: m_numSignals); |
| 544 | } else { //We have initialized offsets, this is an additional Replica attaching |
| 545 | for (int i = m_signalOffset; i < m_methodOffset; ++i) { |
| 546 | const bool res = QMetaObject::connect(sender: this, signal_index: i, receiver: rep, method_index: i, type: Qt::DirectConnection, types: nullptr); |
| 547 | qCDebug(QT_REMOTEOBJECT) << " Connect" <<i<<res<<m_metaObject->method(index: i).name(); |
| 548 | Q_UNUSED(res) |
| 549 | } |
| 550 | if (isInitialized()) { |
| 551 | qCDebug(QT_REMOTEOBJECT) << QStringLiteral("ReplicaImplementation initialized, emitting signal on replica" ); |
| 552 | emit rep->initialized(); //Emit from new replica only |
| 553 | } |
| 554 | if (state() != QRemoteObjectReplica::Valid) { |
| 555 | qCDebug(QT_REMOTEOBJECT) << QStringLiteral("ReplicaImplementation not currently valid, emitting signal on replica" ); |
| 556 | emit rep->stateChanged(state: state(), oldState: m_metaObject ? QRemoteObjectReplica::Default : QRemoteObjectReplica::Uninitialized); |
| 557 | } |
| 558 | |
| 559 | qCDebug(QT_REMOTEOBJECT) << QStringLiteral("configurePrivate finished, added replica to existing ReplicaImplementation" ); |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | void QConnectedReplicaImplementation::configurePrivate(QRemoteObjectReplica *rep) |
| 564 | { |
| 565 | if (m_metaObject) { |
| 566 | // see QRemoteObjectReplicaImplementation::configurePrivate |
| 567 | const bool firstReplicaInstance = (m_methodOffset == 0); |
| 568 | |
| 569 | QRemoteObjectReplicaImplementation::configurePrivate(rep); |
| 570 | |
| 571 | // ensure that notify signals are emitted for the new replica, when |
| 572 | // we are initializing an nth replica of the same type |
| 573 | if (!firstReplicaInstance) { |
| 574 | const int offset = m_propertyOffset; |
| 575 | const int nParam = int(m_propertyStorage.size()); |
| 576 | void *args[] = {nullptr, nullptr}; |
| 577 | for (int i = 0; i < nParam; ++i) { |
| 578 | const int notifyIndex = m_metaObject->property(index: i+offset).notifySignalIndex(); |
| 579 | if (notifyIndex < 0) |
| 580 | continue; |
| 581 | qCDebug(QT_REMOTEOBJECT) << " Before activate" << notifyIndex << m_metaObject->property(index: i+offset).name(); |
| 582 | args[1] = m_propertyStorage[i].data(); |
| 583 | // NOTE: this over-emits (assumes all values have changed) |
| 584 | QMetaObject::activate(sender: rep, rep->metaObject(), local_signal_index: notifyIndex - m_signalOffset, argv: args); |
| 585 | } |
| 586 | } |
| 587 | } else |
| 588 | m_parentsNeedingConnect.append(t: rep); |
| 589 | } |
| 590 | |
| 591 | /*! |
| 592 | \class QRemoteObjectReplica |
| 593 | \inmodule QtRemoteObjects |
| 594 | \brief A class interacting with (but not implementing) a Qt API on the Remote Object network. |
| 595 | |
| 596 | A Remote Object Replica is a QObject proxy for another QObject (called the |
| 597 | \l {Source} object). Once initialized, a replica can be considered a |
| 598 | "latent copy" of the \l {Source} object. That is, every change to a |
| 599 | Q_PROPERTY on the \l {Source}, or signal emitted by the \l {Source} will be |
| 600 | updated/emitted by all \l {Replica} objects. Latency |
| 601 | is introduced by process scheduling by any OSes involved and network |
| 602 | communication latency. As long as the replica has been initialized and the |
| 603 | communication is not disrupted, receipt and order of changes is guaranteed. |
| 604 | |
| 605 | The \l {isInitialized} and \l {state} properties (and corresponding |
| 606 | \l {initialized()}/\l {stateChanged()} signals) allow the state of a |
| 607 | \l {Replica} to be determined. |
| 608 | |
| 609 | While Qt Remote Objects (QtRO) handles the initialization and |
| 610 | synchronization of \l {Replica} objects, there are numerous steps happening |
| 611 | behind the scenes which can fail and that aren't encountered in single |
| 612 | process Qt applications. See \l {Troubleshooting} for advice on how to |
| 613 | handle such issues when using a remote objects network. |
| 614 | */ |
| 615 | |
| 616 | /*! |
| 617 | \enum QRemoteObjectReplica::State |
| 618 | |
| 619 | This enum type specifies the various state codes associated with QRemoteObjectReplica states: |
| 620 | |
| 621 | \value Uninitialized Initial value of DynamicReplica, where nothing is |
| 622 | known about the replica before connection to source. |
| 623 | |
| 624 | \value Default Initial value of static replica, where any defaults set in |
| 625 | the .rep file are available so it can be used if necessary. |
| 626 | |
| 627 | \value Valid Indicates the replica is connected, has good property values |
| 628 | and can be interacted with. |
| 629 | |
| 630 | \value Suspect Error state that occurs if the connection to the source is |
| 631 | lost after it is initialized. |
| 632 | |
| 633 | \value SignatureMismatch Error state that occurs if a connection to the |
| 634 | source is made, but the source and replica are not derived from the same |
| 635 | .rep (only possible for static Replicas). |
| 636 | */ |
| 637 | |
| 638 | /*! |
| 639 | \fn void QRemoteObjectReplica::stateChanged(State state, State oldState) |
| 640 | |
| 641 | This signal is emitted whenever a replica's state toggles between |
| 642 | \l QRemoteObjectReplica::State. |
| 643 | |
| 644 | The change in state is represented with \a state and \a oldState. |
| 645 | |
| 646 | \sa state(), initialized() |
| 647 | */ |
| 648 | |
| 649 | /*! |
| 650 | \fn void QRemoteObjectReplica::initialized() |
| 651 | |
| 652 | This signal is emitted once the replica is initialized. An intialized replica |
| 653 | has all property values set, but has not yet emitted any property change |
| 654 | notifications. |
| 655 | |
| 656 | \sa isInitialized(), stateChanged() |
| 657 | */ |
| 658 | |
| 659 | /*! |
| 660 | \fn void QRemoteObjectReplica::notified() |
| 661 | |
| 662 | This signal is emitted once the replica is initialized and all property change |
| 663 | notifications have been emitted. |
| 664 | |
| 665 | It is sometimes useful to respond to property changes as events. |
| 666 | For example, you might want to display a user notification when a certain |
| 667 | property change occurs. However, this user notification would then also be |
| 668 | triggered when a replica first became \c QRemoteObjectReplica::Valid, as |
| 669 | all property change signals are emitted at that time. This isn't always desirable, |
| 670 | and \c notified allows the developer to distinguish between these two cases. |
| 671 | */ |
| 672 | |
| 673 | /*! |
| 674 | \internal |
| 675 | \enum QRemoteObjectReplica::ConstructorType |
| 676 | */ |
| 677 | |
| 678 | /*! |
| 679 | \property QRemoteObjectReplica::state |
| 680 | \brief Returns the replica state. |
| 681 | |
| 682 | This property holds the replica \l QRemoteObjectReplica::State. |
| 683 | */ |
| 684 | |
| 685 | /*! |
| 686 | \property QRemoteObjectReplica::node |
| 687 | \brief A pointer to the node this object was acquired from. |
| 688 | */ |
| 689 | |
| 690 | /*! |
| 691 | \internal This (protected) constructor for QRemoteObjectReplica can be used to create |
| 692 | replica objects from QML. |
| 693 | */ |
| 694 | QRemoteObjectReplica::QRemoteObjectReplica(ConstructorType t) |
| 695 | : QObject(nullptr) |
| 696 | , d_impl(t == DefaultConstructor ? new QStubReplicaImplementation : nullptr) |
| 697 | { |
| 698 | qRegisterMetaType<State>(typeName: "State" ); |
| 699 | } |
| 700 | |
| 701 | QRemoteObjectReplica::QRemoteObjectReplica(QObjectPrivate &dptr, QObject *parent) |
| 702 | : QObject(dptr, parent) |
| 703 | , d_impl(new QStubReplicaImplementation) |
| 704 | { |
| 705 | } |
| 706 | |
| 707 | /*! |
| 708 | \internal |
| 709 | */ |
| 710 | QRemoteObjectReplica::~QRemoteObjectReplica() |
| 711 | { |
| 712 | } |
| 713 | |
| 714 | /*! |
| 715 | \internal |
| 716 | */ |
| 717 | void QRemoteObjectReplica::send(QMetaObject::Call call, int index, const QVariantList &args) |
| 718 | { |
| 719 | Q_ASSERT(index != -1); |
| 720 | |
| 721 | d_impl->_q_send(call, index, args); |
| 722 | } |
| 723 | |
| 724 | /*! |
| 725 | \internal |
| 726 | */ |
| 727 | QRemoteObjectPendingCall QRemoteObjectReplica::sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) |
| 728 | { |
| 729 | return d_impl->_q_sendWithReply(call, index, args); |
| 730 | } |
| 731 | |
| 732 | /*! |
| 733 | \internal |
| 734 | */ |
| 735 | const QVariant QRemoteObjectReplica::propAsVariant(int i) const |
| 736 | { |
| 737 | return d_impl->getProperty(i); |
| 738 | } |
| 739 | |
| 740 | /*! |
| 741 | \internal |
| 742 | */ |
| 743 | void QRemoteObjectReplica::initializeNode(QRemoteObjectNode *node, const QString &name) |
| 744 | { |
| 745 | node->initializeReplica(instance: this, name); |
| 746 | } |
| 747 | |
| 748 | /*! |
| 749 | \internal |
| 750 | */ |
| 751 | void QRemoteObjectReplica::setProperties(QVariantList &&properties) |
| 752 | { |
| 753 | d_impl->setProperties(std::move(properties)); |
| 754 | } |
| 755 | |
| 756 | /*! |
| 757 | \internal |
| 758 | */ |
| 759 | void QRemoteObjectReplica::setChild(int i, const QVariant &value) |
| 760 | { |
| 761 | d_impl->setProperty(i, value); |
| 762 | } |
| 763 | |
| 764 | /*! |
| 765 | Returns \c true if this replica has been initialized with data from the |
| 766 | \l {Source} object. Returns \c false otherwise. |
| 767 | |
| 768 | \sa state() |
| 769 | */ |
| 770 | bool QRemoteObjectReplica::isInitialized() const |
| 771 | { |
| 772 | return d_impl->isInitialized(); |
| 773 | } |
| 774 | |
| 775 | /*! |
| 776 | Returns the current \l {QRemoteObjectReplica::State}{state} of the replica. |
| 777 | |
| 778 | \sa isInitialized |
| 779 | */ |
| 780 | QRemoteObjectReplica::State QRemoteObjectReplica::state() const |
| 781 | { |
| 782 | return d_impl->state(); |
| 783 | } |
| 784 | |
| 785 | QRemoteObjectNode *QRemoteObjectReplica::node() const |
| 786 | { |
| 787 | return d_impl->node(); |
| 788 | } |
| 789 | |
| 790 | void QRemoteObjectReplica::setNode(QRemoteObjectNode *_node) |
| 791 | { |
| 792 | const QRemoteObjectNode *curNode = node(); |
| 793 | if (curNode) { |
| 794 | qCWarning(QT_REMOTEOBJECT) << "Ignoring call to setNode as the node has already been set" ; |
| 795 | return; |
| 796 | } |
| 797 | d_impl.clear(); |
| 798 | _node->initializeReplica(instance: this); |
| 799 | } |
| 800 | |
| 801 | /*! |
| 802 | \internal |
| 803 | */ |
| 804 | void QRemoteObjectReplica::initialize() |
| 805 | { |
| 806 | } |
| 807 | |
| 808 | /*! |
| 809 | Returns \c true if this replica has been initialized and has a valid |
| 810 | connection with the \l {QRemoteObjectNode} {node} hosting the \l {Source}. |
| 811 | Returns \c false otherwise. |
| 812 | |
| 813 | \sa isInitialized() |
| 814 | */ |
| 815 | bool QRemoteObjectReplica::isReplicaValid() const |
| 816 | { |
| 817 | return state() == Valid; |
| 818 | } |
| 819 | |
| 820 | /*! |
| 821 | Blocking call that waits for the replica to become initialized or until the |
| 822 | \a timeout (in ms) expires. Returns \c true if the replica is initialized |
| 823 | when the call completes, \c false otherwise. |
| 824 | |
| 825 | If \a timeout is -1, this function will not time out. |
| 826 | |
| 827 | \sa isInitialized(), initialized() |
| 828 | */ |
| 829 | bool QRemoteObjectReplica::waitForSource(int timeout) |
| 830 | { |
| 831 | return d_impl->waitForSource(timeout); |
| 832 | } |
| 833 | |
| 834 | QInProcessReplicaImplementation::QInProcessReplicaImplementation(const QString &name, const QMetaObject *meta, QRemoteObjectNode * node) |
| 835 | : QRemoteObjectReplicaImplementation(name, meta, node) |
| 836 | { |
| 837 | } |
| 838 | |
| 839 | QInProcessReplicaImplementation::~QInProcessReplicaImplementation() |
| 840 | { |
| 841 | } |
| 842 | |
| 843 | const QVariant QInProcessReplicaImplementation::getProperty(int i) const |
| 844 | { |
| 845 | Q_ASSERT(connectionToSource); |
| 846 | Q_ASSERT(connectionToSource->m_object); |
| 847 | const int index = i + QRemoteObjectSource::qobjectPropertyOffset; |
| 848 | Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); |
| 849 | return connectionToSource->m_object->metaObject()->property(index).read(obj: connectionToSource->m_object); |
| 850 | } |
| 851 | |
| 852 | void QInProcessReplicaImplementation::setProperties(QVariantList &&) |
| 853 | { |
| 854 | //TODO some verification here maybe? |
| 855 | } |
| 856 | |
| 857 | void QInProcessReplicaImplementation::setProperty(int i, const QVariant &property) |
| 858 | { |
| 859 | Q_ASSERT(connectionToSource); |
| 860 | Q_ASSERT(connectionToSource->m_object); |
| 861 | const int index = i + QRemoteObjectSource::qobjectPropertyOffset; |
| 862 | Q_ASSERT(index >= 0 && index < connectionToSource->m_object->metaObject()->propertyCount()); |
| 863 | connectionToSource->m_object->metaObject()->property(index).write(obj: connectionToSource->m_object, value: property); |
| 864 | } |
| 865 | |
| 866 | void QInProcessReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args) |
| 867 | { |
| 868 | Q_ASSERT(call == QMetaObject::InvokeMetaMethod || call == QMetaObject::WriteProperty); |
| 869 | |
| 870 | const SourceApiMap *api = connectionToSource->m_api; |
| 871 | if (call == QMetaObject::InvokeMetaMethod) { |
| 872 | const int resolvedIndex = api->sourceMethodIndex(index: index - m_methodOffset); |
| 873 | if (resolvedIndex < 0) |
| 874 | qCWarning(QT_REMOTEOBJECT) << "Skipping invalid invocation. Index not found:" << index - m_methodOffset; |
| 875 | else |
| 876 | connectionToSource->invoke(c: call, index: index - m_methodOffset, args); |
| 877 | } else { |
| 878 | const int resolvedIndex = connectionToSource->m_api->sourcePropertyIndex(index: index - m_propertyOffset); |
| 879 | if (resolvedIndex < 0) |
| 880 | qCWarning(QT_REMOTEOBJECT) << "Skipping invalid property setter. Index not found:" << index - m_propertyOffset; |
| 881 | else |
| 882 | connectionToSource->invoke(c: call, index: index - m_propertyOffset, args); |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | QRemoteObjectPendingCall QInProcessReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) |
| 887 | { |
| 888 | Q_ASSERT(call == QMetaObject::InvokeMetaMethod); |
| 889 | |
| 890 | const int ReplicaIndex = index - m_methodOffset; |
| 891 | auto metaType = QMetaType::fromName(name: connectionToSource->m_api->typeName(index: ReplicaIndex).constData()); |
| 892 | if (!metaType.sizeOf()) |
| 893 | metaType = QMetaType(QMetaType::UnknownType); |
| 894 | QVariant returnValue(metaType, nullptr); |
| 895 | |
| 896 | const int resolvedIndex = connectionToSource->m_api->sourceMethodIndex(index: ReplicaIndex); |
| 897 | if (resolvedIndex < 0) { |
| 898 | qCWarning(QT_REMOTEOBJECT) << "Skipping invalid invocation. Index not found:" << ReplicaIndex; |
| 899 | return QRemoteObjectPendingCall(); |
| 900 | } |
| 901 | |
| 902 | connectionToSource->invoke(c: call, index: ReplicaIndex, args, returnValue: &returnValue); |
| 903 | return QRemoteObjectPendingCall::fromCompletedCall(returnValue); |
| 904 | } |
| 905 | |
| 906 | QStubReplicaImplementation::QStubReplicaImplementation() {} |
| 907 | |
| 908 | QStubReplicaImplementation::~QStubReplicaImplementation() {} |
| 909 | |
| 910 | const QVariant QStubReplicaImplementation::getProperty(int i) const |
| 911 | { |
| 912 | Q_ASSERT_X(i >= 0 && i < m_propertyStorage.size(), __FUNCTION__, qPrintable(QString(QLatin1String("0 <= %1 < %2" )).arg(i).arg(m_propertyStorage.size()))); |
| 913 | return m_propertyStorage[i]; |
| 914 | } |
| 915 | |
| 916 | void QStubReplicaImplementation::setProperties(QVariantList &&properties) |
| 917 | { |
| 918 | Q_ASSERT(m_propertyStorage.isEmpty()); |
| 919 | m_propertyStorage.reserve(asize: properties.size()); |
| 920 | m_propertyStorage = std::move(properties); |
| 921 | } |
| 922 | |
| 923 | void QStubReplicaImplementation::setProperty(int i, const QVariant &prop) |
| 924 | { |
| 925 | m_propertyStorage[i] = prop; |
| 926 | } |
| 927 | |
| 928 | void QStubReplicaImplementation::_q_send(QMetaObject::Call call, int index, const QVariantList &args) |
| 929 | { |
| 930 | Q_UNUSED(call) |
| 931 | Q_UNUSED(index) |
| 932 | Q_UNUSED(args) |
| 933 | qWarning(msg: "Tried calling a slot or setting a property on a replica that hasn't been initialized with a node" ); |
| 934 | } |
| 935 | |
| 936 | QRemoteObjectPendingCall QStubReplicaImplementation::_q_sendWithReply(QMetaObject::Call call, int index, const QVariantList &args) |
| 937 | { |
| 938 | Q_UNUSED(call) |
| 939 | Q_UNUSED(index) |
| 940 | Q_UNUSED(args) |
| 941 | qWarning(msg: "Tried calling a slot or setting a property on a replica that hasn't been initialized with a node" ); |
| 942 | return QRemoteObjectPendingCall(); //Invalid |
| 943 | } |
| 944 | |
| 945 | QT_END_NAMESPACE |
| 946 | |