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 | #include "qslotinvoker_p.h" |
34 | #include "qsignalintercepter_p.h" |
35 | #include "qservicedebuglog_p.h" |
36 | #include <qmetaobject.h> |
37 | #include <qmetatype.h> |
38 | #include <qvarlengtharray.h> |
39 | |
40 | #include <stdlib.h> |
41 | |
42 | /*! |
43 | \class QSlotInvoker |
44 | \inpublicgroup QtBaseModule |
45 | \internal |
46 | |
47 | \brief The QSlotInvoker class provides an interface for invoking slots with explicit arguments |
48 | |
49 | IPC mechanisms need to intercept protocol messages and convert them into |
50 | slot invocations, but it is generally impractical to create explicit code |
51 | for every slot that needs to be dispatched. The QSlotInvoker class allows |
52 | an IPC dispatching mechanism to invoke slots in a generic fashion using |
53 | the invoke() method. |
54 | |
55 | Methods that are marked with Q_INVOKABLE or Q_SCRIPTABLE can also |
56 | be invoked with this class. |
57 | |
58 | \sa QSignalIntercepter |
59 | |
60 | \ingroup objectmodel |
61 | */ |
62 | |
63 | QT_BEGIN_NAMESPACE |
64 | |
65 | class QSlotInvokerPrivate |
66 | { |
67 | public: |
68 | QObject *receiver; |
69 | QByteArray member; |
70 | int memberIndex; |
71 | bool destroyed; |
72 | int returnType; |
73 | int *types; |
74 | int numArgs; |
75 | |
76 | ~QSlotInvokerPrivate() |
77 | { |
78 | if ( types ) |
79 | free(ptr: types); |
80 | } |
81 | }; |
82 | |
83 | /*! |
84 | Create a slot invoker that can invoke \a member on \a receiver. |
85 | The object will be attached to \a parent, if present. |
86 | */ |
87 | QSlotInvoker::QSlotInvoker( QObject *receiver, const QByteArray &member, |
88 | QObject *parent ) |
89 | : QObject( parent ) |
90 | { |
91 | d = new QSlotInvokerPrivate(); |
92 | d->receiver = receiver; |
93 | QByteArray name; |
94 | if ( member.size() > 0 && member[0] >= '0' && member[0] <= '9' ) { |
95 | // Strip off the member type code. |
96 | name = member.mid(index: 1); |
97 | } else { |
98 | name = member; |
99 | } |
100 | name = QMetaObject::normalizedSignature( method: name.constData() ); |
101 | d->member = name; |
102 | d->destroyed = false; |
103 | d->returnType = 0; |
104 | d->types = 0; |
105 | d->numArgs = 0; |
106 | if ( receiver && name.size() > 0 ) { |
107 | d->memberIndex |
108 | = receiver->metaObject()->indexOfMethod( method: name.constData() ); |
109 | } else { |
110 | d->memberIndex = -1; |
111 | } |
112 | if ( d->memberIndex != -1 ) { |
113 | QMetaMethod method = receiver->metaObject()->method |
114 | ( index: d->memberIndex ); |
115 | { |
116 | connect( sender: receiver, SIGNAL(destroyed()), |
117 | receiver: this, SLOT(receiverDestroyed()) ); |
118 | d->returnType = |
119 | QSignalIntercepter::typeFromName( name: method.typeName() ); |
120 | d->types = QSignalIntercepter::connectionTypes |
121 | ( member: name, nargs&: d->numArgs ); |
122 | if ( !( d->types ) ) |
123 | d->destroyed = true; |
124 | } |
125 | } else { |
126 | d->destroyed = true; |
127 | } |
128 | } |
129 | |
130 | /*! |
131 | Destroy a slot invoker. |
132 | */ |
133 | QSlotInvoker::~QSlotInvoker() |
134 | { |
135 | delete d; |
136 | } |
137 | |
138 | /*! |
139 | Returns true if the member is present on the object. |
140 | */ |
141 | bool QSlotInvoker::memberPresent() const |
142 | { |
143 | return ! d->destroyed; |
144 | } |
145 | |
146 | /*! |
147 | Returns true if the member can be invoked with \a numArgs arguments. |
148 | That is, the receiver has not been destroyed, the member is present, |
149 | and it requires \a numArgs or less arguments. |
150 | */ |
151 | bool QSlotInvoker::canInvoke( int numArgs ) const |
152 | { |
153 | if ( d->destroyed ) |
154 | return false; |
155 | return ( numArgs >= d->numArgs ); |
156 | } |
157 | |
158 | /*! |
159 | Returns the object that will receive slot invocations. |
160 | */ |
161 | QObject *QSlotInvoker::receiver() const |
162 | { |
163 | return d->receiver; |
164 | } |
165 | |
166 | /*! |
167 | Returns the member that will receiver slot invocations. |
168 | */ |
169 | QByteArray QSlotInvoker::member() const |
170 | { |
171 | return d->member; |
172 | } |
173 | |
174 | /*! |
175 | Returns the parameter types associated with this member. |
176 | */ |
177 | int *QSlotInvoker::parameterTypes() const |
178 | { |
179 | return d->types; |
180 | } |
181 | |
182 | /*! |
183 | Returns the number of parameter types associated with this member. |
184 | */ |
185 | int QSlotInvoker::parameterTypesCount() const |
186 | { |
187 | return d->numArgs; |
188 | } |
189 | |
190 | /*! |
191 | Invokes the slot represented by this object with the argument |
192 | list \a args. The slot's return value is returned from |
193 | this method. If the slot's return type is "void", then a |
194 | QVariant instance of type QVariant::Invalid will be returned. |
195 | |
196 | If it is possible that the slot may throw an exception, |
197 | it is the responsibility of the caller to catch and |
198 | handle the exception. |
199 | */ |
200 | QVariant QSlotInvoker::invoke( const QList<QVariant>& args ) |
201 | { |
202 | int arg; |
203 | QVariant returnValue; |
204 | |
205 | // Create a default instance of the return type for the result buffer. |
206 | if ( d->returnType != (int)QVariant::Invalid ) { |
207 | returnValue = QVariant( d->returnType, (const void *)0 ); |
208 | } |
209 | |
210 | // Bail out if the receiver object has already disappeared. |
211 | if ( d->destroyed ) |
212 | return returnValue; |
213 | |
214 | // Check that the number of arguments is compatible with the slot. |
215 | int numArgs = args.size(); |
216 | if ( numArgs < d->numArgs ) { |
217 | qWarning( msg: "QSlotInvoker::invoke: insufficient arguments for slot" ); |
218 | return returnValue; |
219 | } else if ( numArgs > d->numArgs ) { |
220 | // Drop extraneous arguments. |
221 | numArgs = d->numArgs; |
222 | } |
223 | |
224 | // Construct the raw argument list. |
225 | QVarLengthArray<void *, 32> a( numArgs + 1 ); |
226 | if ( d->returnType == (int)QVariant::Invalid ) |
227 | a[0] = 0; |
228 | else |
229 | a[0] = returnValue.data(); |
230 | for ( arg = 0; arg < numArgs; ++arg ) { |
231 | if ( d->types[arg] == QSignalIntercepter::QVariantId ) { |
232 | a[arg + 1] = (void *)&( args[arg] ); |
233 | } else if ( args[arg].userType() != d->types[arg] ) { |
234 | qWarning( msg: "QSlotInvoker::invoke: argument %d has incorrect type" , |
235 | arg ); |
236 | return QVariant(); |
237 | } else { |
238 | a[arg + 1] = (void *)( args[arg].data() ); |
239 | } |
240 | } |
241 | |
242 | qServiceLog() << "event" << "slot invoked" |
243 | << "class" << "QSlotInvoker" |
244 | << "receiver" << d->receiver->objectName() |
245 | << "member" << QString::fromLatin1(str: d->member) |
246 | << "args" << numArgs; |
247 | |
248 | // Invoke the specified slot. |
249 | d->receiver->qt_metacall( QMetaObject::InvokeMetaMethod, |
250 | d->memberIndex, a.data() ); |
251 | return returnValue; |
252 | } |
253 | |
254 | void QSlotInvoker::receiverDestroyed() |
255 | { |
256 | d->destroyed = true; |
257 | } |
258 | |
259 | #include "moc_qslotinvoker_p.cpp" |
260 | |
261 | QT_END_NAMESPACE |
262 | |