1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtSystems module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34#include "proxyobject_p.h"
35#include <private/qmetaobjectbuilder_p.h>
36#include "qremoteserviceregisterentry_p.h"
37#include "qservicedebuglog_p.h"
38
39#include <qmetaobject.h>
40#include <qtimer.h>
41#include <qcoreevent.h>
42
43#include <QDebug>
44#include <QCoreApplication>
45
46#include <stdlib.h>
47
48QT_BEGIN_NAMESPACE
49
50class QServiceProxyPrivate
51{
52public:
53 QByteArray metadata;
54 QMetaObject* meta;
55 ObjectEndPoint* endPoint;
56 int *localToRemote;
57 int *remoteToLocal;
58
59};
60
61QServiceProxy::QServiceProxy(const QByteArray& metadata, ObjectEndPoint* endPoint, QObject* parent)
62 : QServiceProxyBase(endPoint, parent)
63{
64 Q_ASSERT(endPoint);
65 d = new QServiceProxyPrivate();
66 d->metadata = metadata;
67 d->meta = 0;
68 d->endPoint = endPoint;
69 d->localToRemote = 0;
70 d->remoteToLocal = 0;
71
72 QDataStream stream(d->metadata);
73 QMetaObjectBuilder builder;
74 QMap<QByteArray, const QMetaObject*> refs;
75
76 builder.deserialize(stream, references: refs);
77 if (stream.status() != QDataStream::Ok) {
78 qWarning() << "Invalid metaObject for service received";
79 } else {
80 QMetaObject *remote = builder.toMetaObject();
81
82 builder.setSuperClass(QServiceProxyBase::metaObject());
83
84 QMetaObject *local = builder.toMetaObject();
85
86 d->remoteToLocal = new int[local->methodCount()];
87 d->localToRemote = new int[local->methodCount()];
88
89 for (int i = 0; i < local->methodCount(); i++){
90 const QMetaMethod m = local->method(index: i);
91 int r = remote->indexOfMethod(method: m.methodSignature().constData());
92 d->localToRemote[i] = r;
93 if (r > 0)
94 d->remoteToLocal[r] = i;
95 }
96
97#if defined(QT_SFW_IPC_DEBUG) && defined(QT_SFW_IPC_DEBUG_VERBOSE)
98 QString mapping = QString::fromLatin1("%%% QWE Doing lookup table for ") + endPoint->objectName();
99 for (int i = 0; i < local->methodCount(); i++){
100 const QMetaMethod m = local->method(i);
101 int r = d->localToRemote[i];
102 mapping.append(QString::fromLatin1("\n%%%Mapping %1 from %2 to %3").arg(QString::fromLatin1(m.signature())).arg(i).arg(r));
103 }
104 QServiceDebugLog::instance()->appendToLog(mapping);
105#endif
106
107 d->meta = local;
108
109 endPoint->setLookupTable(local: d->localToRemote, remote: d->remoteToLocal);
110 }
111}
112
113QServiceProxy::~QServiceProxy()
114{
115 qServiceLog() << "event" << "delete"
116 << "class" << "QServiceProxy"
117 << "name" << objectName();
118
119 delete[] d->remoteToLocal;
120 delete[] d->localToRemote;
121 if (d->meta)
122 free(ptr: d->meta);
123 delete d;
124}
125
126//provide custom Q_OBJECT implementation
127const QMetaObject* QServiceProxy::metaObject() const
128{
129 return d->meta;
130}
131
132int QServiceProxy::qt_metacall(QMetaObject::Call c, int id, void **a)
133{
134 id = QServiceProxyBase::qt_metacall(c, id, a);
135 if (id < 0 || !d->meta)
136 return id;
137
138 if (c == QMetaObject::InvokeMetaMethod) {
139
140 const int mcount = d->meta->methodCount() - d->meta->methodOffset();
141 const int metaIndex = id + d->meta->methodOffset();
142
143 QMetaMethod method = d->meta->method(index: metaIndex);
144
145 const int returnType = method.returnType();
146
147 //process arguments
148 const QList<QByteArray> pTypes = method.parameterTypes();
149 const int pTypesCount = pTypes.count();
150 QVariantList args ;
151 if (pTypesCount > 10) {
152 qWarning() << "Cannot call" << method.methodSignature() << ". More than 10 parameter.";
153 return id;
154 }
155 for (int i=0; i < pTypesCount; i++) {
156 const QByteArray& t = pTypes[i];
157
158 int variantType = QMetaType::type(typeName: t);
159
160 if (variantType == QMetaType::QVariant) { //ignore whether QVariant is declared as metatype
161 args << *reinterpret_cast<const QVariant(*)>(a[i+1]);
162 } else if ( variantType == 0 ){
163 qWarning(msg: "%s: argument %s has unknown type. Use qRegisterMetaType to register it.",
164 method.methodSignature().constData(), t.data());
165 return id;
166 } else {
167 args << QVariant(variantType, a[i+1]);
168 }
169 }
170
171 if (returnType == QMetaType::Void) {
172
173 qServiceLog() << "event" << "nonblocking void call"
174 << "method" << QString::fromLatin1(str: method.methodSignature())
175 << "endpoint" << d->endPoint->objectName();
176
177 d->endPoint->invokeRemote(metaIndex: d->localToRemote[metaIndex], args, returnType);
178 } else {
179 //TODO: invokeRemote() parameter list needs review
180
181 qServiceLog() << "event" << "nonblocking call"
182 << "method" << QString::fromLatin1(str: method.methodSignature())
183 << "endpoint" << d->endPoint->objectName();
184
185 QVariant result = d->endPoint->invokeRemote(metaIndex: d->localToRemote[metaIndex], args, returnType);
186 if (result.type() != QVariant::Invalid){
187 if (returnType != QMetaType::QVariant) {
188 QByteArray buffer;
189 QDataStream stream(&buffer, QIODevice::ReadWrite);
190 QMetaType::save(stream, type: returnType, data: result.constData());
191 stream.device()->seek(pos: 0);
192 QMetaType::load(stream, type: returnType, data: a[0]);
193 } else {
194 if (a[0]) *reinterpret_cast< QVariant*>(a[0]) = result;
195 }
196 }
197 }
198 id-=mcount;
199 } else if ( c == QMetaObject::ReadProperty
200 || c == QMetaObject::WriteProperty
201 || c == QMetaObject::ResetProperty ) {
202 const int pCount = d->meta->propertyCount() - d->meta->propertyOffset();
203 const int metaIndex = id + d->meta->propertyOffset();
204 QMetaProperty property = d->meta->property(index: metaIndex);
205 if (property.isValid()) {
206 int pType = property.userType();
207
208 QVariant arg;
209 if ( c == QMetaObject::WriteProperty ) {
210
211 qServiceLog() << "event" << "property write"
212 << "property" << property.name()
213 << "endpoint" << d->endPoint->objectName();
214
215 if (pType == QMetaType::QVariant)
216 arg = *reinterpret_cast<const QVariant(*)>(a[0]);
217 else if (pType == 0) {
218 qWarning(msg: "%s: property %s has unkown type", property.name(), property.typeName());
219 return id;
220 } else {
221 arg = QVariant(pType, a[0]);
222 }
223 }
224 QVariant result;
225 if (c == QMetaObject::ReadProperty) {
226
227 qServiceLog() << "event" << "property read"
228 << "property" << property.name()
229 << "endpoint" << d->endPoint->objectName();
230
231 result = d->endPoint->invokeRemoteProperty(metaIndex, arg, returnType: pType, c);
232 //wrap result for client
233 if (pType != 0) {
234 QByteArray buffer;
235 QDataStream stream(&buffer, QIODevice::ReadWrite);
236 QMetaType::save(stream, type: pType, data: result.constData());
237 stream.device()->seek(pos: 0);
238 QMetaType::load(stream, type: pType, data: a[0]);
239 } else {
240 if (a[0]) *reinterpret_cast< QVariant*>(a[0]) = result;
241 }
242 } else {
243 d->endPoint->invokeRemoteProperty(metaIndex, arg, returnType: pType, c);
244 }
245 }
246 id-=pCount;
247 } else if ( c == QMetaObject::QueryPropertyDesignable
248 || c == QMetaObject::QueryPropertyScriptable
249 || c == QMetaObject::QueryPropertyStored
250 || c == QMetaObject::QueryPropertyEditable
251 || c == QMetaObject::QueryPropertyUser )
252 {
253 //Nothing to do?
254 //These values are part of the transferred meta object already
255 } else {
256 //TODO
257 qWarning() << "MetaCall type" << c << "not yet handled";
258 }
259 return id;
260}
261
262void *QServiceProxy::qt_metacast(const char* className)
263{
264 if (!className) return 0;
265 //this object should not be castable to anything but it's super type
266 return QServiceProxyBase::qt_metacast(className);
267}
268
269class QServiceProxyBasePrivate
270{
271public:
272 QServiceProxy *p;
273 QMetaObject* meta;
274 ObjectEndPoint* endPoint;
275 int ipcfailure;
276 QMetaMethod ipcFailureSignal;
277 int timerId;
278};
279
280QServiceProxyBase::QServiceProxyBase(ObjectEndPoint *endpoint, QObject *parent)
281 : QObject(parent), d(0)
282{
283
284 d = new QServiceProxyBasePrivate();
285 d->meta = 0;
286 d->endPoint = endpoint;
287 d->ipcfailure = -1;
288 d->timerId = startTimer(interval: 1000);
289
290 QMetaObjectBuilder sup;
291 sup.setClassName("QServiceProxyBase");
292 QMetaMethodBuilder b = sup.addSignal(signature: "errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)");
293 d->ipcfailure = b.index();
294 d->meta = sup.toMetaObject();
295 d->ipcFailureSignal = d->meta->method(index: d->meta->methodOffset());
296 Q_ASSERT(d->ipcFailureSignal.methodSignature() == "errorUnrecoverableIPCFault(QService::UnrecoverableIPCError)");
297}
298
299QServiceProxyBase::~QServiceProxyBase()
300{
301 qServiceLog() << "event" << "delete"
302 << "class" << "QServiceProxyBase"
303 << "name" << objectName();
304
305 if (d->meta)
306 free(ptr: d->meta);
307 delete d;
308}
309
310void QServiceProxyBase::connectNotify(const QMetaMethod &signal)
311{
312 if (d->timerId > 0 && signal == d->ipcFailureSignal) {
313 killTimer(id: d->timerId);
314 d->timerId = -1;
315 }
316}
317
318void QServiceProxyBase::timerEvent(QTimerEvent *e)
319{
320 if (e->timerId() == d->timerId) {
321 qWarning() << this << "Someone is using SFW incorrectly. No one connected to errorUnrecoverableIPCFault for class" << this->metaObject()->className() << "in" << qApp->applicationFilePath();
322 killTimer(id: d->timerId);
323 d->timerId = -1;
324 } else {
325 QObject::timerEvent(event: e);
326 }
327}
328
329//provide custom Q_OBJECT implementation
330const QMetaObject* QServiceProxyBase::metaObject() const
331{
332 return d->meta;
333}
334
335void *QServiceProxyBase::qt_metacast(const char* className)
336{
337 if (!className) return 0;
338 //this object should not be castable to anything but QObject
339 return QObject::qt_metacast(className);
340}
341
342int QServiceProxyBase::qt_metacall(QMetaObject::Call c, int id, void **a)
343{
344 id = QObject::qt_metacall(c, id, a);
345 if (id < 0 || !d->meta)
346 return id;
347
348 if (c == QMetaObject::InvokeMetaMethod) {
349 // ipcfailure is the local offset, so is id
350 const int mcount = d->meta->methodCount() - d->meta->methodOffset();
351 int oldid = id;
352 id-=mcount;
353 if (oldid == d->ipcfailure) {
354 QMetaObject::activate(sender: this, d->meta, local_signal_index: d->ipcfailure, argv: a);
355 }
356 }
357 return id;
358}
359
360QT_END_NAMESPACE
361

source code of qtsystems/src/serviceframework/ipc/proxyobject.cpp