LCOV - code coverage report
Current view: top level - interfaceframework - qifsimulationproxy.cpp (source / functions) Coverage Total Hit
Test: QtInterfaceFramework Lines: 82.2 % 118 97
Test Date: 2024-05-18 10:00:28 Functions: 91.7 % 12 11
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 64.2 % 106 68

             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
        

Generated by: LCOV version 2.0-1