764 lines · 32.2 KB · C++
RawDownload
        if (conversionFailures) {            if (conversionFailures < parameterConversionFailureCount || !parameterConversionFailureCount) {                parameterConversionFailureCount = conversionFailures;                parameterConversionFailureId = meta.methodIndex();            }            continue;        }         ScriptContextQtWrapper ourContext(_engine, context);        ScriptContextGuard guard(&ourContext);         int returnTypeId = meta.returnType();         // The Qt MOC engine will automatically call qRegisterMetaType on invokable parameters and properties, but there's        // nothing in there for return values so these need to be explicitly runtime-registered!        Q_ASSERT(returnTypeId != QMetaType::UnknownType);        if (returnTypeId == QMetaType::UnknownType) {            context->throwError(QString("Cannot call native function %1, its return value has not been registered with Qt").arg(fullName()));            return QVariant();        } else if (returnTypeId == QMetaType::Void) {            bool success = meta.invoke(qobject, Qt::DirectConnection, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],                                        qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);            if (!success) {                context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));            }            return QVariant();        } else if (returnTypeId == scriptValueTypeId) {            ScriptValue result;            bool success = meta.invoke(qobject, Qt::DirectConnection, Q_RETURN_ARG(ScriptValue, result), qGenArgs[0],                                        qGenArgs[1], qGenArgs[2], qGenArgs[3], qGenArgs[4], qGenArgs[5], qGenArgs[6],                                        qGenArgs[7], qGenArgs[8], qGenArgs[9]);            if (!success) {                context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));                return QVariant();            }            QScriptValue qResult = ScriptValueQtWrapper::fullUnwrap(_engine, result);            return QVariant::fromValue(qResult);        } else {            // a lot of type conversion assistance thanks to https://stackoverflow.com/questions/28457819/qt-invoke-method-with-qvariant            const char* typeName = meta.typeName();            QVariant qRetVal(returnTypeId, static_cast<void*>(NULL));            QGenericReturnArgument sRetVal(typeName, const_cast<void*>(qRetVal.constData()));             bool success =                meta.invoke(qobject, Qt::DirectConnection, sRetVal, qGenArgs[0], qGenArgs[1], qGenArgs[2], qGenArgs[3],                            qGenArgs[4], qGenArgs[5], qGenArgs[6], qGenArgs[7], qGenArgs[8], qGenArgs[9]);            if (!success) {                context->throwError(QString("Unexpected: Native call of %1 failed").arg(fullName()));                return QVariant();            }            QScriptValue qResult = _engine->castVariantToValue(qRetVal);            return QVariant::fromValue(qResult);        }    }     // we failed to convert the call to C++, try to create a somewhat sane error message    if (parameterConversionFailureCount == 0) {        context->throwError(QString("Native call of %1 failed: unexpected parameter count").arg(fullName()));        return QVariant();    }     const QMetaMethod& meta = _object->metaObject()->method(parameterConversionFailureId);    int methodNumArgs = meta.parameterCount();    Q_ASSERT(methodNumArgs == numArgs);     for (int arg = 0; arg < numArgs; ++arg) {        int methodArgTypeId = meta.parameterType(arg);        Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);        QScriptValue argVal = context->argument(arg);        if (methodArgTypeId != scriptValueTypeId && methodArgTypeId != QMetaType::QVariant) {            QVariant varArgVal;            if (!_engine->castValueToVariant(argVal, varArgVal, methodArgTypeId)) {                QByteArray methodTypeName = QMetaType(methodArgTypeId).name();                QByteArray argTypeName = _engine->valueType(argVal).toLatin1();                context->throwError(QScriptContext::TypeError, QString("Native call of %1 failed: Cannot convert parameter %2 from %3 to %4")                                                                   .arg(fullName()).arg(arg+1).arg(argTypeName, methodTypeName));                return QVariant();            }        }    }     Q_ASSERT(false); // really shouldn't have gotten here -- it didn't work before and it's working now?    return QVariant();    context->throwError(QString("Native call of %1 failed: could not locate an overload with the requested arguments").arg(fullName()));} QString ScriptSignalQtProxy::fullName() const {    Q_ASSERT(_object);    if (!_object) return "";    QString objectName = _object->objectName();    if (!objectName.isEmpty()) {        return QString("%1.%2").arg(objectName, _meta.name());    }    return QString("%1::%2").arg(_object->metaObject()->className(), _meta.name());} // Adapted from https://doc.qt.io/archives/qq/qq16-dynamicqobject.html, for connecting to a signal without a compile-time definition for itint ScriptSignalQtProxy::qt_metacall(QMetaObject::Call call, int id, void** arguments) {    id = ScriptSignalQtProxyBase::qt_metacall(call, id, arguments);    if (id != 0 || call != QMetaObject::InvokeMetaMethod) {        return id;    }     QScriptValueList args;    int numArgs = _meta.parameterCount();    for (int arg = 0; arg < numArgs; ++arg) {        int methodArgTypeId = _meta.parameterType(arg);        Q_ASSERT(methodArgTypeId != QMetaType::UnknownType);        QVariant argValue(methodArgTypeId, arguments[arg+1]);        args.append(_engine->castVariantToValue(argValue));    }     QList<Connection> connections;    withReadLock([&]{        connections = _connections;    });     for (ConnectionList::iterator iter = connections.begin(); iter != connections.end(); ++iter) {        Connection& conn = *iter;            conn.callback.call(conn.thisValue, args);    }     return -1;} int ScriptSignalQtProxy::discoverMetaCallIdx() {    const QMetaObject* ourMeta = metaObject();    return ourMeta->methodCount();} ScriptSignalQtProxy::ConnectionList::iterator ScriptSignalQtProxy::findConnection(QScriptValue thisObject, QScriptValue callback) {    ConnectionList::iterator iter;    withReadLock([&]{        for (iter = _connections.begin(); iter != _connections.end(); ++iter) {            Connection& conn = *iter;            if (conn.callback.strictlyEquals(callback) && conn.thisValue.strictlyEquals(thisObject)) {                break;            }        }    });    return iter;}  void ScriptSignalQtProxy::connect(QScriptValue arg0, QScriptValue arg1) {    QObject* qobject = _object;    if (!qobject) {        QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();        currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");        return;    }     // untangle the arguments    QScriptValue callback;    QScriptValue callbackThis;    if (arg1.isFunction()) {        callbackThis = arg0;        callback = arg1;    } else {        callback = arg0;    }    if (!callback.isFunction()) {        QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();        currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'connect'");        return;    }     // are we already connected?    ConnectionList::iterator lookup = findConnection(callbackThis, callback);    if (lookup != _connections.end()) {        return; // already exists    }     // add a reference to ourselves to the destination callback    QScriptValue destData = callback.data();    Q_ASSERT(!destData.isValid() || destData.isArray());    if (!destData.isArray()) {        destData = static_cast<QScriptEngine*>(_engine)->newArray();    }    {        QScriptValueList args;        args << thisObject();        destData.property("push").call(destData, args);    }    callback.setData(destData);     // add this to our internal list of connections    Connection newConn;    newConn.callback = callback;    newConn.thisValue = callbackThis;        withWriteLock([&]{        _connections.append(newConn);    });     // inform Qt that we're connecting to this signal    if (!_isConnected) {        auto result = QMetaObject::connect(qobject, _meta.methodIndex(), this, _metaCallId);        Q_ASSERT(result);        _isConnected = true;    }} void ScriptSignalQtProxy::disconnect(QScriptValue arg0, QScriptValue arg1) {    QObject* qobject = _object;    if (!qobject) {        QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();        currentContext->throwError(QScriptContext::ReferenceError, "Referencing deleted native object");        return;    }     // untangle the arguments    QScriptValue callback;    QScriptValue callbackThis;    if (arg1.isFunction()) {        callbackThis = arg0;        callback = arg1;    } else {        callback = arg0;    }    if (!callback.isFunction()) {        QScriptContext* currentContext = static_cast<QScriptEngine*>(_engine)->currentContext();        currentContext->throwError(QScriptContext::TypeError, "Function expected as argument to 'disconnect'");        return;    }     // locate this connection in our list of connections    ConnectionList::iterator lookup = findConnection(callbackThis, callback);    if (lookup == _connections.end()) {        return;  // not here    }     // remove it from our internal list of connections    withWriteLock([&]{        _connections.erase(lookup);    });     // remove a reference to ourselves from the destination callback    QScriptValue destData = callback.data();    Q_ASSERT(destData.isArray());    if (destData.isArray()) {        QScriptValue qThis = thisObject();        int len = destData.property("length").toInteger();        bool foundIt = false;        for (int idx = 0; idx < len && !foundIt; ++idx) {            QScriptValue entry = destData.property(idx);            if (entry.strictlyEquals(qThis)) {                foundIt = true;                QScriptValueList args;                args << idx << 1;                destData.property("splice").call(destData, args);            }        }        Q_ASSERT(foundIt);    }     // inform Qt that we're no longer connected to this signal    if (_connections.empty()) {        Q_ASSERT(_isConnected);        bool result = QMetaObject::disconnect(qobject, _meta.methodIndex(), this, _metaCallId);        Q_ASSERT(result);        _isConnected = false;    }}

Lines 501–764 of 764. Previous lines

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t