Branch data Line data Source code
1 : : // Copyright (C) 2021 The Qt Company Ltd.
2 : : // Copyright (C) 2019 Luxoft Sweden AB
3 : : // Copyright (C) 2018 Pelagicore AG
4 : : // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5 : :
6 : : #include "qifsimulationproxy.h"
7 : : #include "qifsimulationengine.h"
8 : :
9 : : #include <QDebug>
10 : : #include <QQmlInfo>
11 : :
12 : : #include <private/qmetaobjectbuilder_p.h>
13 : :
14 : : using namespace Qt::StringLiterals;
15 : :
16 : : QT_BEGIN_NAMESPACE
17 : :
18 [ + + + - ]: 4241 : Q_LOGGING_CATEGORY(qLcIfSimulationEngine, "qt.if.simulationengine")
19 [ + + + - ]: 1126 : Q_LOGGING_CATEGORY(qLcIfRecGuard, "qt.if.simulationengine.recursionguard")
20 : :
21 : : namespace qtif_private {
22 : :
23 : 97 : QIfSimulationProxyBase::QIfSimulationProxyBase(QMetaObject *staticMetaObject, QObject *instance, const QHash<int, int> &methodMap, QObject *parent)
24 : : : QObject(parent)
25 : 97 : , m_noSimulationEngine(false)
26 : 97 : , m_instance(instance)
27 : 97 : , m_staticMetaObject(staticMetaObject)
28 [ + - ]: 97 : , m_methodMap(methodMap)
29 : : {
30 : 97 : }
31 : :
32 : 9210 : const QMetaObject *QIfSimulationProxyBase::metaObject() const
33 : : {
34 : : // Copied from moc_ class code
35 : : // A dynamicMetaObject is created when the type is used from QML and new functions/properties
36 : : // are added. This makes sure that we can access these from C++ as well
37 [ + + ]: 9210 : return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : m_staticMetaObject;
38 : : }
39 : :
40 : 58 : void *QIfSimulationProxyBase::qt_metacast(const char *classname)
41 : : {
42 [ + - ]: 58 : if (!classname)
43 : : return nullptr;
44 : 58 : return m_instance->qt_metacast(classname);
45 : : }
46 : :
47 : 4229 : int QIfSimulationProxyBase::qt_metacall(QMetaObject::Call call, int methodId, void **a)
48 : : {
49 [ + + ]: 4229 : if (m_noSimulationEngine)
50 : : return -1;
51 : :
52 : : // Special handling for the artificial "Base" property.
53 : : // All other properties are forwarded to the qt_metacall generated by moc which translates the
54 : : // absolute id's back to relative ones
55 [ + + ]: 4227 : if (call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty) {
56 : 486 : int basePropertyIndex = m_staticMetaObject->indexOfProperty("Base");
57 [ + + ]: 486 : if (methodId == basePropertyIndex) {
58 : 155 : void *_v = a[0];
59 : 155 : *reinterpret_cast< QObject**>(_v) = m_instance;
60 : 155 : return -1;
61 : : }
62 : : }
63 : :
64 [ + + ]: 4072 : if (call == QMetaObject::InvokeMetaMethod) {
65 : : // The qt_metacall is only used for the dynamic calling of methods and signatures
66 : : // e.g. a signal or method called from QML
67 : : // But it is also called by QObject::activate when a signal is emitted. In case the signal
68 : : // is connected, the slot is called directly and qt_metacall is NOT called.
69 : : // QObject::activate will call qt_metacall for all signatures for the signal which are not
70 : : // connected.
71 : : // If qt_metacall gets called and the call is from our proxied instance, the signal name
72 : : // matches and the signal arguments match as well (either completely or just partially)
73 : : // The call got forwarded from the QObject::activate of the proxied instance. In this
74 : : // case we don't forward the call again to the proxied instance, but handle it ourself.
75 : 26011 : auto tryHandleCall = [this, methodId, a]()
76 : : {
77 [ + + ]: 3741 : if (sender() != m_instance)
78 : : return false;
79 : :
80 : 3698 : QMetaMethod senderSignal = sender()->metaObject()->method(senderSignalIndex());
81 : 3698 : QMetaMethod receiverMethod = m_staticMetaObject->method(methodId);
82 : :
83 [ + + ]: 3698 : if (senderSignal.name() != receiverMethod.name())
84 : : return false;
85 : :
86 [ + - ]: 3696 : if (senderSignal.parameterCount() < receiverMethod.parameterCount())
87 : : return false;
88 : :
89 [ + + ]: 6865 : for (int pI = 0; pI < receiverMethod.parameterCount(); pI++) {
90 [ + - ]: 3169 : if (senderSignal.parameterMetaType(pI) != receiverMethod.parameterMetaType(pI))
91 : : return false;
92 : : }
93 : :
94 : : // qDebug() << "HANDLE CALL" << methodId << m_staticMetaObject->method(methodId).name() << sender() << m_instance;
95 : : // The static MetaObject uses local ids, so we need to subtract the offset
96 : 3696 : QMetaObject::activate(this, m_staticMetaObject, methodId - m_staticMetaObject->methodOffset(), a);
97 : 3696 : return true;
98 : 3741 : };
99 : :
100 [ + + ]: 3741 : if (tryHandleCall()) {
101 : : return 0;
102 : : } else {
103 : : // Forward the signal to the proxied instance.
104 : :
105 : : // As we don't derive from the MetaObject of m_instance, we need to use the methodMap to
106 : : // translate our methodId to the methodId for m_instance
107 : : // qDebug() << "FORWARD CALL" << methodId << m_staticMetaObject->method(methodId).name() << sender() << m_instance;
108 : 45 : int ret = m_instance->qt_metacall(call, m_methodMap.key(methodId), a);
109 : 45 : return ret;
110 : : }
111 : : }
112 : 331 : return m_instance->qt_metacall(call, methodId, a);
113 : : }
114 : :
115 : 0 : void QIfSimulationProxyBase::classBegin()
116 : : {
117 : 0 : }
118 : :
119 : 97 : void QIfSimulationProxyBase::componentComplete()
120 : : {
121 : 97 : setProperty("Base", QVariant::fromValue(m_instance));
122 : 97 : }
123 : :
124 : 55 : QMetaObject QIfSimulationProxyBase::buildObject(const QMetaObject *metaObject, QHash<int, int> &methodMap, QIfSimulationProxyBase::StaticMetacallFunction metaCallFunction)
125 : : {
126 : 55 : QMetaObjectBuilder builder;
127 [ + - ]: 110 : const QString name = QString(u"QIfSimulationProxy_%1"_s).arg(QLatin1String(metaObject->className()));
128 [ + - ]: 110 : builder.setClassName(qPrintable(name));
129 : 55 : builder.setSuperClass(&QObject::staticMetaObject);
130 : 55 : builder.setStaticMetacallFunction(metaCallFunction);
131 : :
132 : : // Build the MetaObject ourself
133 : : // This is needed as QML uses the static_metacall for reading the properties and every QMetaObject
134 : : // has its own set. But as we need to intercept this to forward it to the registered instance, we
135 : : // build our MetaObject completely and have one static_metacall function for all properties
136 : 55 : const QMetaObject *mo = metaObject;
137 : :
138 : : //Search for the QObject base class to know which offset we need to start building from
139 : 55 : const QMetaObject *superClass = mo->superClass();
140 [ + + ]: 177 : while (qstrcmp(superClass->className(), "QObject") != 0) {
141 : 122 : superClass = superClass->superClass();
142 : : }
143 : 55 : const int methodOffset = superClass->methodCount();
144 : 55 : const int propertyOffset = superClass->propertyCount();
145 : :
146 : : //Fill the mapping for all QObject methods.
147 [ + + ]: 275 : for (int i=0; i<methodOffset; ++i)
148 : 220 : methodMap.insert(i, i);
149 : :
150 : : //Add all signals
151 [ - + ]: 110 : qCDebug(qLcIfSimulationEngine) << "Signal Mapping: Original -> Proxy";
152 [ + + ]: 3995 : for (int index = methodOffset; index < mo->methodCount(); ++index) {
153 : 3940 : QMetaMethod mm = mo->method(index);
154 [ + + ]: 3940 : if (mm.methodType() == QMetaMethod::Signal) {
155 : 1933 : auto mb = builder.addMethod(mm);
156 [ - + ]: 3866 : qCDebug(qLcIfSimulationEngine) << index << "->" << methodOffset + mb.index();
157 : 1933 : methodMap.insert(index, methodOffset + mb.index());
158 : : }
159 : : }
160 : :
161 : : //Add all other methods
162 [ - + ]: 110 : qCDebug(qLcIfSimulationEngine) << "Method Mapping: Original -> Proxy";
163 [ + + ]: 3995 : for (int index = methodOffset; index < mo->methodCount(); ++index) {
164 : 3940 : QMetaMethod mm = mo->method(index);
165 [ + + ]: 3940 : if (mm.methodType() != QMetaMethod::Signal) {
166 : 2007 : auto mb = builder.addMethod(mm);
167 [ - + ]: 4014 : qCDebug(qLcIfSimulationEngine) << index << "->" << methodOffset + mb.index();
168 : 2007 : methodMap.insert(index, methodOffset + mb.index());
169 : : }
170 : : }
171 : :
172 : : //Add all properties
173 [ + + ]: 716 : for (int index = propertyOffset; index < mo->propertyCount(); ++index) {
174 : 661 : QMetaProperty prop = mo->property(index);
175 : 661 : builder.addProperty(prop);
176 : : }
177 : : //Add a Base property which works like a attached property
178 : 55 : builder.addProperty("Base", "QObject *");
179 : :
180 : : //Debugging output
181 [ - + ]: 55 : if (qLcIfSimulationEngine().isDebugEnabled()) {
182 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "Original Object:";
183 [ # # ]: 0 : for (int i=0; i < mo->methodCount(); i++) {
184 : 0 : QMetaMethod method = mo->method(i);
185 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature();
186 : : }
187 [ # # ]: 0 : for (int i=0; i < mo->propertyCount(); i++) {
188 : 0 : QMetaProperty prop = mo->property(i);
189 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "property:" << prop.propertyIndex() << prop.name();
190 : 0 : QMetaMethod method = prop.notifySignal();
191 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature();
192 : : }
193 : :
194 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "Proxy Object:";
195 : 0 : mo = builder.toMetaObject();
196 [ # # ]: 0 : for (int i=0; i < mo->methodCount(); i++) {
197 : 0 : QMetaMethod method = mo->method(i);
198 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature();
199 : : }
200 [ # # ]: 0 : for (int i=0; i < mo->propertyCount(); i++) {
201 : 0 : QMetaProperty prop = mo->property(i);
202 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "property:" << prop.propertyIndex() << prop.name();
203 : 0 : QMetaMethod method = prop.notifySignal();
204 [ # # ]: 0 : qCDebug(qLcIfSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature();
205 : : }
206 : : }
207 : :
208 : 55 : return *builder.toMetaObject();
209 : 55 : }
210 : :
211 : 280 : bool QIfSimulationProxyBase::callQmlMethod(const char *function, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
212 : : {
213 [ + - ]: 280 : if (m_noSimulationEngine)
214 : : return false;
215 : :
216 : 280 : bool functionExecuted = false;
217 : 280 : const QMetaObject *mo = metaObject();
218 : :
219 : : // Only invoke the functions declared in QML.
220 : : // Once a function/property is added to a type a new MetaObject gets created which contains
221 : : // _QML in the name.
222 : : // _QML_ For a C++ type registered to QML
223 : : // _QMLTYPE_ For a QML type derived from a C++ type
224 : :
225 : : // qDebug() << "METAOBJECT" << mo->className();
226 : :
227 [ + + + + : 1546 : while (mo && !functionExecuted && QString::fromLatin1(mo->className()).contains(QLatin1String("_QML"))) {
+ + + + ]
228 [ + + ]: 3479 : for (int i=mo->methodOffset(); i<mo->methodCount(); i++) {
229 : : //qDebug() << "CHECKING FOR: " << function << mo->method(i).name();
230 [ + + ]: 3263 : if (mo->method(i).name() != function)
231 : 2989 : continue;
232 : : //qDebug() << "EXECUTING";
233 : 274 : functionExecuted = QMetaObject::invokeMethod(this, function, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
234 : 274 : break;
235 : : }
236 : 490 : mo = mo->superClass();
237 : : }
238 : : return functionExecuted;
239 : : }
240 : :
241 : 97 : void QIfSimulationProxyBase::setup(QIfSimulationEngine *engine)
242 : : {
243 [ + + ]: 97 : if (engine != qmlEngine(this)) {
244 : 1 : qmlWarning(this) << "QIfSimulationProxy can only be used in the same Engine it is registered in";
245 : 1 : m_noSimulationEngine = true;
246 : 1 : return;
247 : : }
248 : :
249 : : // Connect all signals from the instance to the signals of this metaobject.
250 : : // This is needed to relay the signals from the instance to this instance and to QML
251 : 96 : const QMetaObject *mo = m_instance->metaObject();
252 [ + + ]: 7017 : for (int i=0; i<mo->methodCount(); i++) {
253 : 6921 : QMetaMethod mm = mo->method(i);
254 [ + + ]: 6921 : if (mm.methodType() != QMetaMethod::Signal)
255 : 3382 : continue;
256 : 3539 : connect(m_instance, mm, this, m_staticMetaObject->method(m_methodMap.value(i)));
257 : : }
258 : : }
259 : :
260 : : } //namespace
261 : :
262 : : QT_END_NAMESPACE
|