/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt for Python. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "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(PepType(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 = PepType(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_TypeF()) || 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 = PepType(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 = PepType(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; }