diff options
Diffstat (limited to 'sources/pyside2/libpyside/signalmanager.cpp')
-rw-r--r-- | sources/pyside2/libpyside/signalmanager.cpp | 692 |
1 files changed, 0 insertions, 692 deletions
diff --git a/sources/pyside2/libpyside/signalmanager.cpp b/sources/pyside2/libpyside/signalmanager.cpp deleted file mode 100644 index 93847e066..000000000 --- a/sources/pyside2/libpyside/signalmanager.cpp +++ /dev/null @@ -1,692 +0,0 @@ -// -*- mode: cpp;-*- -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt for Python. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "signalmanager.h" -#include "pysidesignal.h" -#include "pysideproperty.h" -#include "pysideproperty_p.h" -#include "pyside.h" -#include "pyside_p.h" -#include "dynamicqmetaobject.h" -#include "pysidemetafunction_p.h" - -#include <autodecref.h> -#include <basewrapper.h> -#include <bindingmanager.h> -#include <gilstate.h> -#include <sbkconverter.h> -#include <sbkstring.h> -#include <sbkstaticstrings.h> - -#include <QtCore/QDebug> -#include <QtCore/QHash> - -#include <algorithm> -#include <limits> - -// These private headers are needed to throw JavaScript exceptions -#if PYSIDE_QML_PRIVATE_API_SUPPORT - #include <private/qv4engine_p.h> - #include <private/qv4context_p.h> - #include <private/qqmldata_p.h> -#if QT_VERSION < 0x050700 - #include <private/qqmlcontextwrapper_p.h> -#endif -#endif - -#if QSLOT_CODE != 1 || QSIGNAL_CODE != 2 -#error QSLOT_CODE and/or QSIGNAL_CODE changed! change the hardcoded stuff to the correct value! -#endif -#define PYSIDE_SLOT '1' -#define PYSIDE_SIGNAL '2' -#include "globalreceiverv2.h" - -namespace { - static PyObject *metaObjectAttr = 0; - - static int callMethod(QObject *object, int id, void **args); - static PyObject *parseArguments(const QList< QByteArray >& paramTypes, void **args); - static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args); - -#ifdef IS_PY3K - static void destroyMetaObject(PyObject *obj) - { - void *ptr = PyCapsule_GetPointer(obj, 0); - auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(ptr); - SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); - if (wrapper) - Shiboken::BindingManager::instance().releaseWrapper(wrapper); - delete meta; - } - -#else - static void destroyMetaObject(void *obj) - { - auto meta = reinterpret_cast<PySide::MetaObjectBuilder *>(obj); - SbkObject *wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); - if (wrapper) - Shiboken::BindingManager::instance().releaseWrapper(wrapper); - delete meta; - } -#endif -} - -namespace PySide { - - -PyObjectWrapper::PyObjectWrapper() - :m_me(Py_None) -{ - // PYSIDE-813: When PYSIDE-164 was solved by adding some thread allowance, - // this code was no longer protected. It was hard to find this connection. - // See the website https://bugreports.qt.io/browse/PYSIDE-813 for details. - Shiboken::GilState gil; - Py_XINCREF(m_me); -} - -PyObjectWrapper::PyObjectWrapper(PyObject *me) - : m_me(me) -{ - Shiboken::GilState gil; - Py_XINCREF(m_me); -} - -PyObjectWrapper::PyObjectWrapper(const PyObjectWrapper &other) - : m_me(other.m_me) -{ - Shiboken::GilState gil; - Py_XINCREF(m_me); -} - -PyObjectWrapper::~PyObjectWrapper() -{ - // Check that Python is still initialized as sometimes this is called by a static destructor - // after Python interpeter is shutdown. - if (!Py_IsInitialized()) - return; - - Shiboken::GilState gil; - Py_XDECREF(m_me); -} - -void PyObjectWrapper::reset(PyObject *o) -{ - Shiboken::GilState gil; - Py_XINCREF(o); - Py_XDECREF(m_me); - m_me = o; -} - -PyObjectWrapper &PyObjectWrapper::operator=(const PySide::PyObjectWrapper &other) -{ - reset(other.m_me); - return *this; -} - -PyObjectWrapper::operator PyObject *() const -{ - return m_me; -} - -QDataStream &operator<<(QDataStream &out, const PyObjectWrapper &myObj) -{ - if (Py_IsInitialized() == 0) { - qWarning() << "Stream operator for PyObject called without python interpreter."; - return out; - } - - static PyObject *reduce_func = 0; - - Shiboken::GilState gil; - if (!reduce_func) { - Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle")); - reduce_func = PyObject_GetAttr(pickleModule, Shiboken::PyName::dumps()); - } - Shiboken::AutoDecRef repr(PyObject_CallFunctionObjArgs(reduce_func, (PyObject *)myObj, NULL)); - if (repr.object()) { - const char *buff = nullptr; - Py_ssize_t size = 0; - if (PyBytes_Check(repr.object())) { - buff = PyBytes_AS_STRING(repr.object()); - size = PyBytes_GET_SIZE(repr.object()); - } else if (Shiboken::String::check(repr.object())) { - buff = Shiboken::String::toCString(repr.object()); - size = Shiboken::String::len(repr.object()); - } - QByteArray data(buff, size); - out << data; - } - return out; -} - -QDataStream &operator>>(QDataStream &in, PyObjectWrapper &myObj) -{ - if (Py_IsInitialized() == 0) { - qWarning() << "Stream operator for PyObject called without python interpreter."; - return in; - } - - static PyObject *eval_func = 0; - - Shiboken::GilState gil; - if (!eval_func) { - Shiboken::AutoDecRef pickleModule(PyImport_ImportModule("pickle")); - eval_func = PyObject_GetAttr(pickleModule, Shiboken::PyName::loads()); - } - - QByteArray repr; - in >> repr; - Shiboken::AutoDecRef pyCode(PyBytes_FromStringAndSize(repr.data(), repr.size())); - Shiboken::AutoDecRef value(PyObject_CallFunctionObjArgs(eval_func, pyCode.object(), 0)); - if (!value.object()) - value.reset(Py_None); - myObj.reset(value); - return in; -} - -}; - -using namespace PySide; - -struct SignalManager::SignalManagerPrivate -{ - SharedMap m_globalReceivers; - - SignalManagerPrivate() - { - m_globalReceivers = SharedMap( new QMap<QByteArray, GlobalReceiverV2 *>() ); - } - - ~SignalManagerPrivate() - { - if (!m_globalReceivers.isNull()) { - // Delete receivers by always retrieving the current first element, because deleting a - // receiver can indirectly delete another one, and if we use qDeleteAll, that could - // cause either a double delete, or iterator invalidation, and thus undefined behavior. - while (!m_globalReceivers->isEmpty()) - delete *m_globalReceivers->cbegin(); - Q_ASSERT(m_globalReceivers->isEmpty()); - } - } -}; - -static void clearSignalManager() -{ - PySide::SignalManager::instance().clear(); -} - -static void PyObject_PythonToCpp_PyObject_PTR(PyObject *pyIn, void *cppOut) -{ - *reinterpret_cast<PyObject **>(cppOut) = pyIn; -} -static PythonToCppFunc is_PyObject_PythonToCpp_PyObject_PTR_Convertible(PyObject *pyIn) -{ - return PyObject_PythonToCpp_PyObject_PTR; -} -static PyObject *PyObject_PTR_CppToPython_PyObject(const void *cppIn) -{ - auto pyOut = reinterpret_cast<PyObject *>(const_cast<void *>(cppIn)); - if (pyOut) - Py_INCREF(pyOut); - return pyOut; -} - -SignalManager::SignalManager() : m_d(new SignalManagerPrivate) -{ - // Register Qt primitive typedefs used on signals. - using namespace Shiboken; - - // Register PyObject type to use in queued signal and slot connections - qRegisterMetaType<PyObjectWrapper>("PyObject"); - qRegisterMetaTypeStreamOperators<PyObjectWrapper>("PyObject"); - qRegisterMetaTypeStreamOperators<PyObjectWrapper>("PyObjectWrapper"); - qRegisterMetaTypeStreamOperators<PyObjectWrapper>("PySide::PyObjectWrapper"); - - SbkConverter *converter = Shiboken::Conversions::createConverter(&PyBaseObject_Type, nullptr); - Shiboken::Conversions::setCppPointerToPythonFunction(converter, PyObject_PTR_CppToPython_PyObject); - Shiboken::Conversions::setPythonToCppPointerFunctions(converter, PyObject_PythonToCpp_PyObject_PTR, is_PyObject_PythonToCpp_PyObject_PTR_Convertible); - Shiboken::Conversions::registerConverterName(converter, "PyObject"); - Shiboken::Conversions::registerConverterName(converter, "object"); - Shiboken::Conversions::registerConverterName(converter, "PyObjectWrapper"); - Shiboken::Conversions::registerConverterName(converter, "PySide::PyObjectWrapper"); - - PySide::registerCleanupFunction(clearSignalManager); - - if (!metaObjectAttr) - metaObjectAttr = Shiboken::String::fromCString("__METAOBJECT__"); -} - -void SignalManager::clear() -{ - delete m_d; - m_d = new SignalManagerPrivate(); -} - -SignalManager::~SignalManager() -{ - delete m_d; -} - -SignalManager &SignalManager::instance() -{ - static SignalManager me; - return me; -} - -QObject *SignalManager::globalReceiver(QObject *sender, PyObject *callback) -{ - SharedMap globalReceivers = m_d->m_globalReceivers; - QByteArray hash = GlobalReceiverV2::hash(callback); - GlobalReceiverV2 *gr = nullptr; - auto it = globalReceivers->find(hash); - if (it == globalReceivers->end()) { - gr = new GlobalReceiverV2(callback, globalReceivers); - globalReceivers->insert(hash, gr); - if (sender) { - gr->incRef(sender); // create a link reference - gr->decRef(); // remove extra reference - } - } else { - gr = it.value(); - if (sender) - gr->incRef(sender); - } - - return reinterpret_cast<QObject *>(gr); -} - -int SignalManager::countConnectionsWith(const QObject *object) -{ - int count = 0; - for (GlobalReceiverV2Map::const_iterator it = m_d->m_globalReceivers->cbegin(), end = m_d->m_globalReceivers->cend(); it != end; ++it) { - if (it.value()->refCount(object)) - count++; - } - return count; -} - -void SignalManager::notifyGlobalReceiver(QObject *receiver) -{ - reinterpret_cast<GlobalReceiverV2 *>(receiver)->notify(); -} - -void SignalManager::releaseGlobalReceiver(const QObject *source, QObject *receiver) -{ - auto gr = reinterpret_cast<GlobalReceiverV2 *>(receiver); - gr->decRef(source); -} - -int SignalManager::globalReceiverSlotIndex(QObject *receiver, const char *signature) const -{ - return reinterpret_cast<GlobalReceiverV2 *>(receiver)->addSlot(signature); -} - -bool SignalManager::emitSignal(QObject *source, const char *signal, PyObject *args) -{ - if (!Signal::checkQtSignal(signal)) - return false; - signal++; - - int signalIndex = source->metaObject()->indexOfSignal(signal); - if (signalIndex != -1) { - // cryptic but works! - // if the signature doesn't have a '(' it's a shor circuited signal, i.e. std::find - // returned the string null terminator. - bool isShortCircuit = !*std::find(signal, signal + std::strlen(signal), '('); - if (isShortCircuit) - return emitShortCircuitSignal(source, signalIndex, args); - else - return MetaFunction::call(source, signalIndex, args); - } - return false; -} - -int SignalManager::qt_metacall(QObject *object, QMetaObject::Call call, int id, void **args) -{ - const QMetaObject *metaObject = object->metaObject(); - PySideProperty *pp = nullptr; - PyObject *pp_name = nullptr; - QMetaProperty mp; - PyObject *pySelf = nullptr; - int methodCount = metaObject->methodCount(); - int propertyCount = metaObject->propertyCount(); - - if (call != QMetaObject::InvokeMetaMethod) { - mp = metaObject->property(id); - if (!mp.isValid()) { - return id - methodCount; - } - - Shiboken::GilState gil; - pySelf = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(object)); - Q_ASSERT(pySelf); - pp_name = Shiboken::String::fromCString(mp.name()); - pp = Property::getObject(pySelf, pp_name); - if (!pp) { - qWarning("Invalid property: %s.", mp.name()); - Py_XDECREF(pp_name); - return id - methodCount; - } - } - - switch(call) { -#ifndef QT_NO_PROPERTIES - case QMetaObject::ReadProperty: - case QMetaObject::WriteProperty: - case QMetaObject::ResetProperty: -# if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - case QMetaObject::QueryPropertyDesignable: - case QMetaObject::QueryPropertyScriptable: - case QMetaObject::QueryPropertyStored: - case QMetaObject::QueryPropertyEditable: - case QMetaObject::QueryPropertyUser: -# endif // < Qt 6 - pp->d->metaCallHandler(pp, pySelf, call, args); - break; -#endif - case QMetaObject::InvokeMetaMethod: - id = callMethod(object, id, args); - break; - - default: - qWarning("Unsupported meta invocation type."); - } - - // WARNING Isn't safe to call any metaObject and/or object methods beyond this point - // because the object can be deleted inside the called slot. - - if (call == QMetaObject::InvokeMetaMethod) { - id = id - methodCount; - } else { - id = id - propertyCount; - } - - if (pp || pp_name) { - Shiboken::GilState gil; - Py_XDECREF(pp); - Py_XDECREF(pp_name); - } - - // Bubbles Python exceptions up to the Javascript engine, if called from one - { - Shiboken::GilState gil; - - if (PyErr_Occurred()) { - -#if PYSIDE_QML_PRIVATE_API_SUPPORT - // This JS engine grabber based off of Qt 5.5's `qjsEngine` function - QQmlData *data = QQmlData::get(object, false); - - if (data && !data->jsWrapper.isNullOrUndefined()) { - QV4::ExecutionEngine *engine = data->jsWrapper.engine(); -#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) - if (engine->currentStackFrame != nullptr) { -#elif QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - if (engine->currentContext->d() != engine->rootContext()->d()) { -#else - QV4::ExecutionContext *ctx = engine->currentContext(); - if (ctx->type == QV4::Heap::ExecutionContext::Type_CallContext || - ctx->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { -#endif - PyObject *errType, *errValue, *errTraceback; - PyErr_Fetch(&errType, &errValue, &errTraceback); - // PYSIDE-464: The error is only valid before PyErr_Restore, - // PYSIDE-464: therefore we take local copies. - Shiboken::AutoDecRef objStr(PyObject_Str(errValue)); - const QString errString = QLatin1String(Shiboken::String::toCString(objStr)); - const bool isSyntaxError = errType == PyExc_SyntaxError; - const bool isTypeError = errType == PyExc_TypeError; - PyErr_Restore(errType, errValue, errTraceback); - - PyErr_Print(); // Note: PyErr_Print clears the error. - -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) - if (isSyntaxError) { - return engine->throwSyntaxError(errString); - } else if (isTypeError) { - return engine->throwTypeError(errString); - } else { - return engine->throwError(errString); - } -#else - if (isSyntaxError) { - return ctx->throwSyntaxError(errString); - } else if (isTypeError) { - return ctx->throwTypeError(errString); - } else { - return ctx->throwError(errString); - } -#endif - } - } -#endif - - int reclimit = Py_GetRecursionLimit(); - // Inspired by Python's errors.c: PyErr_GivenExceptionMatches() function. - // Temporarily bump the recursion limit, so that PyErr_Print will not raise a recursion - // error again. Don't do it when the limit is already insanely high, to avoid overflow. - if (reclimit < (1 << 30)) - Py_SetRecursionLimit(reclimit + 5); - PyErr_Print(); - Py_SetRecursionLimit(reclimit); - } - } - - return id; -} - -int SignalManager::callPythonMetaMethod(const QMetaMethod &method, void **args, PyObject *pyMethod, bool isShortCuit) -{ - Q_ASSERT(pyMethod); - - Shiboken::GilState gil; - PyObject *pyArguments = nullptr; - - if (isShortCuit){ - pyArguments = reinterpret_cast<PyObject *>(args[1]); - } else { - pyArguments = parseArguments(method.parameterTypes(), args); - } - - if (pyArguments) { - Shiboken::Conversions::SpecificConverter *retConverter = nullptr; - const char *returnType = method.typeName(); - if (returnType && std::strcmp("", returnType) && std::strcmp("void", returnType)) { - retConverter = new Shiboken::Conversions::SpecificConverter(returnType); - if (!retConverter || !*retConverter) { - PyErr_Format(PyExc_RuntimeError, "Can't find converter for '%s' to call Python meta method.", returnType); - return -1; - } - } - - Shiboken::AutoDecRef retval(PyObject_CallObject(pyMethod, pyArguments)); - - if (!isShortCuit && pyArguments){ - Py_DECREF(pyArguments); - } - - if (!retval.isNull() && retval != Py_None && !PyErr_Occurred() && retConverter) { - retConverter->toCpp(retval, args[0]); - } - delete retConverter; - } - - return -1; -} - -bool SignalManager::registerMetaMethod(QObject *source, const char *signature, QMetaMethod::MethodType type) -{ - int ret = registerMetaMethodGetIndex(source, signature, type); - return (ret != -1); -} - -static MetaObjectBuilder *metaBuilderFromDict(PyObject *dict) -{ - // PYSIDE-803: The dict in this function is the ob_dict of an SbkObject. - // The "metaObjectAttr" entry is only handled in this file. There is no - // way in this function to involve the interpreter. Therefore, we need - // no GIL. - // Note that "SignalManager::registerMetaMethodGetIndex" has write actions - // that might involve the interpreter, but in that context the GIL is held. - if (!dict || !PyDict_Contains(dict, metaObjectAttr)) - return nullptr; - - // PYSIDE-813: The above assumption is not true in debug mode: - // PyDict_GetItem would touch PyThreadState_GET and the global error state. - // PyDict_GetItemWithError instead can work without GIL. - PyObject *pyBuilder = PyDict_GetItemWithError(dict, metaObjectAttr); -#ifdef IS_PY3K - return reinterpret_cast<MetaObjectBuilder *>(PyCapsule_GetPointer(pyBuilder, nullptr)); -#else - return reinterpret_cast<MetaObjectBuilder *>(PyCObject_AsVoidPtr(pyBuilder)); -#endif -} - -int SignalManager::registerMetaMethodGetIndex(QObject *source, const char *signature, QMetaMethod::MethodType type) -{ - if (!source) { - qWarning("SignalManager::registerMetaMethodGetIndex(\"%s\") called with source=nullptr.", - signature); - return -1; - } - const QMetaObject *metaObject = source->metaObject(); - int methodIndex = metaObject->indexOfMethod(signature); - // Create the dynamic signal is needed - if (methodIndex == -1) { - SbkObject *self = Shiboken::BindingManager::instance().retrieveWrapper(source); - if (!Shiboken::Object::hasCppWrapper(self)) { - qWarning() << "Invalid Signal signature:" << signature; - return -1; - } else { - auto pySelf = reinterpret_cast<PyObject *>(self); - PyObject *dict = self->ob_dict; - MetaObjectBuilder *dmo = metaBuilderFromDict(dict); - - // Create a instance meta object - if (!dmo) { - dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject); -#ifdef IS_PY3K - PyObject *pyDmo = PyCapsule_New(dmo, 0, destroyMetaObject); -#else - PyObject *pyDmo = PyCObject_FromVoidPtr(dmo, destroyMetaObject); -#endif - - PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo); - Py_DECREF(pyDmo); - } - - if (type == QMetaMethod::Signal) - return dmo->addSignal(signature); - else - return dmo->addSlot(signature); - } - } - return methodIndex; -} - -const QMetaObject *SignalManager::retrieveMetaObject(PyObject *self) -{ - // PYSIDE-803: Avoid the GIL in SignalManager::retrieveMetaObject - // This function had the GIL. We do not use the GIL unless we have to. - // metaBuilderFromDict accesses a Python dict, but in that context there - // is no way to reach the interpreter, see "metaBuilderFromDict". - // - // The update function is MetaObjectBuilderPrivate::update in - // dynamicmetaobject.c . That function now uses the GIL when the - // m_dirty flag is set. - Q_ASSERT(self); - - MetaObjectBuilder *builder = metaBuilderFromDict(reinterpret_cast<SbkObject *>(self)->ob_dict); - if (!builder) - builder = &(retrieveTypeUserData(self)->mo); - - return builder->update(); -} - -namespace { - -static int callMethod(QObject *object, int id, void **args) -{ - const QMetaObject *metaObject = object->metaObject(); - QMetaMethod method = metaObject->method(id); - - if (method.methodType() == QMetaMethod::Signal) { - // emit python signal - QMetaObject::activate(object, id, args); - } else { - Shiboken::GilState gil; - auto self = reinterpret_cast<PyObject *>(Shiboken::BindingManager::instance().retrieveWrapper(object)); - QByteArray methodName = method.methodSignature(); - methodName.truncate(methodName.indexOf('(')); - Shiboken::AutoDecRef pyMethod(PyObject_GetAttrString(self, methodName)); - return SignalManager::callPythonMetaMethod(method, args, pyMethod, false); - } - return -1; -} - - -static PyObject *parseArguments(const QList<QByteArray>& paramTypes, void **args) -{ - int argsSize = paramTypes.count(); - PyObject *preparedArgs = PyTuple_New(argsSize); - - for (int i = 0, max = argsSize; i < max; ++i) { - void *data = args[i+1]; - const char *dataType = paramTypes[i].constData(); - Shiboken::Conversions::SpecificConverter converter(dataType); - if (converter) { - PyTuple_SET_ITEM(preparedArgs, i, converter.toPython(data)); - } else { - PyErr_Format(PyExc_TypeError, "Can't call meta function because I have no idea how to handle %s", dataType); - Py_DECREF(preparedArgs); - return 0; - } - } - return preparedArgs; -} - -static bool emitShortCircuitSignal(QObject *source, int signalIndex, PyObject *args) -{ - void *signalArgs[2] = {nullptr, args}; - source->qt_metacall(QMetaObject::InvokeMetaMethod, signalIndex, signalArgs); - return true; -} - -} //namespace |