Detect incompatibilities in repeated type registration
QMetaType used to register a typeName and factory functions for creation/destruction of objects. While it would be possible for a single type name to be registered matching different actual types and memory layouts, there was little that could be done about it. Now that QMetaType is tracking type information with a direct impact on data layout and ABI (size and type flags) it is important that we check and detect binary incompatibilities as early as possible. [Such incompatibilities could arise from type name re-use (technically, ODR violations) or, more commonly, as version mismatch between different shared libraries or plugins.] Only type size and flags are checked as function pointers to inline and template or otherwise non-exported functions could trivially differ across translation units and shared libraries. When registering typedef types, a check is made to ensure the same name doesn't get registered as different types. Change-Id: I8211c3de75d4854ce8fafdb620d3a931c206e0c3 Reviewed-by: Olivier Goffart <ogoffart@woboq.com> Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com> Reviewed-by: Kent Hansen <kent.hansen@nokia.com>bb10
parent
b8cf1d6bdc
commit
5a92dd6124
|
|
@ -465,6 +465,8 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
|
|||
int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
|
||||
normalizedTypeName.size());
|
||||
|
||||
int previousSize = 0;
|
||||
int previousFlags = 0;
|
||||
if (!idx) {
|
||||
QWriteLocker locker(customTypesLock());
|
||||
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
||||
|
|
@ -485,8 +487,33 @@ int QMetaType::registerType(const char *typeName, Deleter deleter,
|
|||
inf.flags = flags;
|
||||
idx = ct->size() + User;
|
||||
ct->append(inf);
|
||||
return idx;
|
||||
}
|
||||
|
||||
if (idx >= User) {
|
||||
previousSize = ct->at(idx - User).size;
|
||||
previousFlags = ct->at(idx - User).flags;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx < User) {
|
||||
previousSize = QMetaType::sizeOf(idx);
|
||||
previousFlags = QMetaType::typeFlags(idx);
|
||||
}
|
||||
|
||||
if (previousSize != size) {
|
||||
qFatal("QMetaType::registerType: Binary compatibility break "
|
||||
"-- Size mismatch for type '%s' [%i]. Previously registered "
|
||||
"size %i, now registering size %i.",
|
||||
normalizedTypeName.constData(), idx, previousSize, size);
|
||||
}
|
||||
if (previousFlags != flags) {
|
||||
qFatal("QMetaType::registerType: Binary compatibility break "
|
||||
"-- Type flags for type '%s' [%i] don't match. Previously "
|
||||
"registered TypeFlags(0x%x), now registering TypeFlags(0x%x).",
|
||||
normalizedTypeName.constData(), idx, previousFlags, int(flags));
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
|
@ -510,25 +537,30 @@ int QMetaType::registerTypedef(const char* typeName, int aliasId)
|
|||
int idx = qMetaTypeStaticType(normalizedTypeName.constData(),
|
||||
normalizedTypeName.size());
|
||||
|
||||
if (idx) {
|
||||
Q_ASSERT(idx == aliasId);
|
||||
return idx;
|
||||
if (!idx) {
|
||||
QWriteLocker locker(customTypesLock());
|
||||
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
||||
normalizedTypeName.size());
|
||||
|
||||
if (!idx) {
|
||||
QCustomTypeInfo inf;
|
||||
inf.typeName = normalizedTypeName;
|
||||
inf.alias = aliasId;
|
||||
inf.creator = 0;
|
||||
inf.deleter = 0;
|
||||
ct->append(inf);
|
||||
return aliasId;
|
||||
}
|
||||
}
|
||||
|
||||
QWriteLocker locker(customTypesLock());
|
||||
idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
|
||||
normalizedTypeName.size());
|
||||
|
||||
if (idx)
|
||||
return idx;
|
||||
|
||||
QCustomTypeInfo inf;
|
||||
inf.typeName = normalizedTypeName;
|
||||
inf.alias = aliasId;
|
||||
inf.creator = 0;
|
||||
inf.deleter = 0;
|
||||
ct->append(inf);
|
||||
return aliasId;
|
||||
if (idx != aliasId) {
|
||||
qFatal("QMetaType::registerTypedef: Binary compatibility break "
|
||||
"-- Type name '%s' previously registered as typedef of '%s' [%i], "
|
||||
"now registering as typedef of '%s' [%i].",
|
||||
normalizedTypeName.constData(), QMetaType::typeName(idx), idx,
|
||||
QMetaType::typeName(aliasId), aliasId);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ private slots:
|
|||
void constructCopy_data();
|
||||
void constructCopy();
|
||||
void typedefs();
|
||||
void registerType();
|
||||
void isRegistered_data();
|
||||
void isRegistered();
|
||||
void registerStreamBuiltin();
|
||||
|
|
@ -828,6 +829,44 @@ void tst_QMetaType::typedefs()
|
|||
QCOMPARE(QMetaType::type("WhityDouble"), ::qMetaTypeId<WhityDouble>());
|
||||
}
|
||||
|
||||
void tst_QMetaType::registerType()
|
||||
{
|
||||
// Built-in
|
||||
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
|
||||
QCOMPARE(qRegisterMetaType<QString>("QString"), int(QMetaType::QString));
|
||||
|
||||
// Custom
|
||||
int fooId = qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo");
|
||||
QVERIFY(fooId >= int(QMetaType::User));
|
||||
QCOMPARE(qRegisterMetaType<TestSpace::Foo>("TestSpace::Foo"), fooId);
|
||||
|
||||
int movableId = qRegisterMetaType<CustomMovable>("CustomMovable");
|
||||
QVERIFY(movableId >= int(QMetaType::User));
|
||||
QCOMPARE(qRegisterMetaType<CustomMovable>("CustomMovable"), movableId);
|
||||
|
||||
// Alias to built-in
|
||||
typedef QString MyString;
|
||||
|
||||
QCOMPARE(qRegisterMetaType<MyString>("MyString"), int(QMetaType::QString));
|
||||
QCOMPARE(qRegisterMetaType<MyString>("MyString"), int(QMetaType::QString));
|
||||
|
||||
QCOMPARE(QMetaType::type("MyString"), int(QMetaType::QString));
|
||||
|
||||
// Alias to custom type
|
||||
typedef CustomMovable MyMovable;
|
||||
typedef TestSpace::Foo MyFoo;
|
||||
|
||||
QCOMPARE(qRegisterMetaType<MyMovable>("MyMovable"), movableId);
|
||||
QCOMPARE(qRegisterMetaType<MyMovable>("MyMovable"), movableId);
|
||||
|
||||
QCOMPARE(QMetaType::type("MyMovable"), movableId);
|
||||
|
||||
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
|
||||
QCOMPARE(qRegisterMetaType<MyFoo>("MyFoo"), fooId);
|
||||
|
||||
QCOMPARE(QMetaType::type("MyFoo"), fooId);
|
||||
}
|
||||
|
||||
class IsRegisteredDummyType { };
|
||||
|
||||
void tst_QMetaType::isRegistered_data()
|
||||
|
|
|
|||
Loading…
Reference in New Issue