Detect incompatibilities in repeated type registration
authorJoão Abecasis <joao.abecasis@nokia.com>
Mon, 30 Jan 2012 13:50:04 +0000 (14:50 +0100)
committerQt by Nokia <qt-info@nokia.com>
Tue, 7 Feb 2012 08:47:51 +0000 (09:47 +0100)
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>
src/corelib/kernel/qmetatype.cpp
tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp

index b31bece..a6b1599 100644 (file)
@@ -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;
-    }
-
-    QWriteLocker locker(customTypesLock());
-    idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
-                                           normalizedTypeName.size());
+    if (!idx) {
+        QWriteLocker locker(customTypesLock());
+        idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(),
+                                               normalizedTypeName.size());
 
-    if (idx)
-        return idx;
+        if (!idx) {
+            QCustomTypeInfo inf;
+            inf.typeName = normalizedTypeName;
+            inf.alias = aliasId;
+            inf.creator = 0;
+            inf.deleter = 0;
+            ct->append(inf);
+            return aliasId;
+        }
+    }
 
-    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;
 }
 
 /*!
index ca2964d..f90e7f4 100644 (file)
@@ -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()