From 39236c01ae6c9bc99ee3a02d8294679e12d9b734 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 22 May 2017 16:44:51 +0200 Subject: move everying into sources/pyside2 in preparation for a subtree merge. this should not be necessary to do in a separate commit, but git is a tad stupid about following history correctly without it. --- sources/pyside2/libpyside/dynamicqmetaobject.cpp | 901 +++++++++++++++++++++++ 1 file changed, 901 insertions(+) create mode 100644 sources/pyside2/libpyside/dynamicqmetaobject.cpp (limited to 'sources/pyside2/libpyside/dynamicqmetaobject.cpp') diff --git a/sources/pyside2/libpyside/dynamicqmetaobject.cpp b/sources/pyside2/libpyside/dynamicqmetaobject.cpp new file mode 100644 index 000000000..651ed70c1 --- /dev/null +++ b/sources/pyside2/libpyside/dynamicqmetaobject.cpp @@ -0,0 +1,901 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of PySide2. +** +** $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 "dynamicqmetaobject.h" +#include "dynamicqmetaobject_p.h" +#include "pysidesignal.h" +#include "pysidesignal_p.h" +#include "pysideproperty.h" +#include "pysideproperty_p.h" +#include "pysideslot_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define EMPTY_META_METHOD "0()" + +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 +{ +public: + QList m_methods; + QList m_properties; + + QMap 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 &strings); + void updateMetaObject(QMetaObject* metaObj); + void writeMethodsData(const QList& methods, unsigned int** data, QLinkedList& strings, int* prtIndex, int nullIndex, int flags); + void writeStringData(char *, QLinkedList &strings); + int getPropertyNotifyId(PySideProperty *property) const; +}; + +bool sortMethodSignalSlot(const MethodData &m1, const MethodData &m2) +{ + if (m1.methodType() == QMetaMethod::Signal) + return m2.methodType() == QMetaMethod::Slot; + return false; +} + +static int registerString(const QByteArray& s, QLinkedList& strings) +{ + int idx = 0; + QLinkedList::const_iterator it = strings.begin(); + QLinkedList::const_iterator itEnd = strings.end(); + while (it != itEnd) { + if (strcmp(*it, s) == 0) + return idx; + ++idx; + ++it; + } + strings.append(s); + return idx; +} + +static int blobSize(QLinkedList &strings) +{ + 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()< &methods) +{ + 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; +} + +static void writeString(char *out, int i, const QByteArray &str, + const int offsetOfStringdataMember, int &stringdataOffset) +{ + 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; +} + +static int qvariant_nameToType(const char* name) +{ + 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; +} + +/* + Returns true if the type is a QVariant types. +*/ +static bool isVariantType(const char* type) +{ + return qvariant_nameToType(type) != 0; +} + +/*! + Returns true if the type is qreal. +*/ +static bool isQRealType(const char *type) +{ + return strcmp(type, "qreal") == 0; +} + +uint PropertyData::flags() 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; +} + +// 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) +{ +} + +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) +{ +} + +void MethodData::clear() +{ + m_signature = m_emptySig; + m_rtype.clear(); +} + +bool MethodData::isValid() const +{ + return m_signature != m_emptySig; +} + +QList MethodData::parameterTypes() const +{ + const char *signature = m_signature.constData(); + QList 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; +} + +int MethodData::parameterCount() const +{ + return parameterTypes().size(); +} + +QByteArray MethodData::name() const +{ + return m_signature.left(qMax(m_signature.indexOf('('), 0)); +} + +PropertyData::PropertyData() + : m_cachedNotifyId(0), m_data(0) +{ +} + +PropertyData::PropertyData(const char* name, int notifyId, PySideProperty* data) + : m_name(name), m_cachedNotifyId(notifyId), m_data(data) +{ +} + +QByteArray PropertyData::type() const +{ + return QByteArray(PySide::Property::getTypeName(m_data)); +} + + +bool PropertyData::isValid() const +{ + return !m_name.isEmpty(); +} + +int PropertyData::cachedNotifyId() const +{ + return m_cachedNotifyId; +} + +bool PropertyData::operator==(const PropertyData& other) const +{ + return m_data == other.m_data; +} + +bool PropertyData::operator==(const char* name) const +{ + return m_name == name; +} + + +DynamicQMetaObject::DynamicQMetaObject(PyTypeObject* type, const QMetaObject* base) + : m_d(new DynamicQMetaObjectPrivate) +{ + 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); +} + +DynamicQMetaObject::DynamicQMetaObject(const char* className, const QMetaObject* metaObject) + : m_d(new DynamicQMetaObjectPrivate) +{ + 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; +} + +DynamicQMetaObject::~DynamicQMetaObject() +{ + free(reinterpret_cast(const_cast(d.stringdata))); + free(const_cast(d.data)); + delete m_d; +} + +int DynamicQMetaObject::addMethod(QMetaMethod::MethodType mtype, const char* signature, const char* type) +{ + int index = -1; + int counter = 0; + + QList::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; +} + +void DynamicQMetaObject::removeMethod(QMetaMethod::MethodType mtype, uint index) +{ + const char* methodSig = method(index).methodSignature(); + QList::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; + } + } +} + +int DynamicQMetaObject::addSignal(const char* signal, const char* type) +{ + return addMethod(QMetaMethod::Signal, signal, type); +} + +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); +} + +int DynamicQMetaObject::addProperty(const char* propertyName, PyObject* data) +{ + int index = m_d->m_properties.indexOf(propertyName); + if (index != -1) + return m_d->m_propertyOffset + index; + + // retrieve notifyId + PySideProperty *property = reinterpret_cast(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(); + } + m_d->m_updated = false; + return m_d->m_propertyOffset + index; +} + +int DynamicQMetaObject::DynamicQMetaObjectPrivate::getPropertyNotifyId(PySideProperty *property) const +{ + 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); + } + } + return notifyId; +} + +void DynamicQMetaObject::addInfo(const char* key, const char* value) +{ + m_d->m_info[key] = value; +} + +void DynamicQMetaObject::addInfo(QMap info) +{ + QMap::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 +{ + if (!m_d->m_updated) { + m_d->updateMetaObject(const_cast(this)); + m_d->m_updated = true; + } + return this; +} + +void DynamicQMetaObject::DynamicQMetaObjectPrivate::writeMethodsData(const QList& methods, + unsigned int** data, + QLinkedList& strings, + int* prtIndex, + int nullIndex, + int flags) +{ + int index = *prtIndex; + int paramsIndex = index + methods.count() * 5; + + QList::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) +{ + // Get all non-QObject-derived base types in method resolution order, filtering out the types + // that can't have signals, slots or properties. + // This enforces registering of all signals and slots at type parsing time, and not later at + // signal connection time, thus making sure no method indices change which would break + // existing connections. + const PyObject *mro = type->tp_mro; + const Py_ssize_t basesCount = PyTuple_GET_SIZE(mro); + PyTypeObject *qObjectType = Shiboken::Conversions::getPythonTypeObject("QObject*"); + QVector basesToCheck; + for (Py_ssize_t i = 0; i < basesCount; ++i) { + PyTypeObject *baseType = reinterpret_cast(PyTuple_GET_ITEM(mro, i)); + if (PyType_IsSubtype(baseType, qObjectType) + || baseType == reinterpret_cast(&SbkObject_Type) + || baseType == reinterpret_cast(&PyBaseObject_Type)) { + continue; + } else { + basesToCheck.append(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]; + PyObject *attrs = baseType->tp_dict; + PyObject *key = 0; + PyObject *value = 0; + Py_ssize_t pos = 0; + + while (PyDict_Next(attrs, &pos, &key, &value)) { + if (Signal::checkType(value)) { + // Register signals. + PySideSignal *data = reinterpret_cast(value); + const char *signalName = Shiboken::String::toCString(key); + data->signalName = strdup(signalName); + QByteArray sig; + sig.reserve(128); + for (int i = 0; i < data->signaturesSize; ++i) { + sig = signalName; + sig += '('; + if (data->signatures[i]) + sig += data->signatures[i]; + sig += ')'; + if (d.superdata->indexOfSignal(sig) == -1) + addSignal(sig, "void"); + } + } + } + } + + Shiboken::AutoDecRef slotAttrName(Shiboken::String::fromCString(PYSIDE_SLOT_LIST_ATTR)); + // 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]; + PyObject *attrs = baseType->tp_dict; + PyObject *key = 0; + PyObject *value = 0; + Py_ssize_t pos = 0; + + typedef std::pair PropPair; + QVector 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)); + if (index == -1) + properties << PropPair(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)); + // Split the slot type and its signature. + QList slotInfo = sig.split(' '); + int index = d.superdata->indexOfSlot(slotInfo[1]); + if (index == -1) + addSlot(slotInfo[1], slotInfo[0]); + } + } + } + } + + // 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 &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(realloc(const_cast(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 &strings) +{ + Q_ASSERT(!(reinterpret_cast(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::iterator is_sorted_until(QList::iterator first, + QList::iterator last, + bool comp(const MethodData &m1, const MethodData &m2)) +{ + if (first != last) { + QList::iterator next = first; + while (++next != last) { + if (comp(*next, *first)) + return next; + ++first; + } + } + return last; +} + +bool is_sorted(QList::iterator first, QList::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(metaObj->d.data); + int index = 0; + QLinkedList 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(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::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::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::iterator end = m_methods.end(); + QList::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 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::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(notifyId) : 0; //signal notify index + i++; + } + } + + data[index++] = 0; // the end + + // Create the m_metadata string. + int size = blobSize(strings); + char *blob = + reinterpret_cast(realloc(reinterpret_cast(const_cast(metaObj->d.stringdata)), size)); + writeStringData(blob, strings); + + metaObj->d.stringdata = reinterpret_cast(blob); + metaObj->d.data = data; +} -- cgit v1.2.3