diff options
Diffstat (limited to 'sources/pyside2/libpyside/dynamicqmetaobject.cpp')
-rw-r--r-- | sources/pyside2/libpyside/dynamicqmetaobject.cpp | 991 |
1 files changed, 302 insertions, 689 deletions
diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp index af2f416c6..b664e149b 100644 --- a/sources/pyside2/libpyside/dynamicqmetaobject.cpp +++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp @@ -45,546 +45,376 @@ #include "pysideproperty_p.h" #include "pysideslot_p.h" -#include <QByteArray> -#include <QString> -#include <QStringList> -#include <QList> -#include <QLinkedList> -#include <QObject> -#include <cstring> -#include <QDebug> -#include <QMetaMethod> #include <shiboken.h> +#include <QtCore/QByteArray> +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QTextStream> +#include <QtCore/QVector> +#include <private/qmetaobjectbuilder_p.h> -#define EMPTY_META_METHOD "0()" +#include <cstring> +#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; // when the meta data is not update - int m_methodOffset; - int m_propertyOffset; - int m_dataSize; - int m_emptyMethod; - int m_nullIndex; - - DynamicQMetaObjectPrivate() - : m_updated(false), m_methodOffset(0), m_propertyOffset(0), - m_dataSize(0), m_emptyMethod(-1), m_nullIndex(0) {} - - int createMetaData(QMetaObject* metaObj, QLinkedList<QByteArray> &strings); - void updateMetaObject(QMetaObject* metaObj); - void writeMethodsData(const QList<MethodData>& methods, unsigned int** data, QLinkedList<QByteArray>& strings, int* prtIndex, int nullIndex, int flags); - void writeStringData(char *, QLinkedList<QByteArray> &strings); + 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; + + const QMetaObject *m_baseObject = nullptr; + MetaObjects m_cachedMetaObjects; + bool m_dirty = true; +}; -static int registerString(const QByteArray& s, QLinkedList<QByteArray>& strings) +QMetaObjectBuilder *MetaObjectBuilderPrivate::ensureBuilder() { - int idx = 0; - QLinkedList<QByteArray>::const_iterator it = strings.begin(); - QLinkedList<QByteArray>::const_iterator itEnd = strings.end(); - while (it != itEnd) { - if (strcmp(*it, s) == 0) - return idx; - ++idx; - ++it; + if (!m_builder) { + m_builder = new QMetaObjectBuilder(); + m_builder->setClassName(m_baseObject->className()); + m_builder->setSuperClass(m_baseObject); } - strings.append(s); - return idx; + return m_builder; } -static int blobSize(QLinkedList<QByteArray> &strings) +MetaObjectBuilder::MetaObjectBuilder(const char *className, const QMetaObject *metaObject) : + m_d(new MetaObjectBuilderPrivate) { - int size = strings.size() * sizeof(QByteArrayData); - - QByteArray str; - QByteArray debug_str; - foreach (const QByteArray &field, strings) { - str.append(field); - str.append(char(0)); - - debug_str.append(field); - debug_str.append('|'); - } - //qDebug()<<debug_str; - size += str.size(); - return size; + 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); } -static int aggregateParameterCount(const QList<MethodData> &methods) +MetaObjectBuilder::MetaObjectBuilder(PyTypeObject *type, const QMetaObject *metaObject) + : m_d(new MetaObjectBuilderPrivate) { - int sum = 0; - for (int i = 0; i < methods.size(); ++i) - sum += methods.at(i).parameterCount() * 2 + 1; // nb_param*2 (type and names) +1 for return type - return sum; + 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); + } } -static void writeString(char *out, int i, const QByteArray &str, - const int offsetOfStringdataMember, int &stringdataOffset) +MetaObjectBuilder::~MetaObjectBuilder() { - 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; + qDeleteAll(m_d->m_cachedMetaObjects); + delete m_d->m_builder; + delete m_d; } -static int qvariant_nameToType(const char* name) +int MetaObjectBuilderPrivate::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { - 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; + 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; } -/* - Returns true if the type is a QVariant types. -*/ -static bool isVariantType(const char* type) +int MetaObjectBuilder::indexOfMethod(QMetaMethod::MethodType mtype, + const QByteArray &signature) const { - return qvariant_nameToType(type) != 0; + return m_d->indexOfMethod(mtype, signature); } -/*! - Returns true if the type is qreal. -*/ -static bool isQRealType(const char *type) +int MetaObjectBuilderPrivate::indexOfProperty(const QByteArray &name) const { - return strcmp(type, "qreal") == 0; + if (m_builder) { + const int result = m_builder->indexOfProperty(name); + if (result >= 0) + return m_baseObject->propertyCount() + result; + } + return m_baseObject->indexOfProperty(name); } -uint PropertyData::flags() const +int MetaObjectBuilder::indexOfProperty(const QByteArray &name) 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; + return m_d->indexOfProperty(name); } -// 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) +static bool checkMethodSignature(const QByteArray &signature) { + // 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; } -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::addSlot(const QByteArray &signature) { + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSlot(signature).index(); } -void MethodData::clear() +int MetaObjectBuilder::addSlot(const char *signature) { - m_signature = m_emptySig; - m_rtype.clear(); + return m_d->addSlot(signature); } -bool MethodData::isValid() const +int MetaObjectBuilderPrivate::addSlot(const QByteArray &signature, + const QByteArray &type) { - 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; + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + QMetaMethodBuilder methodBuilder = ensureBuilder()->addSlot(signature); + methodBuilder.setReturnType(type); + return m_baseObject->methodCount() + methodBuilder.index(); } -int MethodData::parameterCount() const +int MetaObjectBuilder::addSlot(const char *signature, const char *type) { - return parameterTypes().size(); + return m_d->addSlot(signature, type); } -QByteArray MethodData::name() const +int MetaObjectBuilderPrivate::addSignal(const QByteArray &signature) { - return m_signature.left(qMax(m_signature.indexOf('('), 0)); + if (!checkMethodSignature(signature)) + return -1; + m_dirty = true; + return m_baseObject->methodCount() + + ensureBuilder()->addSignal(signature).index(); } -PropertyData::PropertyData() - : m_cachedNotifyId(0), m_data(0) +int MetaObjectBuilder::addSignal(const char *signature) { + return m_d->addSignal(signature); } -PropertyData::PropertyData(const char* name, int notifyId, PySideProperty* data) - : m_name(name), m_cachedNotifyId(notifyId), m_data(data) +void MetaObjectBuilderPrivate::removeMethod(QMetaMethod::MethodType mtype, + int index) { + 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; } -QByteArray PropertyData::type() const +void MetaObjectBuilder::removeMethod(QMetaMethod::MethodType mtype, int index) { - return QByteArray(PySide::Property::getTypeName(m_data)); + m_d->removeMethod(mtype, index); } - -bool PropertyData::isValid() const +int MetaObjectBuilderPrivate::getPropertyNotifyId(PySideProperty *property) const { - return !m_name.isEmpty(); + int notifyId = -1; + if (property->d->notify) { + if (const char *signalNotify = PySide::Property::getNotifyName(property)) + notifyId = indexOfMethod(QMetaMethod::Signal, signalNotify); + } + return notifyId; } -int PropertyData::cachedNotifyId() const +int MetaObjectBuilderPrivate::addProperty(const QByteArray &propertyName, + PyObject *data) { - return m_cachedNotifyId; -} + int index = indexOfProperty(propertyName); + if (index != -1) + return index; -bool PropertyData::operator==(const PropertyData& other) const -{ - return m_data == other.m_data; + 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; } -bool PropertyData::operator==(const char* name) const +int MetaObjectBuilder::addProperty(const char *property, PyObject *data) { - return m_name == name; + return m_d->addProperty(property, data); } - -DynamicQMetaObject::DynamicQMetaObject(PyTypeObject* type, const QMetaObject* base) - : m_d(new DynamicQMetaObjectPrivate) +void MetaObjectBuilderPrivate::addInfo(const QByteArray &key, + const QByteArray &value) { - d.superdata = base; - d.stringdata = NULL; - d.data = NULL; - d.extradata = NULL; - d.relatedMetaObjects = NULL; - d.static_metacall = NULL; - - 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); + ensureBuilder()->addClassInfo(key, value); + m_dirty = true; } -DynamicQMetaObject::DynamicQMetaObject(const char* className, const QMetaObject* metaObject) - : m_d(new DynamicQMetaObjectPrivate) +void MetaObjectBuilder::addInfo(const char *key, const char *value) { - 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; + m_d->addInfo(key, value); } -DynamicQMetaObject::~DynamicQMetaObject() +void MetaObjectBuilderPrivate::addInfo(const QMap<QByteArray, QByteArray> &info) { - free(reinterpret_cast<char *>(const_cast<QByteArrayData *>(d.stringdata))); - free(const_cast<uint*>(d.data)); - delete m_d; + 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::addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type) +void MetaObjectBuilder::addInfo(const QMap<QByteArray, QByteArray> &info) { - 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; - else 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(info); } -void DynamicQMetaObject::removeMethod(QMetaMethod::MethodType mtype, uint index) +void MetaObjectBuilderPrivate::removeProperty(int index) { - 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; - } - } + index -= m_baseObject->propertyCount(); + auto builder = ensureBuilder(); + Q_ASSERT(index >= 0 && index < builder->propertyCount()); + builder->removeProperty(index); + m_dirty = true; } -int DynamicQMetaObject::addSignal(const char* signal, const char* type) +void MetaObjectBuilder::removeProperty(int index) { - return addMethod(QMetaMethod::Signal, signal, type); + m_d->removeProperty(index); } -int DynamicQMetaObject::addSlot(const char* slot, const char* type) -{ - return addMethod(QMetaMethod::Slot, slot, type); -} - -void DynamicQMetaObject::removeSlot(uint index) -{ - removeMethod(QMetaMethod::Slot, 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(QMap<QByteArray, QByteArray> info) +const QMetaObject *MetaObjectBuilderPrivate::update() { - QMap<QByteArray, QByteArray>::const_iterator i = info.constBegin(); - while (i != info.constEnd()) { - m_d->m_info[i.key()] = i.value(); - ++i; + 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; } - m_d->m_updated = false; + return m_cachedMetaObjects.back(); } -const QMetaObject* DynamicQMetaObject::update() const +const QMetaObject *MetaObjectBuilder::update() { - if (!m_d->m_updated) { - m_d->updateMetaObject(const_cast<DynamicQMetaObject*>(this)); - m_d->m_updated = true; - } - return this; + return m_d->update(); } -void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeMethodsData(const QList<MethodData>& methods, - unsigned int** data, - QLinkedList<QByteArray>& strings, - int* prtIndex, - int nullIndex, - int flags) -{ - 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; -} - -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. @@ -594,32 +424,35 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) const PyObject *mro = type->tp_mro; const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro); PyTypeObject *qObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); - QVector<PyTypeObject *> basesToCheck; + + std::vector<PyTypeObject *> basesToCheck; + // Prepend the actual type that we are parsing. + basesToCheck.reserve(1u + basesCount); + basesToCheck.push_back(type); + + auto sbkObjTypeF = reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()); + auto baseObjType = reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type); for (Py_ssize_t i = 0; i < basesCount; ++i) { - PyTypeObject *baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i)); - if (PyType_IsSubtype(baseType, qObjectType) - || baseType == reinterpret_cast<PyTypeObject *>(SbkObject_TypeF()) - || baseType == reinterpret_cast<PyTypeObject *>(&PyBaseObject_Type)) { - continue; - } else { - basesToCheck.append(baseType); + auto baseType = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i)); + if (baseType != sbkObjTypeF && baseType != baseObjType + && PyType_IsSubtype(baseType, qObjectType) == 0) { + basesToCheck.push_back(baseType); } } - // Prepend the actual type that we are parsing. - basesToCheck.prepend(type); // PYSIDE-315: Handle all signals first, in all involved types. - for (int baseIndex = 0, baseEnd = basesToCheck.size(); baseIndex < baseEnd; ++baseIndex) { - PyTypeObject *baseType = basesToCheck[baseIndex]; + // Leave the properties to be registered after signals because they may depend on + // notify signals. + for (PyTypeObject *baseType : basesToCheck) { PyObject *attrs = baseType->tp_dict; - PyObject *key = 0; - PyObject *value = 0; + PyObject *key = nullptr; + PyObject *value = nullptr; Py_ssize_t pos = 0; while (PyDict_Next(attrs, &pos, &key, &value)) { if (Signal::checkType(value)) { // Register signals. - PySideSignal *data = reinterpret_cast<PySideSignal *>(value); + auto data = reinterpret_cast<PySideSignal *>(value); const char *signalName = Shiboken::String::toCString(key); data->signalName = strdup(signalName); QByteArray sig; @@ -630,8 +463,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); } } } @@ -641,261 +474,41 @@ void DynamicQMetaObject::parsePythonType(PyTypeObject *type) // PYSIDE-315: Now take care of the rest. // Signals and slots should be separated, unless the types are modified, later. // We check for this using "is_sorted()". Sorting no longer happens at all. - for (int baseIndex = 0, baseEnd = basesToCheck.size(); baseIndex < baseEnd; ++baseIndex) { - PyTypeObject *baseType = basesToCheck[baseIndex]; + for (PyTypeObject *baseType : basesToCheck) { PyObject *attrs = baseType->tp_dict; - PyObject *key = 0; - PyObject *value = 0; + PyObject *key = nullptr; + PyObject *value = nullptr; Py_ssize_t pos = 0; - typedef std::pair<const char *, PyObject *> PropPair; - QVector<PropPair> properties; - while (PyDict_Next(attrs, &pos, &key, &value)) { 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)); + const int index = m_baseObject->indexOfProperty(Shiboken::String::toCString(key)); if (index == -1) - properties << PropPair(Shiboken::String::toCString(key), value); + addProperty(Shiboken::String::toCString(key), value); } else if (PyFunction_Check(value)) { // Register slots. 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); + } + const int index = m_baseObject->indexOfSlot(signature); + if (index == -1) { + if (type.isEmpty() || type == "void") + addSlot(signature); + else + addSlot(signature, type); + } } } } } - - // Register properties - foreach (const PropPair &propPair, properties) - 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, QLinkedList<QByteArray> &strings) -{ - 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, QLinkedList<QByteArray> &strings) -{ - Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (Q_ALIGNOF(QByteArrayData)-1))); - - int offsetOfStringdataMember = strings.size() * sizeof(QByteArrayData); - int stringdataOffset = 0; - int i = 0; - foreach(const QByteArray& str, strings) { - writeString(out, i, str, offsetOfStringdataMember, stringdataOffset); - i++; - } -} - -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); - uint *data = const_cast<uint*>(metaObj->d.data); - int index = 0; - QLinkedList<QByteArray> strings; - m_dataSize = 0; - - // Recompute the size and reallocate memory - // index is set after the last header field. - index = createMetaData(metaObj, strings); - data = const_cast<uint*>(metaObj->d.data); - - registerString(m_className, strings); // register class string - m_nullIndex = registerString("", strings); // register a null string - - // Write class info. - if (m_info.size()) { - 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.size()) { - if (data[5] == 0) - data[5] = index; - - writeMethodsData(m_methods, &data, strings, &index, m_nullIndex, AccessPublic); - } - - // Write signal/slots parameters. - if (m_methods.size()) { - 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; -} |