diff options
author | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2012-03-09 11:04:36 +0100 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-03-09 12:32:30 +0100 |
commit | 152eec7aa42cf688619beee5792838ce6dd5c7eb (patch) | |
tree | 192ebbe4fc6f87dbaa3f0cab7efc4443d826e10c | |
parent | 86abc878832db67da87b9fe34faa4bc8d71446b0 (diff) | |
parent | 08a993526eb1d98e0e22c6d4ae08d0cf2a587e49 (diff) |
Merge "Merge remote-tracking branch 'origin/containers' into api_changes" into refs/staging/api_changes
77 files changed, 5819 insertions, 1217 deletions
diff --git a/dist/changes-5.0.0 b/dist/changes-5.0.0 index 500e97085b..7b8d1fa0bd 100644 --- a/dist/changes-5.0.0 +++ b/dist/changes-5.0.0 @@ -36,6 +36,13 @@ information about a particular change. - QCoreApplication::translate() will no longer return the source text when the translation is empty. Use lrelease -removeidentical for optimization. +- QString and QByteArray constructors that take a size argument will now treat + negative sizes to indicate nul-terminated strings (a nul-terminated array of + QChar, in the case of QString). In Qt 4, negative sizes were ignored and + result in empty QString and QByteArray, respectively. The size argument to + those constructors now has a default value of -1, thus replacing the separate + constructors that did the same. + - Qt::escape() is deprecated (but can be enabled via QT_DISABLE_DEPRECATED_BEFORE), use QString::toHtmlEscaped() instead. @@ -51,6 +58,11 @@ information about a particular change. * QMetaType::construct() has been renamed to QMetaType::create(). * QMetaType::unregisterType() has been removed. +- QMetaMethod::signature() has been renamed to QMetaMethod::methodSignature(), + and the return type has been changed to QByteArray. This was done to be able + to generate the signature string on demand, rather than always storing it in + the meta-data. + - QTestLib: * The plain-text, xml and lightxml test output formats have been changed to show a test result for every row of test data in data-driven tests. In @@ -533,6 +545,20 @@ Qt for Windows CE QMetaType::User, which means that it points to the first registered custom type, instead of a nonexistent type. +- QMetaType + + * Interpretation of QMetaType::Void was changed. Before, in some cases + it was returned as an invalid type id, but sometimes it was used as a valid + type (C++ "void"). In Qt5, new QMetaType::UnknownType was introduced to + distinguish between these two. QMetaType::UnknownType is an invalid type id + signaling that a type is unknown to QMetaType, and QMetaType::Void + is a valid type id of C++ void type. The difference will be visible for + example in call to QMetaType::typeName(), this function will return null for + QMetaType::UnknownType and a pointer to "void" string for + QMetaType::Void. + Please, notice that QMetaType::UnknownType has value 0, which previously was + reserved for QMetaType::Void. + - QMessageBox diff --git a/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp b/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp index afba3b6a61..7c0c2c2122 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qmetaobject.cpp @@ -107,7 +107,7 @@ for(int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) const QMetaObject* metaObject = obj->metaObject(); QStringList methods; for(int i = metaObject->methodOffset(); i < metaObject->methodCount(); ++i) - methods << QString::fromLatin1(metaObject->method(i).signature()); + methods << QString::fromLatin1(metaObject->method(i).methodSignature()); //! [methodCount] //! [6] diff --git a/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp b/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp index 9d72c42504..d0a7a69884 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qmetatype.cpp @@ -73,7 +73,7 @@ MyStruct s2 = var.value<MyStruct>(); //! [3] int id = QMetaType::type("MyClass"); -if (id != 0) { +if (id != QMetaType::UnknownType) { void *myClassPtr = QMetaType::create(id); ... QMetaType::destroy(id, myClassPtr); diff --git a/src/corelib/json/qjsonvalue.cpp b/src/corelib/json/qjsonvalue.cpp index b4a689da60..2eedef67ce 100644 --- a/src/corelib/json/qjsonvalue.cpp +++ b/src/corelib/json/qjsonvalue.cpp @@ -400,7 +400,7 @@ QString QJsonValue::toString() const if (t != String) return QString(); stringData->ref.ref(); // the constructor below doesn't add a ref. - return QString(*(const QConstStringData<1> *)stringData); + return QString(*(const QStaticStringData<1> *)stringData); } /*! diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index cacd999869..a8c21657cb 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -146,6 +146,90 @@ QT_BEGIN_NAMESPACE static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } +static inline const QByteArrayData &stringData(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision >= 7); + const QByteArrayData &data = mo->d.stringdata[index]; + Q_ASSERT(data.ref.isStatic()); + Q_ASSERT(data.alloc == 0); + Q_ASSERT(data.capacityReserved == 0); + Q_ASSERT(data.size >= 0); + return data; +} + +static inline QByteArray toByteArray(const QByteArrayData &d) +{ + return QByteArray(reinterpret_cast<const QStaticByteArrayData<0> &>(d)); +} + +static inline const char *legacyString(const QMetaObject *mo, int index) +{ + Q_ASSERT(priv(mo->d.data)->revision <= 6); + return reinterpret_cast<const char *>(mo->d.stringdata) + index; +} + +static inline const char *rawStringData(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).data(); + else + return legacyString(mo, index); +} + +static inline int stringSize(const QMetaObject *mo, int index) +{ + if (priv(mo->d.data)->revision >= 7) + return stringData(mo, index).size; + else + return qstrlen(legacyString(mo, index)); +} + +static inline QByteArray typeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (typeInfo & IsUnresolvedType) { + return toByteArray(stringData(mo, typeInfo & TypeNameIndexMask)); + } else { + // ### Use the QMetaType::typeName() that returns QByteArray + const char *t = QMetaType::typeName(typeInfo); + return QByteArray::fromRawData(t, qstrlen(t)); + } +} + +static inline const char *rawTypeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + return typeNameFromTypeInfo(mo, typeInfo).constData(); +} + +static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo) +{ + if (!(typeInfo & IsUnresolvedType)) + return typeInfo; + return QMetaType::type(toByteArray(stringData(mo, typeInfo & TypeNameIndexMask))); +} + +class QMetaMethodPrivate : public QMetaMethod +{ +public: + static const QMetaMethodPrivate *get(const QMetaMethod *q) + { return static_cast<const QMetaMethodPrivate *>(q); } + + inline QByteArray signature() const; + inline QByteArray name() const; + inline int typesDataIndex() const; + inline const char *rawReturnTypeName() const; + inline int returnType() const; + inline int parameterCount() const; + inline int parametersDataIndex() const; + inline uint parameterTypeInfo(int index) const; + inline int parameterType(int index) const; + inline void getParameterTypes(int *types) const; + inline QList<QByteArray> parameterTypes() const; + inline QList<QByteArray> parameterNames() const; + inline QByteArray tag() const; + +private: + QMetaMethodPrivate(); +}; /*! \since 4.5 @@ -249,12 +333,14 @@ int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv) } /*! - \fn const char *QMetaObject::className() const - Returns the class name. \sa superClass() */ +const char *QMetaObject::className() const +{ + return rawStringData(this, 0); +} /*! \fn QMetaObject *QMetaObject::superClass() const @@ -307,7 +393,7 @@ const QObject *QMetaObject::cast(const QObject *obj) const */ QString QMetaObject::tr(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::DefaultCodec, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::DefaultCodec, n); } /*! @@ -315,7 +401,7 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const */ QString QMetaObject::trUtf8(const char *s, const char *c, int n) const { - return QCoreApplication::translate(d.stringdata, s, c, QCoreApplication::UnicodeUTF8, n); + return QCoreApplication::translate(rawStringData(this, 0), s, c, QCoreApplication::UnicodeUTF8, n); } #endif // QT_NO_TRANSLATION @@ -495,7 +581,37 @@ int QMetaObject::classInfoCount() const return n; } +// Returns true if the method defined by the given meta-object&handle +// matches the given name, argument count and argument types, otherwise +// returns false. +static bool methodMatch(const QMetaObject *m, int handle, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + Q_ASSERT(priv(m->d.data)->revision >= 7); + if (int(m->d.data[handle + 1]) != argc) + return false; + + if (toByteArray(stringData(m, m->d.data[handle])) != name) + return false; + + int paramsIndex = m->d.data[handle + 2] + 1; + for (int i = 0; i < argc; ++i) { + uint typeInfo = m->d.data[paramsIndex + i]; + if (types[i].type()) { + if (types[i].type() != typeFromTypeInfo(m, typeInfo)) + return false; + } else { + if (types[i].name() != typeNameFromTypeInfo(m, typeInfo)) + return false; + } + } + + return true; +} + /** \internal +* \obsolete * helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within * the baseObject * \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. @@ -506,6 +622,8 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, const char *method, bool normalizeStringData) { + QByteArray methodName; + QArgumentTypeArray methodArgumentTypes; for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); @@ -513,15 +631,26 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, ? (priv(m->d.data)->signalCount) : 0; if (!normalizeStringData) { for (; i >= end; --i) { - const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; - if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { - *baseObject = m; - return i; + if (priv(m->d.data)->revision >= 7) { + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodArgumentTypes); + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, methodName, methodArgumentTypes.size(), + methodArgumentTypes.constData())) { + *baseObject = m; + return i; + } + } else { + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5*i]); + if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { + *baseObject = m; + return i; + } } } } else if (priv(m->d.data)->revision < 5) { for (; i >= end; --i) { - const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); + const char *stringdata = legacyString(m, m->d.data[priv(m->d.data)->methodData + 5 * i]); const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); if (normalizedSignature == method) { *baseObject = m; @@ -533,6 +662,34 @@ static inline int indexOfMethodRelative(const QMetaObject **baseObject, return -1; } +/** \internal +* helper function for indexOf{Method,Slot,Signal}, returns the relative index of the method within +* the baseObject +* \a MethodType might be MethodSignal or MethodSlot, or 0 to match everything. +*/ +template<int MethodType> +static inline int indexOfMethodRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { + Q_ASSERT(priv(m->d.data)->revision >= 7); + int i = (MethodType == MethodSignal) + ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); + const int end = (MethodType == MethodSlot) + ? (priv(m->d.data)->signalCount) : 0; + + for (; i >= end; --i) { + int handle = priv(m->d.data)->methodData + 5*i; + if (methodMatch(m, handle, name, argc, types)) { + *baseObject = m; + return i; + } + } + } + return -1; +} + /*! \since 4.5 @@ -548,10 +705,16 @@ int QMetaObject::indexOfConstructor(const char *constructor) const { if (priv(d.data)->revision < 2) return -1; - for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { - const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; - if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { - return i; + else if (priv(d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(constructor, types); + return QMetaObjectPrivate::indexOfConstructor(this, name, types.size(), types.constData()); + } else { + for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { + const char *data = legacyString(this, d.data[priv(d.data)->constructorData + 5*i]); + if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { + return i; + } } } return -1; @@ -568,16 +731,60 @@ int QMetaObject::indexOfConstructor(const char *constructor) const int QMetaObject::indexOfMethod(const char *method) const { const QMetaObject *m = this; - int i = indexOfMethodRelative<0>(&m, method, false); - if (i < 0) { - m = this; - i = indexOfMethodRelative<0>(&m, method, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(method, types); + i = indexOfMethodRelative<0>(&m, name, types.size(), types.constData()); + } else { + i = indexOfMethodRelative<0>(&m, method, false); + if (i < 0) { + m = this; + i = indexOfMethodRelative<0>(&m, method, true); + } } if (i >= 0) i += m->methodOffset(); return i; } +// Parses a string of comma-separated types into QArgumentTypes. +static void argumentTypesFromString(const char *str, const char *end, + QArgumentTypeArray &types) +{ + Q_ASSERT(str <= end); + while (str != end) { + if (!types.isEmpty()) + ++str; // Skip comma + const char *begin = str; + int level = 0; + while (str != end && (level > 0 || *str != ',')) { + if (*str == '<') + ++level; + else if (*str == '>') + --level; + ++str; + } + types += QArgumentType(QByteArray(begin, str - begin)); + } +} + +// Given a method \a signature (e.g. "foo(int,double)"), this function +// populates the argument \a types array and returns the method name. +QByteArray QMetaObjectPrivate::decodeMethodSignature( + const char *signature, QArgumentTypeArray &types) +{ + const char *lparens = strchr(signature, '('); + if (!lparens) + return QByteArray(); + const char *rparens = strchr(lparens + 1, ')'); + if (!rparens || *(rparens+1)) + return QByteArray(); + int nameLength = lparens - signature; + argumentTypesFromString(lparens + 1, rparens, types); + return QByteArray::fromRawData(signature, nameLength); +} + /*! Finds \a signal and returns its index; otherwise returns -1. @@ -592,10 +799,17 @@ int QMetaObject::indexOfMethod(const char *method) const int QMetaObject::indexOfSignal(const char *signal) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); - if (i < 0) { - m = this; - i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signal, types); + i = QMetaObjectPrivate::indexOfSignalRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); + if (i < 0) { + m = this; + i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); + } } if (i >= 0) i += m->methodOffset(); @@ -603,6 +817,7 @@ int QMetaObject::indexOfSignal(const char *signal) const } /*! \internal + \obsolete Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found @@ -618,12 +833,37 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", - signal, m->d.superdata->d.stringdata, m->d.stringdata); + signal, rawStringData(m->d.superdata, 0), rawStringData(m, 0)); } #endif return i; } +/*! \internal + Same as QMetaObject::indexOfSignal, but the result is the local offset to the base object. + + \a baseObject will be adjusted to the enclosing QMetaObject, or 0 if the signal is not found +*/ +int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + int i = indexOfMethodRelative<MethodSignal>(baseObject, name, argc, types); +#ifndef QT_NO_DEBUG + const QMetaObject *m = *baseObject; + if (i >= 0 && m && m->d.superdata) { + int conflict = indexOfMethod(m->d.superdata, name, argc, types); + if (conflict >= 0) { + QMetaMethod conflictMethod = m->d.superdata->method(conflict); + qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", + conflictMethod.methodSignature().constData(), + rawStringData(m->d.superdata, 0), rawStringData(m, 0)); + } + } + #endif + return i; +} + /*! Finds \a slot and returns its index; otherwise returns -1. @@ -635,9 +875,16 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, int QMetaObject::indexOfSlot(const char *slot) const { const QMetaObject *m = this; - int i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); - if (i < 0) - i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + int i; + if (priv(m->d.data)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(slot, types); + i = QMetaObjectPrivate::indexOfSlotRelative(&m, name, types.size(), types.constData()); + } else { + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, false); + if (i < 0) + i = QMetaObjectPrivate::indexOfSlotRelative(&m, slot, true); + } if (i >= 0) i += m->methodOffset(); return i; @@ -651,10 +898,108 @@ int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, return indexOfMethodRelative<MethodSlot>(m, slot, normalizeStringData); } +// same as indexOfSignalRelative but for slots. +int QMetaObjectPrivate::indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types) +{ + return indexOfMethodRelative<MethodSlot>(m, name, argc, types); +} + +int QMetaObjectPrivate::indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSignalRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfSlotRelative(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + int i = indexOfMethodRelative<0>(&m, name, argc, types); + if (i >= 0) + i += m->methodOffset(); + return i; +} + +int QMetaObjectPrivate::indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types) +{ + for (int i = priv(m->d.data)->constructorCount-1; i >= 0; --i) { + int handle = priv(m->d.data)->constructorData + 5*i; + if (methodMatch(m, handle, name, argc, types)) + return i; + } + return -1; +} + +/*! + \internal + + Returns true if the \a signalTypes and \a methodTypes are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes) +{ + if (signalArgc < methodArgc) + return false; + for (int i = 0; i < methodArgc; ++i) { + if (signalTypes[i] != methodTypes[i]) + return false; + } + return true; +} + +/*! + \internal + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObjectPrivate::checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method) +{ + if (signal->methodType() != QMetaMethod::Signal) + return false; + if (signal->parameterCount() < method->parameterCount()) + return false; + const QMetaObject *smeta = signal->enclosingMetaObject(); + const QMetaObject *rmeta = method->enclosingMetaObject(); + for (int i = 0; i < method->parameterCount(); ++i) { + uint sourceTypeInfo = signal->parameterTypeInfo(i); + uint targetTypeInfo = method->parameterTypeInfo(i); + if ((sourceTypeInfo & IsUnresolvedType) + || (targetTypeInfo & IsUnresolvedType)) { + QByteArray sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo); + QByteArray targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo); + if (sourceName != targetName) + return false; + } else { + int sourceType = typeFromTypeInfo(smeta, sourceTypeInfo); + int targetType = typeFromTypeInfo(rmeta, targetTypeInfo); + if (sourceType != targetType) + return false; + } + } + return true; +} + static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name) { while (self) { - if (strcmp(self->d.stringdata, name) == 0) + if (strcmp(rawStringData(self, 0), name) == 0) return self; if (self->d.extradata) { const QMetaObject **e; @@ -690,7 +1035,7 @@ int QMetaObject::indexOfEnumerator(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->enumeratorCount - 1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->enumeratorData + 4*i]; + const char *prop = rawStringData(m, m->d.data[d->enumeratorData + 4*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->enumeratorOffset(); return i; @@ -713,7 +1058,7 @@ int QMetaObject::indexOfProperty(const char *name) const while (m) { const QMetaObjectPrivate *d = priv(m->d.data); for (int i = d->propertyCount-1; i >= 0; --i) { - const char *prop = m->d.stringdata + m->d.data[d->propertyData + 3*i]; + const char *prop = rawStringData(m, m->d.data[d->propertyData + 3*i]); if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) { i += m->propertyOffset(); return i; @@ -744,8 +1089,7 @@ int QMetaObject::indexOfClassInfo(const char *name) const const QMetaObject *m = this; while (m && i < 0) { for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) - if (strcmp(name, m->d.stringdata - + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { + if (strcmp(name, rawStringData(m, m->d.data[priv(m->d.data)->classInfoData + 2*i])) == 0) { i += m->classInfoOffset(); break; } @@ -829,16 +1173,20 @@ QMetaProperty QMetaObject::property(int index) const if (i >= 0 && i < priv(d.data)->propertyCount) { int handle = priv(d.data)->propertyData + 3*i; int flags = d.data[handle + 2]; - const char *type = d.stringdata + d.data[handle + 1]; result.mobj = this; result.handle = handle; result.idx = i; if (flags & EnumOrFlag) { + const char *type; + if (priv(d.data)->revision >= 7) + type = rawTypeNameFromTypeInfo(this, d.data[handle + 1]); + else + type = legacyString(this, d.data[handle + 1]); result.menum = enumerator(indexOfEnumerator(type)); if (!result.menum.isValid()) { const char *enum_name = type; - const char *scope_name = d.stringdata; + const char *scope_name = rawStringData(this, 0); char *scope_buffer = 0; const char *colon = strrchr(enum_name, ':'); @@ -934,6 +1282,21 @@ bool QMetaObject::checkConnectArgs(const char *signal, const char *method) return false; } +/*! + \since 5.0 + \overload + + Returns true if the \a signal and \a method arguments are + compatible; otherwise returns false. +*/ +bool QMetaObject::checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method) +{ + return QMetaObjectPrivate::checkConnectArgs( + QMetaMethodPrivate::get(&signal), + QMetaMethodPrivate::get(&method)); +} + static void qRemoveWhitespace(const char *s, char *d) { char last = 0; @@ -1230,7 +1593,7 @@ bool QMetaObject::invokeMethod(QObject *obj, \ingroup objectmodel - A QMetaMethod has a methodType(), a signature(), a list of + A QMetaMethod has a methodType(), a methodSignature(), a list of parameterTypes() and parameterNames(), a return typeName(), a tag(), and an access() specifier. You can use invoke() to invoke the method on an arbitrary QObject. @@ -1275,72 +1638,284 @@ bool QMetaObject::invokeMethod(QObject *obj, \internal */ +QByteArray QMetaMethodPrivate::signature() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QByteArray result; + result.reserve(256); + result += name(); + result += '('; + QList<QByteArray> argTypes = parameterTypes(); + for (int i = 0; i < argTypes.size(); ++i) { + if (i) + result += ','; + result += argTypes.at(i); + } + result += ')'; + return result; +} + +QByteArray QMetaMethodPrivate::name() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle])); +} + +int QMetaMethodPrivate::typesDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 2]; +} + +const char *QMetaMethodPrivate::rawReturnTypeName() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + uint typeInfo = mobj->d.data[typesDataIndex()]; + if (typeInfo & IsUnresolvedType) + return rawStringData(mobj, typeInfo & TypeNameIndexMask); + else { + if (typeInfo == QMetaType::Void) { + // QMetaMethod::typeName() is documented to return an empty string + // if the return type is void, but QMetaType::typeName() returns + // "void". + return ""; + } + return QMetaType::typeName(typeInfo); + } +} + +int QMetaMethodPrivate::returnType() const +{ + return parameterType(-1); +} + +int QMetaMethodPrivate::parameterCount() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[handle + 1]; +} + +int QMetaMethodPrivate::parametersDataIndex() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typesDataIndex() + 1; +} + +uint QMetaMethodPrivate::parameterTypeInfo(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return mobj->d.data[parametersDataIndex() + index]; +} + +int QMetaMethodPrivate::parameterType(int index) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return typeFromTypeInfo(mobj, parameterTypeInfo(index)); +} + +void QMetaMethodPrivate::getParameterTypes(int *types) const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + int dataIndex = parametersDataIndex(); + int argc = parameterCount(); + for (int i = 0; i < argc; ++i) { + int id = typeFromTypeInfo(mobj, mobj->d.data[dataIndex++]); + *(types++) = id; + } +} + +QList<QByteArray> QMetaMethodPrivate::parameterTypes() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList<QByteArray> list; + int argc = parameterCount(); + int paramsIndex = parametersDataIndex(); + for (int i = 0; i < argc; ++i) + list += typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + i]); + return list; +} + +QList<QByteArray> QMetaMethodPrivate::parameterNames() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + QList<QByteArray> list; + int argc = parameterCount(); + int namesIndex = parametersDataIndex() + argc; + for (int i = 0; i < argc; ++i) + list += toByteArray(stringData(mobj, mobj->d.data[namesIndex + i])); + return list; +} + +QByteArray QMetaMethodPrivate::tag() const +{ + Q_ASSERT(priv(mobj->d.data)->revision >= 7); + return toByteArray(stringData(mobj, mobj->d.data[handle + 3])); +} + /*! + \since 5.0 + Returns the signature of this method (e.g., \c{setValue(double)}). \sa parameterTypes(), parameterNames() */ -const char *QMetaMethod::signature() const +QByteArray QMetaMethod::methodSignature() const +{ + if (!mobj) + return QByteArray(); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->signature(); + } else { + const char *sig = rawStringData(mobj, mobj->d.data[handle]); + return QByteArray::fromRawData(sig, qstrlen(sig)); + } +} + +/*! + \since 5.0 + + Returns the name of this method. + + \sa methodSignature(), parameterCount() +*/ +QByteArray QMetaMethod::name() const +{ + if (!mobj) + return QByteArray(); + return QMetaMethodPrivate::get(this)->name(); +} + +/*! + \since 5.0 + + Returns the return type of this method. + + The return value is one of the types that are registered + with QMetaType, or QMetaType::UnknownType if the type is not registered. + + \sa parameterType(), QMetaType, typeName() +*/ +int QMetaMethod::returnType() const + { + if (!mobj) + return QMetaType::UnknownType; + return QMetaMethodPrivate::get(this)->returnType(); +} + +/*! + \since 5.0 + + Returns the number of parameters of this method. + + \sa parameterType(), parameterNames() +*/ +int QMetaMethod::parameterCount() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return QMetaMethodPrivate::get(this)->parameterCount(); +} + +/*! + \since 5.0 + + Returns the type of the parameter at the given \a index. + + The return value is one of the types that are registered + with QMetaType, or QMetaType::UnknownType if the type is not registered. + + \sa parameterCount(), returnType(), QMetaType +*/ +int QMetaMethod::parameterType(int index) const +{ + if (!mobj || index < 0) + return QMetaType::UnknownType; + if (index >= QMetaMethodPrivate::get(this)->parameterCount()) + return QMetaType::UnknownType; + return QMetaMethodPrivate::get(this)->parameterType(index); +} + +/*! + \since 5.0 + \internal + + Gets the parameter \a types of this method. The storage + for \a types must be able to hold parameterCount() items. + + \sa parameterCount(), returnType(), parameterType() +*/ +void QMetaMethod::getParameterTypes(int *types) const +{ + if (!mobj) + return; + QMetaMethodPrivate::get(this)->getParameterTypes(types); } /*! Returns a list of parameter types. - \sa parameterNames(), signature() + \sa parameterNames(), methodSignature() */ QList<QByteArray> QMetaMethod::parameterTypes() const { if (!mobj) return QList<QByteArray>(); - return QMetaObjectPrivate::parameterTypeNamesFromSignature( - mobj->d.stringdata + mobj->d.data[handle]); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterTypes(); + } else { + return QMetaObjectPrivate::parameterTypeNamesFromSignature( + legacyString(mobj, mobj->d.data[handle])); + } } /*! Returns a list of parameter names. - \sa parameterTypes(), signature() + \sa parameterTypes(), methodSignature() */ QList<QByteArray> QMetaMethod::parameterNames() const { QList<QByteArray> list; if (!mobj) return list; - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; - if (*names == 0) { - // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; - while (*signature && *signature != '(') - ++signature; - if (*++signature != ')') - list += QByteArray(); + if (priv(mobj->d.data)->revision >= 7) { + return QMetaMethodPrivate::get(this)->parameterNames(); } else { - --names; - do { - const char *begin = ++names; - while (*names && *names != ',') - ++names; - list += QByteArray(begin, names - begin); - } while (*names); + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); + if (*names == 0) { + // do we have one or zero arguments? + const char *signature = rawStringData(mobj, mobj->d.data[handle]); + while (*signature && *signature != '(') + ++signature; + if (*++signature != ')') + list += QByteArray(); + } else { + --names; + do { + const char *begin = ++names; + while (*names && *names != ',') + ++names; + list += QByteArray(begin, names - begin); + } while (*names); + } + return list; } - return list; } /*! - Returns the return type of this method, or an empty string if the + Returns the return type name of this method, or an empty string if the return type is \e void. */ const char *QMetaMethod::typeName() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 2]; + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->rawReturnTypeName(); + else + return legacyString(mobj, mobj->d.data[handle + 2]); } /*! @@ -1377,7 +1952,10 @@ const char *QMetaMethod::tag() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 3]; + if (priv(mobj->d.data)->revision >= 7) + return QMetaMethodPrivate::get(this)->tag().constData(); + else + return legacyString(mobj, mobj->d.data[handle + 3]); } @@ -1578,12 +2156,14 @@ bool QMetaMethod::invoke(QObject *object, break; } int metaMethodArgumentCount = 0; - { + if (priv(mobj->d.data)->revision >= 7) { + metaMethodArgumentCount = QMetaMethodPrivate::get(this)->parameterCount(); + } else { // based on QMetaObject::parameterNames() - const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *names = rawStringData(mobj, mobj->d.data[handle + 1]); if (*names == 0) { // do we have one or zero arguments? - const char *signature = mobj->d.stringdata + mobj->d.data[handle]; + const char *signature = rawStringData(mobj, mobj->d.data[handle]); while (*signature && *signature != '(') ++signature; if (*++signature != ')') @@ -1661,7 +2241,7 @@ bool QMetaMethod::invoke(QObject *object, for (int i = 1; i < paramCount; ++i) { types[i] = QMetaType::type(typeNames[i]); - if (types[i]) { + if (types[i] != QMetaType::UnknownType) { args[i] = QMetaType::create(types[i], param[i]); ++nargs; } else if (param[i]) { @@ -1803,7 +2383,7 @@ const char *QMetaEnum::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -1831,7 +2411,7 @@ const char *QMetaEnum::key(int index) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; if (index >= 0 && index < count) - return mobj->d.stringdata + mobj->d.data[data + 2*index]; + return rawStringData(mobj, mobj->d.data[data + 2*index]); return 0; } @@ -1878,7 +2458,7 @@ bool QMetaEnum::isFlag() const */ const char *QMetaEnum::scope() const { - return mobj?mobj->d.stringdata : 0; + return mobj?rawStringData(mobj, 0) : 0; } /*! @@ -1910,8 +2490,8 @@ int QMetaEnum::keyToValue(const char *key, bool *ok) const int count = mobj->d.data[handle + 2]; int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) { - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key, mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key, rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { if (ok != 0) *ok = true; return mobj->d.data[data + 2*i + 1]; @@ -1936,7 +2516,7 @@ const char* QMetaEnum::valueToKey(int value) const int data = mobj->d.data[handle + 3]; for (int i = 0; i < count; ++i) if (value == (int)mobj->d.data[data + 2*i + 1]) - return mobj->d.stringdata + mobj->d.data[data + 2*i]; + return rawStringData(mobj, mobj->d.data[data + 2*i]); return 0; } @@ -1979,8 +2559,8 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const } int i; for (i = count-1; i >= 0; --i) - if ((!scope || (qstrlen(mobj->d.stringdata) == scope && strncmp(qualified_key.constData(), mobj->d.stringdata, scope) == 0)) - && strcmp(key, mobj->d.stringdata + mobj->d.data[data + 2*i]) == 0) { + if ((!scope || (stringSize(mobj, 0) == int(scope) && strncmp(qualified_key.constData(), rawStringData(mobj, 0), scope) == 0)) + && strcmp(key, rawStringData(mobj, mobj->d.data[data + 2*i])) == 0) { value |= mobj->d.data[data + 2*i + 1]; break; } @@ -2013,7 +2593,10 @@ QByteArray QMetaEnum::valueToKeys(int value) const v = v & ~k; if (!keys.isEmpty()) keys += '|'; - keys += mobj->d.stringdata + mobj->d.data[data + 2*i]; + if (priv(mobj->d.data)->revision >= 7) + keys += toByteArray(stringData(mobj, mobj->d.data[data + 2*i])); + else + keys += legacyString(mobj, mobj->d.data[data + 2*i]); } } return keys; @@ -2092,7 +2675,7 @@ const char *QMetaProperty::name() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2105,7 +2688,10 @@ const char *QMetaProperty::typeName() const if (!mobj) return 0; int handle = priv(mobj->d.data)->propertyData + 3*idx; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + if (priv(mobj->d.data)->revision >= 7) + return rawTypeNameFromTypeInfo(mobj, mobj->d.data[handle + 1]); + else + return legacyString(mobj, mobj->d.data[handle + 1]); } /*! @@ -2119,14 +2705,21 @@ QVariant::Type QMetaProperty::type() const if (!mobj) return QVariant::Invalid; int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - uint type = flags >> 24; - if (type) + uint type; + if (priv(mobj->d.data)->revision >= 7) { + type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type >= QMetaType::User) + return QVariant::UserType; + } else { + uint flags = mobj->d.data[handle + 2]; + type = flags >> 24; + } + if (type != QMetaType::UnknownType) return QVariant::Type(type); if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId == 0) + if (enumMetaTypeId == QMetaType::UnknownType) return QVariant::Int; } #ifdef QT_COORD_TYPE @@ -2142,18 +2735,29 @@ QVariant::Type QMetaProperty::type() const \since 4.2 Returns this property's user type. The return value is one - of the values that are registered with QMetaType, or 0 if + of the values that are registered with QMetaType, or QMetaType::UnknownType if the type is not registered. \sa type(), QMetaType, typeName() */ int QMetaProperty::userType() const { - QVariant::Type tp = type(); - if (tp != QVariant::UserType) - return tp; + if (!mobj) + return QMetaType::UnknownType; + if (priv(mobj->d.data)->revision >= 7) { + int handle = priv(mobj->d.data)->propertyData + 3*idx; + int type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]); + if (type != QMetaType::UnknownType) + return type; + } else { + QVariant::Type tp = type(); + if (tp != QVariant::UserType) + return tp; + } if (isEnumType()) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); + if (enumMetaTypeId == QMetaType::UnknownType) + return QVariant::Int; // Match behavior of QMetaType::type() return enumMetaTypeId; } return QMetaType::type(typeName()); @@ -2249,18 +2853,30 @@ QVariant QMetaProperty::read(const QObject *object) const with QMetaType) */ int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if (enumMetaTypeId != 0) + if (enumMetaTypeId != QMetaType::UnknownType) t = enumMetaTypeId; } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; - t = (flags >> 24); - if (t == QVariant::Invalid) - t = QMetaType::type(typeName); - if (t == QVariant::Invalid) - t = QVariant::nameToType(typeName); - if (t == QVariant::Invalid) { + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = (flags >> 24); + if (t == QMetaType::UnknownType) { + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + t = QMetaType::type(typeName); + if (t == QMetaType::UnknownType) + t = QVariant::nameToType(typeName); + } + } + if (t == QMetaType::UnknownType) { qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name()); return QVariant(); } @@ -2315,17 +2931,29 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const return false; } else if (v.type() != QVariant::Int && v.type() != QVariant::UInt) { int enumMetaTypeId = QMetaType::type(qualifiedName(menum)); - if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) + if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData()) return false; v = QVariant(*reinterpret_cast<const int *>(v.constData())); } v.convert(QVariant::Int); } else { int handle = priv(mobj->d.data)->propertyData + 3*idx; - uint flags = mobj->d.data[handle + 2]; - t = flags >> 24; - if (t == QVariant::Invalid) { - const char *typeName = mobj->d.stringdata + mobj->d.data[handle + 1]; + const char *typeName = 0; + if (priv(mobj->d.data)->revision >= 7) { + uint typeInfo = mobj->d.data[handle + 1]; + if (!(typeInfo & IsUnresolvedType)) + t = typeInfo; + else { + typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask); + t = QMetaType::type(typeName); + } + } else { + uint flags = mobj->d.data[handle + 2]; + t = flags >> 24; + typeName = legacyString(mobj, mobj->d.data[handle + 1]); + } + if (t == QMetaType::UnknownType) { + const char *typeName = rawStringData(mobj, mobj->d.data[handle + 1]); const char *vtypeName = value.typeName(); if (vtypeName && strcmp(typeName, vtypeName) == 0) t = value.userType(); @@ -2691,7 +3319,7 @@ const char *QMetaClassInfo::name() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle]; + return rawStringData(mobj, mobj->d.data[handle]); } /*! @@ -2703,7 +3331,7 @@ const char* QMetaClassInfo::value() const { if (!mobj) return 0; - return mobj->d.stringdata + mobj->d.data[handle + 1]; + return rawStringData(mobj, mobj->d.data[handle + 1]); } /*! diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h index 9e51af7556..095b196dca 100644 --- a/src/corelib/kernel/qmetaobject.h +++ b/src/corelib/kernel/qmetaobject.h @@ -57,8 +57,13 @@ class Q_CORE_EXPORT QMetaMethod public: inline QMetaMethod() : mobj(0),handle(0) {} - const char *signature() const; + QByteArray methodSignature() const; + QByteArray name() const; const char *typeName() const; + int returnType() const; + int parameterCount() const; + int parameterType(int index) const; + void getParameterTypes(int *types) const; QList<QByteArray> parameterTypes() const; QList<QByteArray> parameterNames() const; const char *tag() const; @@ -137,8 +142,16 @@ public: inline bool isValid() const { return mobj != 0; } private: +#if QT_DEPRECATED_SINCE(5,0) + // signature() has been renamed to methodSignature() in Qt 5. + // Warning, that function returns a QByteArray; check the life time if + // you convert to char*. + char *signature(struct renamedInQt5_warning_checkTheLifeTime * = 0) Q_DECL_EQ_DELETE; +#endif + const QMetaObject *mobj; uint handle; + friend class QMetaMethodPrivate; friend struct QMetaObject; friend struct QMetaObjectPrivate; friend class QObject; diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index 59a5c5f280..ff8dccc4dd 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -105,11 +105,64 @@ enum MetaObjectFlags { RequiresVariantMetaObject = 0x02 }; +enum MetaDataFlags { + IsUnresolvedType = 0x80000000, + TypeNameIndexMask = 0x7FFFFFFF +}; + +class QArgumentType +{ +public: + QArgumentType(int type) + : _type(type) + {} + QArgumentType(const QByteArray &name) + : _type(QMetaType::type(name.constData())), _name(name) + {} + QArgumentType() + : _type(0) + {} + int type() const + { return _type; } + QByteArray name() const + { + if (_type && _name.isEmpty()) + const_cast<QArgumentType *>(this)->_name = QMetaType::typeName(_type); + return _name; + } + bool operator==(const QArgumentType &other) const + { + if (_type) + return _type == other._type; + else if (other._type) + return false; + else + return _name == other._name; + } + bool operator!=(const QArgumentType &other) const + { + if (_type) + return _type != other._type; + else if (other._type) + return true; + else + return _name != other._name; + } + +private: + int _type; + QByteArray _name; +}; + +template <class T, int> class QVarLengthArray; +typedef QVarLengthArray<QArgumentType, 10> QArgumentTypeArray; + +class QMetaMethodPrivate; class QMutex; struct QMetaObjectPrivate { - enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder + enum { OutputRevision = 7 }; // Used by moc, qmetaobjectbuilder and qdbus int revision; int className; @@ -122,6 +175,7 @@ struct QMetaObjectPrivate int signalCount; //since revision 4 // revision 5 introduces changes in normalized signatures, no new members // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself + // revision 7 is Qt 5 static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } @@ -134,6 +188,27 @@ struct QMetaObjectPrivate bool normalizeStringData); static int originalClone(const QMetaObject *obj, int local_method_index); + static QByteArray decodeMethodSignature(const char *signature, + QArgumentTypeArray &types); + static int indexOfSignalRelative(const QMetaObject **baseObject, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSlotRelative(const QMetaObject **m, + const QByteArray &name, int argc, + const QArgumentType *types); + static int indexOfSignal(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfSlot(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfMethod(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static int indexOfConstructor(const QMetaObject *m, const QByteArray &name, + int argc, const QArgumentType *types); + static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, + int methodArgc, const QArgumentType *methodTypes); + static bool checkConnectArgs(const QMetaMethodPrivate *signal, + const QMetaMethodPrivate *method); + static QList<QByteArray> parameterTypeNamesFromSignature(const char *signature); #ifndef QT_NO_QOBJECT diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 8bece6636b..13cd1a684a 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -78,26 +78,17 @@ QT_BEGIN_NAMESPACE */ // copied from moc's generator.cpp -uint qvariant_nameToType(const char* name) +bool isBuiltinType(const QByteArray &type) { - if (!name) - return 0; - - uint tp = QMetaType::type(name); - return tp < QMetaType::User ? tp : 0; -} - -/* - Returns true if the type is a QVariant types. -*/ -bool isVariantType(const char* type) -{ - return qvariant_nameToType(type) != 0; + int id = QMetaType::type(type); + if (!id && !type.isEmpty() && type != "void") + return false; + return (id < QMetaType::User); } +// copied from qmetaobject.cpp static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); } -// end of copied lines from qmetaobject.cpp class QMetaMethodBuilderPrivate { @@ -136,6 +127,21 @@ public: { attributes = ((attributes & ~AccessMask) | (int)value); } + + QList<QByteArray> parameterTypes() const + { + return QMetaObjectPrivate::parameterTypeNamesFromSignature(signature); + } + + int parameterCount() const + { + return parameterTypes().size(); + } + + QByteArray name() const + { + return signature.left(qMax(signature.indexOf('('), 0)); + } }; class QMetaPropertyBuilderPrivate @@ -458,13 +464,13 @@ QMetaMethodBuilder QMetaObjectBuilder::addMethod(const QMetaMethod& prototype) { QMetaMethodBuilder method; if (prototype.methodType() == QMetaMethod::Method) - method = addMethod(prototype.signature()); + method = addMethod(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Signal) - method = addSignal(prototype.signature()); + method = addSignal(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Slot) - method = addSlot(prototype.signature()); + method = addSlot(prototype.methodSignature()); else if (prototype.methodType() == QMetaMethod::Constructor) - method = addConstructor(prototype.signature()); + method = addConstructor(prototype.methodSignature()); method.setReturnType(prototype.typeName()); method.setParameterNames(prototype.parameterNames()); method.setTag(prototype.tag()); @@ -535,7 +541,7 @@ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QByteArray& signatur QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod& prototype) { Q_ASSERT(prototype.methodType() == QMetaMethod::Constructor); - QMetaMethodBuilder ctor = addConstructor(prototype.signature()); + QMetaMethodBuilder ctor = addConstructor(prototype.methodSignature()); ctor.setReturnType(prototype.typeName()); ctor.setParameterNames(prototype.parameterNames()); ctor.setTag(prototype.tag()); @@ -588,7 +594,7 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty& protot if (prototype.hasNotifySignal()) { // Find an existing method for the notify signal, or add a new one. QMetaMethod method = prototype.notifySignal(); - int index = indexOfMethod(method.signature()); + int index = indexOfMethod(method.methodSignature()); if (index == -1) index = addMethod(method).index(); d->properties[property._index].notifySignal = index; @@ -1070,75 +1076,82 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray& name) #define ALIGN(size,type) \ (size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1) -class MetaStringTable +/*! + \class QMetaStringTable + \internal + \brief The QMetaStringTable class can generate a meta-object string table at runtime. +*/ + +QMetaStringTable::QMetaStringTable() + : m_index(0) {} + +// Enters the given value into the string table (if it hasn't already been +// entered). Returns the index of the string. +int QMetaStringTable::enter(const QByteArray &value) { -public: - typedef QHash<QByteArray, int> Entries; // string --> offset mapping - typedef Entries::const_iterator const_iterator; - Entries::const_iterator constBegin() const - { return m_entries.constBegin(); } - Entries::const_iterator constEnd() const - { return m_entries.constEnd(); } + Entries::iterator it = m_entries.find(value); + if (it != m_entries.end()) + return it.value(); + int pos = m_index; + m_entries.insert(value, pos); + ++m_index; + return pos; +} - MetaStringTable() : m_offset(0) {} +int QMetaStringTable::preferredAlignment() +{ +#ifdef Q_ALIGNOF + return Q_ALIGNOF(QByteArrayData); +#else + return sizeof(void *); +#endif +} - int enter(const QByteArray &value) - { - Entries::iterator it = m_entries.find(value); - if (it != m_entries.end()) - return it.value(); - int pos = m_offset; - m_entries.insert(value, pos); - m_offset += value.size() + 1; - return pos; - } +// Returns the size (in bytes) required for serializing this string table. +int QMetaStringTable::blobSize() const +{ + int size = m_entries.size() * sizeof(QByteArrayData); + Entries::const_iterator it; + for (it = m_entries.constBegin(); it != m_entries.constEnd(); ++it) + size += it.key().size() + 1; + return size; +} - int arraySize() const { return m_offset; } +// 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 QMetaStringTable::writeBlob(char *out) +{ + Q_ASSERT(!(reinterpret_cast<quintptr>(out) & (preferredAlignment()-1))); -private: - Entries m_entries; - int m_offset; -}; + int offsetOfStringdataMember = m_entries.size() * sizeof(QByteArrayData); + int stringdataOffset = 0; + for (int i = 0; i < m_entries.size(); ++i) { + const QByteArray &str = m_entries.key(i); + int size = str.size(); + qptrdiff offset = offsetOfStringdataMember + stringdataOffset + - i * sizeof(QByteArrayData); + const QByteArrayData data = { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset }; -// Build the parameter array string for a method. -static QByteArray buildParameterNames - (const QByteArray& signature, const QList<QByteArray>& parameterNames) -{ - // If the parameter name list is specified, then concatenate them. - if (!parameterNames.isEmpty()) { - QByteArray names; - bool first = true; - foreach (const QByteArray &name, parameterNames) { - if (first) - first = false; - else - names += (char)','; - names += name; - } - return names; - } + memcpy(out + i * sizeof(QByteArrayData), &data, sizeof(QByteArrayData)); - // Count commas in the signature, excluding those inside template arguments. - int index = signature.indexOf('('); - if (index < 0) - return QByteArray(); - ++index; - if (index >= signature.size()) - return QByteArray(); - if (signature[index] == ')') - return QByteArray(); - int count = 1; - int brackets = 0; - while (index < signature.size() && signature[index] != ',') { - char ch = signature[index++]; - if (ch == '<') - ++brackets; - else if (ch == '>') - --brackets; - else if (ch == ',' && brackets <= 0) - ++count; + memcpy(out + offsetOfStringdataMember + stringdataOffset, str.constData(), size); + out[offsetOfStringdataMember + stringdataOffset + size] = '\0'; + + stringdataOffset += size + 1; } - return QByteArray(count - 1, ','); +} + +// Returns the sum of all parameters (including return type) for the given +// \a methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList<QMetaMethodBuilderPrivate> &methods) +{ + int sum = 0; + for (int i = 0; i < methods.size(); ++i) + sum += methods.at(i).parameterCount() + 1; // +1 for return type + return sum; } // Build a QMetaObject in "buf" based on the information in "d". @@ -1151,6 +1164,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_UNUSED(expectedSize); // Avoid warning in release mode int size = 0; int dataIndex; + int paramsIndex; int enumIndex; int index; bool hasRevisionedMethods = d->hasRevisionedMethods(); @@ -1181,8 +1195,13 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, break; } } + int methodParametersDataSize = + ((aggregateParameterCount(d->methods) + + aggregateParameterCount(d->constructors)) * 2) // types and parameter names + - d->methods.size() // return "parameters" don't have names + - d->constructors.size(); // "this" parameters don't have names if (buf) { - Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "QMetaObjectBuilder should generate the same version as moc"); + Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->flags = d->flags; pmeta->className = 0; // Class name is always the first string. @@ -1197,6 +1216,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; pmeta->propertyCount = d->properties.size(); pmeta->propertyData = dataIndex; @@ -1218,6 +1239,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, dataIndex += 5 * d->methods.size(); if (hasRevisionedMethods) dataIndex += d->methods.size(); + paramsIndex = dataIndex; + dataIndex += methodParametersDataSize; dataIndex += 3 * d->properties.size(); if (hasNotifySignals) dataIndex += d->properties.size(); @@ -1240,13 +1263,14 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Find the start of the data and string tables. int *data = reinterpret_cast<int *>(pmeta); size += dataIndex * sizeof(int); + ALIGN(size, void *); char *str = reinterpret_cast<char *>(buf + size); if (buf) { if (relocatable) { - meta->d.stringdata = reinterpret_cast<const char *>((quintptr)size); + meta->d.stringdata = reinterpret_cast<const QByteArrayData *>((quintptr)size); meta->d.data = reinterpret_cast<uint *>((quintptr)pmetaSize); } else { - meta->d.stringdata = str; + meta->d.stringdata = reinterpret_cast<const QByteArrayData *>(str); meta->d.data = reinterpret_cast<uint *>(data); } } @@ -1254,7 +1278,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, // Reset the current data position to just past the QMetaObjectPrivate. dataIndex = MetaObjectPrivateFieldCount; - MetaStringTable strings; + QMetaStringTable strings; strings.enter(d->className); // Output the class infos, @@ -1273,24 +1297,21 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->methodData); for (index = 0; index < d->methods.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->methods[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; if (method->methodType() == QMetaMethod::Signal) pmeta->signalCount++; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } if (hasRevisionedMethods) { for (index = 0; index < d->methods.size(); ++index) { @@ -1301,23 +1322,59 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, } } + // Output the method parameters in the class. + Q_ASSERT(!buf || dataIndex == pmeta->methodData + d->methods.size() * 5 + + (hasRevisionedMethods ? d->methods.size() : 0)); + for (int x = 0; x < 2; ++x) { + QList<QMetaMethodBuilderPrivate> &methods = (x == 0) ? d->methods : d->constructors; + for (index = 0; index < methods.size(); ++index) { + QMetaMethodBuilderPrivate *method = &(methods[index]); + QList<QByteArray> paramTypeNames = method->parameterTypes(); + int paramCount = paramTypeNames.size(); + for (int i = -1; i < paramCount; ++i) { + const QByteArray &typeName = (i < 0) ? method->returnType : paramTypeNames.at(i); + int typeInfo; + if (isBuiltinType(typeName)) + typeInfo = QMetaType::type(typeName); + else + typeInfo = IsUnresolvedType | strings.enter(typeName); + if (buf) + data[dataIndex] = typeInfo; + ++dataIndex; + } + + QList<QByteArray> paramNames = method->parameterNames; + while (paramNames.size() < paramCount) + paramNames.append(QByteArray()); + for (int i = 0; i < paramCount; ++i) { + int stringIndex = strings.enter(paramNames.at(i)); + if (buf) + data[dataIndex] = stringIndex; + ++dataIndex; + } + } + } + // Output the properties in the class. Q_ASSERT(!buf || dataIndex == pmeta->propertyData); for (index = 0; index < d->properties.size(); ++index) { QMetaPropertyBuilderPrivate *prop = &(d->properties[index]); int name = strings.enter(prop->name); - int type = strings.enter(prop->type); + + int typeInfo; + if (isBuiltinType(prop->type)) + typeInfo = QMetaType::type(prop->type); + else + typeInfo = IsUnresolvedType | strings.enter(prop->type); + int flags = prop->flags; - if (!isVariantType(prop->type)) { + if (!isBuiltinType(prop->type)) flags |= EnumOrFlag; - } else { - flags |= qvariant_nameToType(prop->type) << 24; - } if (buf) { data[dataIndex] = name; - data[dataIndex + 1] = type; + data[dataIndex + 1] = typeInfo; data[dataIndex + 2] = flags; } dataIndex += 3; @@ -1372,34 +1429,25 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->constructorData); for (index = 0; index < d->constructors.size(); ++index) { QMetaMethodBuilderPrivate *method = &(d->constructors[index]); - int sig = strings.enter(method->signature); - int params; - QByteArray names = buildParameterNames - (method->signature, method->parameterNames); - params = strings.enter(names); - int ret = strings.enter(method->returnType); + int name = strings.enter(method->name()); + int argc = method->parameterCount(); int tag = strings.enter(method->tag); int attrs = method->attributes; if (buf) { - data[dataIndex] = sig; - data[dataIndex + 1] = params; - data[dataIndex + 2] = ret; + data[dataIndex] = name; + data[dataIndex + 1] = argc; + data[dataIndex + 2] = paramsIndex; data[dataIndex + 3] = tag; data[dataIndex + 4] = attrs; } dataIndex += 5; + paramsIndex += 1 + argc * 2; } - size += strings.arraySize(); + size += strings.blobSize(); - if (buf) { - // Write strings to string data array. - MetaStringTable::const_iterator it; - for (it = strings.constBegin(); it != strings.constEnd(); ++it) { - memcpy(str + it.value(), it.key().constData(), it.key().size()); - str[it.value() + it.key().size()] = '\0'; - } - } + if (buf) + strings.writeBlob(str); // Output the zero terminator in the data array. if (buf) @@ -1508,7 +1556,7 @@ void QMetaObjectBuilder::fromRelocatableData(QMetaObject *output, quintptr dataOffset = (quintptr)dataMo->d.data; output->d.superdata = superclass; - output->d.stringdata = buf + stringdataOffset; + output->d.stringdata = reinterpret_cast<const QByteArrayData *>(buf + stringdataOffset); output->d.data = reinterpret_cast<const uint *>(buf + dataOffset); output->d.extradata = 0; } @@ -1896,7 +1944,7 @@ QByteArray QMetaMethodBuilder::returnType() const is empty, then the method's return type is \c{void}. The \a value will be normalized before it is added to the method. - \sa returnType(), signature() + \sa returnType(), parameterTypes(), signature() */ void QMetaMethodBuilder::setReturnType(const QByteArray& value) { @@ -1906,6 +1954,20 @@ void QMetaMethodBuilder::setReturnType(const QByteArray& value) } /*! + Returns the list of parameter types for this method. + + \sa returnType(), parameterNames() +*/ +QList<QByteArray> QMetaMethodBuilder::parameterTypes() const +{ + QMetaMethodBuilderPrivate *d = d_func(); + if (d) + return d->parameterTypes(); + else + return QList<QByteArray>(); +} + +/*! Returns the list of parameter names for this method. \sa setParameterNames() diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 86bc354164..4d766a9197 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -56,6 +56,7 @@ #include <QtCore/qobject.h> #include <QtCore/qmetaobject.h> #include <QtCore/qdatastream.h> +#include <QtCore/qhash.h> #include <QtCore/qmap.h> @@ -203,6 +204,7 @@ public: QByteArray returnType() const; void setReturnType(const QByteArray& value); + QList<QByteArray> parameterTypes() const; QList<QByteArray> parameterNames() const; void setParameterNames(const QList<QByteArray>& value); @@ -318,6 +320,23 @@ private: QMetaEnumBuilderPrivate *d_func() const; }; +class Q_CORE_EXPORT QMetaStringTable +{ +public: + QMetaStringTable(); + + int enter(const QByteArray &value); + + static int preferredAlignment(); + int blobSize() const; + void writeBlob(char *out); + +private: + typedef QHash<QByteArray, int> Entries; // string --> index mapping + Entries m_entries; + int m_index; +}; + Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::AddMembers) Q_DECLARE_OPERATORS_FOR_FLAGS(QMetaObjectBuilder::MetaObjectFlags) diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 410a5cc712..a5dc179113 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -242,6 +242,7 @@ template<> struct TypeDefinition<QRegExp> { static const bool IsAvailable = fals \value QEasingCurve QEasingCurve \value User Base value for user types + \value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered \omitvalue FirstGuiType \omitvalue FirstWidgetsType @@ -311,7 +312,7 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ QT_FOR_EACH_STATIC_TYPE(QT_ADD_STATIC_METATYPE) QT_FOR_EACH_STATIC_ALIAS_TYPE(QT_ADD_STATIC_METATYPE_ALIASES_ITER) QT_FOR_EACH_STATIC_HACKS_TYPE(QT_ADD_STATIC_METATYPE_HACKS_ITER) - {0, 0, QMetaType::Void} + {0, 0, QMetaType::UnknownType} }; Q_CORE_EXPORT const QMetaTypeInterface *qMetaTypeGuiHelper = 0; @@ -348,10 +349,7 @@ Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) void QMetaType::registerStreamOperators(const char *typeName, SaveOperator saveOp, LoadOperator loadOp) { - int idx = type(typeName); - if (!idx) - return; - registerStreamOperators(idx, saveOp, loadOp); + registerStreamOperators(type(typeName), saveOp, loadOp); } /*! \internal @@ -434,7 +432,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) { const QVector<QCustomTypeInfo> * const ct = customTypes(); if (!ct) - return 0; + return QMetaType::UnknownType; for (int v = 0; v < ct->count(); ++v) { const QCustomTypeInfo &customInfo = ct->at(v); @@ -445,7 +443,7 @@ static int qMetaTypeCustomType_unlocked(const char *typeName, int length) return v + QMetaType::User; } } - return 0; + return QMetaType::UnknownType; } /*! \internal @@ -488,11 +486,11 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, int previousSize = 0; int previousFlags = 0; - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.creator = creator; @@ -558,12 +556,12 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) int idx = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QWriteLocker locker(customTypesLock()); idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!idx) { + if (idx == UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; inf.alias = aliasId; @@ -592,17 +590,20 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId) */ bool QMetaType::isRegistered(int type) { - if (type >= 0 && type < User) { - // predefined type + // predefined type + if ((type >= FirstCoreType && type <= LastCoreType) + || (type >= FirstGuiType && type <= LastGuiType) + || (type >= FirstWidgetsType && type <= LastWidgetsType)) { return true; } + QReadLocker locker(customTypesLock()); const QVector<QCustomTypeInfo> * const ct = customTypes(); return ((type >= User) && (ct && ct->count() > type - User) && !ct->at(type - User).typeName.isEmpty()); } /*! - Returns a handle to the type called \a typeName, or 0 if there is + Returns a handle to the type called \a typeName, or QMetaType::UnknownType if there is no such type. \sa isRegistered(), typeName(), Type @@ -611,17 +612,17 @@ int QMetaType::type(const char *typeName) { int length = qstrlen(typeName); if (!length) - return 0; + return UnknownType; int type = qMetaTypeStaticType(typeName, length); - if (!type) { + if (type == UnknownType) { QReadLocker locker(customTypesLock()); type = qMetaTypeCustomType_unlocked(typeName, length); #ifndef QT_NO_QOBJECT - if (!type) { + if (type == UnknownType) { const NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); type = qMetaTypeStaticType(normalizedTypeName.constData(), normalizedTypeName.size()); - if (!type) { + if (type == UnknownType) { type = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size()); } @@ -652,6 +653,7 @@ bool QMetaType::save(QDataStream &stream, int type, const void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -857,6 +859,7 @@ bool QMetaType::load(QDataStream &stream, int type, void *data) return false; switch(type) { + case QMetaType::UnknownType: case QMetaType::Void: case QMetaType::VoidStar: case QMetaType::QObjectStar: @@ -1154,6 +1157,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex)(*static_cast<const NS(QModelIndex)*>(copy)); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1257,6 +1261,7 @@ void *QMetaType::create(int type, const void *copy) case QMetaType::QModelIndex: return new NS(QModelIndex); #endif + case QMetaType::UnknownType: case QMetaType::Void: return 0; default: @@ -1318,6 +1323,7 @@ public: template<typename T> void delegate(const T *where) { DestroyerImpl<T>::Destroy(m_type, const_cast<T*>(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestroyer(m_type, (void*)where); } private: @@ -1381,6 +1387,7 @@ public: template<typename T> void *delegate(const T *copy) { return ConstructorImpl<T>::Construct(m_type, m_where, copy); } void *delegate(const void *) { return m_where; } + void *delegate(const QMetaTypeSwitcher::UnknownType*) { return m_where; } void *delegate(const QMetaTypeSwitcher::NotBuiltinType *copy) { return customTypeConstructor(m_type, m_where, copy); } private: @@ -1470,6 +1477,7 @@ public: template<typename T> void delegate(const T *where) { DestructorImpl<T>::Destruct(m_type, const_cast<T*>(where)); } void delegate(const void *) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType *where) { customTypeDestructor(m_type, (void*)where); } private: @@ -1539,6 +1547,7 @@ public: template<typename T> int delegate(const T*) { return SizeOfImpl<T>::Size(m_type); } + int delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } int delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeSizeOf(m_type); } private: static int customTypeSizeOf(const int type) @@ -1609,13 +1618,14 @@ public: template<typename T> quint32 delegate(const T*) { return FlagsImpl<T>::Flags(m_type); } quint32 delegate(const void*) { return 0; } + quint32 delegate(const QMetaTypeSwitcher::UnknownType*) { return 0; } quint32 delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return customTypeFlags(m_type); } private: const int m_type; static quint32 customTypeFlags(const int type) { const QVector<QCustomTypeInfo> * const ct = customTypes(); - if (Q_UNLIKELY(!ct)) + if (Q_UNLIKELY(!ct || type < QMetaType::User)) return 0; QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(ct->count() <= type - QMetaType::User)) @@ -1796,6 +1806,7 @@ public: template<typename T> void delegate(const T*) { TypeInfoImpl<T>(m_type, info); } void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const QMetaTypeSwitcher::NotBuiltinType*) { customTypeInfo(m_type); } private: void customTypeInfo(const uint type) @@ -1816,7 +1827,7 @@ QMetaType QMetaType::typeInfo(const int type) { TypeInfo typeInfo(type); QMetaTypeSwitcher::switcher<void>(typeInfo, type, 0); - return typeInfo.info.creator || !type ? QMetaType(QMetaType::NoExtensionFlags + return typeInfo.info.creator || type == Void ? QMetaType(QMetaType::NoExtensionFlags , static_cast<const QMetaTypeInterface *>(0) // typeInfo::info is a temporary variable, we can't return address of it. , typeInfo.info.creator , typeInfo.info.deleter @@ -1827,26 +1838,23 @@ QMetaType QMetaType::typeInfo(const int type) , typeInfo.info.size , typeInfo.info.flags , type) - : QMetaType(-1); + : QMetaType(UnknownType); } QMetaType::QMetaType(const int typeId) : m_typeId(typeId) { - if (Q_UNLIKELY(typeId == -1)) { + if (Q_UNLIKELY(typeId == UnknownType)) { // Constructs invalid QMetaType instance. m_extensionFlags = 0xffffffff; Q_ASSERT(!isValid()); } else { // TODO it can be better. *this = QMetaType::typeInfo(typeId); - if (m_typeId > 0 && !m_creator) { + if (m_typeId == UnknownType) m_extensionFlags = 0xffffffff; - m_typeId = -1; - } - if (m_typeId == QMetaType::Void) { + else if (m_typeId == QMetaType::Void) m_extensionFlags = CreateEx | DestroyEx | ConstructEx | DestructEx; - } } } diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 8dc45bb2ed..fb2bce7d7b 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE // F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType) #define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\ - F(Void, 0, void) \ + F(Void, 43, void) \ F(Bool, 1, bool) \ F(Int, 2, int) \ F(UInt, 3, uint) \ @@ -192,8 +192,8 @@ public: // these are merged with QVariant QT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID) - FirstCoreType = Void, - LastCoreType = QModelIndex, + FirstCoreType = Bool, + LastCoreType = Void, FirstGuiType = QFont, LastGuiType = QPolygonF, FirstWidgetsType = QIcon, @@ -202,6 +202,7 @@ public: QReal = sizeof(qreal) == sizeof(double) ? Double : Float, + UnknownType = 0, User = 256 }; @@ -677,7 +678,7 @@ inline QMetaType::~QMetaType() inline bool QMetaType::isValid() const { - return m_typeId >= 0; + return m_typeId != UnknownType; } inline bool QMetaType::isRegistered() const diff --git a/src/corelib/kernel/qmetatypeswitcher_p.h b/src/corelib/kernel/qmetatypeswitcher_p.h index e9c15ea214..ffd188c972 100644 --- a/src/corelib/kernel/qmetatypeswitcher_p.h +++ b/src/corelib/kernel/qmetatypeswitcher_p.h @@ -59,7 +59,8 @@ QT_BEGIN_NAMESPACE class QMetaTypeSwitcher { public: - class NotBuiltinType; + class NotBuiltinType; // type is not a built-in type, but it may be a custom type or an unknown type + class UnknownType; // type not known to QMetaType system template<class ReturnType, class DelegateObject> static ReturnType switcher(DelegateObject &logic, int type, const void *data); }; @@ -74,7 +75,11 @@ ReturnType QMetaTypeSwitcher::switcher(DelegateObject &logic, int type, const vo switch (QMetaType::Type(type)) { QT_FOR_EACH_STATIC_TYPE(QT_METATYPE_SWICHER_CASE) + case QMetaType::UnknownType: + return logic.delegate(static_cast<UnknownType const *>(data)); default: + if (type < QMetaType::User) + return logic.delegate(static_cast<UnknownType const *>(data)); return logic.delegate(static_cast<NotBuiltinType const *>(data)); } } diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 3a4d1da592..edbb84a657 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -105,6 +105,30 @@ static int *queuedConnectionTypes(const QList<QByteArray> &typeNames) return types; } +static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc) +{ + QScopedArrayPointer<int> types(new int [argc + 1]); + for (int i = 0; i < argc; ++i) { + const QArgumentType &type = argumentTypes[i]; + if (type.type()) + types[i] = type.type(); + else if (type.name().endsWith('*')) + types[i] = QMetaType::VoidStar; + else + types[i] = QMetaType::type(type.name()); + + if (!types[i]) { + qWarning("QObject::connect: Cannot queue arguments of type '%s'\n" + "(Make sure '%s' is registered using qRegisterMetaType().)", + type.name().constData(), type.name().constData()); + return 0; + } + } + types[argc] = 0; + + return types.take(); +} + static QBasicMutex _q_ObjectMutexPool[131]; /** \internal @@ -2179,12 +2203,12 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM if (signal.attributes() & QMetaMethod::Compatibility) { if (!(method.attributes() & QMetaMethod::Compatibility)) qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", - sender->className(), signal.signature()); + sender->className(), signal.methodSignature().constData()); } else if ((method.attributes() & QMetaMethod::Compatibility) && method.methodType() == QMetaMethod::Signal) { qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", - sender->className(), signal.signature(), - receiver->className(), method.signature()); + sender->className(), signal.methodSignature().constData(), + receiver->className(), method.methodSignature().constData()); } } @@ -2283,20 +2307,40 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code - int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) { - // check for normalized signatures - tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); - signal = tmp_signal_name.constData() + 1; - - smeta = sender->metaObject(); + QByteArray signalName; + QArgumentTypeArray signalTypes; + int signal_index; + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; + + signalTypes.clear(); + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } + } else { signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - } - if (signal_index < 0) { - // re-use tmp_signal_name and signal from above + if (signal_index < 0) { + // check for normalized signatures + tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); + signal = tmp_signal_name.constData() + 1; + + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) { + // re-use tmp_signal_name and signal from above - smeta = sender->metaObject(); - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + smeta = sender->metaObject(); + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } + } } if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); @@ -2317,36 +2361,71 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign const char *method_arg = method; ++method; // skip code + QByteArray methodName; + QArgumentTypeArray methodTypes; const QMetaObject *rmeta = receiver->metaObject(); int method_index_relative = -1; - switch (membcode) { - case QSLOT_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - break; - case QSIGNAL_CODE: - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - break; - } - - if (method_index_relative < 0) { - // check for normalized methods - tmp_method_name = QMetaObject::normalizedSignature(method); - method = tmp_method_name.constData(); - - // rmeta may have been modified above - rmeta = receiver->metaObject(); + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); + + methodTypes.clear(); + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative( + &rmeta, methodName, methodTypes.size(), methodTypes.constData()); + break; + } + } + } else { switch (membcode) { case QSLOT_CODE: method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); break; case QSIGNAL_CODE: method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); - if (method_index_relative < 0) - method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); break; } + + if (method_index_relative < 0) { + // check for normalized methods + tmp_method_name = QMetaObject::normalizedSignature(method); + method = tmp_method_name.constData(); + + // rmeta may have been modified above + rmeta = receiver->metaObject(); + switch (membcode) { + case QSLOT_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); + break; + case QSIGNAL_CODE: + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); + if (method_index_relative < 0) + method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); + break; + } + } } if (method_index_relative < 0) { @@ -2355,7 +2434,18 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal, method)) { + bool compatibleArgs = true; + if ((QMetaObjectPrivate::get(smeta)->revision < 7) && (QMetaObjectPrivate::get(rmeta)->revision < 7)) { + compatibleArgs = QMetaObject::checkConnectArgs(signal, method); + } else { + if (signalName.isEmpty()) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + if (methodName.isEmpty()) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); + compatibleArgs = QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(), + methodTypes.size(), methodTypes.constData()); + } + if (!compatibleArgs) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, @@ -2365,8 +2455,11 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign int *types = 0; if ((type == Qt::QueuedConnection) - && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) + && ((QMetaObjectPrivate::get(smeta)->revision >= 7) + ? !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size())) + : !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))) { return QMetaObject::Connection(0); + } #ifndef QT_NO_DEBUG if (warnCompat) { @@ -2424,17 +2517,17 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho || method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", - signal.signature(), + signal.methodSignature().constData(), receiver ? receiver->metaObject()->className() : "(null)", - method.signature() ); + method.methodSignature().constData() ); return QMetaObject::Connection(0); } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); int signal_index; int method_index; @@ -2448,20 +2541,20 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho const QMetaObject *rmeta = receiver->metaObject(); if (signal_index == -1) { qWarning("QObject::connect: Can't find signal %s on instance of class %s", - signal.signature(), smeta->className()); + signal.methodSignature().constData(), smeta->className()); return QMetaObject::Connection(0); } if (method_index == -1) { qWarning("QObject::connect: Can't find method %s on instance of class %s", - method.signature(), rmeta->className()); + method.methodSignature().constData(), rmeta->className()); return QMetaObject::Connection(0); } - if (!QMetaObject::checkConnectArgs(signal.signature(), method.signature())) { + if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(), method.methodSignature().constData())) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", - smeta->className(), signal.signature(), - rmeta->className(), method.signature()); + smeta->className(), signal.methodSignature().constData(), + rmeta->className(), method.methodSignature().constData()); return QMetaObject::Connection(0); } @@ -2609,12 +2702,25 @@ bool QObject::disconnect(const QObject *sender, const char *signal, */ bool res = false; const QMetaObject *smeta = sender->metaObject(); + QByteArray signalName; + QArgumentTypeArray signalTypes; + if (signal && (QMetaObjectPrivate::get(smeta)->revision >= 7)) + signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes); + QByteArray methodName; + QArgumentTypeArray methodTypes; + if (method && (QMetaObjectPrivate::get(receiver->metaObject())->revision >= 7)) + methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes); do { int signal_index = -1; if (signal) { - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); - if (signal_index < 0) - signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + if (QMetaObjectPrivate::get(smeta)->revision >= 7) { + signal_index = QMetaObjectPrivate::indexOfSignalRelative( + &smeta, signalName, signalTypes.size(), signalTypes.constData()); + } else { + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); + if (signal_index < 0) + signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); + } if (signal_index < 0) break; signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); @@ -2629,7 +2735,13 @@ bool QObject::disconnect(const QObject *sender, const char *signal, } else { const QMetaObject *rmeta = receiver->metaObject(); do { - int method_index = rmeta->indexOfMethod(method); + int method_index; + if (QMetaObjectPrivate::get(rmeta)->revision >= 7) { + method_index = QMetaObjectPrivate::indexOfMethod( + rmeta, methodName, methodTypes.size(), methodTypes.constData()); + } else { + method_index = rmeta->indexOfMethod(method); + } if (method_index >= 0) while (method_index < rmeta->methodOffset()) rmeta = rmeta->superClass(); @@ -2693,24 +2805,24 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, if(signal.methodType() != QMetaMethod::Signal) { qWarning("QObject::%s: Attempt to %s non-signal %s::%s", "disconnect","unbind", - sender->metaObject()->className(), signal.signature()); + sender->metaObject()->className(), signal.methodSignature().constData()); return false; } } if (method.mobj) { if(method.methodType() == QMetaMethod::Constructor) { qWarning("QObject::disconect: cannot use constructor as argument %s::%s", - receiver->metaObject()->className(), method.signature()); + receiver->metaObject()->className(), method.methodSignature().constData()); return false; } } - // Reconstructing SIGNAL() macro result for signal.signature() string + // Reconstructing SIGNAL() macro result for signal.methodSignature() string QByteArray signalSignature; if (signal.mobj) { - signalSignature.reserve(qstrlen(signal.signature())+1); + signalSignature.reserve(signal.methodSignature().size()+1); signalSignature.append((char)(QSIGNAL_CODE + '0')); - signalSignature.append(signal.signature()); + signalSignature.append(signal.methodSignature()); } int signal_index; @@ -2724,13 +2836,13 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal, // is -1 then this signal is not a member of sender. if (signal.mobj && signal_index == -1) { qWarning("QObject::disconect: signal %s not found on class %s", - signal.signature(), sender->metaObject()->className()); + signal.methodSignature().constData(), sender->metaObject()->className()); return false; } // If this condition is true then method is not a member of receeiver. if (receiver && method.mobj && method_index == -1) { qWarning("QObject::disconect: method %s not found on class %s", - method.signature(), receiver->metaObject()->className()); + method.methodSignature().constData(), receiver->metaObject()->className()); return false; } @@ -3045,7 +3157,8 @@ void QMetaObject::connectSlotsByName(QObject *o) Q_ASSERT(mo); const QObjectList list = o->findChildren<QObject *>(QString()); for (int i = 0; i < mo->methodCount(); ++i) { - const char *slot = mo->method(i).signature(); + QByteArray slotSignature = mo->method(i).methodSignature(); + const char *slot = slotSignature.constData(); Q_ASSERT(slot); if (slot[0] != 'o' || slot[1] != 'n' || slot[2] != '_') continue; @@ -3065,7 +3178,7 @@ void QMetaObject::connectSlotsByName(QObject *o) if (method.methodType() != QMetaMethod::Signal) continue; - if (!qstrncmp(method.signature(), slot + len + 4, slotlen)) { + if (!qstrncmp(method.methodSignature().constData(), slot + len + 4, slotlen)) { int signalOffset, methodOffset; computeOffsets(method.enclosingMetaObject(), &signalOffset, &methodOffset); sigIndex = k + - methodOffset + signalOffset; @@ -3311,9 +3424,17 @@ int QObjectPrivate::signalIndex(const char *signalName) const { Q_Q(const QObject); const QMetaObject *base = q->metaObject(); - int relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); - if (relative_index < 0) - relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + int relative_index; + if (QMetaObjectPrivate::get(base)->revision >= 7) { + QArgumentTypeArray types; + QByteArray name = QMetaObjectPrivate::decodeMethodSignature(signalName, types); + relative_index = QMetaObjectPrivate::indexOfSignalRelative( + &base, name, types.size(), types.constData()); + } else { + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, false); + if (relative_index < 0) + relative_index = QMetaObjectPrivate::indexOfSignalRelative(&base, signalName, true); + } if (relative_index < 0) return relative_index; relative_index = QMetaObjectPrivate::originalClone(base, relative_index); @@ -3536,7 +3657,7 @@ void QObject::dumpObjectInfo() offset = methodOffset - signalOffset; } const QMetaMethod signal = metaObject()->method(signal_index + offset); - qDebug(" signal: %s", signal.signature()); + qDebug(" signal: %s", signal.methodSignature().constData()); // receivers const QObjectPrivate::Connection *c = @@ -3552,7 +3673,7 @@ void QObject::dumpObjectInfo() qDebug(" --> %s::%s %s", receiverMetaObject->className(), c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()), - method.signature()); + method.methodSignature().constData()); c = c->nextConnectionList; } } @@ -3569,7 +3690,7 @@ void QObject::dumpObjectInfo() qDebug(" <-- %s::%s %s", s->sender->metaObject()->className(), s->sender->objectName().isEmpty() ? "unnamed" : qPrintable(s->sender->objectName()), - slot.signature()); + slot.methodSignature().constData()); } } else { qDebug(" <None>"); @@ -4041,9 +4162,16 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa locker.unlock(); // reconstruct the signature to call connectNotify - const char *sig = senderMetaObject->d.stringdata + senderMetaObject->d.data[ - reinterpret_cast<const QMetaObjectPrivate*>(senderMetaObject->d.data)->methodData - + 5 * (signal_index - signalOffset)]; + QByteArray tmp_sig; + const char *sig; + if (QMetaObjectPrivate::get(senderMetaObject)->revision >= 7) { + tmp_sig = senderMetaObject->method(signal_index - signalOffset + methodOffset).methodSignature(); + sig = tmp_sig.constData(); + } else { + sig = reinterpret_cast<const char *>(senderMetaObject->d.stringdata) + + senderMetaObject->d.data[QMetaObjectPrivate::get(senderMetaObject)->methodData + + 5 * (signal_index - signalOffset)]; + } QVarLengthArray<char> signalSignature(qstrlen(sig) + 2); signalSignature.data()[0] = char(QSIGNAL_CODE + '0'); strcpy(signalSignature.data() + 1 , sig); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 4b3829b53e..4c98bad0f9 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -50,11 +50,12 @@ QT_BEGIN_NAMESPACE class QByteArray; +struct QByteArrayData; class QString; #ifndef Q_MOC_OUTPUT_REVISION -#define Q_MOC_OUTPUT_REVISION 64 +#define Q_MOC_OUTPUT_REVISION 65 #endif // The following macros are our "extensions" to C++ @@ -326,6 +327,8 @@ struct Q_CORE_EXPORT QMetaObject QMetaProperty userProperty() const; static bool checkConnectArgs(const char *signal, const char *method); + static bool checkConnectArgs(const QMetaMethod &signal, + const QMetaMethod &method); static QByteArray normalizedSignature(const char *method); static QByteArray normalizedType(const char *type); @@ -439,7 +442,7 @@ struct Q_CORE_EXPORT QMetaObject struct { // private data const QMetaObject *superdata; - const char *stringdata; + const QByteArrayData *stringdata; const uint *data; const void *extradata; } d; @@ -480,9 +483,6 @@ struct QMetaObjectExtraData StaticMetacallFunction static_metacall; }; -inline const char *QMetaObject::className() const -{ return d.stringdata; } - inline const QMetaObject *QMetaObject::superClass() const { return d.superdata; } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 029a261faf..39e8b4360f 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1697,7 +1697,7 @@ void QVariant::load(QDataStream &s) QByteArray name; s >> name; typeId = QMetaType::type(name.constData()); - if (!typeId) { + if (typeId == QMetaType::UnknownType) { s.setStatus(QDataStream::ReadCorruptData); return; } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 5da482d5cd..4d4b2d51ab 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -126,7 +126,7 @@ class Q_CORE_EXPORT QVariant { public: enum Type { - Invalid = QMetaType::Void, + Invalid = QMetaType::UnknownType, Bool = QMetaType::Bool, Int = QMetaType::Int, UInt = QMetaType::UInt, diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 65d346c470..90756d76cc 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -187,7 +187,11 @@ public: return FilteredComparator<T>::compare(m_a, m_b); } - bool delegate(const void*) { return true; } + bool delegate(const void*) { Q_ASSERT(false); return true; } + bool delegate(const QMetaTypeSwitcher::UnknownType*) + { + return true; // for historical reason invalid variant == invalid variant + } bool delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return false; } protected: const QVariant::Private *m_a; @@ -301,7 +305,8 @@ public: return CallIsNull<T>::isNull(m_d); } // we need that as sizof(void) is undefined and it is needed in HasIsNullMethod - bool delegate(const void *) { return m_d->is_null; } + bool delegate(const void *) { Q_ASSERT(false); return m_d->is_null; } + bool delegate(const QMetaTypeSwitcher::UnknownType *) { return m_d->is_null; } bool delegate(const QMetaTypeSwitcher::NotBuiltinType *) { return m_d->is_null; } protected: const QVariant::Private *m_d; @@ -374,8 +379,18 @@ public: void delegate(const void*) { - // QMetaType::Void == QVariant::Invalid, creating an invalid value creates invalid QVariant - // TODO it might go away, check is needed + qWarning("Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead"); + m_x->type = QMetaType::UnknownType; + m_x->is_shared = false; + m_x->is_null = !m_copy; + } + + void delegate(const QMetaTypeSwitcher::UnknownType*) + { + if (m_x->type != QMetaType::UnknownType) { + qWarning("Trying to construct an instance of an invalid type, type id: %i", m_x->type); + m_x->type = QMetaType::UnknownType; + } m_x->is_shared = false; m_x->is_null = !m_copy; } @@ -421,7 +436,8 @@ public: qWarning("Trying to destruct an instance of an invalid type, type id: %i", m_d->type); } // Ignore nonconstructible type - void delegate(const void*) {} + void delegate(const QMetaTypeSwitcher::UnknownType*) {} + void delegate(const void*) { Q_ASSERT(false); } private: QVariant::Private *m_d; }; @@ -466,10 +482,11 @@ public: { qWarning("Trying to stream an instance of an invalid type, type id: %i", m_d->type); } - void delegate(const void*) + void delegate(const QMetaTypeSwitcher::UnknownType*) { m_debugStream.nospace() << "QVariant::Invalid"; } + void delegate(const void*) { Q_ASSERT(false); } private: QDebug m_debugStream; QVariant::Private *m_d; diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 7ff005f9a1..be96d895a2 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1657,7 +1657,7 @@ void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalInd #ifdef QSTATEMACHINE_DEBUG qDebug() << q_func() << ": sending signal event ( sender =" << sender - << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')'; + << ", signal =" << sender->metaObject()->method(signalIndex).methodSignature().constData() << ')'; #endif postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs)); processEvents(DirectProcessing); @@ -2211,10 +2211,29 @@ void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation) // Begin moc-generated code -- modify carefully (check "HAND EDIT" parts)! +struct qt_meta_stringdata_QSignalEventGenerator_t { + QByteArrayData data[3]; + char stringdata[32]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) { \ + Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ + offsetof(qt_meta_stringdata_QSignalEventGenerator_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + } +static const qt_meta_stringdata_QSignalEventGenerator_t qt_meta_stringdata_QSignalEventGenerator = { + { +QT_MOC_LITERAL(0, 0, 21), +QT_MOC_LITERAL(1, 22, 7), +QT_MOC_LITERAL(2, 30, 0) + }, + "QSignalEventGenerator\0execute\0\0" +}; +#undef QT_MOC_LITERAL + static const uint qt_meta_data_QSignalEventGenerator[] = { // content: - 6, // revision + 7, // revision 0, // classname 0, 0, // classinfo 1, 14, // methods @@ -2224,14 +2243,13 @@ static const uint qt_meta_data_QSignalEventGenerator[] = { 0, // flags 0, // signalCount - // slots: signature, parameters, type, tag, flags - 23, 22, 22, 22, 0x0a, + // slots: name, argc, parameters, tag, flags + 1, 0, 19, 2, 0x0a, - 0 // eod -}; + // slots: parameters + QMetaType::Void, -static const char qt_meta_stringdata_QSignalEventGenerator[] = { - "QSignalEventGenerator\0\0execute()\0" + 0 // eod }; void QSignalEventGenerator::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) @@ -2252,7 +2270,7 @@ const QMetaObjectExtraData QSignalEventGenerator::staticMetaObjectExtraData = { }; const QMetaObject QSignalEventGenerator::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator, + { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator.data, qt_meta_data_QSignalEventGenerator, &staticMetaObjectExtraData } }; @@ -2264,7 +2282,7 @@ const QMetaObject *QSignalEventGenerator::metaObject() const void *QSignalEventGenerator::qt_metacast(const char *_clname) { if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator)) + if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator.stringdata)) return static_cast<void*>(const_cast< QSignalEventGenerator*>(this)); return QObject::qt_metacast(_clname); } diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp new file mode 100644 index 0000000000..8498d0e4d5 --- /dev/null +++ b/src/corelib/tools/qarraydata.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qarraydata.h> +#include <QtCore/private/qtools_p.h> + +#include <stdlib.h> + +QT_BEGIN_NAMESPACE + +const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + +static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; +static const QArrayData qt_array_unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + +QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocationOptions options) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + + // Don't allocate empty headers + if (!(options & RawData) && !capacity) + return !(options & Unsharable) + ? const_cast<QArrayData *>(&qt_array_empty) + : const_cast<QArrayData *>(&qt_array_unsharable_empty); + + size_t headerSize = sizeof(QArrayData); + + // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we + // can properly align the data array. This assumes malloc is able to + // provide appropriate alignment for the header -- as it should! + // Padding is skipped when allocating a header for RawData. + if (!(options & RawData)) + headerSize += (alignment - Q_ALIGNOF(QArrayData)); + + // Allocate additional space if array is growing + if (options & Grow) + capacity = qAllocMore(objectSize * capacity, headerSize) / objectSize; + + size_t allocSize = headerSize + objectSize * capacity; + + QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); + if (header) { + quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) + & ~(alignment - 1); + + header->ref.atomic.store(bool(!(options & Unsharable))); + header->size = 0; + header->alloc = capacity; + header->capacityReserved = bool(options & CapacityReserved); + header->offset = data - quintptr(header); + } + + return header; +} + +void QArrayData::deallocate(QArrayData *data, size_t objectSize, + size_t alignment) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + Q_UNUSED(objectSize) Q_UNUSED(alignment) + + if (data == &qt_array_unsharable_empty) + return; + + ::free(data); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h new file mode 100644 index 0000000000..351a75aade --- /dev/null +++ b/src/corelib/tools/qarraydata.h @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATA_H +#define QARRAYDATA_H + +#include <QtCore/qrefcount.h> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +struct Q_CORE_EXPORT QArrayData +{ + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; // in bytes from beginning of header + + void *data() + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<char *>(this) + offset; + } + + const void *data() const + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<const char *>(this) + offset; + } + + // This refers to array data mutability, not "header data" represented by + // data members in QArrayData. Shared data (array and header) must still + // follow COW principles. + bool isMutable() const + { + return alloc != 0; + } + + enum AllocationOption { + CapacityReserved = 0x1, + Unsharable = 0x2, + RawData = 0x4, + Grow = 0x8, + + Default = 0 + }; + + Q_DECLARE_FLAGS(AllocationOptions, AllocationOption) + + AllocationOptions detachFlags() const + { + AllocationOptions result; + if (!ref.isSharable()) + result |= Unsharable; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + AllocationOptions cloneFlags() const + { + AllocationOptions result; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + static QArrayData *allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocationOptions options = Default) + Q_REQUIRED_RESULT; + static void deallocate(QArrayData *data, size_t objectSize, + size_t alignment); + + static const QArrayData shared_null; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocationOptions) + +template <class T> +struct QTypedArrayData + : QArrayData +{ + typedef T *iterator; + typedef const T *const_iterator; + + T *data() { return static_cast<T *>(QArrayData::data()); } + const T *data() const { return static_cast<const T *>(QArrayData::data()); } + + T *begin() { return data(); } + T *end() { return data() + size; } + const T *begin() const { return data(); } + const T *end() const { return data() + size; } + + class AlignmentDummy { QArrayData header; T data; }; + + static QTypedArrayData *allocate(size_t capacity, + AllocationOptions options = Default) Q_REQUIRED_RESULT + { + return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T), + Q_ALIGNOF(AlignmentDummy), capacity, options)); + } + + static void deallocate(QArrayData *data) + { + QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy)); + } + + static QTypedArrayData *fromRawData(const T *data, size_t n, + AllocationOptions options = Default) + { + QTypedArrayData *result = allocate(0, options | RawData); + if (result) { + Q_ASSERT(!result->ref.isShared()); // No shared empty, please! + + result->offset = reinterpret_cast<const char *>(data) + - reinterpret_cast<const char *>(result); + result->size = n; + } + return result; + } + + static QTypedArrayData *sharedNull() + { + return static_cast<QTypedArrayData *>( + const_cast<QArrayData *>(&QArrayData::shared_null)); + } +}; + +template <class T, size_t N> +struct QStaticArrayData +{ + QArrayData header; + T data[N]; +}; + +// Support for returning QArrayDataPointer<T> from functions +template <class T> +struct QArrayDataPointerRef +{ + QTypedArrayData<T> *ptr; +}; + +#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ + Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, \ + (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ + & ~(Q_ALIGNOF(type) - 1) } \ + /**/ + +//////////////////////////////////////////////////////////////////////////////// +// Q_ARRAY_LITERAL + +// The idea here is to place a (read-only) copy of header and array data in an +// mmappable portion of the executable (typically, .rodata section). This is +// accomplished by hiding a static const instance of QStaticArrayData, which is +// POD. + +#if defined(Q_COMPILER_VARIADIC_MACROS) +#if defined(Q_COMPILER_LAMBDA) +// Hide array inside a lambda +#define Q_ARRAY_LITERAL(Type, ...) \ + ([]() -> QArrayDataPointerRef<Type> { \ + /* MSVC 2010 Doesn't support static variables in a lambda, but */ \ + /* happily accepts them in a static function of a lambda-local */ \ + /* struct :-) */ \ + struct StaticWrapper { \ + static QArrayDataPointerRef<Type> get() \ + { \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + return ref; \ + } \ + }; \ + return StaticWrapper::get(); \ + }()) \ + /**/ +#elif defined(Q_CC_GNU) +// Hide array within GCC's __extension__ {( )} block +#define Q_ARRAY_LITERAL(Type, ...) \ + __extension__ ({ \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + ref; \ + }) \ + /**/ +#endif +#endif // defined(Q_COMPILER_VARIADIC_MACROS) + +#if defined(Q_ARRAY_LITERAL) +#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ + union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + \ + /* Portable compile-time array size computation */ \ + Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + enum { Size = sizeof(data) / sizeof(data[0]) }; \ + \ + static const QStaticArrayData<Type, Size> literal = { \ + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \ + \ + QArrayDataPointerRef<Type> ref = \ + { static_cast<QTypedArrayData<Type> *>( \ + const_cast<QArrayData *>(&literal.header)) }; \ + /**/ +#else +// As a fallback, memory is allocated and data copied to the heap. + +// The fallback macro does NOT use variadic macros and does NOT support +// variable number of arguments. It is suitable for char arrays. + +namespace QtPrivate { + template <class T, size_t N> + inline QArrayDataPointerRef<T> qMakeArrayLiteral(const T (&array)[N]) + { + union { T type_must_be_POD; } dummy; Q_UNUSED(dummy) + + QArrayDataPointerRef<T> result = { QTypedArrayData<T>::allocate(N) }; + Q_CHECK_PTR(result.ptr); + + ::memcpy(result.ptr->data(), array, N * sizeof(T)); + result.ptr->size = N; + + return result; + } +} + +#define Q_ARRAY_LITERAL(Type, Array) \ + QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)<Type>( Array ) +#endif // !defined(Q_ARRAY_LITERAL) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h new file mode 100644 index 0000000000..1b8ed3372d --- /dev/null +++ b/src/corelib/tools/qarraydataops.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAOPS_H +#define QARRAYDATAOPS_H + +#include <QtCore/qarraydata.h> + +#include <new> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + +template <class T> +struct QPodArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memcpy(this->end(), b, (e - b) * sizeof(T)); + this->size += e - b; + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) + ::memcpy(iter, &t, sizeof(T)); + this->size += n; + } + + void destroyAll() // Call from destructors, ONLY! + { + Q_ASSERT(this->ref.atomic.load() == 0); + + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memmove(where + (e - b), where, (this->end() - where) * sizeof(T)); + ::memcpy(where, b, (e - b) * sizeof(T)); + this->size += (e - b); + } +}; + +template <class T> +struct QGenericArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + T *iter = this->end(); + for (; b != e; ++iter, ++b) { + new (iter) T(*b); + ++this->size; + } + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) { + new (iter) T(t); + ++this->size; + } + } + + void destroyAll() // Call from destructors, ONLY + { + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + + Q_ASSERT(this->ref.atomic.load() == 0); + + const T *const b = this->begin(); + const T *i = this->end(); + + while (i != b) + (--i)->~T(); + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Array may be truncated at where in case of exceptions + + T *const end = this->end(); + const T *readIter = end; + T *writeIter = end + (e - b); + + const T *const step1End = where + qMax(e - b, end - where); + + struct Destructor + { + Destructor(T *&it) + : iter(&it) + , end(it) + { + } + + void commit() + { + iter = &end; + } + + ~Destructor() + { + for (; *iter != end; --*iter) + (*iter)->~T(); + } + + T **iter; + T *end; + } destroyer(writeIter); + + // Construct new elements in array + do { + --readIter, --writeIter; + new (writeIter) T(*readIter); + } while (writeIter != step1End); + + while (writeIter != end) { + --e, --writeIter; + new (writeIter) T(*e); + } + + destroyer.commit(); + this->size += destroyer.end - end; + + // Copy assign over existing elements + while (readIter != where) { + --readIter, --writeIter; + *writeIter = *readIter; + } + + while (writeIter != where) { + --e, --writeIter; + *writeIter = *e; + } + } +}; + +template <class T> +struct QMovableArrayOps + : QGenericArrayOps<T> +{ + // using QGenericArrayOps<T>::copyAppend; + // using QGenericArrayOps<T>::destroyAll; + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Provides strong exception safety guarantee, + // provided T::~T() nothrow + + struct ReversibleDisplace + { + ReversibleDisplace(T *start, T *finish, size_t diff) + : begin(start) + , end(finish) + , displace(diff) + { + ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); + } + + void commit() { displace = 0; } + + ~ReversibleDisplace() + { + if (displace) + ::memmove(begin, begin + displace, (end - begin) * sizeof(T)); + } + + T *const begin; + T *const end; + size_t displace; + + } displace(where, this->end(), size_t(e - b)); + + struct CopyConstructor + { + CopyConstructor(T *w) : where(w) {} + + void copy(const T *src, const T *const srcEnd) + { + n = 0; + for (; src != srcEnd; ++src) { + new (where + n) T(*src); + ++n; + } + n = 0; + } + + ~CopyConstructor() + { + while (n) + where[--n].~T(); + } + + T *const where; + size_t n; + } copier(where); + + copier.copy(b, e); + displace.commit(); + this->size += (e - b); + } +}; + +template <class T, class = void> +struct QArrayOpsSelector +{ + typedef QGenericArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + !QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QPodArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QMovableArrayOps<T> Type; +}; + +} // namespace QtPrivate + +template <class T> +struct QArrayDataOps + : QtPrivate::QArrayOpsSelector<T>::Type +{ +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h new file mode 100644 index 0000000000..f5ad53aa54 --- /dev/null +++ b/src/corelib/tools/qarraydatapointer.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAPOINTER_H +#define QARRAYDATAPOINTER_H + +#include <QtCore/qarraydataops.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +template <class T> +struct QArrayDataPointer +{ +private: + typedef QTypedArrayData<T> Data; + typedef QArrayDataOps<T> DataOps; + +public: + QArrayDataPointer() + : d(Data::sharedNull()) + { + } + + QArrayDataPointer(const QArrayDataPointer &other) + : d(other.d->ref.ref() + ? other.d + : other.clone(other.d->cloneFlags())) + { + } + + explicit QArrayDataPointer(QTypedArrayData<T> *ptr) + : d(ptr) + { + Q_CHECK_PTR(ptr); + } + + QArrayDataPointer(QArrayDataPointerRef<T> ref) + : d(ref.ptr) + { + } + + QArrayDataPointer &operator=(const QArrayDataPointer &other) + { + QArrayDataPointer tmp(other); + this->swap(tmp); + return *this; + } + +#ifdef Q_COMPILER_RVALUE_REFS + QArrayDataPointer(QArrayDataPointer &&other) + : d(other.d) + { + other.d = Data::sharedNull(); + } + + QArrayDataPointer &operator=(QArrayDataPointer &&other) + { + this->swap(other); + return *this; + } +#endif + + DataOps &operator*() const + { + Q_ASSERT(d); + return *static_cast<DataOps *>(d); + } + + DataOps *operator->() const + { + Q_ASSERT(d); + return static_cast<DataOps *>(d); + } + + ~QArrayDataPointer() + { + if (!d->ref.deref()) { + (*this)->destroyAll(); + Data::deallocate(d); + } + } + + bool isNull() const + { + return d == Data::sharedNull(); + } + + Data *data() const + { + return d; + } + + void setSharable(bool sharable) + { + // Can't call setSharable on static read-only data, like shared_null + // and the internal shared-empties. + if (d->alloc == 0 && d->size == 0) { + d = Data::allocate(0, sharable + ? QArrayData::Default + : QArrayData::Unsharable); + return; + } + + detach(); + d->ref.setSharable(sharable); + } + + void swap(QArrayDataPointer &other) + { + qSwap(d, other.d); + } + + void clear() + { + QArrayDataPointer tmp(d); + d = Data::sharedNull(); + } + + bool detach() + { + if (!d->isMutable() || d->ref.isShared()) { + Data *copy = clone(d->detachFlags()); + QArrayDataPointer old(d); + d = copy; + return true; + } + + return false; + } + +private: + Data *clone(QArrayData::AllocationOptions options) const Q_REQUIRED_RESULT + { + QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, + options)); + if (d->size) + copy->copyAppend(d->begin(), d->end()); + + Data *result = copy.d; + copy.d = Data::sharedNull(); + return result; + } + + Data *d; +}; + +template <class T> +inline bool operator==(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() == rhs.data(); +} + +template <class T> +inline bool operator!=(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() != rhs.data(); +} + +template <class T> +inline void qSwap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) +{ + p1.swap(p2); +} + +QT_END_NAMESPACE + +namespace std +{ + template <class T> + inline void swap( + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p1, + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p2) + { + p1.swap(p2); + } +} + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index b6719c9536..ac936b1d0a 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -57,7 +57,7 @@ #include <string.h> #include <stdlib.h> -#define IS_RAW_DATA(d) ((d)->offset != 0) +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData)) QT_BEGIN_NAMESPACE @@ -69,24 +69,25 @@ int qFindByteArray( int qAllocMore(int alloc, int extra) { - if (alloc == 0 && extra == 0) - return 0; - const int page = 1 << 12; - int nalloc; - alloc += extra; - if (alloc < 1<<6) { - nalloc = (1<<3) + ((alloc >>3) << 3); - } else { - // don't do anything if the loop will overflow signed int. - if (alloc >= INT_MAX/2) - return INT_MAX; - nalloc = (alloc < page) ? 1 << 3 : page; - while (nalloc < alloc) { - if (nalloc <= 0) - return INT_MAX; - nalloc *= 2; - } - } + Q_ASSERT(alloc >= 0 && extra >= 0); + Q_ASSERT(alloc < (1 << 30) - extra); + + unsigned nalloc = alloc + extra; + + // Round up to next power of 2 + + // Assuming container is growing, always overshoot + //--nalloc; + + nalloc |= nalloc >> 1; + nalloc |= nalloc >> 2; + nalloc |= nalloc >> 4; + nalloc |= nalloc >> 8; + nalloc |= nalloc >> 16; + ++nalloc; + + Q_ASSERT(nalloc > unsigned(alloc + extra)); + return nalloc - extra; } @@ -554,7 +555,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) } d.take(); // realloc was successful d.reset(p); - d->offset = 0; + d->offset = sizeof(QByteArrayData); int res = ::uncompress((uchar*)d->data(), &len, (uchar*)data+4, nbytes-4); @@ -576,11 +577,11 @@ QByteArray qUncompress(const uchar* data, int nbytes) d.take(); // realloc was successful d.reset(p); } - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[len] = 0; return QByteArray(d.take(), 0, 0); @@ -614,10 +615,10 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; /*! \class QByteArray @@ -904,7 +905,7 @@ QByteArray &QByteArray::operator=(const char *str) x = const_cast<Data *>(&shared_empty.ba); } else { int len = qstrlen(str); - if (d->ref != 1 || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) + if (d->ref.isShared() || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) realloc(len); x = d; memcpy(x->data(), str, len + 1); // include null terminator @@ -1289,38 +1290,16 @@ void QByteArray::chop(int n) \sa isEmpty() */ -/*! \fn QByteArray::QByteArray(const char *str) - - Constructs a byte array initialized with the string \a str. - - QByteArray makes a deep copy of the string data. -*/ - -QByteArray::QByteArray(const char *str) -{ - if (!str) { - d = const_cast<Data *>(&shared_null.ba); - } else if (!*str) { - d = const_cast<Data *>(&shared_empty.ba); - } else { - int len = qstrlen(str); - d = static_cast<Data *>(malloc(sizeof(Data) + len + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = len; - d->alloc = len; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), str, len+1); // include null terminator - } -} - /*! Constructs a byte array containing the first \a size bytes of array \a data. If \a data is 0, a null byte array is constructed. + If \a size is negative, \a data is assumed to point to a nul-terminated + string and its length is determined dynamically. The terminating + nul-character is not considered part of the byte array. + QByteArray makes a deep copy of the string data. \sa fromRawData() @@ -1330,18 +1309,22 @@ QByteArray::QByteArray(const char *data, int size) { if (!data) { d = const_cast<Data *>(&shared_null.ba); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.ba); } else { - d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), data, size); - d->data()[size] = '\0'; + if (size < 0) + size = strlen(data); + if (!size) { + d = const_cast<Data *>(&shared_empty.ba); + } else { + d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = sizeof(QByteArrayData); + memcpy(d->data(), data, size); + d->data()[size] = '\0'; + } } } @@ -1359,11 +1342,11 @@ QByteArray::QByteArray(int size, char ch) } else { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); memset(d->data(), ch, size); d->data()[size] = '\0'; } @@ -1379,11 +1362,11 @@ QByteArray::QByteArray(int size, Qt::Initialization) { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[size] = '\0'; } @@ -1405,7 +1388,7 @@ void QByteArray::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1426,15 +1409,15 @@ void QByteArray::resize(int size) // Data *x = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = size; x->capacityReserved = false; - x->offset = 0; + x->offset = sizeof(QByteArrayData); x->data()[size] = '\0'; d = x; } else { - if (d->ref != 1 || size > int(d->alloc) + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(qAllocMore(size, sizeof(Data))); if (int(d->alloc) >= size) { @@ -1465,14 +1448,14 @@ QByteArray &QByteArray::fill(char ch, int size) void QByteArray::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast<Data *>(malloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = alloc; x->capacityReserved = d->capacityReserved; - x->offset = 0; + x->offset = sizeof(QByteArrayData); ::memcpy(x->data(), d->data(), x->size); x->data()[x->size] = '\0'; if (!d->ref.deref()) @@ -1482,7 +1465,7 @@ void QByteArray::realloc(int alloc) Data *x = static_cast<Data *>(::realloc(d, sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->alloc = alloc; - x->offset = 0; + x->offset = sizeof(QByteArrayData); d = x; } } @@ -1504,7 +1487,7 @@ void QByteArray::expand(int i) QByteArray QByteArray::nulTerminated() const { // is this fromRawData? - if (!d->offset) + if (!IS_RAW_DATA(d)) return *this; // no, then we're sure we're zero terminated QByteArray copy(*this); @@ -1566,7 +1549,7 @@ QByteArray &QByteArray::prepend(const char *str) QByteArray &QByteArray::prepend(const char *str, int len) { if (str) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memmove(d->data()+len, d->data(), d->size); memcpy(d->data(), str, len); @@ -1584,7 +1567,7 @@ QByteArray &QByteArray::prepend(const char *str, int len) QByteArray &QByteArray::prepend(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); memmove(d->data()+1, d->data(), d->size); d->data()[0] = ch; @@ -1622,7 +1605,7 @@ QByteArray &QByteArray::append(const QByteArray &ba) if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; } else if (ba.d != &shared_null.ba) { - if (d->ref != 1 || d->size + ba.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + ba.d->size > int(d->alloc)) realloc(qAllocMore(d->size + ba.d->size, sizeof(Data))); memcpy(d->data() + d->size, ba.d->data(), ba.d->size); d->size += ba.d->size; @@ -1656,7 +1639,7 @@ QByteArray& QByteArray::append(const char *str) { if (str) { int len = qstrlen(str); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len + 1); // include null terminator d->size += len; @@ -1681,7 +1664,7 @@ QByteArray &QByteArray::append(const char *str, int len) if (len < 0) len = qstrlen(str); if (str && len) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len); // include null terminator d->size += len; @@ -1698,7 +1681,7 @@ QByteArray &QByteArray::append(const char *str, int len) QByteArray& QByteArray::append(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); d->data()[d->size++] = ch; d->data()[d->size] = '\0'; @@ -3889,11 +3872,11 @@ QByteArray QByteArray::fromRawData(const char *data, int size) } else { x = static_cast<Data *>(malloc(sizeof(Data) + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = data - (x->d + sizeof(qptrdiff)); + x->offset = data - reinterpret_cast<char *>(x); } return QByteArray(x, 0, 0); } @@ -3914,14 +3897,14 @@ QByteArray QByteArray::fromRawData(const char *data, int size) */ QByteArray &QByteArray::setRawData(const char *data, uint size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(data, size); } else { if (data) { d->size = size; - d->offset = data - (d->d + sizeof(qptrdiff)); + d->offset = data - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->size = 0; *d->data() = 0; } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 09c43988fd..e65a9201c2 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -124,32 +124,31 @@ struct QByteArrayData int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - char d[sizeof(qptrdiff)]; - }; - inline char *data() { return d + sizeof(qptrdiff) + offset; } - inline const char *data() const { return d + sizeof(qptrdiff) + offset; } + + qptrdiff offset; + + inline char *data() { return reinterpret_cast<char *>(this) + offset; } + inline const char *data() const { return reinterpret_cast<const char *>(this) + offset; } }; -template<int N> struct QConstByteArrayData +template<int N> struct QStaticByteArrayData { - const QByteArrayData ba; - const char data[N + 1]; + QByteArrayData ba; + char data[N + 1]; }; -template<int N> struct QConstByteArrayDataPtr +template<int N> struct QStaticByteArrayDataPtr { - const QConstByteArrayData<N> *ptr; + const QStaticByteArrayData<N> *ptr; }; #if defined(Q_COMPILER_LAMBDA) -# define QByteArrayLiteral(str) ([]() -> QConstByteArrayDataPtr<sizeof(str) - 1> { \ +# define QByteArrayLiteral(str) ([]() -> QStaticByteArrayDataPtr<sizeof(str) - 1> { \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ return holder; }()) #elif defined(Q_CC_GNU) @@ -160,9 +159,9 @@ template<int N> struct QConstByteArrayDataPtr # define QByteArrayLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ holder; }) #endif @@ -180,8 +179,7 @@ private: public: inline QByteArray(); - QByteArray(const char *); - QByteArray(const char *, int size); + QByteArray(const char *, int size = -1); QByteArray(int size, char c); QByteArray(int size, Qt::Initialization); inline QByteArray(const QByteArray &); @@ -379,16 +377,16 @@ public: bool isNull() const; template <int n> - inline QByteArray(const QConstByteArrayData<n> &dd) + inline QByteArray(const QStaticByteArrayData<n> &dd) : d(const_cast<QByteArrayData *>(&dd.ba)) {} template <int N> - Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr<N> dd) + Q_DECL_CONSTEXPR inline QByteArray(QStaticByteArrayDataPtr<N> dd) : d(const_cast<QByteArrayData *>(&dd.ptr->ba)) {} private: operator QNoImplicitBoolCast() const; - static const QConstByteArrayData<1> shared_null; - static const QConstByteArrayData<1> shared_empty; + static const QStaticByteArrayData<1> shared_null; + static const QStaticByteArrayData<1> shared_empty; Data *d; QByteArray(Data *dd, int /*dummy*/, int /*dummy*/) : d(dd) {} void realloc(int alloc); @@ -424,9 +422,9 @@ inline const char *QByteArray::data() const inline const char *QByteArray::constData() const { return d->data(); } inline void QByteArray::detach() -{ if (d->ref != 1 || d->offset) realloc(d->size); } +{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) realloc(d->size); } inline bool QByteArray::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) { d->ref.ref(); } @@ -435,7 +433,7 @@ inline int QByteArray::capacity() const inline void QByteArray::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -446,11 +444,12 @@ inline void QByteArray::reserve(int asize) inline void QByteArray::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(d->size); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index d5703e8b2a..897de77f2e 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,7 +166,7 @@ static int countBits(int hint) const int MinNumBits = 4; const QHashData QHashData::shared_null = { - 0, 0, Q_REFCOUNT_INITIALIZER(-1), 0, 0, MinNumBits, 0, 0, true, false, 0 + 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, true, false, 0 }; void *QHashData::allocateNode(int nodeAlign) @@ -196,7 +196,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d = new QHashData; d->fakeNext = 0; d->buckets = 0; - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->nodeSize = nodeSize; d->userNumBits = userNumBits; diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 9e4007c26e..91d015c394 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -288,8 +288,8 @@ public: void reserve(int size); inline void squeeze() { reserve(1); } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QHashData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QHash<Key, T> &other) const { return d == other.d; } diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index b31ef3e5e9..6e66f804c0 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE const QLinkedListData QLinkedListData::shared_null = { const_cast<QLinkedListData *>(&QLinkedListData::shared_null), const_cast<QLinkedListData *>(&QLinkedListData::shared_null), - Q_REFCOUNT_INITIALIZER(-1), 0, true + Q_REFCOUNT_INITIALIZE_STATIC, 0, true }; /*! \class QLinkedList diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 2e6d05ac35..28f190c7fa 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -94,8 +94,8 @@ public: inline int size() const { return d->size; } inline void detach() - { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QLinkedListData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QLinkedList<T> &other) const { return d == other.d; } @@ -241,8 +241,6 @@ private: template <typename T> inline QLinkedList<T>::~QLinkedList() { - if (!d) - return; if (!d->ref.deref()) free(d); } @@ -252,7 +250,7 @@ void QLinkedList<T>::detach_helper() { union { QLinkedListData *d; Node *e; } x; x.d = new QLinkedListData; - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->size = d->size; x.d->sharable = true; Node *original = e->n; @@ -265,6 +263,7 @@ void QLinkedList<T>::detach_helper() copy = copy->n; } QT_CATCH(...) { copy->n = x.e; + Q_ASSERT(!x.d->ref.deref()); // Don't trigger assert in free free(x.d); QT_RETHROW; } @@ -281,14 +280,13 @@ void QLinkedList<T>::free(QLinkedListData *x) { Node *y = reinterpret_cast<Node*>(x); Node *i = y->n; - if (x->ref == 0) { - while(i != y) { - Node *n = i; - i = i->n; - delete n; - } - delete x; + Q_ASSERT(x->ref.atomic.load() == 0); + while (i != y) { + Node *n = i; + i = i->n; + delete n; } + delete x; } template <typename T> diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 263045a25d..9ee4c0a797 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, true, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }; static int grow(int size) { @@ -87,8 +87,7 @@ QListData::Data *QListData::detach_grow(int *idx, int num) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; - t->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: // Something which looks like an append will put the data at the beginning, @@ -129,8 +128,7 @@ QListData::Data *QListData::detach(int alloc) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; - t->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; if (!alloc) { t->begin = 0; @@ -146,7 +144,7 @@ QListData::Data *QListData::detach(int alloc) void QListData::realloc(int alloc) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); Data *x = static_cast<Data *>(::realloc(d, DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(x); @@ -159,7 +157,7 @@ void QListData::realloc(int alloc) // ensures that enough space is available to append n elements void **QListData::append(int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int e = d->end; if (e + n > d->alloc) { int b = d->begin; @@ -190,7 +188,7 @@ void **QListData::append(const QListData& l) void **QListData::prepend() { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (d->begin == 0) { if (d->end >= d->alloc / 3) realloc(grow(d->alloc + 1)); @@ -208,7 +206,7 @@ void **QListData::prepend() void **QListData::insert(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (i <= 0) return prepend(); int size = d->end - d->begin; @@ -247,7 +245,7 @@ void **QListData::insert(int i) void QListData::remove(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; if (i - d->begin < d->end - i) { if (int offset = i - d->begin) @@ -262,7 +260,7 @@ void QListData::remove(int i) void QListData::remove(int i, int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; int middle = i + n/2; if (middle - d->begin < d->end - middle) { @@ -278,7 +276,7 @@ void QListData::remove(int i, int n) void QListData::move(int from, int to) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (from == to) return; @@ -318,7 +316,7 @@ void QListData::move(int from, int to) void **QListData::erase(void **xi) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int i = xi - (d->array + d->begin); remove(i); return d->array + d->begin + i; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index bf6933732c..bc3350f42b 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -71,7 +71,6 @@ struct Q_CORE_EXPORT QListData { struct Data { QtPrivate::RefCount ref; int alloc, begin, end; - uint sharable : 1; void *array[1]; }; enum { DataHeaderSize = sizeof(Data) - sizeof(void *) }; @@ -114,7 +113,7 @@ class QList public: inline QList() : d(const_cast<QListData::Data *>(&QListData::shared_null)) { } - inline QList(const QList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + QList(const QList<T> &l); ~QList(); QList<T> &operator=(const QList<T> &l); #ifdef Q_COMPILER_RVALUE_REFS @@ -132,17 +131,25 @@ public: inline int size() const { return p.size(); } - inline void detach() { if (d->ref != 1) detach_helper(); } + inline void detach() { if (d->ref.isShared()) detach_helper(); } inline void detachShared() { // The "this->" qualification is needed for GCCE. - if (d->ref != 1 && this->d != &QListData::shared_null) + if (d->ref.isShared() && this->d != &QListData::shared_null) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QListData::shared_null) d->sharable = sharable; } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QListData::shared_null) + d->ref.setSharable(sharable); + } inline bool isSharedWith(const QList<T> &other) const { return d == other.d; } inline bool isEmpty() const { return p.isEmpty(); } @@ -419,13 +426,8 @@ template <typename T> Q_INLINE_TEMPLATE QList<T> &QList<T>::operator=(const QList<T> &l) { if (d != l.d) { - QListData::Data *o = l.d; - o->ref.ref(); - if (!d->ref.deref()) - dealloc(d); - d = o; - if (!d->sharable) - detach_helper(); + QList<T> tmp(l); + tmp.swap(*this); } return *this; } @@ -478,7 +480,7 @@ template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) { if (d->alloc < alloc) { - if (d->ref != 1) + if (d->ref.isShared()) detach_helper(alloc); else p.realloc(alloc); @@ -488,7 +490,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(INT_MAX, 1); QT_TRY { node_construct(n, t); @@ -522,7 +524,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) template <typename T> inline void QList<T>::prepend(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(0, 1); QT_TRY { node_construct(n, t); @@ -556,7 +558,7 @@ inline void QList<T>::prepend(const T &t) template <typename T> inline void QList<T>::insert(int i, const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(i, 1); QT_TRY { node_construct(n, t); @@ -708,6 +710,28 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::detach_helper() } template <typename T> +Q_OUTOFLINE_TEMPLATE QList<T>::QList(const QList<T> &l) + : d(l.d) +{ + if (!d->ref.ref()) { + p.detach(d->alloc); + + struct Cleanup + { + Cleanup(QListData::Data *d) : d_(d) {} + ~Cleanup() { if (d_) qFree(d_); } + + QListData::Data *d_; + } tryCatch(d); + + node_copy(reinterpret_cast<Node *>(p.begin()), + reinterpret_cast<Node *>(p.end()), + reinterpret_cast<Node *>(l.p.begin())); + tryCatch.d_ = 0; + } +} + +template <typename T> Q_OUTOFLINE_TEMPLATE QList<T>::~QList() { if (!d->ref.deref()) @@ -802,7 +826,7 @@ Q_OUTOFLINE_TEMPLATE QList<T> &QList<T>::operator+=(const QList<T> &l) if (isEmpty()) { *this = l; } else { - Node *n = (d->ref != 1) + Node *n = (d->ref.isShared()) ? detach_helper_grow(INT_MAX, l.size()) : reinterpret_cast<Node *>(p.append(l.p)); QT_TRY { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 103d074941..c922d7aab0 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE const QMapData QMapData::shared_null = { const_cast<QMapData *>(&shared_null), { const_cast<QMapData *>(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, false, true, false, 0 + Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0 }; QMapData *QMapData::createData(int alignment) @@ -63,7 +63,7 @@ QMapData *QMapData::createData(int alignment) Node *e = reinterpret_cast<Node *>(d); e->backward = e; e->forward[0] = e; - d->ref = 1; + d->ref.initializeOwned(); d->topLevel = 0; d->size = 0; d->randomBits = 0; diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 515b60bea6..dc358a8106 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -179,7 +179,7 @@ public: inline QMap() : d(const_cast<QMapData *>(&QMapData::shared_null)) { } inline QMap(const QMap<Key, T> &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } - inline ~QMap() { if (!d) return; if (!d->ref.deref()) freeData(d); } + inline ~QMap() { if (!d->ref.deref()) freeData(d); } QMap<Key, T> &operator=(const QMap<Key, T> &other); #ifdef Q_COMPILER_RVALUE_REFS @@ -199,8 +199,8 @@ public: inline bool isEmpty() const { return d->size == 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QMapData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QMap<Key, T> &other) const { return d == other.d; } inline void setInsertInOrder(bool ordered) { if (ordered) detach(); if (d != &QMapData::shared_null) d->insertInOrder = ordered; } diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index bc673214fa..16b1d339b3 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -55,37 +55,61 @@ namespace QtPrivate class RefCount { public: - inline void ref() { - if (atomic.load() > 0) + inline bool ref() { + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count != -1) // !isStatic atomic.ref(); + return true; } inline bool deref() { - if (atomic.load() <= 0) + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count == -1) // isStatic return true; return atomic.deref(); } - inline bool operator==(int value) const - { return atomic.load() == value; } - inline bool operator!=(int value) const - { return atomic.load() != value; } - inline bool operator!() const - { return !atomic.load(); } - inline operator int() const - { return atomic.load(); } - inline RefCount &operator=(int value) - { atomic.store(value); return *this; } - inline RefCount &operator=(const RefCount &other) - { atomic.store(other.atomic.load()); return *this; } + bool setSharable(bool sharable) + { + Q_ASSERT(!isShared()); + if (sharable) + return atomic.testAndSetRelaxed(0, 1); + else + return atomic.testAndSetRelaxed(1, 0); + } + + bool isStatic() const + { + // Persistent object, never deleted + return atomic.load() == -1; + } + + bool isSharable() const + { + // Sharable === Shared ownership. + return atomic.load() != 0; + } + + bool isShared() const + { + int count = atomic.load(); + return (count != 1) && (count != 0); + } + + void initializeOwned() { atomic.store(1); } + void initializeUnsharable() { atomic.store(0); } QBasicAtomicInt atomic; }; -#define Q_REFCOUNT_INITIALIZER(a) { Q_BASIC_ATOMIC_INITIALIZER(a) } - } +#define Q_REFCOUNT_INITIALIZE_STATIC { Q_BASIC_ATOMIC_INITIALIZER(-1) } + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 083abcbaad..58eb711168 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -94,6 +94,8 @@ #define ULLONG_MAX quint64_C(18446744073709551615) #endif +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData)) + QT_BEGIN_NAMESPACE #ifdef QT_USE_ICU @@ -793,8 +795,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; -const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; int QString::grow(int size) { @@ -1015,63 +1017,44 @@ int QString::toUcs4_helper(const ushort *uc, int length, uint *out) Constructs a string initialized with the first \a size characters of the QChar array \a unicode. + If \a unicode is 0, a null string is constructed. + + If \a size is negative, \a unicode is assumed to point to a nul-terminated + array and its length is determined dynamically. The terminating + nul-character is not considered part of the string. + QString makes a deep copy of the string data. The unicode data is copied as is and the Byte Order Mark is preserved if present. + + \sa fromRawData() */ QString::QString(const QChar *unicode, int size) { if (!unicode) { d = const_cast<Data *>(&shared_null.str); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.str); } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; + if (size < 0) { + size = 0; + while (unicode[size] != 0) + ++size; + } + if (!size) { + d = const_cast<Data *>(&shared_empty.str); + } else { + d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = sizeof(QStringData); + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; + } } } /*! - \since 4.7 - - Constructs a string initialized with the characters of the QChar array - \a unicode, which must be terminated with a 0. - - QString makes a deep copy of the string data. The unicode data is copied as - is and the Byte Order Mark is preserved if present. -*/ -QString::QString(const QChar *unicode) -{ - if (!unicode) { - d = const_cast<Data *>(&shared_null.str); - } else { - int size = 0; - while (unicode[size] != 0) - ++size; - if (!size) { - d = const_cast<Data *>(&shared_empty.str); - } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; - } - } -} - - -/*! Constructs a string of the given \a size with every character set to \a ch. @@ -1084,11 +1067,11 @@ QString::QString(int size, QChar ch) } else { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *i = d->data() + size; ushort *b = d->data(); @@ -1108,11 +1091,11 @@ QString::QString(int size, Qt::Initialization) { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; } @@ -1130,11 +1113,11 @@ QString::QString(QChar ch) { d = (Data *) ::malloc(sizeof(Data) + 2*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = 1; d->alloc = 1; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[0] = ch.unicode(); d->data()[1] = '\0'; } @@ -1232,7 +1215,7 @@ void QString::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1243,7 +1226,7 @@ void QString::resize(int size) QString::free(d); d = x; } else { - if (d->ref != 1 || size > int(d->alloc) || + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(grow(size)); if (int(d->alloc) >= size) { @@ -1306,14 +1289,14 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { Data *x = static_cast<Data *>(::malloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = (uint) alloc; x->capacityReserved = d->capacityReserved; - x->offset =0; + x->offset = sizeof(QStringData); ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); x->data()[x->size] = 0; if (!d->ref.deref()) @@ -1324,7 +1307,7 @@ void QString::realloc(int alloc) Q_CHECK_PTR(p); d = p; d->alloc = alloc; - d->offset = 0; + d->offset = sizeof(QStringData); } } @@ -1536,7 +1519,7 @@ QString &QString::append(const QString &str) if (d == &shared_null.str) { operator=(str); } else { - if (d->ref != 1 || d->size + str.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + str.d->size > int(d->alloc)) realloc(grow(d->size + str.d->size)); memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar)); d->size += str.d->size; @@ -1556,7 +1539,7 @@ QString &QString::append(const QLatin1String &str) const uchar *s = (const uchar *)str.latin1(); if (s) { int len = str.size(); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(grow(d->size + len)); ushort *i = d->data() + d->size; while ((*i++ = *s++)) @@ -1599,7 +1582,7 @@ QString &QString::append(const QLatin1String &str) */ QString &QString::append(QChar ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = ch.unicode(); d->data()[d->size] = '\0'; @@ -3390,15 +3373,11 @@ QString QString::right(int n) const QString QString::mid(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QString(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; if (position == 0 && n == d->size) return *this; @@ -3753,11 +3732,11 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) size = qstrlen(str); d = static_cast<Data *>(::malloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *dst = d->data(); /* SIMD: @@ -4765,7 +4744,7 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, const ushort *QString::utf16() const { - if (d->offset) + if (IS_RAW_DATA(d)) const_cast<QString*>(this)->realloc(); // ensure '\\0'-termination for ::fromRawData strings return d->data(); } @@ -7067,11 +7046,11 @@ QString QString::fromRawData(const QChar *unicode, int size) } else { x = static_cast<Data *>(::malloc(sizeof(Data) + sizeof(ushort))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; - x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); + x->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(x); } return QString(x, 0); } @@ -7092,14 +7071,14 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(unicode, size); } else { if (unicode) { d->size = size; - d->offset = (const ushort *)unicode - (d->d + sizeof(qptrdiff)/sizeof(ushort)); + d->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QStringData); d->size = 0; } } @@ -8059,15 +8038,11 @@ QStringRef QString::rightRef(int n) const QStringRef QString::midRef(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QStringRef(); - if (n < 0) - n = d->size - position; - if (position < 0) { - n += position; + if (position < 0) position = 0; - } - if (n + position > d->size) + if (n < 0 || n > d->size - position) n = d->size - position; return QStringRef(this, position, n); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 4d02fbe66d..26959d81f3 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -75,25 +75,24 @@ struct QStringData { int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - ushort d[sizeof(qptrdiff)/sizeof(ushort)]; - }; - inline ushort *data() { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } - inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } + + qptrdiff offset; + + inline ushort *data() { return reinterpret_cast<ushort *>(reinterpret_cast<char *>(this) + offset); } + inline const ushort *data() const { return reinterpret_cast<const ushort *>(reinterpret_cast<const char *>(this) + offset); } }; -template<int N> struct QConstStringData; -template<int N> struct QConstStringDataPtr +template<int N> struct QStaticStringData; +template<int N> struct QStaticStringDataPtr { - const QConstStringData<N> *ptr; + const QStaticStringData<N> *ptr; }; #if defined(Q_COMPILER_UNICODE_STRINGS) -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const char16_t data[N + 1]; + QStringData str; + char16_t data[N + 1]; }; #define QT_UNICODE_LITERAL_II(str) u"" str @@ -102,10 +101,10 @@ template<int N> struct QConstStringData || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) \ || (!defined(__SIZEOF_WCHAR_T__) && defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536)) // wchar_t is 2 bytes -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const wchar_t data[N + 1]; + QStringData str; + wchar_t data[N + 1]; }; #if defined(Q_CC_MSVC) @@ -115,21 +114,21 @@ template<int N> struct QConstStringData #endif #else -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const ushort data[N + 1]; + QStringData str; + ushort data[N + 1]; }; #endif #if defined(QT_UNICODE_LITERAL_II) # define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) # if defined(Q_COMPILER_LAMBDA) -# define QStringLiteral(str) ([]() -> QConstStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ +# define QStringLiteral(str) ([]() -> QStaticStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ return holder; }()) # elif defined(Q_CC_GNU) @@ -140,9 +139,9 @@ template<int N> struct QConstStringData # define QStringLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ holder; }) # endif #endif @@ -160,8 +159,7 @@ public: typedef QStringData Data; inline QString(); - QString(const QChar *unicode, int size); // Qt5: don't cap size < 0 - explicit QString(const QChar *unicode); // Qt5: merge with the above + explicit QString(const QChar *unicode, int size = -1); QString(QChar c); QString(int size, QChar c); inline QString(const QLatin1String &latin1); @@ -340,7 +338,7 @@ public: inline QString &prepend(const QLatin1String &s) { return insert(0, s); } inline QString &operator+=(QChar c) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = c.unicode(); d->data()[d->size] = '\0'; @@ -592,9 +590,9 @@ public: QString(int size, Qt::Initialization); template <int n> - inline QString(const QConstStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} + inline QString(const QStaticStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} template <int N> - Q_DECL_CONSTEXPR inline QString(QConstStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} + Q_DECL_CONSTEXPR inline QString(QStaticStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) @@ -606,8 +604,8 @@ private: QString &operator=(const QByteArray &a); #endif - static const QConstStringData<1> shared_null; - static const QConstStringData<1> shared_empty; + static const QStaticStringData<1> shared_null; + static const QStaticStringData<1> shared_empty; Data *d; inline QString(Data *dd, int /*dummy*/) : d(dd) {} @@ -709,9 +707,9 @@ inline QChar *QString::data() inline const QChar *QString::constData() const { return reinterpret_cast<const QChar*>(d->data()); } inline void QString::detach() -{ if (d->ref != 1 || d->offset) realloc(); } +{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) realloc(); } inline bool QString::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QString &QString::operator=(const QLatin1String &s) { *this = fromLatin1(s.latin1(), s.size()); @@ -874,7 +872,7 @@ inline QString::~QString() { if (!d->ref.deref()) free(d); } inline void QString::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -885,11 +883,12 @@ inline void QString::reserve(int asize) inline void QString::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index e34425be7e..0eb8aa8903 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -219,9 +219,9 @@ template <> struct QConcatenable<QString> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<QConstStringDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticStringDataPtr<N> > : private QAbstractConcatenable { - typedef QConstStringDataPtr<N> type; + typedef QStaticStringDataPtr<N> type; typedef QString ConvertTo; enum { ExactSize = true }; static int size(const type &) { return N; } @@ -324,9 +324,9 @@ template <> struct QConcatenable<QByteArray> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticByteArrayDataPtr<N> > : private QAbstractConcatenable { - typedef QConstByteArrayDataPtr<N> type; + typedef QStaticByteArrayDataPtr<N> type; typedef QByteArray ConvertTo; enum { ExactSize = false }; static int size(const type &) { return N; } @@ -338,9 +338,8 @@ template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbs #endif static inline void appendTo(const type &ba, char *&out) { - const char *a = ba.ptr->data; - while (*a) - *out++ = *a++; + ::memcpy(out, ba.ptr->data, N); + out += N; } }; diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 75c219bbc9..1a746dc061 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -54,15 +54,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, true, false, 0 }; - -QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) -{ - QVectorData* p = (QVectorData *)::malloc(sizeofTypedData + (size - 1) * sizeofT); - Q_CHECK_PTR(p); - ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT); - return p; -} +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; QVectorData *QVectorData::allocate(int size, int alignment) { @@ -84,11 +76,9 @@ void QVectorData::free(QVectorData *x, int alignment) ::free(x); } -int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive) +int QVectorData::grow(int sizeOfHeader, int size, int sizeOfT) { - if (excessive) - return size + size / 2; - return qAllocMore(size * sizeofT, sizeofTypedData - sizeofT) / sizeofT; + return qAllocMore(size * sizeOfT, sizeOfHeader) / sizeOfT; } /*! diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 4230e55ff5..c119ef43ae 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -65,37 +65,28 @@ QT_BEGIN_NAMESPACE struct Q_CORE_EXPORT QVectorData { QtPrivate::RefCount ref; - int alloc; int size; -#if defined(Q_PROCESSOR_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) - // workaround for bug in gcc 3.4.2 - uint sharable; - uint capacity; - uint reserved; -#else - uint sharable : 1; - uint capacity : 1; - uint reserved : 30; -#endif + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; + + void* data() { return reinterpret_cast<char *>(this) + this->offset; } static const QVectorData shared_null; - // ### Qt 5: rename to 'allocate()'. The current name causes problems for - // some debugges when the QVector is member of a class within an unnamed namespace. - // ### Qt 5: can be removed completely. (Ralf) - static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); - static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); + static int grow(int sizeOfHeader, int size, int sizeOfT); }; template <typename T> -struct QVectorTypedData : private QVectorData -{ // private inheritance as we must not access QVectorData member thought QVectorTypedData - // as this would break strict aliasing rules. (in the case of shared_null) - T array[1]; +struct QVectorTypedData : QVectorData +{ + T* begin() { return reinterpret_cast<T *>(this->data()); } + T* end() { return begin() + this->size; } - static inline void free(QVectorTypedData<T> *x, int alignment) { QVectorData::free(static_cast<QVectorData *>(x), alignment); } + static QVectorTypedData *sharedNull() { return static_cast<QVectorTypedData *>(const_cast<QVectorData *>(&QVectorData::shared_null)); } }; class QRegion; @@ -104,27 +95,30 @@ template <typename T> class QVector { typedef QVectorTypedData<T> Data; - union { - QVectorData *d; -#if defined(Q_CC_SUN) && (__SUNPRO_CC <= 0x550) - QVectorTypedData<T> *p; -#else - Data *p; -#endif - }; + Data *d; public: - // ### Qt 5: Consider making QVector non-shared to get at least one - // "really fast" container. See tests/benchmarks/corelib/tools/qvector/ - inline QVector() : d(const_cast<QVectorData *>(&QVectorData::shared_null)) { } + inline QVector() : d(Data::sharedNull()) { } explicit QVector(int size); QVector(int size, const T &t); - inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } - inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } + inline QVector(const QVector<T> &v) + { + if (v.d->ref.ref()) { + d = v.d; + } else { + d = Data::sharedNull(); + realloc(0, int(v.d->alloc)); + qCopy(v.d->begin(), v.d->end(), d->begin()); + d->size = v.d->size; + d->capacityReserved = v.d->capacityReserved; + } + } + + inline ~QVector() { if (!d->ref.deref()) free(d); } QVector<T> &operator=(const QVector<T> &v); #ifdef Q_COMPILER_RVALUE_REFS inline QVector<T> operator=(QVector<T> &&other) - { qSwap(p, other.p); return *this; } + { qSwap(d, other.d); return *this; } #endif inline void swap(QVector<T> &other) { qSwap(d, other.d); } #ifdef Q_COMPILER_INITIALIZER_LISTS @@ -139,18 +133,27 @@ public: void resize(int size); - inline int capacity() const { return d->alloc; } + inline int capacity() const { return int(d->alloc); } void reserve(int size); - inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } + inline void squeeze() { realloc(d->size, d->size); d->capacityReserved = 0; } + + inline void detach() { if (!isDetached()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != Data::sharedNull()) + d->ref.setSharable(sharable); + } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QVectorData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QVector<T> &other) const { return d == other.d; } - inline T *data() { detach(); return p->array; } - inline const T *data() const { return p->array; } - inline const T *constData() const { return p->array; } + inline T *data() { detach(); return d->begin(); } + inline const T *data() const { return d->begin(); } + inline const T *constData() const { return d->begin(); } void clear(); const T &at(int i) const; @@ -243,12 +246,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return p->array; } - inline const_iterator begin() const { return p->array; } - inline const_iterator constBegin() const { return p->array; } - inline iterator end() { detach(); return p->array + d->size; } - inline const_iterator end() const { return p->array + d->size; } - inline const_iterator constEnd() const { return p->array + d->size; } + inline iterator begin() { detach(); return d->begin(); } + inline const_iterator begin() const { return d->begin(); } + inline const_iterator constBegin() const { return d->begin(); } + inline iterator end() { detach(); return d->end(); } + inline const_iterator end() const { return d->end(); } + inline const_iterator constEnd() const { return d->end(); } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -313,46 +316,49 @@ private: friend class QRegion; // Optimization for QRegion::rects() void detach_helper(); - QVectorData *malloc(int alloc); + Data *malloc(int alloc); void realloc(int size, int alloc); void free(Data *d); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + + class AlignmentDummy { QVectorData header; T array[1]; }; + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(AlignmentDummy, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - inline int alignOfTypedData() const + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax<int>(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(AlignmentDummy); #else - return 0; + return sizeof(void *); #endif } }; template <typename T> void QVector<T>::detach_helper() -{ realloc(d->size, d->alloc); } +{ realloc(d->size, int(d->alloc)); } template <typename T> void QVector<T>::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (d->ref == 1) d->capacity = 1; } +{ if (asize > int(d->alloc)) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } template <typename T> void QVector<T>::resize(int asize) -{ realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? - QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo<T>::isStatic) - : d->alloc); } +{ realloc(asize, (asize > int(d->alloc) || (!d->capacityReserved && asize < d->size && asize < int(d->alloc >> 1))) ? + QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) + : int(d->alloc)); } template <typename T> inline void QVector<T>::clear() { *this = QVector<T>(); } template <typename T> inline const T &QVector<T>::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::at", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline const T &QVector<T>::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline T &QVector<T>::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); @@ -388,39 +394,37 @@ inline void QVector<T>::replace(int i, const T &t) template <typename T> QVector<T> &QVector<T>::operator=(const QVector<T> &v) { - QVectorData *o = v.d; - o->ref.ref(); - if (!d->ref.deref()) - free(p); - d = o; - if (!d->sharable) - detach_helper(); + if (v.d != d) { + QVector<T> tmp(v); + tmp.swap(*this); + } return *this; } template <typename T> -inline QVectorData *QVector<T>::malloc(int aalloc) +inline typename QVector<T>::Data *QVector<T>::malloc(int aalloc) { - QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *vectordata = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); - return vectordata; + return static_cast<Data *>(vectordata); } template <typename T> QVector<T>::QVector(int asize) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); if (QTypeInfo<T>::isComplex) { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) new (--i) T; } else { - qMemSet(p->array, 0, asize * sizeof(T)); + qMemSet(d->begin(), 0, asize * sizeof(T)); } } @@ -428,12 +432,13 @@ template <typename T> QVector<T>::QVector(int asize, const T &t) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - while (i != p->array) + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + T* i = d->end(); + while (i != d->begin()) new (--i) T(t); } @@ -442,14 +447,22 @@ template <typename T> QVector<T>::QVector(std::initializer_list<T> args) { d = malloc(int(args.size())); - d->ref = 1; - d->alloc = d->size = int(args.size()); - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - auto it = args.end(); - while (i != p->array) - new (--i) T(*(--it)); + d->ref.initializeOwned(); + d->size = int(args.size()); + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + if (QTypeInfo<T>::isComplex) { + T* b = d->begin(); + T* i = d->end(); + const T* s = args.end(); + while (i != b) + new(--i) T(*--s); + } else { + // std::initializer_list<T>::iterator is guaranteed to be + // const T* ([support.initlist]/1), so can be memcpy'ed away from: + ::memcpy(d->begin(), args.begin(), args.size() * sizeof(T)); + } } #endif @@ -457,14 +470,12 @@ template <typename T> void QVector<T>::free(Data *x) { if (QTypeInfo<T>::isComplex) { - T* b = x->array; - union { QVectorData *d; Data *p; } u; - u.p = x; - T* i = b + u.d->size; + T* b = x->begin(); + T* i = b + x->size; while (i-- != b) i->~T(); } - x->free(x, alignOfTypedData()); + Data::free(x, alignOfTypedData()); } template <typename T> @@ -473,84 +484,82 @@ void QVector<T>::realloc(int asize, int aalloc) Q_ASSERT(asize <= aalloc); T *pOld; T *pNew; - union { QVectorData *d; Data *p; } x; - x.d = d; + Data *x = d; - if (QTypeInfo<T>::isComplex && asize < d->size && d->ref == 1 ) { + if (QTypeInfo<T>::isComplex && asize < d->size && isDetached()) { // call the destructor on all objects that need to be // destroyed when shrinking - pOld = p->array + d->size; - pNew = p->array + asize; + pOld = d->begin() + d->size; + pNew = d->begin() + asize; while (asize < d->size) { (--pOld)->~T(); d->size--; } } - if (aalloc != d->alloc || d->ref != 1) { + if (aalloc != int(d->alloc) || !isDetached()) { // (re)allocate memory if (QTypeInfo<T>::isStatic) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); - x.d->size = 0; - } else if (d->ref != 1) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); + x = malloc(aalloc); + Q_CHECK_PTR(x); + x->size = 0; + } else if (!isDetached()) { + x = malloc(aalloc); + Q_CHECK_PTR(x); if (QTypeInfo<T>::isComplex) { - x.d->size = 0; + x->size = 0; } else { - ::memcpy(x.p, p, sizeOfTypedData() + (qMin(aalloc, d->alloc) - 1) * sizeof(T)); - x.d->size = d->size; + ::memcpy(x, d, offsetOfTypedData() + qMin(uint(aalloc), d->alloc) * sizeof(T)); + x->size = d->size; } } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(d, offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + d->alloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); - x.d = d = mem; - x.d->size = d->size; + x = d = static_cast<Data *>(mem); + x->size = d->size; } QT_CATCH (const std::bad_alloc &) { - if (aalloc > d->alloc) // ignore the error in case we are just shrinking. + if (aalloc > int(d->alloc)) // ignore the error in case we are just shrinking. QT_RETHROW; } } - x.d->ref = 1; - x.d->alloc = aalloc; - x.d->sharable = true; - x.d->capacity = d->capacity; - x.d->reserved = 0; + x->ref.initializeOwned(); + x->alloc = uint(aalloc); + x->capacityReserved = d->capacityReserved; + x->offset = offsetOfTypedData(); } if (QTypeInfo<T>::isComplex) { QT_TRY { - pOld = p->array + x.d->size; - pNew = x.p->array + x.d->size; + pOld = d->begin() + x->size; + pNew = x->begin() + x->size; // copy objects from the old array into the new array const int toMove = qMin(asize, d->size); - while (x.d->size < toMove) { + while (x->size < toMove) { new (pNew++) T(*pOld++); - x.d->size++; + x->size++; } // construct all new objects when growing - while (x.d->size < asize) { + while (x->size < asize) { new (pNew++) T; - x.d->size++; + x->size++; } } QT_CATCH (...) { - free(x.p); + free(x); QT_RETHROW; } - } else if (asize > x.d->size) { + } else if (asize > x->size) { // initialize newly allocated memory to 0 - qMemSet(x.p->array + x.d->size, 0, (asize - x.d->size) * sizeof(T)); + qMemSet(x->end(), 0, (asize - x->size) * sizeof(T)); } - x.d->size = asize; + x->size = asize; - if (d != x.d) { + if (d != x) { if (!d->ref.deref()) - free(p); - d = x.d; + free(d); + d = x; } } @@ -560,31 +569,31 @@ Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const if (i < 0 || i >= d->size) { return T(); } - return p->array[i]; + return d->begin()[i]; } template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : d->begin()[i]); } template <typename T> void QVector<T>::append(const T &t) { - if (d->ref != 1 || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > int(d->alloc)) { const T copy(t); - realloc(d->size, (d->size + 1 > d->alloc) ? - QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo<T>::isStatic) - : d->alloc); + realloc(d->size, (d->size + 1 > int(d->alloc)) ? + QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) + : int(d->alloc)); if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(copy); + new (d->end()) T(copy); else - p->array[d->size] = copy; + *d->end() = copy; } else { if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(t); + new (d->end()) T(t); else - p->array[d->size] = t; + *d->end() = t; } ++d->size; } @@ -592,27 +601,26 @@ void QVector<T>::append(const T &t) template <typename T> typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, const T &t) { - int offset = int(before - p->array); + int offset = int(before - d->begin()); if (n != 0) { const T copy(t); - if (d->ref != 1 || d->size + n > d->alloc) - realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), - QTypeInfo<T>::isStatic)); + if (!isDetached() || d->size + n > int(d->alloc)) + realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo<T>::isStatic) { - T *b = p->array + d->size; - T *i = p->array + d->size + n; + T *b = d->end(); + T *i = d->end() + n; while (i != b) new (--i) T; - i = p->array + d->size; + i = d->end(); T *j = i + n; - b = p->array + offset; + b = d->begin() + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = p->array + offset; + T *b = d->begin() + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -620,29 +628,29 @@ typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, c } d->size += n; } - return p->array + offset; + return d->begin() + offset; } template <typename T> typename QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) { - int f = int(abegin - p->array); - int l = int(aend - p->array); + int f = int(abegin - d->begin()); + int l = int(aend - d->begin()); int n = l - f; detach(); if (QTypeInfo<T>::isComplex) { - qCopy(p->array+l, p->array+d->size, p->array+f); - T *i = p->array+d->size; - T* b = p->array+d->size-n; + qCopy(d->begin()+l, d->end(), d->begin()+f); + T *i = d->end(); + T* b = d->end()-n; while (i != b) { --i; i->~T(); } } else { - memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); + memmove(d->begin() + f, d->begin() + l, (d->size-l)*sizeof(T)); } d->size -= n; - return p->array + f; + return d->begin() + f; } template <typename T> @@ -652,9 +660,9 @@ bool QVector<T>::operator==(const QVector<T> &v) const return false; if (d == v.d) return true; - T* b = p->array; + T* b = d->begin(); T* i = b + d->size; - T* j = v.p->array + d->size; + T* j = v.d->end(); while (i != b) if (!(*--i == *--j)) return false; @@ -667,8 +675,8 @@ QVector<T> &QVector<T>::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = p->array + d->size; - T *b = p->array; + T *i = d->end(); + T *b = d->begin(); while (i != b) *--i = copy; } @@ -681,9 +689,9 @@ QVector<T> &QVector<T>::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = p->array + newSize; - T *i = l.p->array + l.d->size; - T *b = l.p->array; + T *w = d->begin() + newSize; + T *i = l.d->end(); + T *b = l.d->begin(); while (i != b) { if (QTypeInfo<T>::isComplex) new (--w) T(*--i); @@ -700,11 +708,11 @@ int QVector<T>::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = p->array + from - 1; - T* e = p->array + d->size; + T* n = d->begin() + from - 1; + T* e = d->end(); while (++n != e) if (*n == t) - return n - p->array; + return n - d->begin(); } return -1; } @@ -717,8 +725,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = p->array; - T* n = p->array + from + 1; + T* b = d->begin(); + T* n = d->begin() + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -730,8 +738,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const template <typename T> bool QVector<T>::contains(const T &t) const { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) return true; @@ -742,8 +750,8 @@ template <typename T> int QVector<T>::count(const T &t) const { int c = 0; - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) ++c; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 250789a969..ac347404fd 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -2,6 +2,9 @@ HEADERS += \ tools/qalgorithms.h \ + tools/qarraydata.h \ + tools/qarraydataops.h \ + tools/qarraydatapointer.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ @@ -56,6 +59,7 @@ HEADERS += \ SOURCES += \ + tools/qarraydata.cpp \ tools/qbitarray.cpp \ tools/qbytearray.cpp \ tools/qbytearraymatcher.cpp \ diff --git a/src/dbus/qdbusabstractadaptor.cpp b/src/dbus/qdbusabstractadaptor.cpp index 7bdd947a37..bacf93bac8 100644 --- a/src/dbus/qdbusabstractadaptor.cpp +++ b/src/dbus/qdbusabstractadaptor.cpp @@ -181,7 +181,7 @@ void QDBusAbstractAdaptor::setAutoRelaySignals(bool enable) continue; // try to connect/disconnect to a signal on the parent that has the same method signature - QByteArray sig = QMetaObject::normalizedSignature(mm.signature()); + QByteArray sig = QMetaObject::normalizedSignature(mm.methodSignature().constData()); if (them->indexOfSignal(sig) == -1) continue; sig.prepend(QSIGNAL_CODE + '0'); @@ -307,7 +307,7 @@ void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void ** // invalid signal signature // qDBusParametersForMethod has not yet complained about this one qWarning("QDBusAbstractAdaptor: Cannot relay signal %s::%s", - senderMetaObject->className(), mm.signature()); + senderMetaObject->className(), mm.methodSignature().constData()); return; } @@ -323,10 +323,38 @@ void QDBusAdaptorConnector::relay(QObject *senderObj, int lastSignalIdx, void ** // modify carefully: this has been hand-edited! // the relaySlot slot gets called with the void** array +struct qt_meta_stringdata_QDBusAdaptorConnector_t { + QByteArrayData data[10]; + char stringdata[96]; +}; +#define QT_MOC_LITERAL(idx, ofs, len) { \ + Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ + offsetof(qt_meta_stringdata_QDBusAdaptorConnector_t, stringdata) + ofs \ + - idx * sizeof(QByteArrayData) \ + } +static const qt_meta_stringdata_QDBusAdaptorConnector_t qt_meta_stringdata_QDBusAdaptorConnector = { + { +QT_MOC_LITERAL(0, 0, 21), +QT_MOC_LITERAL(1, 22, 11), +QT_MOC_LITERAL(2, 34, 0), +QT_MOC_LITERAL(3, 35, 3), +QT_MOC_LITERAL(4, 39, 18), +QT_MOC_LITERAL(5, 58, 10), +QT_MOC_LITERAL(6, 69, 3), +QT_MOC_LITERAL(7, 73, 4), +QT_MOC_LITERAL(8, 78, 9), +QT_MOC_LITERAL(9, 88, 6) + }, + "QDBusAdaptorConnector\0relaySignal\0\0" + "obj\0const QMetaObject*\0metaObject\0sid\0" + "args\0relaySlot\0polish\0" +}; +#undef QT_MOC_LITERAL + static const uint qt_meta_data_QDBusAdaptorConnector[] = { // content: - 6, // revision + 7, // revision 0, // classname 0, 0, // classinfo 3, 14, // methods @@ -336,20 +364,21 @@ static const uint qt_meta_data_QDBusAdaptorConnector[] = { 0, // flags 1, // signalCount - // signals: signature, parameters, type, tag, flags - 47, 23, 22, 22, 0x05, + // signals: name, argc, parameters, tag, flags + 1, 4, 29, 2, 0x05, - // slots: signature, parameters, type, tag, flags - 105, 22, 22, 22, 0x0a, - 117, 22, 22, 22, 0x0a, + // slots: name, argc, parameters, tag, flags + 8, 0, 38, 2, 0x0a, + 9, 0, 39, 2, 0x0a, - 0 // eod -}; + // signals: parameters + QMetaType::Void, QMetaType::QObjectStar, 0x80000000 | 4, QMetaType::Int, QMetaType::QVariantList, 3, 5, 6, 7, -static const char qt_meta_stringdata_QDBusAdaptorConnector[] = { - "QDBusAdaptorConnector\0\0obj,metaObject,sid,args\0" - "relaySignal(QObject*,const QMetaObject*,int,QVariantList)\0" - "relaySlot()\0polish()\0" + // slots: parameters + QMetaType::Void, + QMetaType::Void, + + 0 // eod }; void QDBusAdaptorConnector::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) @@ -371,7 +400,7 @@ const QMetaObjectExtraData QDBusAdaptorConnector::staticMetaObjectExtraData = { }; const QMetaObject QDBusAdaptorConnector::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector, + { &QObject::staticMetaObject, qt_meta_stringdata_QDBusAdaptorConnector.data, qt_meta_data_QDBusAdaptorConnector, &staticMetaObjectExtraData } }; @@ -383,7 +412,7 @@ const QMetaObject *QDBusAdaptorConnector::metaObject() const void *QDBusAdaptorConnector::qt_metacast(const char *_clname) { if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector)) + if (!strcmp(_clname, qt_meta_stringdata_QDBusAdaptorConnector.stringdata)) return static_cast<void*>(const_cast< QDBusAdaptorConnector*>(this)); return QObject::qt_metacast(_clname); } diff --git a/src/dbus/qdbusabstractinterface.cpp b/src/dbus/qdbusabstractinterface.cpp index eeaf0b13eb..cb9f2e7360 100644 --- a/src/dbus/qdbusabstractinterface.cpp +++ b/src/dbus/qdbusabstractinterface.cpp @@ -446,7 +446,7 @@ QDBusMessage QDBusAbstractInterface::callWithArgumentList(QDBus::CallMode mode, for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) { QMetaMethod mm = mo->method(i); - if (QByteArray(mm.signature()).startsWith(match)) { + if (mm.methodSignature().startsWith(match)) { // found a method with the same name as what we're looking for // hopefully, nobody is overloading asynchronous and synchronous methods with // the same name diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index f0c8224be2..8d46ee4801 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -640,7 +640,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, continue; // check name: - QByteArray slotname = mm.signature(); + QByteArray slotname = mm.methodSignature(); int paren = slotname.indexOf('('); if (paren != name.length() || !slotname.startsWith(name)) continue; @@ -686,7 +686,7 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, ++i; // make sure that the output parameters have signatures too - if (returnType != 0 && QDBusMetaType::typeToSignature(returnType) == 0) + if (returnType != QMetaType::UnknownType && returnType != QMetaType::Void && QDBusMetaType::typeToSignature(returnType) == 0) continue; bool ok = true; @@ -919,7 +919,7 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q // output arguments QVariantList outputArgs; void *null = 0; - if (metaTypes[0] != QMetaType::Void) { + if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) { QVariant arg(metaTypes[0], null); outputArgs.append( arg ); params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); @@ -1188,7 +1188,7 @@ void QDBusConnectionPrivate::relaySignal(QObject *obj, const QMetaObject *mo, in QString interface = qDBusInterfaceFromMetaObject(mo); QMetaMethod mm = mo->method(signalId); - QByteArray memberName = mm.signature(); + QByteArray memberName = mm.methodSignature(); memberName.truncate(memberName.indexOf('(')); // check if it's scriptable diff --git a/src/dbus/qdbusinterface.cpp b/src/dbus/qdbusinterface.cpp index d390f395ee..b76dd733a2 100644 --- a/src/dbus/qdbusinterface.cpp +++ b/src/dbus/qdbusinterface.cpp @@ -280,7 +280,7 @@ int QDBusInterfacePrivate::metacall(QMetaObject::Call c, int id, void **argv) } else if (mm.methodType() == QMetaMethod::Slot || mm.methodType() == QMetaMethod::Method) { // method call relay from Qt world to D-Bus world // get D-Bus equivalent signature - QString methodName = QLatin1String(metaObject->dbusNameForMethod(id)); + QString methodName = QString::fromLatin1(mm.name()); const int *inputTypes = metaObject->inputTypesForMethod(id); int inputTypesCount = *inputTypes; diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index bd7b83bf65..ce8146f639 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -54,6 +54,7 @@ #include "qdbusabstractinterface_p.h" #include <private/qmetaobject_p.h> +#include <private/qmetaobjectbuilder_p.h> #ifndef QT_NO_DBUS @@ -69,8 +70,7 @@ public: private: struct Method { - QByteArray parameters; - QByteArray typeName; + QList<QByteArray> parameterNames; QByteArray tag; QByteArray name; QVarLengthArray<int, 4> inputTypes; @@ -103,10 +103,12 @@ private: void parseMethods(); void parseSignals(); void parseProperties(); + + static int aggregateParameterCount(const QMap<QByteArray, Method> &map); }; static const int intsPerProperty = 2; -static const int intsPerMethod = 3; +static const int intsPerMethod = 2; struct QDBusMetaObjectPrivate : public QMetaObjectPrivate { @@ -132,6 +134,30 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature, const QDBusIntrospection::Annotations &annotations, const char *direction, int id) { + struct QDBusRawTypeHandler { + static void destroy(void *) + { + qFatal("Cannot destroy placeholder type QDBusRawType"); + } + + static void *create(const void *) + { + qFatal("Cannot create placeholder type QDBusRawType"); + return 0; + } + + static void destruct(void *) + { + qFatal("Cannot destruct placeholder type QDBusRawType"); + } + + static void *construct(void *, const void *) + { + qFatal("Cannot construct placeholder type QDBusRawType"); + return 0; + } + }; + Type result; result.id = QVariant::Invalid; @@ -157,8 +183,13 @@ QDBusMetaObjectGenerator::findType(const QByteArray &signature, if (type == QVariant::Invalid || signature != QDBusMetaType::typeToSignature(type)) { // type is still unknown or doesn't match back to the signature that it // was expected to, so synthesize a fake type - type = QMetaType::VoidStar; typeName = "QDBusRawType<0x" + signature.toHex() + ">*"; + type = QMetaType::registerType(typeName, QDBusRawTypeHandler::destroy, + QDBusRawTypeHandler::create, + QDBusRawTypeHandler::destruct, + QDBusRawTypeHandler::construct, + sizeof(void *), + QMetaType::MovableType); } result.name = typeName; @@ -215,8 +246,7 @@ void QDBusMetaObjectGenerator::parseMethods() mm.inputTypes.append(type.id); - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append(','); @@ -235,13 +265,9 @@ void QDBusMetaObjectGenerator::parseMethods() mm.outputTypes.append(type.id); - if (i == 0) { - // return value - mm.typeName = type.name; - } else { + if (i != 0) { // non-const ref parameter - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append("&,"); @@ -250,12 +276,10 @@ void QDBusMetaObjectGenerator::parseMethods() if (!ok) continue; // convert the last commas: - if (!mm.parameters.isEmpty()) { - mm.parameters.truncate(mm.parameters.length() - 1); + if (!mm.parameterNames.isEmpty()) prototype[prototype.length() - 1] = ')'; - } else { + else prototype.append(')'); - } // check the async tag if (m.annotations.value(QLatin1String(ANNOTATION_NO_WAIT)) == QLatin1String("true")) @@ -295,8 +319,7 @@ void QDBusMetaObjectGenerator::parseSignals() mm.inputTypes.append(type.id); - mm.parameters.append(arg.name.toLatin1()); - mm.parameters.append(','); + mm.parameterNames.append(arg.name.toLatin1()); prototype.append(type.name); prototype.append(','); @@ -304,12 +327,10 @@ void QDBusMetaObjectGenerator::parseSignals() if (!ok) continue; // convert the last commas: - if (!mm.parameters.isEmpty()) { - mm.parameters.truncate(mm.parameters.length() - 1); + if (!mm.parameterNames.isEmpty()) prototype[prototype.length() - 1] = ')'; - } else { + else prototype.append(')'); - } // meta method flags mm.flags = AccessProtected | MethodSignal | MethodScriptable; @@ -342,49 +363,27 @@ void QDBusMetaObjectGenerator::parseProperties() if (p.access != QDBusIntrospection::Property::Read) mp.flags |= Writable; - if (mp.typeName == "QDBusVariant") - mp.flags |= QMetaType::QVariant << 24; - else if (mp.type < 0xff) - // encode the type in the flags - mp.flags |= mp.type << 24; - // add the property: properties.insert(name, mp); } } -void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) +// Returns the sum of all parameters (including return type) for the given +// \a map of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +int QDBusMetaObjectGenerator::aggregateParameterCount(const QMap<QByteArray, Method> &map) { - class MetaStringTable - { - public: - typedef QHash<QByteArray, int> Entries; // string --> offset mapping - typedef Entries::const_iterator const_iterator; - Entries::const_iterator constBegin() const - { return m_entries.constBegin(); } - Entries::const_iterator constEnd() const - { return m_entries.constEnd(); } - - MetaStringTable() : m_offset(0) {} - - int enter(const QByteArray &value) - { - Entries::iterator it = m_entries.find(value); - if (it != m_entries.end()) - return it.value(); - int pos = m_offset; - m_entries.insert(value, pos); - m_offset += value.size() + 1; - return pos; - } - - int arraySize() const { return m_offset; } - - private: - Entries m_entries; - int m_offset; - }; + int sum = 0; + QMap<QByteArray, Method>::const_iterator it; + for (it = map.constBegin(); it != map.constEnd(); ++it) { + const Method &m = it.value(); + sum += m.inputTypes.size() + qMax(1, m.outputTypes.size()); + } + return sum; +} +void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) +{ // this code here is mostly copied from qaxbase.cpp // with a few modifications to make it cleaner @@ -396,8 +395,14 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) QVarLengthArray<int> idata; idata.resize(sizeof(QDBusMetaObjectPrivate) / sizeof(int)); + int methodParametersDataSize = + ((aggregateParameterCount(signals_) + + aggregateParameterCount(methods)) * 2) // types and parameter names + - signals_.count() // return "parameters" don't have names + - methods.count(); // ditto + QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data()); - Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "QtDBus meta-object generator should generate the same version as moc"); + Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 7, "QtDBus meta-object generator should generate the same version as moc"); header->revision = QMetaObjectPrivate::OutputRevision; header->className = 0; header->classInfoCount = 0; @@ -405,7 +410,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) header->methodCount = signals_.count() + methods.count(); header->methodData = idata.size(); header->propertyCount = properties.count(); - header->propertyData = header->methodData + header->methodCount * 5; + header->propertyData = header->methodData + header->methodCount * 5 + methodParametersDataSize; header->enumeratorCount = 0; header->enumeratorData = 0; header->constructorCount = 0; @@ -417,7 +422,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) header->methodDBusData = header->propertyDBusData + header->propertyCount * intsPerProperty; int data_size = idata.size() + - (header->methodCount * (5+intsPerMethod)) + + (header->methodCount * (5+intsPerMethod)) + methodParametersDataSize + (header->propertyCount * (3+intsPerProperty)); foreach (const Method &mm, signals_) data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); @@ -425,10 +430,11 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) data_size += 2 + mm.inputTypes.count() + mm.outputTypes.count(); idata.resize(data_size + 1); - MetaStringTable strings; + QMetaStringTable strings; strings.enter(className.toLatin1()); int offset = header->methodData; + int parametersOffset = offset + header->methodCount * 5; int signatureOffset = header->methodDBusData; int typeidOffset = header->methodDBusData + header->methodCount * intsPerMethod; idata[typeidOffset++] = 0; // eod @@ -439,16 +445,45 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) QMap<QByteArray, Method> &map = (x == 0) ? signals_ : methods; for (QMap<QByteArray, Method>::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) { - // form "prototype\0parameters\0typeName\0tag\0methodname\0" const Method &mm = it.value(); - idata[offset++] = strings.enter(it.key()); // prototype - idata[offset++] = strings.enter(mm.parameters); - idata[offset++] = strings.enter(mm.typeName); + int argc = mm.inputTypes.size() + qMax(0, mm.outputTypes.size() - 1); + + idata[offset++] = strings.enter(mm.name); + idata[offset++] = argc; + idata[offset++] = parametersOffset; idata[offset++] = strings.enter(mm.tag); idata[offset++] = mm.flags; - idata[signatureOffset++] = strings.enter(mm.name); + // Parameter types + for (int i = -1; i < argc; ++i) { + int type; + QByteArray typeName; + if (i < 0) { // Return type + if (!mm.outputTypes.isEmpty()) + type = mm.outputTypes.first(); + else + type = QMetaType::Void; + } else if (i < mm.inputTypes.size()) { + type = mm.inputTypes.at(i); + } else { + Q_ASSERT(mm.outputTypes.size() > 1); + type = mm.outputTypes.at(i - mm.inputTypes.size() + 1); + // Output parameters are references; type id not available + typeName = QMetaType::typeName(type); + typeName.append('&'); + } + Q_ASSERT(type != QMetaType::UnknownType); + int typeInfo; + if (!typeName.isEmpty()) + typeInfo = IsUnresolvedType | strings.enter(typeName); + else + typeInfo = type; + idata[parametersOffset++] = typeInfo; + } + // Parameter names + for (int i = 0; i < argc; ++i) + idata[parametersOffset++] = strings.enter(mm.parameterNames.at(i)); idata[signatureOffset++] = typeidOffset; idata[typeidOffset++] = mm.inputTypes.count(); @@ -462,9 +497,12 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) } } - Q_ASSERT(offset == header->propertyData); + Q_ASSERT(offset == header->methodData + header->methodCount * 5); + Q_ASSERT(parametersOffset = header->propertyData); Q_ASSERT(signatureOffset == header->methodDBusData + header->methodCount * intsPerMethod); Q_ASSERT(typeidOffset == idata.size()); + offset += methodParametersDataSize; + Q_ASSERT(offset == header->propertyData); // add each property signatureOffset = header->propertyDBusData; @@ -472,9 +510,10 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) it != properties.constEnd(); ++it) { const Property &mp = it.value(); - // form is "name\0typeName\0signature\0" + // form is name, typeinfo, flags idata[offset++] = strings.enter(it.key()); // name - idata[offset++] = strings.enter(mp.typeName); + Q_ASSERT(mp.type != QMetaType::UnknownType); + idata[offset++] = mp.type; idata[offset++] = mp.flags; idata[signatureOffset++] = strings.enter(mp.signature); @@ -484,14 +523,8 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) Q_ASSERT(offset == header->propertyDBusData); Q_ASSERT(signatureOffset == header->methodDBusData); - char *string_data = new char[strings.arraySize()]; - { - MetaStringTable::const_iterator it; - for (it = strings.constBegin(); it != strings.constEnd(); ++it) { - memcpy(string_data + it.value(), it.key().constData(), it.key().size()); - string_data[it.value() + it.key().size()] = '\0'; - } - } + char *string_data = new char[strings.blobSize()]; + strings.writeBlob(string_data); uint *uint_data = new uint[idata.size()]; memcpy(uint_data, idata.data(), idata.size() * sizeof(int)); @@ -499,7 +532,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) // put the metaobject together obj->d.data = uint_data; obj->d.extradata = 0; - obj->d.stringdata = string_data; + obj->d.stringdata = reinterpret_cast<const QByteArrayData *>(string_data); obj->d.superdata = &QDBusAbstractInterface::staticMetaObject; } @@ -613,22 +646,12 @@ static inline const QDBusMetaObjectPrivate *priv(const uint* data) return reinterpret_cast<const QDBusMetaObjectPrivate *>(data); } -const char *QDBusMetaObject::dbusNameForMethod(int id) const -{ - //id -= methodOffset(); - if (id >= 0 && id < priv(d.data)->methodCount) { - int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return d.stringdata + d.data[handle]; - } - return 0; -} - const int *QDBusMetaObject::inputTypesForMethod(int id) const { //id -= methodOffset(); if (id >= 0 && id < priv(d.data)->methodCount) { int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return reinterpret_cast<const int*>(d.data + d.data[handle + 1]); + return reinterpret_cast<const int*>(d.data + d.data[handle]); } return 0; } @@ -638,7 +661,7 @@ const int *QDBusMetaObject::outputTypesForMethod(int id) const //id -= methodOffset(); if (id >= 0 && id < priv(d.data)->methodCount) { int handle = priv(d.data)->methodDBusData + id*intsPerMethod; - return reinterpret_cast<const int*>(d.data + d.data[handle + 2]); + return reinterpret_cast<const int*>(d.data + d.data[handle + 1]); } return 0; } diff --git a/src/dbus/qdbusmetaobject_p.h b/src/dbus/qdbusmetaobject_p.h index 7a8de41fa0..98d6105c72 100644 --- a/src/dbus/qdbusmetaobject_p.h +++ b/src/dbus/qdbusmetaobject_p.h @@ -71,12 +71,11 @@ struct Q_DBUS_EXPORT QDBusMetaObject: public QMetaObject QDBusError &error); ~QDBusMetaObject() { - delete [] d.stringdata; + delete [] reinterpret_cast<const char *>(d.stringdata); delete [] d.data; } // methods (slots & signals): - const char *dbusNameForMethod(int id) const; const int *inputTypesForMethod(int id) const; const int *outputTypesForMethod(int id) const; diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index b0d640608a..7c8d8cf679 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -311,7 +311,7 @@ bool QDBusMetaType::demarshall(const QDBusArgument &arg, int id, void *data) int QDBusMetaType::signatureToType(const char *signature) { if (!signature) - return QVariant::Invalid; + return QMetaType::UnknownType; QDBusMetaTypeId::init(); switch (signature[0]) @@ -378,7 +378,7 @@ int QDBusMetaType::signatureToType(const char *signature) } // fall through default: - return QVariant::Invalid; + return QMetaType::UnknownType; } } diff --git a/src/dbus/qdbusmisc.cpp b/src/dbus/qdbusmisc.cpp index 7d68bf1185..88bab88a13 100644 --- a/src/dbus/qdbusmisc.cpp +++ b/src/dbus/qdbusmisc.cpp @@ -141,7 +141,7 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes) for ( ; it != end; ++it) { const QByteArray &type = *it; if (type.endsWith('*')) { - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // pointer? return -1; } @@ -152,7 +152,7 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes) int id = QMetaType::type(basictype); if (id == 0) { - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // invalid type in method parameter list return -1; } else if (QDBusMetaType::typeToSignature(id) == 0) @@ -164,14 +164,14 @@ int qDBusParametersForMethod(const QMetaMethod &mm, QList<int>& metaTypes) } if (seenMessage) { // && !type.endsWith('&') - //qWarning("Could not parse the method '%s'", mm.signature()); + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // non-output parameters after message or after output params return -1; // not allowed } int id = QMetaType::type(type); - if (id == 0) { - //qWarning("Could not parse the method '%s'", mm.signature()); + if (id == QMetaType::UnknownType) { + //qWarning("Could not parse the method '%s'", mm.methodSignature().constData()); // invalid type in method parameter list return -1; } diff --git a/src/dbus/qdbusxmlgenerator.cpp b/src/dbus/qdbusxmlgenerator.cpp index a6572b2c86..550c82a0f2 100644 --- a/src/dbus/qdbusxmlgenerator.cpp +++ b/src/dbus/qdbusxmlgenerator.cpp @@ -126,7 +126,7 @@ static QString generateInterfaceXml(const QMetaObject *mo, int flags, int method // now add methods: for (int i = methodOffset; i < mo->methodCount(); ++i) { QMetaMethod mm = mo->method(i); - QByteArray signature = mm.signature(); + QByteArray signature = mm.methodSignature(); int paren = signature.indexOf('('); bool isSignal; diff --git a/src/gui/accessible/qaccessibleobject.cpp b/src/gui/accessible/qaccessibleobject.cpp index e587ad077c..1f4e005dfe 100644 --- a/src/gui/accessible/qaccessibleobject.cpp +++ b/src/gui/accessible/qaccessibleobject.cpp @@ -78,10 +78,10 @@ QList<QByteArray> QAccessibleObjectPrivate::actionList() const continue; if (!qstrcmp(member.tag(), "QACCESSIBLE_SLOT")) { - if (member.signature() == defaultAction) + if (member.methodSignature() == defaultAction) actionList.prepend(defaultAction); else - actionList << member.signature(); + actionList << member.methodSignature(); } } diff --git a/src/testlib/qsignaldumper.cpp b/src/testlib/qsignaldumper.cpp index 4fd870b644..c7b9f31b84 100644 --- a/src/testlib/qsignaldumper.cpp +++ b/src/testlib/qsignaldumper.cpp @@ -66,7 +66,7 @@ enum { IndentSpacesCount = 4 }; static QByteArray memberName(const QMetaMethod &member) { - QByteArray ba = member.signature(); + QByteArray ba = member.methodSignature(); return ba.left(ba.indexOf('(')); } @@ -112,7 +112,8 @@ static void qSignalDumperCallback(QObject *caller, int method_index, void **argv quintptr addr = quintptr(*reinterpret_cast<void **>(argv[i + 1])); str.append(QByteArray::number(addr, 16)); - } else if (typeId != QMetaType::Void) { + } else if (typeId != QMetaType::UnknownType) { + Q_ASSERT(typeId != QMetaType::Void); // void parameter => metaobject is corrupt str.append(arg) .append('(') .append(QVariant(typeId, argv[i + 1]).toString().toLocal8Bit()) @@ -152,7 +153,7 @@ static void qSignalDumperCallbackSlot(QObject *caller, int method_index, void ** str += QByteArray::number(quintptr(caller), 16); str += ") "; - str += member.signature(); + str += member.methodSignature(); qPrintMessage(str); } diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h index eb52ebf706..70944baadf 100644 --- a/src/testlib/qsignalspy.h +++ b/src/testlib/qsignalspy.h @@ -122,9 +122,11 @@ private: QList<QByteArray> params = member.parameterTypes(); for (int i = 0; i < params.count(); ++i) { int tp = QMetaType::type(params.at(i).constData()); - if (tp == QMetaType::Void) + if (tp == QMetaType::UnknownType) { + Q_ASSERT(tp != QMetaType::Void); // void parameter => metaobject is corrupt qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.", params.at(i).constData()); + } args << tp; } } diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 5aac97cfab..c1d19b8098 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1061,7 +1061,8 @@ static bool isValidSlot(const QMetaMethod &sl) if (sl.access() != QMetaMethod::Private || !sl.parameterTypes().isEmpty() || qstrlen(sl.typeName()) || sl.methodType() != QMetaMethod::Slot) return false; - const char *sig = sl.signature(); + QByteArray signature = sl.methodSignature(); + const char *sig = signature.constData(); int len = qstrlen(sig); if (len < 2) return false; @@ -1084,7 +1085,7 @@ static void qPrintTestSlots(FILE *stream) for (int i = 0; i < QTest::currentTestObject->metaObject()->methodCount(); ++i) { QMetaMethod sl = QTest::currentTestObject->metaObject()->method(i); if (isValidSlot(sl)) - fprintf(stream, "%s\n", sl.signature()); + fprintf(stream, "%s\n", sl.methodSignature().constData()); } } @@ -1109,7 +1110,7 @@ static void qPrintDataTags(FILE *stream) // Retrieve local tags: QStringList localTags; QTestTable table; - char *slot = qstrdup(tf.signature()); + char *slot = qstrdup(tf.methodSignature().constData()); slot[strlen(slot) - 2] = '\0'; QByteArray member; member.resize(qstrlen(slot) + qstrlen("_data()") + 1); @@ -1781,7 +1782,7 @@ static void qInvokeTestMethods(QObject *testObject) if (QTest::testFuncs) { for (int i = 0; i != QTest::testFuncCount; i++) { - if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).signature(), + if (!qInvokeTestMethod(metaObject->method(QTest::testFuncs[i].function()).methodSignature().constData(), QTest::testFuncs[i].data())) { break; } @@ -1795,7 +1796,7 @@ static void qInvokeTestMethods(QObject *testObject) for (int i = 0; i != methodCount; i++) { if (!isValidSlot(testMethods[i])) continue; - if (!qInvokeTestMethod(testMethods[i].signature())) + if (!qInvokeTestMethod(testMethods[i].methodSignature().constData())) break; } delete[] testMethods; diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index ac602fd6e8..1284518fc5 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -54,30 +54,37 @@ QT_BEGIN_NAMESPACE -uint qvariant_nameToType(const QByteArray &name) +uint nameToBuiltinType(const QByteArray &name) { if (name.isEmpty()) return 0; uint tp = QMetaType::type(name.constData()); - return tp < QMetaType::User ? tp : 0; + return tp < uint(QMetaType::User) ? tp : QMetaType::UnknownType; } /* - Returns true if the type is a QVariant types. + Returns true if the type is a built-in type. */ -bool isVariantType(const QByteArray &type) -{ - return qvariant_nameToType(type) != 0; +bool isBuiltinType(const QByteArray &type) + { + int id = QMetaType::type(type.constData()); + if (id == QMetaType::UnknownType) + return false; + return (id < QMetaType::User); } -/*! - Returns true if the type is qreal. -*/ -static bool isQRealType(const QByteArray &type) -{ - return (type == "qreal"); -} +static const char *metaTypeEnumValueString(int type) + { +#define RETURN_METATYPENAME_STRING(MetaTypeName, MetaTypeId, RealType) \ + case QMetaType::MetaTypeName: return #MetaTypeName; + + switch (type) { +QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) + } +#undef RETURN_METATYPENAME_STRING + return 0; + } Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, FILE *outfile) : out(outfile), cdef(classDef), metaTypes(metaTypes) @@ -109,24 +116,28 @@ static inline int lengthOfEscapeSequence(const QByteArray &s, int i) return i - startPos; } -int Generator::strreg(const QByteArray &s) +void Generator::strreg(const QByteArray &s) { - int idx = 0; - for (int i = 0; i < strings.size(); ++i) { - const QByteArray &str = strings.at(i); - if (str == s) - return idx; - idx += str.length() + 1; - for (int i = 0; i < str.length(); ++i) { - if (str.at(i) == '\\') { - int cnt = lengthOfEscapeSequence(str, i) - 1; - idx -= cnt; - i += cnt; - } - } - } - strings.append(s); - return idx; + if (!strings.contains(s)) + strings.append(s); +} + +int Generator::stridx(const QByteArray &s) +{ + int i = strings.indexOf(s); + Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings"); + return i; +} + +// Returns the sum of all parameters (including return type) for the given +// \a list of methods. This is needed for calculating the size of the methods' +// parameter type/name meta-data. +static int aggregateParameterCount(const QList<FunctionDef> &list) +{ + int sum = 0; + for (int i = 0; i < list.count(); ++i) + sum += list.at(i).arguments.count() + 1; // +1 for return type + return sum; } void Generator::generateCode() @@ -135,10 +146,6 @@ void Generator::generateCode() bool isQObject = (cdef->classname == "QObject"); bool isConstructible = !cdef->constructorList.isEmpty(); -// -// build the data array -// - // filter out undeclared enumerators and sets { QList<EnumDef> enumList; @@ -156,15 +163,119 @@ void Generator::generateCode() cdef->enumList = enumList; } +// +// Register all strings used in data section +// + strreg(cdef->qualified); + registerClassInfoStrings(); + registerFunctionStrings(cdef->signalList); + registerFunctionStrings(cdef->slotList); + registerFunctionStrings(cdef->methodList); + registerFunctionStrings(cdef->constructorList); + registerPropertyStrings(); + registerEnumStrings(); QByteArray qualifiedClassNameIdentifier = cdef->qualified; qualifiedClassNameIdentifier.replace(':', '_'); +// +// Build stringdata struct +// + fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData()); + fprintf(out, " QByteArrayData data[%d];\n", strings.size()); + { + int len = 0; + for (int i = 0; i < strings.size(); ++i) + len += strings.at(i).length() + 1; + fprintf(out, " char stringdata[%d];\n", len + 1); + } + fprintf(out, "};\n"); + + // Macro that expands into a QByteArrayData. The offset member is + // calculated from 1) the offset of the actual characters in the + // stringdata.stringdata member, and 2) the stringdata.data index of the + // QByteArrayData being defined. This calculation relies on the + // QByteArrayData::data() implementation returning simply "this + offset". + fprintf(out, "#define QT_MOC_LITERAL(idx, ofs, len) { \\\n" + " Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \\\n" + " offsetof(qt_meta_stringdata_%s_t, stringdata) + ofs \\\n" + " - idx * sizeof(QByteArrayData) \\\n" + " }\n", + qualifiedClassNameIdentifier.constData()); + + fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n", + qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + fprintf(out, " {\n"); + { + int idx = 0; + for (int i = 0; i < strings.size(); ++i) { + if (i) + fprintf(out, ",\n"); + const QByteArray &str = strings.at(i); + fprintf(out, "QT_MOC_LITERAL(%d, %d, %d)", i, idx, str.length()); + idx += str.length() + 1; + for (int j = 0; j < str.length(); ++j) { + if (str.at(j) == '\\') { + int cnt = lengthOfEscapeSequence(str, j) - 1; + idx -= cnt; + j += cnt; + } + } + } + fprintf(out, "\n },\n"); + } + +// +// Build stringdata array +// + fprintf(out, " \""); + int col = 0; + int len = 0; + for (int i = 0; i < strings.size(); ++i) { + QByteArray s = strings.at(i); + len = s.length(); + if (col && col + len >= 72) { + fprintf(out, "\"\n \""); + col = 0; + } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { + fprintf(out, "\"\""); + len += 2; + } + int idx = 0; + while (idx < s.length()) { + if (idx > 0) { + col = 0; + fprintf(out, "\"\n \""); + } + int spanLen = qMin(70, s.length() - idx); + // don't cut escape sequences at the end of a line + int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); + if (backSlashPos >= idx) { + int escapeLen = lengthOfEscapeSequence(s, backSlashPos); + spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); + } + fwrite(s.constData() + idx, 1, spanLen, out); + idx += spanLen; + col += spanLen; + } + + fputs("\\0", out); + col += len + 2; + } + +// Terminate stringdata struct + fprintf(out, "\"\n};\n"); + fprintf(out, "#undef QT_MOC_LITERAL\n\n"); + +// +// build the data array +// + int index = MetaObjectPrivateFieldCount; fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData()); fprintf(out, "\n // content:\n"); fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision)); - fprintf(out, " %4d, // classname\n", strreg(cdef->qualified)); + fprintf(out, " %4d, // classname\n", stridx(cdef->qualified)); fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0); index += cdef->classInfoList.count() * 2; @@ -173,6 +284,15 @@ void Generator::generateCode() index += methodCount * 5; if (cdef->revisionedMethods) index += methodCount; + int paramsIndex = index; + int totalParameterCount = aggregateParameterCount(cdef->signalList) + + aggregateParameterCount(cdef->slotList) + + aggregateParameterCount(cdef->methodList) + + aggregateParameterCount(cdef->constructorList); + index += totalParameterCount * 2 // types and parameter names + - methodCount // return "parameters" don't have names + - cdef->constructorList.count(); // "this" parameters don't have names + fprintf(out, " %4d, %4d, // properties\n", cdef->propertyList.count(), cdef->propertyList.count() ? index : 0); index += cdef->propertyList.count() * 3; if(cdef->notifyableProperties) @@ -199,17 +319,17 @@ void Generator::generateCode() // // Build signals array first, otherwise the signal indices would be wrong // - generateFunctions(cdef->signalList, "signal", MethodSignal); + generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex); // // Build slots array // - generateFunctions(cdef->slotList, "slot", MethodSlot); + generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex); // // Build method array // - generateFunctions(cdef->methodList, "method", MethodMethod); + generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex); // // Build method version arrays @@ -221,6 +341,15 @@ void Generator::generateCode() } // +// Build method parameters array +// + generateFunctionParameters(cdef->signalList, "signal"); + generateFunctionParameters(cdef->slotList, "slot"); + generateFunctionParameters(cdef->methodList, "method"); + if (isConstructible) + generateFunctionParameters(cdef->constructorList, "constructor"); + +// // Build property array // generateProperties(); @@ -234,7 +363,7 @@ void Generator::generateCode() // Build constructors array // if (isConstructible) - generateFunctions(cdef->constructorList, "constructor", MethodConstructor); + generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex); // // Terminate data array @@ -242,46 +371,6 @@ void Generator::generateCode() fprintf(out, "\n 0 // eod\n};\n\n"); // -// Build stringdata array -// - fprintf(out, "static const char qt_meta_stringdata_%s[] = {\n", qualifiedClassNameIdentifier.constData()); - fprintf(out, " \""); - int col = 0; - int len = 0; - for (int i = 0; i < strings.size(); ++i) { - QByteArray s = strings.at(i); - len = s.length(); - if (col && col + len >= 72) { - fprintf(out, "\"\n \""); - col = 0; - } else if (len && s.at(0) >= '0' && s.at(0) <= '9') { - fprintf(out, "\"\""); - len += 2; - } - int idx = 0; - while (idx < s.length()) { - if (idx > 0) { - col = 0; - fprintf(out, "\"\n \""); - } - int spanLen = qMin(70, s.length() - idx); - // don't cut escape sequences at the end of a line - int backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1); - if (backSlashPos >= idx) { - int escapeLen = lengthOfEscapeSequence(s, backSlashPos); - spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, s.length() - idx); - } - fwrite(s.constData() + idx, 1, spanLen, out); - idx += spanLen; - col += spanLen; - } - - fputs("\\0", out); - col += len + 2; - } - fprintf(out, "\"\n};\n\n"); - -// // Generate internal qt_static_metacall() function // if (cdef->hasQObject && !isQt) @@ -293,7 +382,7 @@ void Generator::generateCode() QList<QByteArray> extraList; for (int i = 0; i < cdef->propertyList.count(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); - if (!isVariantType(p.type) && !metaTypes.contains(p.type) && !p.type.contains('*') && + if (!isBuiltinType(p.type) && !metaTypes.contains(p.type) && !p.type.contains('*') && !p.type.contains('<') && !p.type.contains('>')) { int s = p.type.lastIndexOf("::"); if (s > 0) { @@ -356,8 +445,9 @@ void Generator::generateCode() fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData()); else fprintf(out, " { 0, "); - fprintf(out, "qt_meta_stringdata_%s,\n qt_meta_data_%s, ", - qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData()); + fprintf(out, "qt_meta_stringdata_%s.data,\n" + " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(), + qualifiedClassNameIdentifier.constData()); if (!hasExtraData) fprintf(out, "0 }\n"); else @@ -379,7 +469,7 @@ void Generator::generateCode() // fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData()); fprintf(out, " if (!_clname) return 0;\n"); - fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s))\n" + fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata))\n" " return static_cast<void*>(const_cast< %s*>(this));\n", qualifiedClassNameIdentifier.constData(), cdef->classname.constData()); for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one @@ -430,6 +520,15 @@ void Generator::generateCode() } +void Generator::registerClassInfoStrings() +{ + for (int i = 0; i < cdef->classInfoList.size(); ++i) { + const ClassInfoDef &c = cdef->classInfoList.at(i); + strreg(c.name); + strreg(c.value); + } +} + void Generator::generateClassInfos() { if (cdef->classInfoList.isEmpty()) @@ -439,32 +538,37 @@ void Generator::generateClassInfos() for (int i = 0; i < cdef->classInfoList.size(); ++i) { const ClassInfoDef &c = cdef->classInfoList.at(i); - fprintf(out, " %4d, %4d,\n", strreg(c.name), strreg(c.value)); + fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value)); } } -void Generator::generateFunctions(const QList<FunctionDef>& list, const char *functype, int type) +void Generator::registerFunctionStrings(const QList<FunctionDef>& list) { - if (list.isEmpty()) - return; - fprintf(out, "\n // %ss: signature, parameters, type, tag, flags\n", functype); - for (int i = 0; i < list.count(); ++i) { const FunctionDef &f = list.at(i); - QByteArray sig = f.name + '('; - QByteArray arguments; + strreg(f.name); + if (!isBuiltinType(f.normalizedType)) + strreg(f.normalizedType); + strreg(f.tag); for (int j = 0; j < f.arguments.count(); ++j) { const ArgumentDef &a = f.arguments.at(j); - if (j) { - sig += ","; - arguments += ","; - } - sig += a.normalizedType; - arguments += a.name; + if (!isBuiltinType(a.normalizedType)) + strreg(a.normalizedType); + strreg(a.name); } - sig += ')'; + } +} + +void Generator::generateFunctions(const QList<FunctionDef>& list, const char *functype, int type, int ¶msIndex) +{ + if (list.isEmpty()) + return; + fprintf(out, "\n // %ss: name, argc, parameters, tag, flags\n", functype); + + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); unsigned char flags = type; if (f.access == FunctionDef::Private) @@ -487,8 +591,12 @@ void Generator::generateFunctions(const QList<FunctionDef>& list, const char *fu flags |= MethodScriptable; if (f.revision > 0) flags |= MethodRevisioned; - fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", strreg(sig), - strreg(arguments), strreg(f.normalizedType), strreg(f.tag), flags); + + int argc = f.arguments.count(); + fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x,\n", + stridx(f.name), argc, paramsIndex, stridx(f.tag), flags); + + paramsIndex += 1 + argc * 2; } } @@ -502,6 +610,54 @@ void Generator::generateFunctionRevisions(const QList<FunctionDef>& list, const } } +void Generator::generateFunctionParameters(const QList<FunctionDef>& list, const char *functype) +{ + if (list.isEmpty()) + return; + fprintf(out, "\n // %ss: parameters\n", functype); + for (int i = 0; i < list.count(); ++i) { + const FunctionDef &f = list.at(i); + fprintf(out, " "); + + // Types + for (int j = -1; j < f.arguments.count(); ++j) { + if (j > -1) + fputc(' ', out); + const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType; + if (isBuiltinType(typeName)) { + int type = nameToBuiltinType(typeName); + const char *valueString = metaTypeEnumValueString(type); + if (valueString) + fprintf(out, "QMetaType::%s", valueString); + else + fprintf(out, "%4d", type); + } else { + Q_ASSERT(!typeName.isEmpty() || f.isConstructor); + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(typeName)); + } + fputc(',', out); + } + + // Parameter names + for (int j = 0; j < f.arguments.count(); ++j) { + const ArgumentDef &arg = f.arguments.at(j); + fprintf(out, " %4d,", stridx(arg.name)); + } + + fprintf(out, "\n"); + } +} + +void Generator::registerPropertyStrings() +{ + for (int i = 0; i < cdef->propertyList.count(); ++i) { + const PropertyDef &p = cdef->propertyList.at(i); + strreg(p.name); + if (!isBuiltinType(p.type)) + strreg(p.type); + } +} + void Generator::generateProperties() { // @@ -513,11 +669,8 @@ void Generator::generateProperties() for (int i = 0; i < cdef->propertyList.count(); ++i) { const PropertyDef &p = cdef->propertyList.at(i); uint flags = Invalid; - if (!isVariantType(p.type)) { + if (!isBuiltinType(p.type)) flags |= EnumOrFlag; - } else if (!isQRealType(p.type)) { - flags |= qvariant_nameToType(p.type) << 24; - } if (!p.read.isEmpty()) flags |= Readable; if (!p.write.isEmpty()) { @@ -567,12 +720,20 @@ void Generator::generateProperties() if (p.final) flags |= Final; - fprintf(out, " %4d, %4d, ", - strreg(p.name), - strreg(p.type)); - if (!(flags >> 24) && isQRealType(p.type)) - fprintf(out, "(QMetaType::QReal << 24) | "); - fprintf(out, "0x%.8x,\n", flags); + fprintf(out, " %4d, ", stridx(p.name)); + + if (isBuiltinType(p.type)) { + int type = nameToBuiltinType(p.type); + const char *valueString = metaTypeEnumValueString(type); + if (valueString) + fprintf(out, "QMetaType::%s", valueString); + else + fprintf(out, "%4d", type); + } else { + fprintf(out, "0x%.8x | %d", IsUnresolvedType, stridx(p.type)); + } + + fprintf(out, ", 0x%.8x,\n", flags); } if(cdef->notifyableProperties) { @@ -596,6 +757,16 @@ void Generator::generateProperties() } } +void Generator::registerEnumStrings() +{ + for (int i = 0; i < cdef->enumList.count(); ++i) { + const EnumDef &e = cdef->enumList.at(i); + strreg(e.name); + for (int j = 0; j < e.values.count(); ++j) + strreg(e.values.at(j)); + } +} + void Generator::generateEnums(int index) { if (cdef->enumDeclarations.isEmpty()) @@ -607,7 +778,7 @@ void Generator::generateEnums(int index) for (i = 0; i < cdef->enumList.count(); ++i) { const EnumDef &e = cdef->enumList.at(i); fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n", - strreg(e.name), + stridx(e.name), cdef->enumDeclarations.value(e.name) ? 1 : 0, e.values.count(), index); @@ -624,7 +795,7 @@ void Generator::generateEnums(int index) code += "::" + e.name; code += "::" + val; fprintf(out, " %4d, uint(%s),\n", - strreg(val), code.constData()); + stridx(val), code.constData()); } } } @@ -926,8 +1097,9 @@ void Generator::generateStaticMetacall() fprintf(out, " switch (_id) {\n"); for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) { const FunctionDef &f = methodList.at(methodindex); + Q_ASSERT(!f.normalizedType.isEmpty()); fprintf(out, " case %d: ", methodindex); - if (f.normalizedType.size()) + if (f.normalizedType != "void") fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData()); fprintf(out, "_t->"); if (f.inPrivateClass.size()) @@ -942,7 +1114,7 @@ void Generator::generateStaticMetacall() isUsed_a = true; } fprintf(out, ");"); - if (f.normalizedType.size()) { + if (f.normalizedType != "void") { fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ", noRef(f.normalizedType).constData()); isUsed_a = true; @@ -1021,7 +1193,8 @@ void Generator::generateSignal(FunctionDef *def,int index) constQualifier = "const"; } - if (def->arguments.isEmpty() && def->normalizedType.isEmpty()) { + Q_ASSERT(!def->normalizedType.isEmpty()); + if (def->arguments.isEmpty() && def->normalizedType == "void") { fprintf(out, ")%s\n{\n" " QMetaObject::activate(%s, &staticMetaObject, %d, 0);\n" "}\n", constQualifier, thisPtr.constData(), index); @@ -1036,11 +1209,11 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, "%s _t%d%s", a.type.name.constData(), offset++, a.rightType.constData()); } fprintf(out, ")%s\n{\n", constQualifier); - if (def->type.name.size() && def->normalizedType.size()) + if (def->type.name.size() && def->normalizedType != "void") fprintf(out, " %s _t0 = %s();\n", noRef(def->normalizedType).constData(), noRef(def->normalizedType).constData()); fprintf(out, " void *_a[] = { "); - if (def->normalizedType.isEmpty()) { + if (def->normalizedType == "void") { fprintf(out, "0"); } else { if (def->returnTypeIsVolatile) @@ -1056,7 +1229,7 @@ void Generator::generateSignal(FunctionDef *def,int index) fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i); fprintf(out, " };\n"); fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index); - if (def->normalizedType.size()) + if (def->normalizedType != "void") fprintf(out, " return _t0;\n"); fprintf(out, "}\n"); } diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 46eee4ca06..c85d24fd15 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -55,17 +55,23 @@ public: Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, FILE *outfile = 0); void generateCode(); private: + void registerClassInfoStrings(); void generateClassInfos(); - void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type); + void registerFunctionStrings(const QList<FunctionDef> &list); + void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, int ¶msIndex); void generateFunctionRevisions(const QList<FunctionDef>& list, const char *functype); + void generateFunctionParameters(const QList<FunctionDef> &list, const char *functype); + void registerEnumStrings(); void generateEnums(int index); + void registerPropertyStrings(); void generateProperties(); void generateMetacall(); void generateStaticMetacall(); void generateSignal(FunctionDef *def, int index); void generatePluginMetaData(); - int strreg(const QByteArray &); // registers a string and returns its id + void strreg(const QByteArray &); // registers a string + int stridx(const QByteArray &); // returns a string's id QList<QByteArray> strings; QByteArray purestSuperClass; QList<QByteArray> metaTypes; diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index 4189c29de1..e85aadbba7 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -75,9 +75,7 @@ static QByteArray normalizeType(const QByteArray &ba, bool fixScope = false) } } *d = '\0'; - QByteArray result; - if (strncmp("void", buf, d - buf) != 0) - result = normalizeTypeInternal(buf, d, fixScope); + QByteArray result = normalizeTypeInternal(buf, d, fixScope); if (buf != stackbuf) delete [] buf; return result; @@ -822,8 +820,8 @@ void Moc::generate(FILE *out) if (classList.size() && classList.first().classname == "Qt") fprintf(out, "#include <QtCore/qobject.h>\n"); - if (mustIncludeQMetaTypeH) - fprintf(out, "#include <QtCore/qmetatype.h>\n"); + fprintf(out, "#include <QtCore/qbytearray.h>\n"); // For QByteArrayData + fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type if (mustIncludeQPluginH) fprintf(out, "#include <QtCore/qplugin.h>\n"); @@ -981,8 +979,6 @@ void Moc::createPropertyDef(PropertyDef &propDef) type = "qlonglong"; else if (type == "ULongLong") type = "qulonglong"; - else if (type == "qreal") - mustIncludeQMetaTypeH = true; propDef.type = type; diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h index 9e9225da0a..b12ec492ff 100644 --- a/src/tools/moc/moc.h +++ b/src/tools/moc/moc.h @@ -200,7 +200,7 @@ class Moc : public Parser public: Moc(Preprocessor &p) : preprocessor(p), noInclude(false), generatedCode(false), - mustIncludeQMetaTypeH(false), mustIncludeQPluginH(false) + mustIncludeQPluginH(false) {} QByteArray filename; @@ -208,7 +208,6 @@ public: Preprocessor &preprocessor; bool noInclude; bool generatedCode; - bool mustIncludeQMetaTypeH; bool mustIncludeQPluginH; QByteArray includePath; QList<QByteArray> includeFiles; diff --git a/src/tools/moc/outputrevision.h b/src/tools/moc/outputrevision.h index 2ce5b9b765..cff0f98fca 100644 --- a/src/tools/moc/outputrevision.h +++ b/src/tools/moc/outputrevision.h @@ -43,6 +43,6 @@ #define OUTPUTREVISION_H // if the output revision changes, you MUST change it in qobjectdefs.h too -enum { mocOutputRevision = 64 }; // moc format output revision +enum { mocOutputRevision = 65 }; // moc format output revision #endif // OUTPUTREVISION_H diff --git a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp index 1651d00738..f44a671180 100644 --- a/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp +++ b/tests/auto/corelib/kernel/qmetamethod/tst_qmetamethod.cpp @@ -223,7 +223,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject()") << QByteArray("MethodTestObject()") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>()) << (QList<QByteArray>()) << (QList<QByteArray>()) @@ -259,7 +259,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(int)") << QByteArray("MethodTestObject(int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("int")) << (QList<QByteArray>() << QByteArray("constructorIntArg")) @@ -295,7 +295,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(qreal)") << QByteArray("MethodTestObject(qreal)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << qMetaTypeId<qreal>()) << (QList<QByteArray>() << QByteArray("qreal")) << (QList<QByteArray>() << QByteArray("constructorQRealArg")) @@ -331,7 +331,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(QString)") << QByteArray("MethodTestObject(QString)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::QString)) << (QList<QByteArray>() << QByteArray("QString")) << (QList<QByteArray>() << QByteArray("constructorQStringArg")) @@ -367,7 +367,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomType)") << QByteArray("MethodTestObject(CustomType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << qMetaTypeId<CustomType>()) << (QList<QByteArray>() << QByteArray("CustomType")) << (QList<QByteArray>() << QByteArray("constructorCustomTypeArg")) @@ -403,7 +403,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(CustomUnregisteredType)") << QByteArray("MethodTestObject(CustomUnregisteredType)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << 0) << (QList<QByteArray>() << QByteArray("CustomUnregisteredType")) << (QList<QByteArray>() << QByteArray("constructorCustomUnregisteredTypeArg")) @@ -536,7 +536,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") << QByteArray("MethodTestObject(bool,int,uint,qlonglong,qulonglong,double,long,short,char,ulong,ushort,uchar,float)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << parameterTypes << parameterTypeNames << parameterNames << QMetaMethod::Public << QMetaMethod::Constructor; @@ -571,7 +571,7 @@ void tst_QMetaMethod::method_data() QTest::newRow("MethodTestObject(bool,int)") << QByteArray("MethodTestObject(bool,int)") - << int(QMetaType::Void) << QByteArray("") + << int(QMetaType::UnknownType) << QByteArray("") << (QList<int>() << int(QMetaType::Bool) << int(QMetaType::Int)) << (QList<QByteArray>() << QByteArray("bool") << QByteArray("int")) << (QList<QByteArray>() << QByteArray("") << QByteArray("")) @@ -603,15 +603,50 @@ void tst_QMetaMethod::method() QCOMPARE(method.methodType(), methodType); QCOMPARE(method.access(), access); - QCOMPARE(method.signature(), signature.constData()); + QVERIFY(!method.methodSignature().isEmpty()); + if (method.methodSignature() != signature) { + // QMetaMethod should always produce a semantically equivalent signature + int signatureIndex = (methodType == QMetaMethod::Constructor) + ? mo->indexOfConstructor(method.methodSignature()) + : mo->indexOfMethod(method.methodSignature()); + QCOMPARE(signatureIndex, index); + } - QCOMPARE(method.tag(), ""); + QByteArray computedName = signature.left(signature.indexOf('(')); + QCOMPARE(method.name(), computedName); - QCOMPARE(method.typeName(), returnTypeName.constData()); - QCOMPARE(QMetaType::type(method.typeName()), returnType); + QCOMPARE(method.tag(), ""); + QCOMPARE(method.returnType(), returnType); + if (QByteArray(method.typeName()) != returnTypeName) { + // QMetaMethod should always produce a semantically equivalent typename + QCOMPARE(QMetaType::type(method.typeName()), QMetaType::type(returnTypeName)); + } - QCOMPARE(method.parameterTypes(), parameterTypeNames); + if (method.parameterTypes() != parameterTypeNames) { + // QMetaMethod should always produce semantically equivalent typenames + QList<QByteArray> actualTypeNames = method.parameterTypes(); + QCOMPARE(actualTypeNames.size(), parameterTypeNames.size()); + for (int i = 0; i < parameterTypeNames.size(); ++i) { + QCOMPARE(QMetaType::type(actualTypeNames.at(i)), + QMetaType::type(parameterTypeNames.at(i))); + } + } QCOMPARE(method.parameterNames(), parameterNames); + + QCOMPARE(method.parameterCount(), parameterTypes.size()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(method.parameterType(i), parameterTypes.at(i)); + + { + QVector<int> actualParameterTypes(parameterTypes.size()); + method.getParameterTypes(actualParameterTypes.data()); + for (int i = 0; i < parameterTypes.size(); ++i) + QCOMPARE(actualParameterTypes.at(i), parameterTypes.at(i)); + } + + // Bogus indexes + QCOMPARE(method.parameterType(-1), 0); + QCOMPARE(method.parameterType(parameterTypes.size()), 0); } void tst_QMetaMethod::invalidMethod() diff --git a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp index 09fd0a7adb..b6b68338cd 100644 --- a/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp +++ b/tests/auto/corelib/kernel/qmetaobject/tst_qmetaobject.cpp @@ -978,25 +978,25 @@ void tst_QMetaObject::propertyNotify() QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); QMetaMethod signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value6Changed()"); + QCOMPARE(signal.methodSignature(), QByteArray("value6Changed()")); prop = mo->property(mo->indexOfProperty("value7")); QVERIFY(prop.isValid()); QVERIFY(prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), "value7Changed(QString)"); + QCOMPARE(signal.methodSignature(), QByteArray("value7Changed(QString)")); prop = mo->property(mo->indexOfProperty("value8")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); prop = mo->property(mo->indexOfProperty("value")); QVERIFY(prop.isValid()); QVERIFY(!prop.hasNotifySignal()); signal = prop.notifySignal(); - QCOMPARE(signal.signature(), (const char *)0); + QCOMPARE(signal.methodSignature(), QByteArray()); } void tst_QMetaObject::propertyConstant() @@ -1114,7 +1114,7 @@ void tst_QMetaObject::indexOfMethod() QFETCH(bool, isSignal); int idx = object->metaObject()->indexOfMethod(name); QVERIFY(idx >= 0); - QCOMPARE(object->metaObject()->method(idx).signature(), name.constData()); + QCOMPARE(object->metaObject()->method(idx).methodSignature(), name); QCOMPARE(object->metaObject()->indexOfSlot(name), isSignal ? -1 : idx); QCOMPARE(object->metaObject()->indexOfSignal(name), !isSignal ? -1 : idx); } diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 97b14a374e..f187425c84 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -217,6 +217,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(nullMethod.signature(), QByteArray()); QVERIFY(nullMethod.methodType() == QMetaMethod::Method); QVERIFY(nullMethod.returnType().isEmpty()); + QVERIFY(nullMethod.parameterTypes().isEmpty()); QVERIFY(nullMethod.parameterNames().isEmpty()); QVERIFY(nullMethod.tag().isEmpty()); QVERIFY(nullMethod.access() == QMetaMethod::Public); @@ -229,6 +230,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -242,6 +244,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -267,6 +270,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -276,6 +280,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("int")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -296,6 +301,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Method); QCOMPARE(method1.returnType(), QByteArray("int")); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(method1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(method1.tag(), QByteArray("tag")); QVERIFY(method1.access() == QMetaMethod::Private); @@ -305,6 +311,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -320,6 +327,7 @@ void tst_QMetaObjectBuilder::method() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Method); QCOMPARE(method2.returnType(), QByteArray("QString")); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(method2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(method2.tag(), QByteArray("Q_FOO")); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -347,6 +355,7 @@ void tst_QMetaObjectBuilder::slot() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Slot); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Public); @@ -359,6 +368,7 @@ void tst_QMetaObjectBuilder::slot() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Slot); QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Public); @@ -384,6 +394,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method1.signature(), QByteArray("foo(QString,int)")); QVERIFY(method1.methodType() == QMetaMethod::Signal); QVERIFY(method1.returnType().isEmpty()); + QCOMPARE(method1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(method1.parameterNames().isEmpty()); QVERIFY(method1.tag().isEmpty()); QVERIFY(method1.access() == QMetaMethod::Protected); @@ -396,6 +407,7 @@ void tst_QMetaObjectBuilder::signal() QCOMPARE(method2.signature(), QByteArray("bar(QString)")); QVERIFY(method2.methodType() == QMetaMethod::Signal); QVERIFY(method2.returnType().isEmpty()); + QCOMPARE(method2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(method2.parameterNames().isEmpty()); QVERIFY(method2.tag().isEmpty()); QVERIFY(method2.access() == QMetaMethod::Protected); @@ -421,6 +433,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QVERIFY(ctor1.returnType().isEmpty()); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QVERIFY(ctor1.parameterNames().isEmpty()); QVERIFY(ctor1.tag().isEmpty()); QVERIFY(ctor1.access() == QMetaMethod::Public); @@ -433,6 +446,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -458,6 +472,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -466,6 +481,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QVERIFY(ctor2.returnType().isEmpty()); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QVERIFY(ctor2.parameterNames().isEmpty()); QVERIFY(ctor2.tag().isEmpty()); QVERIFY(ctor2.access() == QMetaMethod::Public); @@ -484,6 +500,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor1.signature(), QByteArray("foo(QString,int)")); QVERIFY(ctor1.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor1.returnType(), QByteArray("int")); + QCOMPARE(ctor1.parameterTypes(), QList<QByteArray>() << "QString" << "int"); QCOMPARE(ctor1.parameterNames(), QList<QByteArray>() << "a" << "b"); QCOMPARE(ctor1.tag(), QByteArray("tag")); QVERIFY(ctor1.access() == QMetaMethod::Private); @@ -492,6 +509,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -506,6 +524,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(ctor2.signature(), QByteArray("bar(QString)")); QVERIFY(ctor2.methodType() == QMetaMethod::Constructor); QCOMPARE(ctor2.returnType(), QByteArray("QString")); + QCOMPARE(ctor2.parameterTypes(), QList<QByteArray>() << "QString"); QCOMPARE(ctor2.parameterNames(), QList<QByteArray>() << "c"); QCOMPARE(ctor2.tag(), QByteArray("Q_FOO")); QVERIFY(ctor2.access() == QMetaMethod::Protected); @@ -525,6 +544,7 @@ void tst_QMetaObjectBuilder::constructor() QCOMPARE(prototypeConstructor.signature(), QByteArray("SomethingOfEverything()")); QVERIFY(prototypeConstructor.methodType() == QMetaMethod::Constructor); QCOMPARE(prototypeConstructor.returnType(), QByteArray()); + QVERIFY(prototypeConstructor.parameterTypes().isEmpty()); QVERIFY(prototypeConstructor.access() == QMetaMethod::Public); QCOMPARE(prototypeConstructor.index(), 1); @@ -1161,12 +1181,15 @@ bool tst_QMetaObjectBuilder::checkForSideEffects static bool sameMethod(const QMetaMethod& method1, const QMetaMethod& method2) { - if (QByteArray(method1.signature()) != QByteArray(method2.signature())) + if (method1.methodSignature() != method2.methodSignature()) return false; if (QByteArray(method1.typeName()) != QByteArray(method2.typeName())) return false; + if (method1.parameterTypes() != method2.parameterTypes()) + return false; + if (method1.parameterNames() != method2.parameterNames()) return false; @@ -1466,7 +1489,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break; default: { QMetaMethod ctor = _o->metaObject()->constructor(_id); - qFatal("You forgot to add a case for CreateInstance %s", ctor.signature()); + qFatal("You forgot to add a case for CreateInstance %s", ctor.methodSignature().constData()); } } } else if (_c == QMetaObject::InvokeMetaMethod) { @@ -1478,7 +1501,7 @@ void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, case 2: *reinterpret_cast<QVariantList(*)>(_a[0]) = _t->listInvokableQRealQString(*reinterpret_cast<qreal(*)>(_a[1]), *reinterpret_cast<QString(*)>(_a[2])); break; default: { QMetaMethod method = _o->metaObject()->method(_o->metaObject()->methodOffset() + _id); - qFatal("You forgot to add a case for InvokeMetaMethod %s", method.signature()); + qFatal("You forgot to add a case for InvokeMetaMethod %s", method.methodSignature().constData()); } } } else if (_c == QMetaObject::IndexOfMethod) { diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 572ecc9ba8..84d28c7959 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -332,6 +332,7 @@ void tst_QMetaType::typeName_data() QT_FOR_EACH_STATIC_TYPE(TYPENAME_DATA) QT_FOR_EACH_STATIC_ALIAS_TYPE(TYPENAME_DATA_ALIAS) + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << static_cast<const char*>(0); } void tst_QMetaType::typeName() @@ -642,6 +643,8 @@ void tst_QMetaType::sizeOf_data() { QTest::addColumn<QMetaType::Type>("type"); QTest::addColumn<int>("size"); + + QTest::newRow("QMetaType::UnknownType") << QMetaType::UnknownType << 0; #define ADD_METATYPE_TEST_ROW(MetaTypeName, MetaTypeId, RealType) \ QTest::newRow(#RealType) << QMetaType::MetaTypeName << int(QTypeInfo<RealType>::sizeOf); FOR_EACH_CORE_METATYPE(ADD_METATYPE_TEST_ROW) @@ -1040,6 +1043,7 @@ void tst_QMetaType::isRegistered_data() QTest::newRow("-1") << -1 << false; QTest::newRow("-42") << -42 << false; QTest::newRow("IsRegisteredDummyType + 1") << (dummyTypeId + 1) << false; + QTest::newRow("QMetaType::UnknownType") << int(QMetaType::UnknownType) << false; } void tst_QMetaType::isRegistered() diff --git a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp index 2d180b88ea..021079a8e7 100644 --- a/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp +++ b/tests/auto/corelib/kernel/qobject/moc_oldnormalizeobject.cpp @@ -90,7 +90,7 @@ static const char qt_meta_stringdata_OldNormalizeObject[] = { }; const QMetaObject OldNormalizeObject::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_OldNormalizeObject, + { &QObject::staticMetaObject, reinterpret_cast<const QByteArrayData *>(qt_meta_stringdata_OldNormalizeObject), qt_meta_data_OldNormalizeObject, 0 } }; diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index a6ad1d53bc..c6667ff2a8 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -1793,56 +1793,56 @@ void tst_QObject::metamethod() QMetaMethod m; m = mobj->method(mobj->indexOfMethod("invoke1()")); - QVERIFY(QByteArray(m.signature()) == "invoke1()"); + QVERIFY(m.methodSignature() == "invoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke1()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke1()"); + QVERIFY(m.methodSignature() == "sinvoke1()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke2()")); - QVERIFY(QByteArray(m.signature()) == "invoke2()"); + QVERIFY(m.methodSignature() == "invoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke2()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke2()"); + QVERIFY(m.methodSignature() == "sinvoke2()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("invoke3()")); - QVERIFY(QByteArray(m.signature()) == "invoke3()"); + QVERIFY(m.methodSignature() == "invoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("sinvoke3()")); - QVERIFY(QByteArray(m.signature()) == "sinvoke3()"); + QVERIFY(m.methodSignature() == "sinvoke3()"); QVERIFY(m.methodType() == QMetaMethod::Method); QVERIFY(m.access() == QMetaMethod::Private); QVERIFY((m.attributes() & QMetaMethod::Scriptable)); QVERIFY(!(m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("signal5()")); - QVERIFY(QByteArray(m.signature()) == "signal5()"); + QVERIFY(m.methodSignature() == "signal5()"); QVERIFY(m.methodType() == QMetaMethod::Signal); QVERIFY(m.access() == QMetaMethod::Protected); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); QVERIFY((m.attributes() & QMetaMethod::Compatibility)); m = mobj->method(mobj->indexOfMethod("aPublicSlot()")); - QVERIFY(QByteArray(m.signature()) == "aPublicSlot()"); + QVERIFY(m.methodSignature() == "aPublicSlot()"); QVERIFY(m.methodType() == QMetaMethod::Slot); QVERIFY(m.access() == QMetaMethod::Public); QVERIFY(!(m.attributes() & QMetaMethod::Scriptable)); diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 5aa1389b65..c48a384e58 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -3557,6 +3557,10 @@ void tst_QVariant::loadQVariantFromDataStream(QDataStream::Version version) stream >> typeName >> loadedVariant; const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QVariant constructedVariant(static_cast<QVariant::Type>(id)); QCOMPARE(constructedVariant.userType(), id); @@ -3576,6 +3580,10 @@ void tst_QVariant::saveQVariantFromDataStream(QDataStream::Version version) dataFileStream >> typeName; QByteArray data = file.readAll(); const int id = QMetaType::type(typeName.toLatin1()); + if (id == QMetaType::Void) { + // Void type is not supported by QVariant + return; + } QBuffer buffer; buffer.open(QIODevice::ReadWrite); @@ -3636,7 +3644,9 @@ void tst_QVariant::debugStream_data() const char *tagName = QMetaType::typeName(id); if (!tagName) continue; - QTest::newRow(tagName) << QVariant(static_cast<QVariant::Type>(id)) << id; + if (id != QMetaType::Void) { + QTest::newRow(tagName) << QVariant(static_cast<QVariant::Type>(id)) << id; + } } QTest::newRow("QBitArray(111)") << QVariant(QBitArray(3, true)) << qMetaTypeId<QBitArray>(); QTest::newRow("CustomStreamableClass") << QVariant(qMetaTypeId<CustomStreamableClass>(), 0) << qMetaTypeId<CustomStreamableClass>(); diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro new file mode 100644 index 0000000000..8e368117fa --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/qarraydata.pro @@ -0,0 +1,5 @@ +TARGET = tst_qarraydata +SOURCES += tst_qarraydata.cpp +HEADERS += simplevector.h +QT = core testlib +CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h new file mode 100644 index 0000000000..fe8108bff2 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QARRAY_TEST_SIMPLE_VECTOR_H +#define QARRAY_TEST_SIMPLE_VECTOR_H + +#include <QtCore/qarraydata.h> +#include <QtCore/qarraydatapointer.h> + +#include <algorithm> + +template <class T> +struct SimpleVector +{ +private: + typedef QTypedArrayData<T> Data; + +public: + typedef T value_type; + typedef typename Data::iterator iterator; + typedef typename Data::const_iterator const_iterator; + + SimpleVector() + { + } + + SimpleVector(size_t n, const T &t) + : d(Data::allocate(n)) + { + if (n) + d->copyAppend(n, t); + } + + SimpleVector(const T *begin, const T *end) + : d(Data::allocate(end - begin)) + { + if (end - begin) + d->copyAppend(begin, end); + } + + SimpleVector(QArrayDataPointerRef<T> ptr) + : d(ptr) + { + } + + explicit SimpleVector(Data *ptr) + : d(ptr) + { + } + + bool empty() const { return d->size == 0; } + bool isNull() const { return d.isNull(); } + bool isEmpty() const { return this->empty(); } + + bool isStatic() const { return d->ref.isStatic(); } + bool isShared() const { return d->ref.isShared(); } + bool isSharedWith(const SimpleVector &other) const { return d == other.d; } + bool isSharable() const { return d->ref.isSharable(); } + + void setSharable(bool sharable) { d.setSharable(sharable); } + + size_t size() const { return d->size; } + size_t capacity() const { return d->alloc; } + + iterator begin() { detach(); return d->begin(); } + iterator end() { detach(); return d->end(); } + + const_iterator begin() const { return d->begin(); } + const_iterator end() const { return d->end(); } + + const_iterator constBegin() const { return begin(); } + const_iterator constEnd() const { return end(); } + + T &operator[](size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + T &at(size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + + const T &operator[](size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + const T &at(size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + + T &front() + { + Q_ASSERT(!isEmpty()); + detach(); + return *begin(); + } + + T &back() + { + Q_ASSERT(!isEmpty()); + detach(); + return *(end() - 1); + } + + const T &front() const + { + Q_ASSERT(!isEmpty()); + return *begin(); + } + + const T &back() const + { + Q_ASSERT(!isEmpty()); + return *(end() - 1); + } + + void reserve(size_t n) + { + if (n == 0) + return; + + if (n <= capacity()) { + if (d->capacityReserved) + return; + if (!d->ref.isShared()) { + d->capacityReserved = 1; + return; + } + } + + SimpleVector detached(Data::allocate(qMax(n, size()), + d->detachFlags() | Data::CapacityReserved)); + if (size()) + detached.d->copyAppend(constBegin(), constEnd()); + detached.swap(*this); + } + + void prepend(const_iterator first, const_iterator last) + { + if (!d->size) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags() | Data::Grow)); + + detached.d->copyAppend(first, last); + detached.d->copyAppend(begin, begin + d->size); + detached.swap(*this); + + return; + } + + d->insert(begin, first, last); + } + + void append(const_iterator first, const_iterator last) + { + if (first == last) + return; + + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags() | Data::Grow)); + + if (d->size) { + const T *const begin = constBegin(); + detached.d->copyAppend(begin, begin + d->size); + } + detached.d->copyAppend(first, last); + detached.swap(*this); + + return; + } + + d->copyAppend(first, last); + } + + void insert(int position, const_iterator first, const_iterator last) + { + if (position < 0) + position += d->size + 1; + + if (position <= 0) { + prepend(first, last); + return; + } + + if (size_t(position) >= size()) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + T *const where = begin + position; + const T *const end = begin + d->size; + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags() | Data::Grow)); + + if (position) + detached.d->copyAppend(begin, where); + detached.d->copyAppend(first, last); + detached.d->copyAppend(where, end); + detached.swap(*this); + + return; + } + + if ((first >= where && first < end) + || (last > where && last <= end)) { + // Copy overlapping data first and only then shuffle it into place + T *start = d->begin() + position; + T *middle = d->end(); + + d->copyAppend(first, last); + std::rotate(start, middle, d->end()); + + return; + } + + d->insert(where, first, last); + } + + void swap(SimpleVector &other) + { + qSwap(d, other.d); + } + + void clear() + { + d.clear(); + } + + void detach() + { + d.detach(); + } + + static SimpleVector fromRawData(const T *data, size_t size, + QArrayData::AllocationOptions options = Data::Default) + { + return SimpleVector(Data::fromRawData(data, size, options)); + } + +private: + QArrayDataPointer<T> d; +}; + +template <class T> +bool operator==(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + if (lhs.isSharedWith(rhs)) + return true; + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template <class T> +bool operator!=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs == rhs); +} + +template <class T> +bool operator<(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template <class T> +bool operator>(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return rhs < lhs; +} + +template <class T> +bool operator<=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(rhs < lhs); +} + +template <class T> +bool operator>=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs < rhs); +} + +namespace std { + template <class T> + void swap(SimpleVector<T> &v1, SimpleVector<T> &v2) + { + v1.swap(v2); + } +} + +#endif // include guard diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp new file mode 100644 index 0000000000..884f4f7d1d --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -0,0 +1,1464 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/QString> +#include <QtCore/qarraydata.h> + +#include "simplevector.h" + +struct SharedNullVerifier +{ + SharedNullVerifier() + { + Q_ASSERT(QArrayData::shared_null.ref.isStatic()); + Q_ASSERT(QArrayData::shared_null.ref.isShared()); + Q_ASSERT(QArrayData::shared_null.ref.isSharable()); + } +}; + +// This is meant to verify/ensure that shared_null is not being dynamically +// initialized and stays away from the order-of-static-initialization fiasco. +// +// Of course, if this was to fail, qmake and the build should have crashed and +// burned before we ever got to this point :-) +SharedNullVerifier globalInit; + +class tst_QArrayData : public QObject +{ + Q_OBJECT + +private slots: + void referenceCounting(); + void sharedNullEmpty(); + void staticData(); + void simpleVector(); + void simpleVectorReserve_data(); + void simpleVectorReserve(); + void allocate_data(); + void allocate(); + void alignment_data(); + void alignment(); + void typedData(); + void gccBug43247(); + void arrayOps(); + void setSharable_data(); + void setSharable(); + void fromRawData(); + void literals(); + void variadicLiterals(); + void rValueReferences(); + void grow(); +}; + +template <class T> const T &const_(const T &t) { return t; } + +void tst_QArrayData::referenceCounting() +{ + { + // Reference counting initialized to 1 (owned) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 1); + + QVERIFY(!array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 0); + + // Now would be a good time to free/release allocated data + } + + { + // Reference counting initialized to 0 (non-sharable) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), 0); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(!array.ref.isSharable()); + + QVERIFY(!array.ref.ref()); + // Reference counting fails, data should be copied + QCOMPARE(array.ref.atomic.load(), 0); + + QVERIFY(!array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), 0); + + // Free/release data + } + + { + // Reference counting initialized to -1 (static read-only data) + QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + + QCOMPARE(array.ref.atomic.load(), -1); + + QVERIFY(array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(array.ref.atomic.load(), -1); + + QVERIFY(array.ref.deref()); + QCOMPARE(array.ref.atomic.load(), -1); + } +} + +void tst_QArrayData::sharedNullEmpty() +{ + QArrayData *null = const_cast<QArrayData *>(&QArrayData::shared_null); + QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0); + + QVERIFY(null->ref.isStatic()); + QVERIFY(null->ref.isSharable()); + QVERIFY(null->ref.isShared()); + + QVERIFY(empty->ref.isStatic()); + QVERIFY(empty->ref.isSharable()); + QVERIFY(empty->ref.isShared()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null->ref.ref()); + QVERIFY(empty->ref.ref()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null->ref.deref()); + QVERIFY(empty->ref.deref()); + + QCOMPARE(null->ref.atomic.load(), -1); + QCOMPARE(empty->ref.atomic.load(), -1); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(null->alloc, 0u); + QCOMPARE(null->capacityReserved, 0u); + + QCOMPARE(empty->size, 0); + QCOMPARE(empty->alloc, 0u); + QCOMPARE(empty->capacityReserved, 0u); +} + +void tst_QArrayData::staticData() +{ + QStaticArrayData<char, 10> charArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10), + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + }; + QStaticArrayData<int, 10> intArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QStaticArrayData<double, 10> doubleArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(double, 10), + { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f } + }; + + QCOMPARE(charArray.header.size, 10); + QCOMPARE(intArray.header.size, 10); + QCOMPARE(doubleArray.header.size, 10); + + QCOMPARE(charArray.header.data(), reinterpret_cast<void *>(&charArray.data)); + QCOMPARE(intArray.header.data(), reinterpret_cast<void *>(&intArray.data)); + QCOMPARE(doubleArray.header.data(), reinterpret_cast<void *>(&doubleArray.data)); +} + +void tst_QArrayData::simpleVector() +{ + QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + QStaticArrayData<int, 7> data1 = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), + { 0, 1, 2, 3, 4, 5, 6 } + }; + + int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + SimpleVector<int> v1; + SimpleVector<int> v2(v1); + SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v4(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v7(10, 5); + SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + + v3 = v1; + v1.swap(v3); + v4.clear(); + + QVERIFY(v1.isNull()); + QVERIFY(v2.isNull()); + QVERIFY(v3.isNull()); + QVERIFY(v4.isNull()); + QVERIFY(!v5.isNull()); + QVERIFY(!v6.isNull()); + QVERIFY(!v7.isNull()); + QVERIFY(!v8.isNull()); + + QVERIFY(v1.isEmpty()); + QVERIFY(v2.isEmpty()); + QVERIFY(v3.isEmpty()); + QVERIFY(v4.isEmpty()); + QVERIFY(v5.isEmpty()); + QVERIFY(!v6.isEmpty()); + QVERIFY(!v7.isEmpty()); + QVERIFY(!v8.isEmpty()); + + QCOMPARE(v1.size(), size_t(0)); + QCOMPARE(v2.size(), size_t(0)); + QCOMPARE(v3.size(), size_t(0)); + QCOMPARE(v4.size(), size_t(0)); + QCOMPARE(v5.size(), size_t(0)); + QCOMPARE(v6.size(), size_t(7)); + QCOMPARE(v7.size(), size_t(10)); + QCOMPARE(v8.size(), size_t(10)); + + QCOMPARE(v1.capacity(), size_t(0)); + QCOMPARE(v2.capacity(), size_t(0)); + QCOMPARE(v3.capacity(), size_t(0)); + QCOMPARE(v4.capacity(), size_t(0)); + QCOMPARE(v5.capacity(), size_t(0)); + // v6.capacity() is unspecified, for now + QVERIFY(v7.capacity() >= size_t(10)); + QVERIFY(v8.capacity() >= size_t(10)); + + QVERIFY(v1.isStatic()); + QVERIFY(v2.isStatic()); + QVERIFY(v3.isStatic()); + QVERIFY(v4.isStatic()); + QVERIFY(v5.isStatic()); + QVERIFY(v6.isStatic()); + QVERIFY(!v7.isStatic()); + QVERIFY(!v8.isStatic()); + + QVERIFY(v1.isShared()); + QVERIFY(v2.isShared()); + QVERIFY(v3.isShared()); + QVERIFY(v4.isShared()); + QVERIFY(v5.isShared()); + QVERIFY(v6.isShared()); + QVERIFY(!v7.isShared()); + QVERIFY((SimpleVector<int>(v7), v7.isShared())); + QVERIFY(!v7.isShared()); + QVERIFY(!v8.isShared()); + + QVERIFY(v1.isSharable()); + QVERIFY(v2.isSharable()); + QVERIFY(v3.isSharable()); + QVERIFY(v4.isSharable()); + QVERIFY(v5.isSharable()); + QVERIFY(v6.isSharable()); + QVERIFY(v7.isSharable()); + QVERIFY(v8.isSharable()); + + QVERIFY(v1.isSharedWith(v2)); + QVERIFY(v1.isSharedWith(v3)); + QVERIFY(v1.isSharedWith(v4)); + QVERIFY(!v1.isSharedWith(v5)); + QVERIFY(!v1.isSharedWith(v6)); + + QVERIFY(v1.constBegin() == v1.constEnd()); + QVERIFY(v4.constBegin() == v4.constEnd()); + QVERIFY(v6.constBegin() + v6.size() == v6.constEnd()); + QVERIFY(v7.constBegin() + v7.size() == v7.constEnd()); + QVERIFY(v8.constBegin() + v8.size() == v8.constEnd()); + + QVERIFY(v1 == v2); + QVERIFY(v1 == v3); + QVERIFY(v1 == v4); + QVERIFY(v1 == v5); + QVERIFY(!(v1 == v6)); + + QVERIFY(v1 != v6); + QVERIFY(v4 != v6); + QVERIFY(v5 != v6); + QVERIFY(!(v1 != v5)); + + QVERIFY(v1 < v6); + QVERIFY(!(v6 < v1)); + QVERIFY(v6 > v1); + QVERIFY(!(v1 > v6)); + QVERIFY(v1 <= v6); + QVERIFY(!(v6 <= v1)); + QVERIFY(v6 >= v1); + QVERIFY(!(v1 >= v6)); + + { + SimpleVector<int> temp(v6); + + QCOMPARE(const_(v6).front(), 0); + QCOMPARE(const_(v6).back(), 6); + + QVERIFY(temp.isShared()); + QVERIFY(temp.isSharedWith(v6)); + + QCOMPARE(temp.front(), 0); + QCOMPARE(temp.back(), 6); + + // Detached + QVERIFY(!temp.isShared()); + const int *const tempBegin = temp.begin(); + + for (size_t i = 0; i < v6.size(); ++i) { + QCOMPARE(const_(v6)[i], int(i)); + QCOMPARE(const_(v6).at(i), int(i)); + QCOMPARE(&const_(v6)[i], &const_(v6).at(i)); + + QCOMPARE(const_(v8)[i], const_(v6)[i]); + + QCOMPARE(temp[i], int(i)); + QCOMPARE(temp.at(i), int(i)); + QCOMPARE(&temp[i], &temp.at(i)); + } + + // A single detach should do + QCOMPARE(temp.begin(), tempBegin); + } + + { + int count = 0; + Q_FOREACH (int value, v7) { + QCOMPARE(value, 5); + ++count; + } + + QCOMPARE(count, 10); + } + + { + int count = 0; + Q_FOREACH (int value, v8) { + QCOMPARE(value, count); + ++count; + } + + QCOMPARE(count, 10); + } + + v5 = v6; + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + + v1.swap(v6); + QVERIFY(v6.isNull()); + QVERIFY(v1.isSharedWith(v5)); + + { + using std::swap; + swap(v1, v6); + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + } + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.clear(); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.insert(0, v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(30)); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.insert(10, v6.constBegin(), v6.constEnd()); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(60)); + QCOMPARE(v6.size(), size_t(30)); + + for (int i = 0; i < 30; ++i) + QCOMPARE(v6[i], v8[i % 10]); + + v1.insert(v1.size(), v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(90)); + + v1.insert(-1, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(100)); + + v1.insert(-11, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(110)); + + v1.insert(-200, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(120)); + + for (int i = 0; i < 120; ++i) + QCOMPARE(v1[i], v8[i % 10]); + + { + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy1(v7); + QVERIFY(copy1.isSharedWith(v7)); + + v7.setSharable(false); + QVERIFY(!v7.isSharable()); + + QVERIFY(!copy1.isSharedWith(v7)); + QCOMPARE(v7.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(v7[i], copy1[i]); + + SimpleVector<int> clone(v7); + QVERIFY(!clone.isSharedWith(v7)); + QCOMPARE(clone.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(clone[i], copy1[i]); + + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy2(v7); + QVERIFY(copy2.isSharedWith(v7)); + } + + { + SimpleVector<int> null; + SimpleVector<int> empty(0, 5); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(false); + empty.setSharable(false); + + QVERIFY(!null.isSharable()); + QVERIFY(!empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + } +} + +Q_DECLARE_METATYPE(SimpleVector<int>) + +void tst_QArrayData::simpleVectorReserve_data() +{ + QTest::addColumn<SimpleVector<int> >("vector"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<size_t>("size"); + + QTest::newRow("null") << SimpleVector<int>() << size_t(0) << size_t(0); + QTest::newRow("empty") << SimpleVector<int>(0, 42) << size_t(0) << size_t(0); + QTest::newRow("non-empty") << SimpleVector<int>(5, 42) << size_t(5) << size_t(5); + + static const QStaticArrayData<int, 15> array = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 15), + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; + QArrayDataPointerRef<int> p = { + static_cast<QTypedArrayData<int> *>( + const_cast<QArrayData *>(&array.header)) }; + + QTest::newRow("static") << SimpleVector<int>(p) << size_t(0) << size_t(15); + QTest::newRow("raw-data") << SimpleVector<int>::fromRawData(array.data, 15) << size_t(0) << size_t(15); +} + +void tst_QArrayData::simpleVectorReserve() +{ + QFETCH(SimpleVector<int>, vector); + QFETCH(size_t, capacity); + QFETCH(size_t, size); + + QVERIFY(!capacity || capacity >= size); + + QCOMPARE(vector.capacity(), capacity); + QCOMPARE(vector.size(), size); + + const SimpleVector<int> copy(vector); + + vector.reserve(0); + QCOMPARE(vector.capacity(), capacity); + QCOMPARE(vector.size(), size); + + vector.reserve(10); + + // zero-capacity (immutable) resets with detach + if (!capacity) + capacity = size; + + QCOMPARE(vector.capacity(), qMax(size_t(10), capacity)); + QCOMPARE(vector.size(), size); + + vector.reserve(20); + QCOMPARE(vector.capacity(), size_t(20)); + QCOMPARE(vector.size(), size); + + vector.reserve(30); + QCOMPARE(vector.capacity(), size_t(30)); + QCOMPARE(vector.size(), size); + + QVERIFY(vector == copy); +} + +struct Deallocator +{ + Deallocator(size_t objectSize, size_t alignment) + : objectSize(objectSize) + , alignment(alignment) + { + } + + ~Deallocator() + { + Q_FOREACH (QArrayData *data, headers) + QArrayData::deallocate(data, objectSize, alignment); + } + + size_t objectSize; + size_t alignment; + QVector<QArrayData *> headers; +}; + +Q_DECLARE_METATYPE(const QArrayData *) +Q_DECLARE_METATYPE(QArrayData::AllocationOptions) + +void tst_QArrayData::allocate_data() +{ + QTest::addColumn<size_t>("objectSize"); + QTest::addColumn<size_t>("alignment"); + QTest::addColumn<QArrayData::AllocationOptions>("allocateOptions"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<bool>("isSharable"); + QTest::addColumn<const QArrayData *>("commonEmpty"); + + struct { + char const *typeName; + size_t objectSize; + size_t alignment; + } types[] = { + { "char", sizeof(char), Q_ALIGNOF(char) }, + { "short", sizeof(short), Q_ALIGNOF(short) }, + { "void *", sizeof(void *), Q_ALIGNOF(void *) } + }; + + QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0); + QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable); + + QVERIFY(shared_empty); + QVERIFY(unsharable_empty); + + struct { + char const *description; + QArrayData::AllocationOptions allocateOptions; + bool isCapacityReserved; + bool isSharable; + const QArrayData *commonEmpty; + } options[] = { + { "Default", QArrayData::Default, false, true, shared_empty }, + { "Reserved", QArrayData::CapacityReserved, true, true, shared_empty }, + { "Reserved | Unsharable", + QArrayData::CapacityReserved | QArrayData::Unsharable, true, false, + unsharable_empty }, + { "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty }, + { "Grow", QArrayData::Grow, false, true, shared_empty } + }; + + for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) + for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j) + QTest::newRow(qPrintable( + QLatin1String(types[i].typeName) + + QLatin1String(": ") + + QLatin1String(options[j].description))) + << types[i].objectSize << types[i].alignment + << options[j].allocateOptions << options[j].isCapacityReserved + << options[j].isSharable << options[j].commonEmpty; +} + +void tst_QArrayData::allocate() +{ + QFETCH(size_t, objectSize); + QFETCH(size_t, alignment); + QFETCH(QArrayData::AllocationOptions, allocateOptions); + QFETCH(bool, isCapacityReserved); + QFETCH(bool, isSharable); + QFETCH(const QArrayData *, commonEmpty); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + // Shared Empty + QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, + QArrayData::AllocationOptions(allocateOptions)), commonEmpty); + + Deallocator keeper(objectSize, minAlignment); + keeper.headers.reserve(1024); + + for (int capacity = 1; capacity <= 1024; capacity <<= 1) { + QArrayData *data = QArrayData::allocate(objectSize, minAlignment, + capacity, QArrayData::AllocationOptions(allocateOptions)); + keeper.headers.append(data); + + QCOMPARE(data->size, 0); + if (allocateOptions & QArrayData::Grow) + QVERIFY(data->alloc > uint(capacity)); + else + QCOMPARE(data->alloc, uint(capacity)); + QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); + QCOMPARE(data->ref.isSharable(), isSharable); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', objectSize * capacity); + } +} + +class Unaligned +{ + char dummy[8]; +}; + +void tst_QArrayData::alignment_data() +{ + QTest::addColumn<size_t>("alignment"); + + for (int i = 1; i < 10; ++i) { + size_t alignment = 1u << i; + QTest::newRow(qPrintable(QString::number(alignment))) << alignment; + } +} + +void tst_QArrayData::alignment() +{ + QFETCH(size_t, alignment); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + Deallocator keeper(sizeof(Unaligned), minAlignment); + keeper.headers.reserve(100); + + for (int i = 0; i < 100; ++i) { + QArrayData *data = QArrayData::allocate(sizeof(Unaligned), + minAlignment, 8, QArrayData::Default); + keeper.headers.append(data); + + QVERIFY(data); + QCOMPARE(data->size, 0); + QVERIFY(data->alloc >= uint(8)); + + // These conditions should hold as long as header and array are + // allocated together + QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData))); + QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData) + + minAlignment - Q_ALIGNOF(QArrayData))); + + // Data is aligned + QCOMPARE(quintptr(data->data()) % alignment, quintptr(0u)); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', sizeof(Unaligned) * 8); + } +} + +void tst_QArrayData::typedData() +{ + QStaticArrayData<int, 10> data = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QCOMPARE(data.header.size, 10); + + { + QTypedArrayData<int> *array = + static_cast<QTypedArrayData<int> *>(&data.header); + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + const QTypedArrayData<int> *array = + static_cast<const QTypedArrayData<int> *>(&data.header); + + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::const_iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + QTypedArrayData<int> *null = QTypedArrayData<int>::sharedNull(); + QTypedArrayData<int> *empty = QTypedArrayData<int>::allocate(0); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(empty->size, 0); + + QCOMPARE(null->begin(), null->end()); + QCOMPARE(empty->begin(), empty->end()); + } + + + { + Deallocator keeper(sizeof(char), + Q_ALIGNOF(QTypedArrayData<char>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<char>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(char)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(short), + Q_ALIGNOF(QTypedArrayData<short>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<short>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(short)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(double), + Q_ALIGNOF(QTypedArrayData<double>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<double>::allocate(10); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(double)); + + keeper.headers.clear(); + QTypedArrayData<double>::deallocate(array); + + QVERIFY(true); + } +} + +void tst_QArrayData::gccBug43247() +{ + // This test tries to verify QArrayData is not affected by GCC optimizer + // bug #43247. + // Reported on GCC 4.4.3, Linux, affects QVector + + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (3)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (4)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (5)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (6)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (7)"); + + SimpleVector<int> array(10, 0); + // QVector<int> vector(10, 0); + + for (int i = 0; i < 10; ++i) { + if (i >= 3 && i < 8) + qDebug("GCC Optimization bug #43247 not triggered (%i)", i); + + // When access to data is implemented through an array of size 1, this + // line lets the compiler assume i == 0, and the conditional above is + // skipped. + QVERIFY(array.at(i) == 0); + // QVERIFY(vector.at(i) == 0); + } +} + +struct CountedObject +{ + CountedObject() + : id(liveCount++) + { + } + + CountedObject(const CountedObject &other) + : id(other.id) + { + ++liveCount; + } + + ~CountedObject() + { + --liveCount; + } + + CountedObject &operator=(const CountedObject &other) + { + id = other.id; + return *this; + } + + struct LeakChecker + { + LeakChecker() + : previousLiveCount(liveCount) + { + } + + ~LeakChecker() + { + QCOMPARE(liveCount, previousLiveCount); + } + + private: + const size_t previousLiveCount; + }; + + size_t id; // not unique + static size_t liveCount; +}; + +size_t CountedObject::liveCount = 0; + +void tst_QArrayData::arrayOps() +{ + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + + const int intArray[5] = { 80, 101, 100, 114, 111 }; + const QString stringArray[5] = { + QLatin1String("just"), + QLatin1String("for"), + QLatin1String("testing"), + QLatin1String("a"), + QLatin1String("vector") + }; + const CountedObject objArray[5]; + + QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic); + QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic); + QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + for (size_t i = 0; i < 5; ++i) + QCOMPARE(objArray[i].id, i); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (I) + SimpleVector<int> vi(intArray, intArray + 5); + SimpleVector<QString> vs(stringArray, stringArray + 5); + SimpleVector<CountedObject> vo(objArray, objArray + 5); + + QCOMPARE(CountedObject::liveCount, size_t(10)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], intArray[i]); + QVERIFY(vs[i].isSharedWith(stringArray[i])); + QCOMPARE(vo[i].id, objArray[i].id); + } + + //////////////////////////////////////////////////////////////////////////// + // destroyAll + vi.clear(); + vs.clear(); + vo.clear(); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (II) + int referenceInt = 7; + QString referenceString = QLatin1String("reference"); + CountedObject referenceObject; + + vi = SimpleVector<int>(5, referenceInt); + vs = SimpleVector<QString>(5, referenceString); + vo = SimpleVector<CountedObject>(5, referenceObject); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QCOMPARE(CountedObject::liveCount, size_t(11)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + //////////////////////////////////////////////////////////////////////////// + // insert + vi.reserve(30); + vs.reserve(30); + vo.reserve(30); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QVERIFY(vi.capacity() >= 30); + QVERIFY(vs.capacity() >= 30); + QVERIFY(vo.capacity() >= 30); + + // Displace as many elements as array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(10)); + QCOMPARE(vs.size(), size_t(10)); + QCOMPARE(vo.size(), size_t(10)); + + // Displace more elements than array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(15)); + QCOMPARE(vs.size(), size_t(15)); + QCOMPARE(vo.size(), size_t(15)); + + // Displace less elements than array is extended by + vi.insert(5, vi.constBegin(), vi.constEnd()); + vs.insert(5, vs.constBegin(), vs.constEnd()); + vo.insert(5, vo.constBegin(), vo.constEnd()); + + QCOMPARE(vi.size(), size_t(30)); + QCOMPARE(vs.size(), size_t(30)); + QCOMPARE(vo.size(), size_t(30)); + + QCOMPARE(CountedObject::liveCount, size_t(36)); + for (int i = 0; i < 15; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 15; i < 20; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + for (int i = 20; i < 25; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 25; i < 30; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } +} + +Q_DECLARE_METATYPE(QArrayDataPointer<int>) + +static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array, + int fillValue, size_t size) +{ + const int *iter = array->begin(); + const int *const end = array->end(); + + for (size_t i = 0; i < size; ++i, ++iter) + if (*iter != fillValue) + return false; + + if (iter != end) + return false; + + return true; +} + +void tst_QArrayData::setSharable_data() +{ + QTest::addColumn<QArrayDataPointer<int> >("array"); + QTest::addColumn<size_t>("size"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<int>("fillValue"); + + QArrayDataPointer<int> null; + QArrayDataPointer<int> empty; empty.clear(); + + static QStaticArrayData<int, 10> staticArrayData = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + }; + + QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10, + QArrayData::Default)); + QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> staticArray( + static_cast<QTypedArrayData<int> *>(&staticArrayData.header)); + QArrayDataPointer<int> rawData( + QTypedArrayData<int>::fromRawData(staticArrayData.data, 10)); + + nonEmpty->copyAppend(5, 1); + nonEmptyReserved->copyAppend(7, 2); + + QTest::newRow("shared-null") << null << size_t(0) << size_t(0) << false << 0; + QTest::newRow("shared-empty") << empty << size_t(0) << size_t(0) << false << 0; + // unsharable-empty implicitly tested in shared-empty + QTest::newRow("empty-reserved") << emptyReserved << size_t(0) << size_t(5) << true << 0; + QTest::newRow("non-empty") << nonEmpty << size_t(5) << size_t(10) << false << 1; + QTest::newRow("non-empty-reserved") << nonEmptyReserved << size_t(7) << size_t(15) << true << 2; + QTest::newRow("static-array") << staticArray << size_t(10) << size_t(0) << false << 3; + QTest::newRow("raw-data") << rawData << size_t(10) << size_t(0) << false << 3; +} + +void tst_QArrayData::setSharable() +{ + QFETCH(QArrayDataPointer<int>, array); + QFETCH(size_t, size); + QFETCH(size_t, capacity); + QFETCH(bool, isCapacityReserved); + QFETCH(int, fillValue); + + QVERIFY(array->ref.isShared()); // QTest has a copy + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + // shared-null becomes shared-empty, may otherwise detach + array.setSharable(true); + + QVERIFY(array->ref.isSharable()); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QVERIFY(array->ref.isSharable()); + QCOMPARE(copy.data(), array.data()); + } + + // Unshare, must detach + array.setSharable(false); + + // Immutability (alloc == 0) is lost on detach + if (capacity == 0 && size != 0) + capacity = size; + + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + // Null/empty is always shared + QCOMPARE(copy->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(copy->ref.isSharable()); + + QCOMPARE(size_t(copy->size), size); + QCOMPARE(size_t(copy->alloc), capacity); + QCOMPARE(bool(copy->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(copy, fillValue, size)); + } + + // Make sharable, again + array.setSharable(true); + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QCOMPARE(copy.data(), array.data()); + } + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); +} + +void tst_QArrayData::fromRawData() +{ + static const int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + { + // Default: Immutable, sharable + SimpleVector<int> raw = SimpleVector<int>::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Default); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + QVERIFY(!raw.isShared()); + QVERIFY(SimpleVector<int>(raw).isSharedWith(raw)); + QVERIFY(!raw.isShared()); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } + + { + // Immutable, unsharable + SimpleVector<int> raw = SimpleVector<int>::fromRawData(array, + sizeof(array)/sizeof(array[0]), QArrayData::Unsharable); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + SimpleVector<int> copy(raw); + QVERIFY(!copy.isSharedWith(raw)); + QVERIFY(!raw.isShared()); + + QCOMPARE(copy.size(), size_t(11)); + + for (size_t i = 0; i < 11; ++i) + QCOMPARE(const_(copy)[i], const_(raw)[i]); + + QCOMPARE(raw.size(), size_t(11)); + QCOMPARE(raw.constBegin(), array); + QCOMPARE(raw.constEnd(), array + sizeof(array)/sizeof(array[0])); + + // Detach + QCOMPARE(raw.back(), 11); + QVERIFY(raw.constBegin() != array); + } +} + +void tst_QArrayData::literals() +{ + { + QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + // wchar_t is not necessarily 2-bytes + QArrayDataPointer<wchar_t> d = Q_ARRAY_LITERAL(wchar_t, L"ABCDEFGHIJ"); + QCOMPARE(d->size, 10 + 1); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], wchar_t('A' + i)); + } + + { + SimpleVector<char> v = Q_ARRAY_LITERAL(char, "ABCDEFGHIJ"); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(11)); + // v.capacity() is unspecified, for now + +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + QVERIFY(v.isStatic()); +#endif + + QVERIFY(v.isSharable()); + QCOMPARE(v.constBegin() + v.size(), v.constEnd()); + + for (int i = 0; i < 10; ++i) + QCOMPARE(const_(v)[i], char('A' + i)); + QCOMPARE(const_(v)[10], char('\0')); + } +} + +void tst_QArrayData::variadicLiterals() +{ +#if defined(Q_COMPILER_VARIADIC_MACROS) \ + && (defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU)) + { + QArrayDataPointer<int> d = + Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], i); + } + + { + QArrayDataPointer<char> d = Q_ARRAY_LITERAL(char, + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) + QCOMPARE(d->data()[i], char('A' + i)); + } + + { + QArrayDataPointer<const char *> d = Q_ARRAY_LITERAL(const char *, + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); + QCOMPARE(d->size, 10); + for (int i = 0; i < 10; ++i) { + QCOMPARE(d->data()[i][0], char('A' + i)); + QCOMPARE(d->data()[i][1], '\0'); + } + } + + { + SimpleVector<int> v = Q_ARRAY_LITERAL(int, 0, 1, 2, 3, 4, 5, 6); + + QVERIFY(!v.isNull()); + QVERIFY(!v.isEmpty()); + QCOMPARE(v.size(), size_t(7)); + // v.capacity() is unspecified, for now + + QVERIFY(v.isStatic()); + + QVERIFY(v.isSharable()); + QVERIFY(v.constBegin() + v.size() == v.constEnd()); + + for (int i = 0; i < 7; ++i) + QCOMPARE(const_(v)[i], i); + } +#else + QSKIP("Variadic Q_ARRAY_LITERAL not available in current configuration."); +#endif // defined(Q_COMPILER_VARIADIC_MACROS) +} + +#ifdef Q_COMPILER_RVALUE_REFS +// std::remove_reference is in C++11, but requires library support +template <class T> struct RemoveReference { typedef T Type; }; +template <class T> struct RemoveReference<T &> { typedef T Type; }; + +// single-argument std::move is in C++11, but requires library support +template <class T> +typename RemoveReference<T>::Type &&cxx11Move(T &&t) +{ + return static_cast<typename RemoveReference<T>::Type &&>(t); +} + +struct CompilerHasCxx11ImplicitMoves +{ + static bool value() + { + DetectImplicitMove d(cxx11Move(DetectImplicitMove())); + return d.constructor == DetectConstructor::MoveConstructor; + } + + struct DetectConstructor + { + Q_DECL_CONSTEXPR DetectConstructor() + : constructor(DefaultConstructor) + { + } + + Q_DECL_CONSTEXPR DetectConstructor(const DetectConstructor &) + : constructor(CopyConstructor) + { + } + + Q_DECL_CONSTEXPR DetectConstructor(DetectConstructor &&) + : constructor(MoveConstructor) + { + } + + enum Constructor { + DefaultConstructor, + CopyConstructor, + MoveConstructor + }; + + Constructor constructor; + }; + + struct DetectImplicitMove + : DetectConstructor + { + }; +}; +#endif + +void tst_QArrayData::rValueReferences() +{ +#ifdef Q_COMPILER_RVALUE_REFS + if (!CompilerHasCxx11ImplicitMoves::value()) + QSKIP("Implicit move ctor not supported in current configuration"); + + SimpleVector<int> v1(1, 42); + SimpleVector<int> v2; + + const SimpleVector<int>::const_iterator begin = v1.constBegin(); + + QVERIFY(!v1.isNull()); + QVERIFY(v2.isNull()); + + // move-assign + v2 = cxx11Move(v1); + + QVERIFY(v1.isNull()); + QVERIFY(!v2.isNull()); + QCOMPARE(v2.constBegin(), begin); + + SimpleVector<int> v3(cxx11Move(v2)); + + QVERIFY(v1.isNull()); + QVERIFY(v2.isNull()); + QVERIFY(!v3.isNull()); + QCOMPARE(v3.constBegin(), begin); + + QCOMPARE(v3.size(), size_t(1)); + QCOMPARE(v3.front(), 42); +#else + QSKIP("RValue references are not supported in current configuration"); +#endif +} + +void tst_QArrayData::grow() +{ + SimpleVector<int> vector; + + QCOMPARE(vector.size(), size_t(0)); + + size_t previousCapacity = vector.capacity(); + size_t allocations = 0; + for (size_t i = 1; i <= (1 << 20); ++i) { + int source[1] = { i }; + vector.append(source, source + 1); + QCOMPARE(vector.size(), i); + if (vector.capacity() != previousCapacity) { + previousCapacity = vector.capacity(); + ++allocations; + } + } + QCOMPARE(vector.size(), size_t(1 << 20)); + + // QArrayData::Grow prevents excessive allocations on a growing container + QVERIFY(allocations > 20 / 2); + QVERIFY(allocations < 20 * 2); + + for (size_t i = 0; i < (1 << 20); ++i) + QCOMPARE(const_(vector).at(i), int(i + 1)); +} + +QTEST_APPLESS_MAIN(tst_QArrayData) +#include "tst_qarraydata.moc" diff --git a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp index 5e53683abd..13a6b3d471 100644 --- a/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/corelib/tools/qbytearray/tst_qbytearray.cpp @@ -1078,18 +1078,25 @@ void tst_QByteArray::toULongLong() // global function defined in qbytearray.cpp void tst_QByteArray::qAllocMore() { - static const int t[] = { - INT_MIN, INT_MIN + 1, -1234567, -66000, -1025, - -3, -1, 0, +1, +3, +1025, +66000, +1234567, INT_MAX - 1, INT_MAX, - INT_MAX/3 - }; - static const int N = sizeof(t)/sizeof(t[0]); - - // make sure qAllocMore() doesn't loop infinitely on any input - for (int i = 0; i < N; ++i) { - for (int j = 0; j < N; ++j) { - ::qAllocMore(t[i], t[j]); - } + using QT_PREPEND_NAMESPACE(qAllocMore); + + // Not very important, but please behave :-) + QVERIFY(qAllocMore(0, 0) >= 0); + + for (int i = 1; i < 1 << 8; i <<= 1) + QVERIFY(qAllocMore(i, 0) >= i); + + for (int i = 1 << 8; i < 1 << 30; i <<= 1) { + const int alloc = qAllocMore(i, 0); + + QVERIFY(alloc >= i); + QCOMPARE(qAllocMore(i - 8, 8), alloc - 8); + QCOMPARE(qAllocMore(i - 16, 16), alloc - 16); + QCOMPARE(qAllocMore(i - 24, 24), alloc - 24); + QCOMPARE(qAllocMore(i - 32, 32), alloc - 32); + + QVERIFY(qAllocMore(i - 1, 0) >= i - 1); + QVERIFY(qAllocMore(i + 1, 0) >= i + 1); } } @@ -1587,8 +1594,8 @@ void tst_QByteArray::literals() QVERIFY(str.length() == 4); QVERIFY(str == "abcd"); - QVERIFY(str.data_ptr()->ref == -1); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->ref.isStatic()); + QVERIFY(str.data_ptr()->offset == sizeof(QByteArrayData)); const char *s = str.constData(); QByteArray str2 = str; diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index fbb821c730..3baa47f013 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -54,6 +54,9 @@ class tst_QList : public QObject Q_OBJECT private slots: + void init(); + void cleanup(); + void length() const; void lengthSignature() const; void append() const; @@ -90,8 +93,100 @@ private slots: void initializeList() const; void const_shared_null() const; + void setSharable1_data() const; + void setSharable1() const; + void setSharable2_data() const; + void setSharable2() const; + +private: + int dummyForGuard; +}; + +struct Complex +{ + Complex(int val) + : value(val) + , checkSum(this) + { + ++liveCount; + } + + Complex(Complex const &other) + : value(other.value) + , checkSum(this) + { + ++liveCount; + } + + Complex &operator=(Complex const &other) + { + check(); other.check(); + + value = other.value; + return *this; + } + + ~Complex() + { + --liveCount; + check(); + } + + operator int() const { return value; } + + bool operator==(Complex const &other) const + { + check(); other.check(); + return value == other.value; + } + + bool check() const + { + if (this != checkSum) { + ++errorCount; + return false; + } + return true; + } + + struct Guard + { + Guard() : initialLiveCount(liveCount) {} + ~Guard() { if (liveCount != initialLiveCount) ++errorCount; } + + private: + Q_DISABLE_COPY(Guard); + int initialLiveCount; + }; + + static void resetErrors() { errorCount = 0; } + static int errors() { return errorCount; } + +private: + static int errorCount; + static int liveCount; + + int value; + void *checkSum; }; +int Complex::errorCount = 0; +int Complex::liveCount = 0; + +void tst_QList::init() +{ + Complex::resetErrors(); + new (&dummyForGuard) Complex::Guard(); +} + +void tst_QList::cleanup() +{ + QCOMPARE(Complex::errors(), 0); + + reinterpret_cast<Complex::Guard *>(&dummyForGuard)->~Guard(); + QCOMPARE(Complex::errors(), 0); +} + void tst_QList::length() const { /* Empty list. */ @@ -696,5 +791,82 @@ void tst_QList::const_shared_null() const QVERIFY(!list2.isDetached()); } +Q_DECLARE_METATYPE(QList<int>); +Q_DECLARE_METATYPE(QList<Complex>); + +template <class T> +void generateSetSharableData() +{ + QTest::addColumn<QList<T> >("list"); + QTest::addColumn<int>("size"); + + QTest::newRow("null") << QList<T>() << 0; + QTest::newRow("non-empty") << (QList<T>() << T(0) << T(1) << T(2) << T(3) << T(4)) << 5; +} + +template <class T> +void runSetSharableTest() +{ + QFETCH(QList<T>, list); + QFETCH(int, size); + + QVERIFY(!list.isDetached()); // Shared with QTest + + list.setSharable(true); + + QCOMPARE(list.size(), size); + + { + QList<T> copy(list); + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + list.setSharable(false); + QVERIFY(list.isDetached() || list.isSharedWith(QList<T>())); + + { + QList<T> copy(list); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QList<T>())); + QCOMPARE(copy.size(), size); + QCOMPARE(copy, list); + } + + list.setSharable(true); + + { + QList<T> copy(list); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(list)); + } + + for (int i = 0; i < list.size(); ++i) + QCOMPARE(int(list[i]), i); + + QCOMPARE(list.size(), size); +} + +void tst_QList::setSharable1_data() const +{ + generateSetSharableData<int>(); +} + +void tst_QList::setSharable2_data() const +{ + generateSetSharableData<Complex>(); +} + +void tst_QList::setSharable1() const +{ + runSetSharableTest<int>(); +} + +void tst_QList::setSharable2() const +{ + runSetSharableTest<Complex>(); +} + QTEST_APPLESS_MAIN(tst_QList) #include "tst_qlist.moc" diff --git a/tests/auto/corelib/tools/qstring/tst_qstring.cpp b/tests/auto/corelib/tools/qstring/tst_qstring.cpp index f007d44262..a8f706ff80 100644 --- a/tests/auto/corelib/tools/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/tools/qstring/tst_qstring.cpp @@ -1421,16 +1421,51 @@ void tst_QString::mid() QVERIFY(a.mid(9999).isNull()); QVERIFY(a.mid(9999,1).isNull()); + QCOMPARE(a.mid(-1, 6), QString("ABCDEF")); + QCOMPARE(a.mid(-100, 6), QString("ABCDEF")); + QVERIFY(a.mid(INT_MAX).isNull()); + QVERIFY(a.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.mid(-5, INT_MAX), a); + QCOMPARE(a.mid(-1, INT_MAX), a); + QCOMPARE(a.mid(0, INT_MAX), a); + QCOMPARE(a.mid(1, INT_MAX), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.mid(5, INT_MAX), QString("FGHIEfGEFG")); + QVERIFY(a.mid(20, INT_MAX).isNull()); + QCOMPARE(a.mid(-1, -1), a); + QString n; QVERIFY(n.mid(3,3).isNull()); QVERIFY(n.mid(0,0).isNull()); QVERIFY(n.mid(9999,0).isNull()); QVERIFY(n.mid(9999,1).isNull()); + QVERIFY(n.mid(-1, 6).isNull()); + QVERIFY(n.mid(-100, 6).isNull()); + QVERIFY(n.mid(INT_MAX).isNull()); + QVERIFY(n.mid(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.mid(-5, INT_MAX).isNull()); + QVERIFY(n.mid(-1, INT_MAX).isNull()); + QVERIFY(n.mid(0, INT_MAX).isNull()); + QVERIFY(n.mid(1, INT_MAX).isNull()); + QVERIFY(n.mid(5, INT_MAX).isNull()); + QVERIFY(n.mid(20, INT_MAX).isNull()); + QVERIFY(n.mid(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.mid(5, 4), QString("pine")); QCOMPARE(x.mid(5), QString("pineapples")); + QCOMPARE(x.mid(-1, 6), QString("Nine p")); + QCOMPARE(x.mid(-100, 6), QString("Nine p")); + QVERIFY(x.mid(INT_MAX).isNull()); + QVERIFY(x.mid(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.mid(-5, INT_MAX), x); + QCOMPARE(x.mid(-1, INT_MAX), x); + QCOMPARE(x.mid(0, INT_MAX), x); + QCOMPARE(x.mid(1, INT_MAX), QString("ine pineapples")); + QCOMPARE(x.mid(5, INT_MAX), QString("pineapples")); + QVERIFY(x.mid(20, INT_MAX).isNull()); + QCOMPARE(x.mid(-1, -1), x); } void tst_QString::midRef() @@ -1447,16 +1482,51 @@ void tst_QString::midRef() QVERIFY(a.midRef(9999).toString().isEmpty()); QVERIFY(a.midRef(9999,1).toString().isEmpty()); + QCOMPARE(a.midRef(-1, 6).toString(), QString("ABCDEF")); + QCOMPARE(a.midRef(-100, 6).toString(), QString("ABCDEF")); + QVERIFY(a.midRef(INT_MAX).isNull()); + QVERIFY(a.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(a.midRef(-5, INT_MAX).toString(), a); + QCOMPARE(a.midRef(-1, INT_MAX).toString(), a); + QCOMPARE(a.midRef(0, INT_MAX).toString(), a); + QCOMPARE(a.midRef(1, INT_MAX).toString(), QString("BCDEFGHIEfGEFG")); + QCOMPARE(a.midRef(5, INT_MAX).toString(), QString("FGHIEfGEFG")); + QVERIFY(a.midRef(20, INT_MAX).isNull()); + QCOMPARE(a.midRef(-1, -1).toString(), a); + QString n; QVERIFY(n.midRef(3,3).toString().isEmpty()); QVERIFY(n.midRef(0,0).toString().isEmpty()); QVERIFY(n.midRef(9999,0).toString().isEmpty()); QVERIFY(n.midRef(9999,1).toString().isEmpty()); + QVERIFY(n.midRef(-1, 6).isNull()); + QVERIFY(n.midRef(-100, 6).isNull()); + QVERIFY(n.midRef(INT_MAX).isNull()); + QVERIFY(n.midRef(INT_MAX, INT_MAX).isNull()); + QVERIFY(n.midRef(-5, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, INT_MAX).isNull()); + QVERIFY(n.midRef(0, INT_MAX).isNull()); + QVERIFY(n.midRef(1, INT_MAX).isNull()); + QVERIFY(n.midRef(5, INT_MAX).isNull()); + QVERIFY(n.midRef(20, INT_MAX).isNull()); + QVERIFY(n.midRef(-1, -1).isNull()); + QString x = "Nine pineapples"; QCOMPARE(x.midRef(5, 4).toString(), QString("pine")); QCOMPARE(x.midRef(5).toString(), QString("pineapples")); + QCOMPARE(x.midRef(-1, 6).toString(), QString("Nine p")); + QCOMPARE(x.midRef(-100, 6).toString(), QString("Nine p")); + QVERIFY(x.midRef(INT_MAX).isNull()); + QVERIFY(x.midRef(INT_MAX, INT_MAX).isNull()); + QCOMPARE(x.midRef(-5, INT_MAX).toString(), x); + QCOMPARE(x.midRef(-1, INT_MAX).toString(), x); + QCOMPARE(x.midRef(0, INT_MAX).toString(), x); + QCOMPARE(x.midRef(1, INT_MAX).toString(), QString("ine pineapples")); + QCOMPARE(x.midRef(5, INT_MAX).toString(), QString("pineapples")); + QVERIFY(x.midRef(20, INT_MAX).isNull()); + QCOMPARE(x.midRef(-1, -1).toString(), x); } void tst_QString::stringRef() @@ -5056,8 +5126,8 @@ void tst_QString::literals() QVERIFY(str.length() == 4); QVERIFY(str == QLatin1String("abcd")); - QVERIFY(str.data_ptr()->ref == -1); - QVERIFY(str.data_ptr()->offset == 0); + QVERIFY(str.data_ptr()->ref.isStatic()); + QVERIFY(str.data_ptr()->offset == sizeof(QStringData)); const QChar *s = str.constData(); QString str2 = str; diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index c79aee4187..67ca547736 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -85,6 +85,8 @@ private slots: void initializeList(); void const_shared_null(); + void setSharable_data(); + void setSharable(); }; void tst_QVector::constructors() const @@ -946,5 +948,97 @@ void tst_QVector::const_shared_null() QVERIFY(!v2.isDetached()); } +Q_DECLARE_METATYPE(QVector<int>); + +void tst_QVector::setSharable_data() +{ + QTest::addColumn<QVector<int> >("vector"); + QTest::addColumn<int>("size"); + QTest::addColumn<int>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + + QVector<int> null; + QVector<int> empty(0, 5); + QVector<int> emptyReserved; + QVector<int> nonEmpty; + QVector<int> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 0 << 0 << false; + QTest::newRow("empty") << empty << 0 << 0 << false; + QTest::newRow("empty, Reserved") << emptyReserved << 0 << 10 << true; + QTest::newRow("non-empty") << nonEmpty << 5 << 0 << false; + QTest::newRow("non-empty, Reserved") << nonEmptyReserved << 7 << 15 << true; +} + +void tst_QVector::setSharable() +{ + QFETCH(QVector<int>, vector); + QFETCH(int, size); + QFETCH(int, capacity); + QFETCH(bool, isCapacityReserved); + + QVERIFY(!vector.isDetached()); // Shared with QTest + + vector.setSharable(true); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + vector.setSharable(false); + QVERIFY(vector.isDetached() || vector.isSharedWith(QVector<int>())); + + { + QVector<int> copy(vector); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QVector<int>())); + QCOMPARE(copy.size(), size); + if (isCapacityReserved) + QVERIFY2(copy.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + QCOMPARE(copy, vector); + } + + vector.setSharable(true); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + for (int i = 0; i < vector.size(); ++i) + QCOMPARE(vector[i], i); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); +} + QTEST_APPLESS_MAIN(tst_QVector) #include "tst_qvector.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index d8961559e5..38225e12f7 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -1,6 +1,7 @@ TEMPLATE=subdirs SUBDIRS=\ qalgorithms \ + qarraydata \ qbitarray \ qbytearray \ qbytearraymatcher \ diff --git a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp index ed4ed4c6a2..a523a66bdd 100644 --- a/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp +++ b/tests/auto/dbus/qdbusmetaobject/tst_qdbusmetaobject.cpp @@ -397,16 +397,21 @@ void tst_QDBusMetaObject::types() for (int i = metaobject->methodOffset(); i < metaobject->methodCount(); ++i) { QMetaMethod expected = metaobject->method(i); - int methodIdx = result->indexOfMethod(expected.signature()); + int methodIdx = result->indexOfMethod(expected.methodSignature().constData()); QVERIFY(methodIdx != -1); QMetaMethod constructed = result->method(methodIdx); QCOMPARE(int(constructed.access()), int(expected.access())); QCOMPARE(int(constructed.methodType()), int(expected.methodType())); + QCOMPARE(constructed.name(), expected.name()); + QCOMPARE(constructed.parameterCount(), expected.parameterCount()); QCOMPARE(constructed.parameterNames(), expected.parameterNames()); QCOMPARE(constructed.parameterTypes(), expected.parameterTypes()); + for (int j = 0; j < constructed.parameterCount(); ++j) + QCOMPARE(constructed.parameterType(j), expected.parameterType(j)); QCOMPARE(constructed.tag(), expected.tag()); QCOMPARE(constructed.typeName(), expected.typeName()); + QCOMPARE(constructed.returnType(), expected.returnType()); } for (int i = metaobject->propertyOffset(); i < metaobject->propertyCount(); ++i) { @@ -427,6 +432,8 @@ void tst_QDBusMetaObject::types() QCOMPARE(constructed.isUser(), expected.isUser()); QCOMPARE(constructed.isWritable(), expected.isWritable()); QCOMPARE(constructed.typeName(), expected.typeName()); + QCOMPARE(constructed.type(), expected.type()); + QCOMPARE(constructed.userType(), expected.userType()); } } @@ -667,6 +674,204 @@ const char PropertyTest4_xml[] = "<annotation name=\"com.trolltech.QtDBus.QtTypeName\" value=\"Struct1\"/>" "</property>"; +class PropertyTest_b: public QObject +{ + Q_OBJECT + Q_PROPERTY(bool property READ property WRITE setProperty) +public: + bool property() { return false; } + void setProperty(bool) { } +}; +const char PropertyTest_b_xml[] = + "<property name=\"property\" type=\"b\" access=\"readwrite\"/>"; + +class PropertyTest_y: public QObject +{ + Q_OBJECT + Q_PROPERTY(uchar property READ property WRITE setProperty) +public: + uchar property() { return 0; } + void setProperty(uchar) { } +}; +const char PropertyTest_y_xml[] = + "<property name=\"property\" type=\"y\" access=\"readwrite\"/>"; + +class PropertyTest_n: public QObject +{ + Q_OBJECT + Q_PROPERTY(short property READ property WRITE setProperty) +public: + short property() { return 0; } + void setProperty(short) { } +}; +const char PropertyTest_n_xml[] = + "<property name=\"property\" type=\"n\" access=\"readwrite\"/>"; + +class PropertyTest_q: public QObject +{ + Q_OBJECT + Q_PROPERTY(ushort property READ property WRITE setProperty) +public: + ushort property() { return 0; } + void setProperty(ushort) { } +}; +const char PropertyTest_q_xml[] = + "<property name=\"property\" type=\"q\" access=\"readwrite\"/>"; + +class PropertyTest_u: public QObject +{ + Q_OBJECT + Q_PROPERTY(uint property READ property WRITE setProperty) +public: + uint property() { return 0; } + void setProperty(uint) { } +}; +const char PropertyTest_u_xml[] = + "<property name=\"property\" type=\"u\" access=\"readwrite\"/>"; + +class PropertyTest_x: public QObject +{ + Q_OBJECT + Q_PROPERTY(qlonglong property READ property WRITE setProperty) +public: + qlonglong property() { return 0; } + void setProperty(qlonglong) { } +}; +const char PropertyTest_x_xml[] = + "<property name=\"property\" type=\"x\" access=\"readwrite\"/>"; + +class PropertyTest_t: public QObject +{ + Q_OBJECT + Q_PROPERTY(qulonglong property READ property WRITE setProperty) +public: + qulonglong property() { return 0; } + void setProperty(qulonglong) { } +}; +const char PropertyTest_t_xml[] = + "<property name=\"property\" type=\"t\" access=\"readwrite\"/>"; + +class PropertyTest_d: public QObject +{ + Q_OBJECT + Q_PROPERTY(double property READ property WRITE setProperty) +public: + double property() { return 0; } + void setProperty(double) { } +}; +const char PropertyTest_d_xml[] = + "<property name=\"property\" type=\"d\" access=\"readwrite\"/>"; + +class PropertyTest_s: public QObject +{ + Q_OBJECT + Q_PROPERTY(QString property READ property WRITE setProperty) +public: + QString property() { return QString(); } + void setProperty(QString) { } +}; +const char PropertyTest_s_xml[] = + "<property name=\"property\" type=\"s\" access=\"readwrite\"/>"; + +class PropertyTest_v: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusVariant property READ property WRITE setProperty) +public: + QDBusVariant property() { return QDBusVariant(); } + void setProperty(QDBusVariant) { } +}; +const char PropertyTest_v_xml[] = + "<property name=\"property\" type=\"v\" access=\"readwrite\"/>"; + +class PropertyTest_o: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusObjectPath property READ property WRITE setProperty) +public: + QDBusObjectPath property() { return QDBusObjectPath(); } + void setProperty(QDBusObjectPath) { } +}; +const char PropertyTest_o_xml[] = + "<property name=\"property\" type=\"o\" access=\"readwrite\"/>"; + +class PropertyTest_g: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusSignature property READ property WRITE setProperty) +public: + QDBusSignature property() { return QDBusSignature(); } + void setProperty(QDBusSignature) { } +}; +const char PropertyTest_g_xml[] = + "<property name=\"property\" type=\"g\" access=\"readwrite\"/>"; + +class PropertyTest_h: public QObject +{ + Q_OBJECT + Q_PROPERTY(QDBusUnixFileDescriptor property READ property WRITE setProperty) +public: + QDBusUnixFileDescriptor property() { return QDBusUnixFileDescriptor(); } + void setProperty(QDBusUnixFileDescriptor) { } +}; +const char PropertyTest_h_xml[] = + "<property name=\"property\" type=\"h\" access=\"readwrite\"/>"; + +class PropertyTest_ay: public QObject +{ + Q_OBJECT + Q_PROPERTY(QByteArray property READ property WRITE setProperty) +public: + QByteArray property() { return QByteArray(); } + void setProperty(QByteArray) { } +}; +const char PropertyTest_ay_xml[] = + "<property name=\"property\" type=\"ay\" access=\"readwrite\"/>"; + +class PropertyTest_as: public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList property READ property WRITE setProperty) +public: + QStringList property() { return QStringList(); } + void setProperty(QStringList) { } +}; +const char PropertyTest_as_xml[] = + "<property name=\"property\" type=\"as\" access=\"readwrite\"/>"; + +class PropertyTest_av: public QObject +{ + Q_OBJECT + Q_PROPERTY(QVariantList property READ property WRITE setProperty) +public: + QVariantList property() { return QVariantList(); } + void setProperty(QVariantList) { } +}; +const char PropertyTest_av_xml[] = + "<property name=\"property\" type=\"av\" access=\"readwrite\"/>"; + +class PropertyTest_ao: public QObject +{ + Q_OBJECT + Q_PROPERTY(QList<QDBusObjectPath> property READ property WRITE setProperty) +public: + QList<QDBusObjectPath> property() { return QList<QDBusObjectPath>(); } + void setProperty(QList<QDBusObjectPath>) { } +}; +const char PropertyTest_ao_xml[] = + "<property name=\"property\" type=\"ao\" access=\"readwrite\"/>"; + +class PropertyTest_ag: public QObject +{ + Q_OBJECT + Q_PROPERTY(QList<QDBusSignature> property READ property WRITE setProperty) +public: + QList<QDBusSignature> property() { return QList<QDBusSignature>(); } + void setProperty(QList<QDBusSignature>) { } +}; +const char PropertyTest_ag_xml[] = + "<property name=\"property\" type=\"ag\" access=\"readwrite\"/>"; + void tst_QDBusMetaObject::properties_data() { QTest::addColumn<const QMetaObject *>("metaobject"); @@ -676,6 +881,25 @@ void tst_QDBusMetaObject::properties_data() QTest::newRow("readwrite") << &PropertyTest2::staticMetaObject << QString(PropertyTest2_xml); QTest::newRow("write") << &PropertyTest3::staticMetaObject << QString(PropertyTest3_xml); QTest::newRow("customtype") << &PropertyTest4::staticMetaObject << QString(PropertyTest4_xml); + + QTest::newRow("bool") << &PropertyTest_b::staticMetaObject << QString(PropertyTest_b_xml); + QTest::newRow("byte") << &PropertyTest_y::staticMetaObject << QString(PropertyTest_y_xml); + QTest::newRow("short") << &PropertyTest_n::staticMetaObject << QString(PropertyTest_n_xml); + QTest::newRow("ushort") << &PropertyTest_q::staticMetaObject << QString(PropertyTest_q_xml); + QTest::newRow("uint") << &PropertyTest_u::staticMetaObject << QString(PropertyTest_u_xml); + QTest::newRow("qlonglong") << &PropertyTest_x::staticMetaObject << QString(PropertyTest_x_xml); + QTest::newRow("qulonglong") << &PropertyTest_t::staticMetaObject << QString(PropertyTest_t_xml); + QTest::newRow("double") << &PropertyTest_d::staticMetaObject << QString(PropertyTest_d_xml); + QTest::newRow("QString") << &PropertyTest_s::staticMetaObject << QString(PropertyTest_s_xml); + QTest::newRow("QDBusVariant") << &PropertyTest_v::staticMetaObject << QString(PropertyTest_v_xml); + QTest::newRow("QDBusObjectPath") << &PropertyTest_o::staticMetaObject << QString(PropertyTest_o_xml); + QTest::newRow("QDBusSignature") << &PropertyTest_g::staticMetaObject << QString(PropertyTest_g_xml); + QTest::newRow("QDBusUnixFileDescriptor") << &PropertyTest_h::staticMetaObject << QString(PropertyTest_h_xml); + QTest::newRow("QByteArray") << &PropertyTest_ay::staticMetaObject << QString(PropertyTest_ay_xml); + QTest::newRow("QStringList") << &PropertyTest_as::staticMetaObject << QString(PropertyTest_as_xml); + QTest::newRow("QVariantList") << &PropertyTest_av::staticMetaObject << QString(PropertyTest_av_xml); + QTest::newRow("QList<QDBusObjectPath>") << &PropertyTest_ao::staticMetaObject << QString(PropertyTest_ao_xml); + QTest::newRow("QList<QDBusSignature>") << &PropertyTest_ag::staticMetaObject << QString(PropertyTest_ag_xml); } void tst_QDBusMetaObject::properties() diff --git a/tests/auto/tools/moc/tst_moc.cpp b/tests/auto/tools/moc/tst_moc.cpp index 49095048bf..e8639eec47 100644 --- a/tests/auto/tools/moc/tst_moc.cpp +++ b/tests/auto/tools/moc/tst_moc.cpp @@ -748,7 +748,7 @@ void tst_Moc::classinfoWithEscapes() QCOMPARE(mobj->methodCount() - mobj->methodOffset(), 1); QMetaMethod mm = mobj->method(mobj->methodOffset()); - QCOMPARE(mm.signature(), "slotWithAReallyLongName(int)"); + QCOMPARE(mm.methodSignature(), QByteArray("slotWithAReallyLongName(int)")); } void tst_Moc::trNoopInClassInfo() @@ -1093,14 +1093,14 @@ void tst_Moc::invokable() { const QMetaObject &mobj = InvokableBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 6); - QVERIFY(mobj.method(5).signature() == QByteArray("foo()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("foo()")); } { const QMetaObject &mobj = InvokableBeforeInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("foo()")); - QVERIFY(mobj.method(6).signature() == QByteArray("bar()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("foo()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("bar()")); } } @@ -1109,22 +1109,22 @@ void tst_Moc::singleFunctionKeywordSignalAndSlot() { const QMetaObject &mobj = SingleFunctionKeywordBeforeReturnType::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordBeforeInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } { const QMetaObject &mobj = SingleFunctionKeywordAfterInline::staticMetaObject; QCOMPARE(mobj.methodCount(), 7); - QVERIFY(mobj.method(5).signature() == QByteArray("mySignal()")); - QVERIFY(mobj.method(6).signature() == QByteArray("mySlot()")); + QVERIFY(mobj.method(5).methodSignature() == QByteArray("mySignal()")); + QVERIFY(mobj.method(6).methodSignature() == QByteArray("mySlot()")); } } @@ -1231,7 +1231,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(0); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass(QObject*)"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QObject*)")); QCOMPARE(mm.typeName(), ""); QList<QByteArray> paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); @@ -1244,7 +1244,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(1); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass()"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass()")); QCOMPARE(mm.typeName(), ""); QCOMPARE(mm.parameterNames().size(), 0); QCOMPARE(mm.parameterTypes().size(), 0); @@ -1253,7 +1253,7 @@ void tst_Moc::constructors() QMetaMethod mm = mo->constructor(2); QCOMPARE(mm.access(), QMetaMethod::Public); QCOMPARE(mm.methodType(), QMetaMethod::Constructor); - QCOMPARE(mm.signature(), "CtorTestClass(QString)"); + QCOMPARE(mm.methodSignature(), QByteArray("CtorTestClass(QString)")); QCOMPARE(mm.typeName(), ""); QList<QByteArray> paramNames = mm.parameterNames(); QCOMPARE(paramNames.size(), 1); diff --git a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp index 82632a018c..e97b044dbc 100644 --- a/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp +++ b/tests/auto/widgets/widgets/qmdiarea/tst_qmdiarea.cpp @@ -1275,7 +1275,7 @@ static int numberOfConnectedSignals(MySubWindow *subWindow) QMetaMethod method = subWindow->metaObject()->method(i); if (method.methodType() == QMetaMethod::Signal) { QString signature(QLatin1String("2")); - signature += QLatin1String(method.signature()); + signature += QLatin1String(method.methodSignature().constData()); numConnectedSignals += subWindow->receivers(signature.toLatin1()); } } diff --git a/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp b/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp index ab26158f9a..6d7c5c3853 100644 --- a/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp +++ b/tests/benchmarks/corelib/kernel/qmetaobject/main.cpp @@ -174,7 +174,7 @@ void tst_qmetaobject::indexOfMethod_data() const QMetaObject *mo = &QTreeView::staticMetaObject; for (int i = 0; i < mo->methodCount(); ++i) { QMetaMethod method = mo->method(i); - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } @@ -197,7 +197,7 @@ void tst_qmetaobject::indexOfSignal_data() QMetaMethod method = mo->method(i); if (method.methodType() != QMetaMethod::Signal) continue; - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } @@ -220,7 +220,7 @@ void tst_qmetaobject::indexOfSlot_data() QMetaMethod method = mo->method(i); if (method.methodType() != QMetaMethod::Slot) continue; - QByteArray sig = method.signature(); + QByteArray sig = method.methodSignature(); QTest::newRow(sig) << sig; } } diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index 7e570d93ff..0fdfa86f1d 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -66,24 +66,18 @@ QT_BEGIN_NAMESPACE template <typename T> class QRawVector { - struct Data : QVectorData { T array[1]; }; + typedef QVectorTypedData<T> Data; T *m_begin; int m_size; int m_alloc; public: - //static Data dummy; - //int headerOffset() { return (char*)&dummy.array - (char*)&dummy; } - inline int headerOffset() const { - // gcc complains about: return offsetof(Data, array); and also - // does not like '0' in the expression below. - return (char *)&(((Data *)(1))->array) - (char *)1; - } - inline Data *toBase(T *begin) const - { return (Data*)((char*)begin - headerOffset()); } - inline T *fromBase(void *d) const - { return (T*)((char*)d + headerOffset()); } + static Data *toBase(T *begin) + { return (Data*)((char*)begin - offsetOfTypedData()); } + static T *fromBase(void *d) + { return (T*)((char*)d + offsetOfTypedData()); } + inline QRawVector() { m_begin = fromBase(0); m_alloc = m_size = 0; realloc(m_size, m_alloc, true); } explicit QRawVector(int size); @@ -270,17 +264,20 @@ private: T *allocate(int alloc); void realloc(int size, int alloc, bool ref); void free(T *begin, int size); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + + class AlignmentDummy { QVectorData header; T array[1]; }; + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(AlignmentDummy, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - static inline int alignOfTypedData() + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax<int>(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(AlignmentDummy); #else - return 0; + return sizeof(void *); #endif } @@ -288,11 +285,11 @@ public: QVector<T> mutateToVector() { Data *d = toBase(m_begin); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = m_alloc; d->size = m_size; - d->sharable = 0; - d->capacity = 0; + d->capacityReserved = 0; + d->offset = offsetOfTypedData(); QVector<T> v; *reinterpret_cast<QVectorData **>(&v) = d; @@ -309,7 +306,7 @@ void QRawVector<T>::reserve(int asize) template <typename T> void QRawVector<T>::resize(int asize) { realloc(asize, (asize > m_alloc || (asize < m_size && asize < (m_alloc >> 1))) - ? QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo<T>::isStatic) + ? QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) : m_alloc, false); } template <typename T> inline void QRawVector<T>::clear() @@ -370,7 +367,7 @@ QRawVector<T> &QRawVector<T>::operator=(const QRawVector<T> &v) template <typename T> inline T *QRawVector<T>::allocate(int aalloc) { - QVectorData *d = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *d = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(d); return fromBase(d); } @@ -446,10 +443,9 @@ void QRawVector<T>::realloc(int asize, int aalloc, bool ref) changed = true; } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate( - toBase(m_begin), sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() -+ (xalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(toBase(m_begin), + offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + xalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); xbegin = fromBase(mem); xsize = m_size; @@ -511,8 +507,7 @@ void QRawVector<T>::append(const T &t) { if (m_size + 1 > m_alloc) { const T copy(t); - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + 1, sizeof(T), - QTypeInfo<T>::isStatic), false); + realloc(m_size, QVectorData::grow(offsetOfTypedData(), m_size + 1, sizeof(T)), false); if (QTypeInfo<T>::isComplex) new (m_begin + m_size) T(copy); else @@ -533,8 +528,7 @@ typename QRawVector<T>::iterator QRawVector<T>::insert(iterator before, size_typ if (n != 0) { const T copy(t); if (m_size + n > m_alloc) - realloc(m_size, QVectorData::grow(sizeOfTypedData(), m_size + n, sizeof(T), - QTypeInfo<T>::isStatic), false); + realloc(m_size, QVectorData::grow(offsetOfTypedData(), m_size + n, sizeof(T)), false); if (QTypeInfo<T>::isStatic) { T *b = m_begin + m_size; T *i = m_begin + m_size + n; |