diff options
-rw-r--r-- | sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp | 2 | ||||
-rw-r--r-- | sources/pyside2/libpyside/CMakeLists.txt | 3 | ||||
-rw-r--r-- | sources/pyside2/libpyside/dynamicqmetaobject.cpp | 908 | ||||
-rw-r--r-- | sources/pyside2/libpyside/dynamicqmetaobject.h | 38 | ||||
-rw-r--r-- | sources/pyside2/libpyside/globalreceiverv2.cpp | 10 | ||||
-rw-r--r-- | sources/pyside2/libpyside/globalreceiverv2.h | 5 | ||||
-rw-r--r-- | sources/pyside2/libpyside/pyside.cpp | 11 | ||||
-rw-r--r-- | sources/pyside2/libpyside/pyside_p.h | 6 | ||||
-rw-r--r-- | sources/pyside2/libpyside/pysideclassinfo.cpp | 5 | ||||
-rw-r--r-- | sources/pyside2/libpyside/signalmanager.cpp | 44 | ||||
-rw-r--r-- | sources/pyside2/tests/QtCore/qmetaobject_test.py | 6 |
11 files changed, 344 insertions, 694 deletions
diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 4e45cfdcc..5f13497ea 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -172,13 +172,13 @@ int PySide::qmlRegisterType(PyObject *pyObj, const char *uri, int versionMajor, type.versionMajor = versionMajor; type.versionMinor = versionMinor; type.elementName = qmlName; - type.metaObject = metaObject; type.extensionObjectCreate = 0; type.extensionMetaObject = 0; type.customParser = 0; ++nextType; } + type.metaObject = metaObject; // Snapshot may have changed. int qmlTypeId = QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); if (qmlTypeId == -1) { diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt index 101b32e4a..ec6713b62 100644 --- a/sources/pyside2/libpyside/CMakeLists.txt +++ b/sources/pyside2/libpyside/CMakeLists.txt @@ -82,7 +82,8 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${SHIBOKEN_INCLUDE_DIR} ${SHIBOKEN_PYTHON_INCLUDE_DIR} ${QML_INCLUDES} - ${Qt5Core_INCLUDE_DIRS}) + ${Qt5Core_INCLUDE_DIRS} + ${Qt5Core_PRIVATE_INCLUDE_DIRS}) add_library(pyside2 SHARED ${libpyside_SRC} ${other_files}) target_link_libraries(pyside2 ${SHIBOKEN_PYTHON_LIBRARIES} diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp index 525e24f4a..5cbfa70f9 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject.cpp +++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp @@ -50,521 +50,371 @@ #include <QtCore/QByteArray> #include <QtCore/QObject> #include <QtCore/QStringList> +#include <QtCore/QTextStream> #include <QtCore/QVector> +#include <private/qmetaobjectbuilder_p.h> #include <cstring> - -#define EMPTY_META_METHOD "0()" +#include <vector> using namespace PySide; -enum PropertyFlags { - Invalid = 0x00000000, - Readable = 0x00000001, - Writable = 0x00000002, - Resettable = 0x00000004, - EnumOrFlag = 0x00000008, - StdCppSet = 0x00000100, -// Override = 0x00000200, - Constant = 0x00000400, - Final = 0x00000800, - Designable = 0x00001000, - ResolveDesignable = 0x00002000, - Scriptable = 0x00004000, - ResolveScriptable = 0x00008000, - Stored = 0x00010000, - ResolveStored = 0x00020000, - Editable = 0x00040000, - ResolveEditable = 0x00080000, - User = 0x00100000, - ResolveUser = 0x00200000, - Notify = 0x00400000 -}; - -// these values are from moc source code, generator.cpp:66 -enum MethodFlags { - AccessPrivate = 0x00, - AccessProtected = 0x01, - AccessPublic = 0x02, - MethodMethod = 0x00, - MethodSignal = 0x04, - MethodSlot = 0x08, - MethodConstructor = 0x0c, - MethodCompatibility = 0x10, - MethodCloned = 0x20, - MethodScriptable = 0x40 -}; - -enum MetaDataFlags { - IsUnresolvedType = 0x80000000, - TypeNameIndexMask = 0x7FFFFFFF -}; - -class DynamicQMetaObject::DynamicQMetaObjectPrivate +// MetaObjectBuilder: Provides the QMetaObject's returned by +// QObject::metaObject() for PySide2 objects. There are several +// scenarios to consider: +// 1) A plain Qt class (say QTimer) is instantiated. In that case, +// return the base meta object until a modification is made by +// adding methods, properties or class info (cf qmetaobject_test.py). +// In that case, instantiate a QMetaObjectBuilder inheriting the +// base meta meta object, add the method and return the result +// of QMetaObjectBuilder::toMetaObject() (with dirty handling should +// further modifications be made). +// 2) A Python class inheriting a Qt class is instantiated. For this, +// instantiate a QMetaObjectBuilder and add the methods/properties +// found by inspecting the Python class. + +class MetaObjectBuilderPrivate { public: - QList<MethodData> m_methods; - QList<PropertyData> m_properties; - - QMap<QByteArray, QByteArray> m_info; - QByteArray m_className; - - bool m_updated = false; // when the meta data is not update - int m_methodOffset = 0; - int m_propertyOffset = 0; - int m_dataSize = 0; - int m_emptyMethod = -1; - int m_nullIndex = 0; - - int createMetaData(QMetaObject* metaObj); - void updateMetaObject(QMetaObject* metaObj); - void writeMethodsData(const QList<MethodData>& methods, unsigned int** data, - QByteArrayList& strings, int* prtIndex, - int nullIndex, int flags); - void writeStringData(char *, const QByteArrayList& strings) const; + using MetaObjects = std::vector<const QMetaObject *>; + + QMetaObjectBuilder *ensureBuilder(); + void parsePythonType(PyTypeObject *type); + int indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const; + int indexOfProperty(const QByteArray &name) const; + int addSlot(const QByteArray &signature); + int addSlot(const QByteArray &signature, const QByteArray &type); + int addSignal(const QByteArray &signature); + void removeMethod(QMetaMethod::MethodType mtype, int index); int getPropertyNotifyId(PySideProperty *property) const; -}; + int addProperty(const QByteArray &property, PyObject *data); + void addInfo(const QByteArray &key, const QByteArray &value); + void addInfo(const QMap<QByteArray, QByteArray> &info); + void removeProperty(int index); + const QMetaObject *update(); -bool sortMethodSignalSlot(const MethodData &m1, const MethodData &m2) -{ - if (m1.methodType() == QMetaMethod::Signal) - return m2.methodType() == QMetaMethod::Slot; - return false; -} + QMetaObjectBuilder *m_builder = nullptr; -static int registerString(const QByteArray& s, QByteArrayList& strings) -{ - int idx = strings.indexOf(s); - if (idx == -1) { - idx = strings.size(); - strings.append(s); - } - return idx; -} - -static int blobSize(const QByteArrayList &strings) -{ - int size = strings.size() * int(sizeof(QByteArrayData)); - for (const QByteArray &field : strings) - size += field.size() + 1; - return size; -} - -static int aggregateParameterCount(const QList<MethodData> &methods) -{ - int sum = 0; - for (const auto &method : methods) - sum += method.parameterCount() * 2 + 1; // nb_param*2 (type and names) +1 for return type - return sum; -} + const QMetaObject *m_baseObject = nullptr; + MetaObjects m_cachedMetaObjects; + bool m_dirty = true; +}; -static void writeString(char *out, int i, const QByteArray &str, - const int offsetOfStringdataMember, int &stringdataOffset) +QMetaObjectBuilder *MetaObjectBuilderPrivate::ensureBuilder() { - int size = str.size(); - qptrdiff offset = offsetOfStringdataMember + stringdataOffset - - i * sizeof(QByteArrayData); - const QByteArrayData data = - Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset); - - memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); - - memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); - out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; - - stringdataOffset += size + 1; + if (!m_builder) { + m_builder = new QMetaObjectBuilder(); + m_builder->setClassName(m_baseObject->className()); + m_builder->setSuperClass(m_baseObject); + } + return m_builder; } -static int qvariant_nameToType(const char* name) +MetaObjectBuilder::MetaObjectBuilder(const char *className, const QMetaObject *metaObject) : + m_d(new MetaObjectBuilderPrivate) { - if (!name) - return 0; - - if (strcmp(name, "QVariant") == 0) - return 0xffffffff; - if (strcmp(name, "QCString") == 0) - return QMetaType::QByteArray; - if (strcmp(name, "Q_LLONG") == 0) - return QMetaType::LongLong; - if (strcmp(name, "Q_ULLONG") == 0) - return QMetaType::ULongLong; - if (strcmp(name, "QIconSet") == 0) - return QMetaType::QIcon; - - uint tp = QMetaType::type(name); - return tp < QMetaType::User ? tp : 0; + m_d->m_baseObject = metaObject; + m_d->m_builder = new QMetaObjectBuilder(); + m_d->m_builder->setClassName(className); + m_d->m_builder->setSuperClass(metaObject); + m_d->m_builder->setClassName(className); } -/* - Returns true if the type is a QVariant types. -*/ -static bool isVariantType(const char* type) +MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject) + : m_d(new MetaObjectBuilderPrivate) { - return qvariant_nameToType(type) != 0; + m_d->m_baseObject = metaObject; + const char *className = type->tp_name; + if (const char *lastDot = strrchr(type->tp_name, '.')) + className = lastDot + 1; + // Different names indicate a Python class inheriting a Qt class. + // Parse the type. + if (strcmp(className, metaObject->className()) != 0) { + m_d->m_builder = new QMetaObjectBuilder(); + m_d->m_builder->setClassName(className); + m_d->m_builder->setSuperClass(metaObject); + m_d->parsePythonType(type); + } } -/*! - Returns true if the type is qreal. -*/ -static bool isQRealType(const char *type) +MetaObjectBuilder::~MetaObjectBuilder() { - return strcmp(type, "qreal") == 0; + qDeleteAll(m_d->m_cachedMetaObjects); + delete m_d->m_builder; + delete m_d; } -uint PropertyData::flags() const +int MetaObjectBuilderPrivate::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { - const QByteArray btype(type()); - const char* typeName = btype.data(); - uint flags = Invalid; - if (!isVariantType(typeName)) - flags |= EnumOrFlag; - else if (!isQRealType(typeName)) - flags |= qvariant_nameToType(typeName) << 24; - - if (PySide::Property::isReadable(m_data)) - flags |= Readable; - - if (PySide::Property::isWritable(m_data)) - flags |= Writable; - - if (PySide::Property::hasReset(m_data)) - flags |= Resettable; - - if (PySide::Property::isDesignable(m_data)) - flags |= Designable; - else - flags |= ResolveDesignable; - - if (PySide::Property::isScriptable(m_data)) - flags |= Scriptable; - else - flags |= ResolveScriptable; - - if (PySide::Property::isStored(m_data)) - flags |= Stored; - else - flags |= ResolveStored; - - //EDITABLE - flags |= ResolveEditable; - - if (PySide::Property::isUser(m_data)) - flags |= User; - else - flags |= ResolveUser; - - if (m_cachedNotifyId != -1) - flags |= Notify; - - if (PySide::Property::isConstant(m_data)) - flags |= Constant; - - if (PySide::Property::isFinal(m_data)) - flags |= Final; - - return flags; + int result = -1; + if (m_builder) { + switch (mtype) { + case QMetaMethod::Signal: + result = m_builder->indexOfSignal(signature); + break; + case QMetaMethod::Slot: + result = m_builder->indexOfSlot(signature); + break; + case QMetaMethod::Constructor: + result = m_builder->indexOfConstructor(signature); + break; + case QMetaMethod::Method: + result = m_builder->indexOfMethod(signature); + break; + } + if (result >= 0) + return result + m_baseObject->methodCount(); + } + switch (mtype) { + case QMetaMethod::Signal: + result = m_baseObject->indexOfSignal(signature); + break; + case QMetaMethod::Slot: + result = m_baseObject->indexOfSlot(signature); + break; + case QMetaMethod::Constructor: + result = m_baseObject->indexOfConstructor(signature); + break; + case QMetaMethod::Method: + result = m_baseObject->indexOfMethod(signature); + break; + } + return result; } -// const QByteArray with EMPTY_META_METHOD, used to save some memory -const QByteArray MethodData::m_emptySig(EMPTY_META_METHOD); - -MethodData::MethodData() - : m_signature(m_emptySig) +int MetaObjectBuilder::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { + return m_d->indexOfMethod(mtype, signature); } -MethodData::MethodData(QMetaMethod::MethodType mtype, const QByteArray& signature, const QByteArray& rtype) - : m_signature(QMetaObject::normalizedSignature(signature.constData())) - , m_rtype(QMetaObject::normalizedSignature(rtype.constData())) - , m_mtype(mtype) +int MetaObjectBuilderPrivate::indexOfProperty(const QByteArray &name) const { + if (m_builder) { + const int result = m_builder->indexOfProperty(name); + if (result >= 0) + return m_baseObject->propertyCount() + result; + } + return m_baseObject->indexOfProperty(name); } -void MethodData::clear() +int MetaObjectBuilder::indexOfProperty(const QByteArray &name) const { - m_signature = m_emptySig; - m_rtype.clear(); + return m_d->indexOfProperty(name); } -bool MethodData::isValid() const +static bool checkMethodSignature(const QByteArray &signature) { - return m_signature != m_emptySig; -} - -QList<QByteArray> MethodData::parameterTypes() const -{ - const char *signature = m_signature.constData(); - QList<QByteArray> list; - while (*signature && *signature != '(') - ++signature; - while (*signature && *signature != ')' && *++signature != ')') { - const char *begin = signature; - int level = 0; - while (*signature && (level > 0 || *signature != ',') && *signature != ')') { - if (*signature == '<') - ++level; - else if (*signature == '>') - --level; - ++signature; - } - list += QByteArray(begin, signature - begin); - } - return list; + // Common mistake not to add parentheses to the signature. + const int openParen = signature.indexOf('('); + const int closingParen = signature.lastIndexOf(')'); + const bool ok = openParen != -1 && closingParen != -1 && openParen < closingParen; + if (!ok) { + const QByteArray message = + "MetaObjectBuilder::addMethod: Invalid method signature provided for \"" + + signature + '"'; + PyErr_WarnEx(PyExc_RuntimeWarning, message.constData(), 0); + } + return ok; } -int MethodData::parameterCount() const +int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature) { - return parameterTypes().size(); + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSlot(signature).index(); } -QByteArray MethodData::name() const +int MetaObjectBuilder::addSlot(const char *signature) { - return m_signature.left(qMax(m_signature.indexOf('('), 0)); + return m_d->addSlot(signature); } -PropertyData::PropertyData() - : m_cachedNotifyId(0), m_data(0) +int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature, + const QByteArray &type) { + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature); + methodBuilder.setReturnType(type); + return m_baseObject->methodCount() + methodBuilder.index(); } -PropertyData::PropertyData(const char* name, int notifyId, PySideProperty* data) - : m_name(name), m_cachedNotifyId(notifyId), m_data(data) +int MetaObjectBuilder::addSlot(const char *signature, const char *type) { + return m_d->addSlot(signature, type); } -QByteArray PropertyData::type() const +int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature) { - return QByteArray(PySide::Property::getTypeName(m_data)); + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSignal(signature).index(); } - -bool PropertyData::isValid() const +int MetaObjectBuilder::addSignal(const char *signature) { - return !m_name.isEmpty(); + return m_d->addSignal(signature); } -int PropertyData::cachedNotifyId() const +void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype, + int index) { - return m_cachedNotifyId; + index -= m_baseObject->methodCount(); + auto builder = ensureBuilder(); + Q_ASSERT(index >= 0 && index < builder->methodCount()); + switch (mtype) { + case QMetaMethod::Constructor: + builder->removeConstructor(index); + break; + default: + builder->removeMethod(index); + break; + } + m_dirty = true; } -bool PropertyData::operator==(const PropertyData& other) const +void MetaObjectBuilder::removeMethod(QMetaMethod::MethodType mtype, int index) { - return m_data == other.m_data; + m_d->removeMethod(mtype, index); } -bool PropertyData::operator==(const char* name) const +int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) const { - return m_name == name; + int notifyId = -1; + if (property->d->notify) { + if (const char *signalNotify = PySide::Property::getNotifyName(property)) + notifyId = indexOfMethod(QMetaMethod::Signal, signalNotify); + } + return notifyId; } - -DynamicQMetaObject::DynamicQMetaObject(PyTypeObject* type, const QMetaObject* base) - : m_d(new DynamicQMetaObjectPrivate) +int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName, + PyObject *data) { - d.superdata = base; - d.stringdata = NULL; - d.data = NULL; - d.extradata = NULL; - d.relatedMetaObjects = NULL; - d.static_metacall = NULL; + int index = indexOfProperty(propertyName); + if (index != -1) + return index; - m_d->m_className = QByteArray(type->tp_name).split('.').last(); - m_d->m_methodOffset = base->methodCount() - 1; - m_d->m_propertyOffset = base->propertyCount() - 1; - parsePythonType(type); + PySideProperty *property = reinterpret_cast<PySideProperty *>(data); + int propertyNotifyId = getPropertyNotifyId(property); + if (propertyNotifyId >= 0) + propertyNotifyId -= m_baseObject->methodCount(); + auto newProperty = + ensureBuilder()->addProperty(propertyName, property->d->typeName, + propertyNotifyId); + index = newProperty.index() + m_baseObject->propertyCount(); + m_dirty = true; + return index; } -DynamicQMetaObject::DynamicQMetaObject(const char* className, const QMetaObject* metaObject) - : m_d(new DynamicQMetaObjectPrivate) +int MetaObjectBuilder::addProperty(const char *property, PyObject *data) { - d.superdata = metaObject; - d.stringdata = 0; - d.data = 0; - d.extradata = 0; - d.relatedMetaObjects = NULL; - d.static_metacall = NULL; - - m_d->m_className = className; - m_d->m_methodOffset = metaObject->methodCount() - 1; - m_d->m_propertyOffset = metaObject->propertyCount() - 1; + return m_d->addProperty(property, data); } -DynamicQMetaObject::~DynamicQMetaObject() +void MetaObjectBuilderPrivate::addInfo(const QByteArray &key, + const QByteArray &value) { - free(reinterpret_cast<char *>(const_cast<QByteArrayData *>(d.stringdata))); - free(const_cast<uint*>(d.data)); - delete m_d; + ensureBuilder()->addClassInfo(key, value); + m_dirty = true; } -int DynamicQMetaObject::addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type) +void MetaObjectBuilder::addInfo(const char *key, const char *value) { - int index = -1; - int counter = 0; - - QList<MethodData>::iterator it = m_d->m_methods.begin(); - for (; it != m_d->m_methods.end(); ++it) { - if ((it->signature() == signature) && (it->methodType() == mtype)) - return m_d->m_methodOffset + counter; - if (!it->isValid()) - index = counter; - counter++; - } - - // Common mistake not to add parentheses to the signature. - if ((strchr(signature, ')') == 0) || ((strchr(signature, '(') == 0))) { - const QString message = - QLatin1String("DynamicQMetaObject::addMethod: Invalid method signature " - "provided for ") + QLatin1String(signature); - const QByteArray messageLatin = message.toLatin1(); - PyErr_WarnEx(PyExc_RuntimeWarning, messageLatin.constData(), 0); - return -1; - } - - //has blank method - if (index != -1) { - m_d->m_methods[index] = MethodData(mtype, signature, type); - index++; - } else { - m_d->m_methods << MethodData(mtype, signature, type); - index = m_d->m_methods.size(); - } - - m_d->m_updated = false; - return m_d->m_methodOffset + index; + m_d->addInfo(key, value); } -void DynamicQMetaObject::removeMethod(QMetaMethod::MethodType mtype, uint index) +void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info) { - const char* methodSig = method(index).methodSignature(); - QList<MethodData>::iterator it = m_d->m_methods.begin(); - for (; it != m_d->m_methods.end(); ++it) { - if ((it->signature() == methodSig) && (it->methodType() == mtype)){ - it->clear(); - m_d->m_updated = false; - break; - } - } + auto builder = ensureBuilder(); + for (auto i = info.constBegin(), end = info.constEnd(); i != end; ++i) + builder->addClassInfo(i.key(), i.value()); + m_dirty = true; } -int DynamicQMetaObject::addSignal(const char* signal, const char* type) +void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info) { - return addMethod(QMetaMethod::Signal, signal, type); + m_d->addInfo(info); } -int DynamicQMetaObject::addSlot(const char* slot, const char* type) +void MetaObjectBuilderPrivate::removeProperty(int index) { - return addMethod(QMetaMethod::Slot, slot, type); + index -= m_baseObject->propertyCount(); + auto builder = ensureBuilder(); + Q_ASSERT(index >= 0 && index < builder->propertyCount()); + builder->removeProperty(index); + m_dirty = true; } -void DynamicQMetaObject::removeSlot(uint index) +void MetaObjectBuilder::removeProperty(int index) { - removeMethod(QMetaMethod::Slot, index); + m_d->removeProperty(index); } -void DynamicQMetaObject::removeSignal(uint index) -{ - removeMethod(QMetaMethod::Signal, index); -} +// PYSIDE-315: Instead of sorting the items and maybe breaking indices, we +// ensure that the signals and slots are sorted by the improved +// parsePythonType() (signals must go before slots). The order can only +// become distorted if the class is modified after creation. In that +// case, we give a warning. -int DynamicQMetaObject::addProperty(const char* propertyName, PyObject* data) +static QString msgMethodSortOrder(const QMetaObject *mo, int offendingIndex) { - int index = m_d->m_properties.indexOf(propertyName); - if (index != -1) - return m_d->m_propertyOffset + index; - - // retrieve notifyId - PySideProperty *property = reinterpret_cast<PySideProperty *>(data); - const int notifyId = m_d->getPropertyNotifyId(property); - - //search for a empty space - PropertyData blank; - index = m_d->m_properties.indexOf(blank); - if (index != -1) { - m_d->m_properties[index] = PropertyData(propertyName, notifyId, property); - } else { - m_d->m_properties << PropertyData(propertyName, notifyId, property); - index = m_d->m_properties.size(); + QString result; + QTextStream str(&result); + str << "\n\n*** Sort Warning ***\nSignals and slots in QMetaObject '" + << mo->className() + << "' are not ordered correctly, this may lead to issues.\n"; + const int methodOffset = mo->methodOffset(); + for (int m = methodOffset, methodCount = mo->methodCount(); m < methodCount; ++m) { + const auto method = mo->method(m); + str << (m - methodOffset + 1) << (m > offendingIndex ? '!' : ' ') + << (method.methodType() == QMetaMethod::Signal ? " Signal " : " Slot ") + << method.methodSignature() << '\n'; } - m_d->m_updated = false; - return m_d->m_propertyOffset + index; + return result; } -int DynamicQMetaObject::DynamicQMetaObjectPrivate::getPropertyNotifyId(PySideProperty *property) const +static void checkMethodOrder(const QMetaObject *metaObject) { - int notifyId = -1; - if (property->d->notify) { - const char *signalNotify = PySide::Property::getNotifyName(property); - if (signalNotify) { - const MethodData signalObject(QMetaMethod::Signal, signalNotify, ""); - notifyId = m_methods.indexOf(signalObject); + const int lastMethod = metaObject->methodCount() - 1; + for (int m = metaObject->methodOffset(); m < lastMethod; ++m) { + if (metaObject->method(m).methodType() == QMetaMethod::Slot + && metaObject->method(m + 1).methodType() == QMetaMethod::Signal) { + const auto message = msgMethodSortOrder(metaObject, m); + PyErr_WarnEx(PyExc_RuntimeWarning, qPrintable(message), 0); + // Prevent a warning from being turned into an error. We cannot easily unwind. + PyErr_Clear(); + break; } } - return notifyId; -} - -void DynamicQMetaObject::addInfo(const char* key, const char* value) -{ - m_d->m_info[key] = value; -} - -void DynamicQMetaObject::addInfo(const QMap<QByteArray, QByteArray> &info) -{ - QMap<QByteArray, QByteArray>::const_iterator i = info.constBegin(); - while (i != info.constEnd()) { - m_d->m_info[i.key()] = i.value(); - ++i; - } - m_d->m_updated = false; } -const QMetaObject* DynamicQMetaObject::update() const +const QMetaObject *MetaObjectBuilderPrivate::update() { - if (!m_d->m_updated) { - m_d->updateMetaObject(const_cast<DynamicQMetaObject*>(this)); - m_d->m_updated = true; + if (!m_builder) + return m_baseObject; + if (m_cachedMetaObjects.empty() || m_dirty) { + m_cachedMetaObjects.push_back(m_builder->toMetaObject()); + checkMethodOrder(m_cachedMetaObjects.back()); + m_dirty = false; } - return this; + return m_cachedMetaObjects.back(); } -void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeMethodsData(const QList<MethodData>& methods, - unsigned int** data, - QByteArrayList& strings, - int* prtIndex, - int nullIndex, - int flags) +const QMetaObject *MetaObjectBuilder::update() { - int index = *prtIndex; - int paramsIndex = index + methods.count() * 5; - - QList<MethodData>::const_iterator it = methods.begin(); - - if (m_emptyMethod == -1) - m_emptyMethod = registerString(EMPTY_META_METHOD, strings); - - for (; it != methods.end(); ++it) { - int name_idx = 0; - int argc = it->parameterCount(); - if (it->signature() != EMPTY_META_METHOD) - name_idx = registerString(it->name(), strings); - else - name_idx = m_emptyMethod; // func name - - (*data)[index++] = name_idx; - (*data)[index++] = argc; // argc (previously: arg name) - (*data)[index++] = paramsIndex; //parameter index - (*data)[index++] = nullIndex; // tags - (*data)[index++] = flags | (it->methodType() == QMetaMethod::Signal ? MethodSignal : MethodSlot); - - if (it->methodType() == QMetaMethod::Signal) - (*data)[13] += 1; //signal count - - paramsIndex += 1 + argc * 2; - } - *prtIndex = index; + return m_d->update(); } -void DynamicQMetaObject::parsePythonType(PyTypeObject *type) +void MetaObjectBuilderPrivate::parsePythonType(PyTypeObject *type) { // Get all non-QObject-derived base types in method resolution order, filtering out the types // that can't have signals, slots or properties. @@ -609,8 +459,8 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (data->signatures[i]) sig += data->signatures[i]; sig += ')'; - if (d.superdata->indexOfSignal(sig) == -1) - addSignal(sig, "void"); + if (m_baseObject->indexOfSignal(sig) == -1) + m_builder->addSignal(sig); } } } @@ -634,7 +484,7 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (Property::checkType(value)) { // Leave the properties to be registered after signals because they may depend on // notify signals. - int index = d.superdata->indexOfProperty(Shiboken::String::toCString(key)); + int index = m_baseObject->indexOfProperty(Shiboken::String::toCString(key)); if (index == -1) properties << PropPair(Shiboken::String::toCString(key), value); } else if (PyFunction_Check(value)) { @@ -642,13 +492,23 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) if (PyObject_HasAttr(value, slotAttrName)) { PyObject *signatureList = PyObject_GetAttr(value, slotAttrName); for (Py_ssize_t i = 0, i_max = PyList_Size(signatureList); i < i_max; ++i) { - PyObject *signature = PyList_GET_ITEM(signatureList, i); - QByteArray sig(Shiboken::String::toCString(signature)); + PyObject *pySignature = PyList_GET_ITEM(signatureList, i); + QByteArray signature(Shiboken::String::toCString(pySignature)); // Split the slot type and its signature. - QList<QByteArray> slotInfo = sig.split(' '); - int index = d.superdata->indexOfSlot(slotInfo[1]); - if (index == -1) - addSlot(slotInfo[1], slotInfo[0]); + QByteArray type; + const int spacePos = signature.indexOf(' '); + if (spacePos != -1) { + type = signature.left(spacePos); + signature.remove(0, spacePos + 1); + } + int index = m_baseObject->indexOfSlot(signature); + if (index == -1) { + if (type.isEmpty() || type == "void") { + addSlot(signature); + } else { + addSlot(signature, type); + } + } } } } @@ -659,219 +519,3 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) addProperty(propPair.first, propPair.second); } } - -/*! - Allocate the meta data table. - Returns the index in the table corresponding to the header fields count. -*/ -int DynamicQMetaObject::DynamicQMetaObjectPrivate::createMetaData(QMetaObject* metaObj) -{ - const int n_methods = m_methods.size(); - const int n_properties = m_properties.size(); - const int n_info = m_info.size(); - - int header[] = {7, // revision (Used by moc, qmetaobjectbuilder and qdbus) - 0, // class name index in m_metadata - n_info, 0, // classinfo and classinfo index - n_methods, 0, // method count and method list index - n_properties, 0, // prop count and prop indexes - 0, 0, // enum count and enum index - 0, 0, // constructors (since revision 2) - 0, // flags (since revision 3) - 0}; // signal count (since revision 4) - - const int HEADER_LENGHT = sizeof(header)/sizeof(int); - - m_dataSize = HEADER_LENGHT; - m_dataSize += n_info*2; //class info: name, value - m_dataSize += n_methods*5; //method: name, argc, parameters, tag, flags - m_dataSize += n_properties*4; //property: name, type, flags - m_dataSize += 1; //eod - - m_dataSize += aggregateParameterCount(m_methods); // types and parameter names - - uint* data = reinterpret_cast<uint*>(realloc(const_cast<uint*>(metaObj->d.data), m_dataSize * sizeof(uint))); - - Q_ASSERT(data); - std::memcpy(data, header, sizeof(header)); - - metaObj->d.data = data; - - return HEADER_LENGHT; -} - -// Writes strings to string data struct. -// The struct consists of an array of QByteArrayData, followed by a char array -// containing the actual strings. This format must match the one produced by -// moc (see generator.cpp). -void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeStringData(char *out, - const QByteArrayList& strings) const -{ - Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (Q_ALIGNOF(QByteArrayData)-1))); - - const int size = strings.size(); - const int offsetOfStringdataMember = size * int(sizeof(QByteArrayData)); - int stringdataOffset = 0; - for (int i = 0; i < size; ++i) - writeString(out, i, strings.at(i), offsetOfStringdataMember, stringdataOffset); -} - -QList<MethodData>::iterator is_sorted_until(QList<MethodData>::iterator first, - QList<MethodData>::iterator last, - bool comp(const MethodData &m1, const MethodData &m2)) -{ - if (first != last) { - QList<MethodData>::iterator next = first; - while (++next != last) { - if (comp(*next, *first)) - return next; - ++first; - } - } - return last; -} - -bool is_sorted(QList<MethodData>::iterator first, QList<MethodData>::iterator last, - bool comp(const MethodData &m1, const MethodData &m2)) -{ - return is_sorted_until(first, last, comp) == last; -} - -void DynamicQMetaObject::DynamicQMetaObjectPrivate::updateMetaObject(QMetaObject* metaObj) -{ - Q_ASSERT(!m_updated); - m_dataSize = 0; - - // Recompute the size and reallocate memory - // index is set after the last header field. - int index = createMetaData(metaObj); - uint *data = const_cast<uint*>(metaObj->d.data); - - QByteArrayList strings; - strings.append(m_className); // register class string - m_nullIndex = registerString(QByteArrayLiteral(""), strings); // register a null string - - // Write class info. - if (!m_info.isEmpty()) { - if (data[3] == 0) - data[3] = index; - - QMap<QByteArray, QByteArray>::const_iterator i = m_info.constBegin(); //TODO: info is a hash this can fail - while (i != m_info.constEnd()) { - int valueIndex = registerString(i.value(), strings); - int keyIndex = registerString(i.key(), strings); - data[index++] = keyIndex; - data[index++] = valueIndex; - i++; - } - } - - // Write methods first, then properties, to be consistent with moc. - // Write signals/slots (signals must be written first, see indexOfMethodRelative in - // qmetaobject.cpp). - - QList<MethodData>::iterator it; - // PYSIDE-315: Instead of sorting the items and maybe breaking indices, - // we ensure that the signals and slots are sorted by the improved parsePythonType(). - // The order can only become distorted if the class is modified after creation. - // In that case, we give a warning. - if (!is_sorted(m_methods.begin(), m_methods.end(), sortMethodSignalSlot)) { - const char *metaObjectName = this->m_className.data(); - PyObject *txt = PyBytes_FromFormat("\n\n*** Sort Warning ***\n" - "Signals and slots in QMetaObject '%s' are not ordered correctly, " - "this may lead to issues.\n", metaObjectName); - it = m_methods.begin(); - QList<MethodData>::iterator end = m_methods.end(); - QList<MethodData>::iterator until = is_sorted_until(m_methods.begin(), m_methods.end(), - sortMethodSignalSlot); - for (; it != end; ++it) { - PyObject *atxt = PyBytes_FromFormat("%d%s %s %s\n", it - m_methods.begin() + 1, - until >= it + 1 ? " " : "!", - it->methodType() == QMetaMethod::Signal ? "Signal" : "Slot ", - it->signature().data() ); - PyBytes_ConcatAndDel(&txt, atxt); - } - PyErr_WarnEx(PyExc_RuntimeWarning, PyBytes_AsString(txt), 0); - Py_DECREF(txt); - // Prevent a warning from being turned into an error. We cannot easily unwind. - PyErr_Clear(); - } - - if (!m_methods.empty()) { - if (data[5] == 0) - data[5] = index; - - writeMethodsData(m_methods, &data, strings, &index, m_nullIndex, AccessPublic); - } - - // Write signal/slots parameters. - if (!m_methods.empty()) { - for (it = m_methods.begin(); it != m_methods.end(); ++it) { - QList<QByteArray> paramTypeNames = it->parameterTypes(); - int paramCount = paramTypeNames.size(); - for (int i = -1; i < paramCount; ++i) { - const QByteArray &typeName = (i < 0) ? it->returnType() : paramTypeNames.at(i); - int typeInfo; - if (QtPrivate::isBuiltinType(typeName)) - typeInfo = QMetaType::type(typeName); - else - typeInfo = IsUnresolvedType | registerString(typeName, strings); - data[index++] = typeInfo; - } - - // Parameter names (use a null string) - for (int i = 0; i < paramCount; ++i) { - data[index++] = m_nullIndex; - } - } - } - - // Write properties. - if (m_properties.size()) { - if (data[7] == 0) - data[7] = index; - - QList<PropertyData>::const_iterator i = m_properties.constBegin(); - while (i != m_properties.constEnd()) { - if (i->isValid()) { - data[index++] = registerString(i->name(), strings); // name - } else - data[index++] = m_nullIndex; - - // Find out the property type index. - int typeInfo = m_nullIndex; - if (i->isValid()) { - const QByteArray &typeName = i->type(); - if (QtPrivate::isBuiltinType(typeName)) - typeInfo = QMetaType::type(typeName); - else - typeInfo = IsUnresolvedType | registerString(typeName, strings); - } - data[index++] = typeInfo; // normalized type - - data[index++] = i->flags(); - i++; - } - - // Write properties notify. - i = m_properties.constBegin(); - while (i != m_properties.constEnd()) { - // Recompute notifyId, because sorting the methods might have changed the relative - // index. - const int notifyId = getPropertyNotifyId(i->data()); - data[index++] = notifyId >= 0 ? static_cast<uint>(notifyId) : 0; //signal notify index - i++; - } - } - - data[index++] = 0; // the end - - // Create the m_metadata string. - int size = blobSize(strings); - char *blob = - reinterpret_cast<char *>(realloc(reinterpret_cast<char *>(const_cast<QByteArrayData *>(metaObj->d.stringdata)), size)); - writeStringData(blob, strings); - - metaObj->d.stringdata = reinterpret_cast<const QByteArrayData *>(blob); - metaObj->d.data = data; -} diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.h b/sources/pyside2/libpyside/dynamicqmetaobject.h index e68c7dd50..1fbe73ea4 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject.h +++ b/sources/pyside2/libpyside/dynamicqmetaobject.h @@ -45,39 +45,37 @@ #include <QtCore/QMetaObject> #include <QtCore/QMetaMethod> +class MetaObjectBuilderPrivate; + namespace PySide { -class DynamicQMetaObject : public QMetaObject +class MetaObjectBuilder { - Q_DISABLE_COPY(DynamicQMetaObject) + Q_DISABLE_COPY(MetaObjectBuilder) public: - DynamicQMetaObject(const char* className, const QMetaObject* metaObject); - DynamicQMetaObject(PyTypeObject* type, const QMetaObject* metaobject); - ~DynamicQMetaObject(); + MetaObjectBuilder(const char *className, const QMetaObject *metaObject); + MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject); + ~MetaObjectBuilder(); - int addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type); - void removeMethod(QMetaMethod::MethodType mtype, uint index); - int addSignal(const char* signal, const char* type = 0); - int addSlot(const char* slot, const char* type = 0); - int addProperty(const char* property, PyObject* data); - void addInfo(const char* key, const char* value); + int indexOfMethod(QMetaMethod::MethodType mtype, const QByteArray &signature) const; + int indexOfProperty(const QByteArray &name) const; + int addSlot(const char *signature); + int addSlot(const char *signature, const char *type); + int addSignal(const char *signature); + void removeMethod(QMetaMethod::MethodType mtype, int index); + int addProperty(const char *property, PyObject *data); + void addInfo(const char *key, const char *value); void addInfo(const QMap<QByteArray, QByteArray> &info); - void removeSignal(uint idex); - void removeSlot(uint index); - void removeProperty(uint index); + void removeProperty(int index); - const QMetaObject* update() const; + const QMetaObject *update(); private: - class DynamicQMetaObjectPrivate; - DynamicQMetaObjectPrivate* m_d; - - void parsePythonType(PyTypeObject *type); + MetaObjectBuilderPrivate *m_d; }; - } #endif diff --git a/sources/pyside2/libpyside/globalreceiverv2.cpp b/sources/pyside2/libpyside/globalreceiverv2.cpp index a1a695759..43ce50a75 100644 --- a/sources/pyside2/libpyside/globalreceiverv2.cpp +++ b/sources/pyside2/libpyside/globalreceiverv2.cpp @@ -160,10 +160,8 @@ int DynamicSlotDataV2::id(const char* signature) const int DynamicSlotDataV2::addSlot(const char* signature) { int index = id(signature); - if (index == -1) { - DynamicQMetaObject *dmo = const_cast<DynamicQMetaObject*>(reinterpret_cast<const DynamicQMetaObject*>(m_parent->metaObject())); - index = m_signatures[signature] = dmo->addSlot(signature); - } + if (index == -1) + index = m_signatures[signature] = m_parent->metaObjectBuilder().addSlot(signature); return index; } @@ -202,7 +200,7 @@ GlobalReceiverV2::GlobalReceiverV2(PyObject *callback, SharedMap map) : DESTROY_SIGNAL_ID = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); if (DESTROY_SLOT_ID == 0) - DESTROY_SLOT_ID = m_metaObject.indexOfSlot(RECEIVER_DESTROYED_SLOT_NAME); + DESTROY_SLOT_ID = m_metaObject.indexOfMethod(QMetaMethod::Slot, RECEIVER_DESTROYED_SLOT_NAME); } @@ -306,7 +304,7 @@ QByteArray GlobalReceiverV2::hash(PyObject* callback) const QMetaObject* GlobalReceiverV2::metaObject() const { - return m_metaObject.update(); + return const_cast<GlobalReceiverV2 *>(this)->m_metaObject.update(); } int GlobalReceiverV2::qt_metacall(QMetaObject::Call call, int id, void** args) diff --git a/sources/pyside2/libpyside/globalreceiverv2.h b/sources/pyside2/libpyside/globalreceiverv2.h index b12823a84..b92be93a8 100644 --- a/sources/pyside2/libpyside/globalreceiverv2.h +++ b/sources/pyside2/libpyside/globalreceiverv2.h @@ -135,8 +135,11 @@ public: **/ static QByteArray hash(PyObject* callback); + const MetaObjectBuilder &metaObjectBuilder() const { return m_metaObject; } + MetaObjectBuilder &metaObjectBuilder() { return m_metaObject; } + private: - DynamicQMetaObject m_metaObject; + MetaObjectBuilder m_metaObject; DynamicSlotDataV2 *m_data; QList<const QObject*> m_refs; SharedMap m_sharedMap; diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp index 856e5b92c..6e4a3efd4 100644 --- a/sources/pyside2/libpyside/pyside.cpp +++ b/sources/pyside2/libpyside/pyside.cpp @@ -204,7 +204,7 @@ void initDynamicMetaObject(SbkObjectType* type, const QMetaObject* base, std::si Shiboken::ObjectType::setTypeUserData(type, userData, Shiboken::callCppDestructor<TypeUserData>); //initialize staticQMetaObject property - void* metaObjectPtr = &userData->mo; + void *metaObjectPtr = const_cast<QMetaObject *>(userData->mo.update()); static SbkConverter* converter = Shiboken::Conversions::getConverter("QMetaObject"); if (!converter) return; @@ -229,13 +229,13 @@ TypeUserData *retrieveTypeUserData(PyObject *pyObj) return retrieveTypeUserData(pyTypeObj); } -DynamicQMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj) +const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj) { TypeUserData *userData = retrieveTypeUserData(pyTypeObj); - return userData ? &(userData->mo) : nullptr; + return userData ? userData->mo.update() : nullptr; } -DynamicQMetaObject *retrieveMetaObject(PyObject *pyObj) +const QMetaObject *retrieveMetaObject(PyObject *pyObj) { auto pyTypeObj = PyType_Check(pyObj) ? reinterpret_cast<PyTypeObject *>(pyObj) : Py_TYPE(pyObj); @@ -268,8 +268,7 @@ void initQObjectSubType(SbkObjectType *type, PyObject *args, PyObject * /* kwds qWarning("Sub class of QObject not inheriting QObject!? Crash will happen when using %s.", className.constData()); return; } - userData->mo.update(); - initDynamicMetaObject(type, &userData->mo, userData->cppObjSize); + initDynamicMetaObject(type, userData->mo.update(), userData->cppObjSize); } PyObject* getMetaDataFromQObject(QObject* cppSelf, PyObject* self, PyObject* name) diff --git a/sources/pyside2/libpyside/pyside_p.h b/sources/pyside2/libpyside/pyside_p.h index 66a37fc7a..1084a40a1 100644 --- a/sources/pyside2/libpyside/pyside_p.h +++ b/sources/pyside2/libpyside/pyside_p.h @@ -55,7 +55,7 @@ struct TypeUserData explicit TypeUserData(PyTypeObject* type, const QMetaObject* metaobject, std::size_t size) : mo(type, metaobject), cppObjSize(size) {} - DynamicQMetaObject mo; + MetaObjectBuilder mo; std::size_t cppObjSize; }; @@ -63,8 +63,8 @@ TypeUserData *retrieveTypeUserData(SbkObjectType *sbkTypeObj); TypeUserData *retrieveTypeUserData(PyTypeObject *pyTypeObj); TypeUserData *retrieveTypeUserData(PyObject *pyObj); // For QML -PYSIDE_API DynamicQMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj); -PYSIDE_API DynamicQMetaObject *retrieveMetaObject(PyObject *pyObj); +PYSIDE_API const QMetaObject *retrieveMetaObject(PyTypeObject *pyTypeObj); +PYSIDE_API const QMetaObject *retrieveMetaObject(PyObject *pyObj); } //namespace PySide diff --git a/sources/pyside2/libpyside/pysideclassinfo.cpp b/sources/pyside2/libpyside/pysideclassinfo.cpp index 88b2b89f5..88292024f 100644 --- a/sources/pyside2/libpyside/pysideclassinfo.cpp +++ b/sources/pyside2/libpyside/pysideclassinfo.cpp @@ -108,8 +108,9 @@ PyObject *classCall(PyObject *self, PyObject *args, PyObject * /* kw */) PyTypeObject *klassType = reinterpret_cast<PyTypeObject*>(klass); if (Shiboken::ObjectType::checkType(klassType)) { - if (PySide::DynamicQMetaObject *mo = PySide::retrieveMetaObject(klassType)) { - mo->addInfo(PySide::ClassInfo::getMap(data)); + if (auto userData = PySide::retrieveTypeUserData(klassType)) { + PySide::MetaObjectBuilder &mo = userData->mo; + mo.addInfo(PySide::ClassInfo::getMap(data)); pData->m_alreadyWrapped = true; validClass = true; } diff --git a/sources/pyside2/libpyside/signalmanager.cpp b/sources/pyside2/libpyside/signalmanager.cpp index f505fde5a..8925ffd35 100644 --- a/sources/pyside2/libpyside/signalmanager.cpp +++ b/sources/pyside2/libpyside/signalmanager.cpp @@ -90,7 +90,7 @@ namespace { static void destroyMetaObject(PyObject* obj) { void* ptr = PyCapsule_GetPointer(obj, 0); - PySide::DynamicQMetaObject* meta = reinterpret_cast<PySide::DynamicQMetaObject*>(ptr); + auto meta = reinterpret_cast<PySide::MetaObjectBuilder*>(ptr); SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); if (wrapper) Shiboken::BindingManager::instance().releaseWrapper(wrapper); @@ -100,7 +100,7 @@ namespace { #else static void destroyMetaObject(void* obj) { - PySide::DynamicQMetaObject* meta = reinterpret_cast<PySide::DynamicQMetaObject*>(obj); + auto meta = reinterpret_cast<PySide::MetaObjectBuilder*>(obj); SbkObject* wrapper = Shiboken::BindingManager::instance().retrieveWrapper(meta); if (wrapper) Shiboken::BindingManager::instance().releaseWrapper(wrapper); @@ -549,6 +549,19 @@ bool SignalManager::registerMetaMethod(QObject* source, const char* signature, Q return (ret != -1); } +static MetaObjectBuilder *metaBuilderFromDict(PyObject* dict) +{ + if (!dict || !PyDict_Contains(dict, metaObjectAttr)) + return nullptr; + + PyObject *pyBuilder = PyDict_GetItem(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) { @@ -565,13 +578,13 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa qWarning() << "Invalid Signal signature:" << signature; return -1; } else { - DynamicQMetaObject *dmo = 0; PyObject *pySelf = reinterpret_cast<PyObject*>(self); PyObject* dict = self->ob_dict; + MetaObjectBuilder *dmo = metaBuilderFromDict(dict); // Create a instance meta object - if (!dict || !PyDict_Contains(dict, metaObjectAttr)) { - dmo = new DynamicQMetaObject(Py_TYPE(pySelf), metaObject); + if (!dmo) { + dmo = new MetaObjectBuilder(Py_TYPE(pySelf), metaObject); #ifdef IS_PY3K PyObject* pyDmo = PyCapsule_New(dmo, 0, destroyMetaObject); #else @@ -580,8 +593,6 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa PyObject_SetAttr(pySelf, metaObjectAttr, pyDmo); Py_DECREF(pyDmo); - } else { - dmo = reinterpret_cast<DynamicQMetaObject*>(const_cast<QMetaObject*>(metaObject)); } if (type == QMetaMethod::Signal) @@ -596,24 +607,13 @@ int SignalManager::registerMetaMethodGetIndex(QObject* source, const char* signa const QMetaObject* SignalManager::retrieveMetaObject(PyObject *self) { Shiboken::GilState gil; - DynamicQMetaObject *mo = 0; Q_ASSERT(self); - PyObject* dict = reinterpret_cast<SbkObject*>(self)->ob_dict; - if (dict && PyDict_Contains(dict, metaObjectAttr)) { - PyObject *pyMo = PyDict_GetItem(dict, metaObjectAttr); - -#ifdef IS_PY3K - mo = reinterpret_cast<DynamicQMetaObject*>(PyCapsule_GetPointer(pyMo, 0)); -#else - mo = reinterpret_cast<DynamicQMetaObject*>(PyCObject_AsVoidPtr(pyMo)); -#endif - } else { - mo = PySide::retrieveMetaObject(self); - } + MetaObjectBuilder *builder = metaBuilderFromDict(reinterpret_cast<SbkObject*>(self)->ob_dict); + if (!builder) + builder = &(retrieveTypeUserData(self)->mo); - mo->update(); - return mo; + return builder->update(); } namespace { diff --git a/sources/pyside2/tests/QtCore/qmetaobject_test.py b/sources/pyside2/tests/QtCore/qmetaobject_test.py index 12b5312a6..73ab81c7c 100644 --- a/sources/pyside2/tests/QtCore/qmetaobject_test.py +++ b/sources/pyside2/tests/QtCore/qmetaobject_test.py @@ -78,6 +78,12 @@ class qmetaobject_test(unittest.TestCase): #self.assertTrue(slot_index != signal_index) + # PYSIDE-784, plain Qt objects should not have intermediary + # metaObjects. + def test_PlainQObject(self): + timer = QTimer() + self.assertEqual(timer.metaObject().superClass().className(), + "QObject") if __name__ == '__main__': unittest.main() |