Create a way to inform moc about private signals.
authorStephen Kelly <stephen.kelly@kdab.com>
Mon, 9 Jul 2012 07:56:51 +0000 (09:56 +0200)
committerQt by Nokia <qt-info@nokia.com>
Thu, 12 Jul 2012 23:51:42 +0000 (01:51 +0200)
Moc checks for the use of the QPrivateSignal struct, which is part of
the Q_OBJECT macro and is private to each class that uses it. Moc then
generates a name of the signal which does not include the private
struct, and generates code to invoke such signals with an instance of
the private struct.

This way we can mark private signals as such and prevent them from
being emitted from subclasses or from outside of the class entirely.

The drawback to this is that it only works if the private
signal has no default arguments. However, at least in Qt, there are
no such signals.

Change-Id: Id16eadaa8d3c36a2c3b265077877f3e1d8304c84
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
src/corelib/kernel/qobjectdefs.h
src/tools/moc/generator.cpp
src/tools/moc/moc.cpp
src/tools/moc/moc.h
tests/auto/tools/moc/tst_moc.cpp

index e840706..ba348b5 100644 (file)
@@ -154,7 +154,8 @@ public: \
     QT_TR_FUNCTIONS \
     virtual int qt_metacall(QMetaObject::Call, int, void **); \
 private: \
-    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
+    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
+    struct QPrivateSignal {};
 
 /* qmake ignore Q_OBJECT */
 #define Q_OBJECT_FAKE Q_OBJECT
index bcf70cb..09a4603 100644 (file)
@@ -540,7 +540,8 @@ void Generator::registerFunctionStrings(const QList<FunctionDef>& list)
             strreg(f.normalizedType);
         strreg(f.tag);
 
-        for (int j = 0; j < f.arguments.count(); ++j) {
+        int argsCount = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
+        for (int j = 0; j < argsCount; ++j) {
             const ArgumentDef &a = f.arguments.at(j);
             if (!isBuiltinType(a.normalizedType))
                 strreg(a.normalizedType);
@@ -580,7 +581,7 @@ void Generator::generateFunctions(const QList<FunctionDef>& list, const char *fu
         if (f.revision > 0)
             flags |= MethodRevisioned;
 
-        int argc = f.arguments.count();
+        int argc = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
         fprintf(out, "    %4d, %4d, %4d, %4d, 0x%02x,\n",
             stridx(f.name), argc, paramsIndex, stridx(f.tag), flags);
 
@@ -608,7 +609,8 @@ void Generator::generateFunctionParameters(const QList<FunctionDef>& list, const
         fprintf(out, "    ");
 
         // Types
-        for (int j = -1; j < f.arguments.count(); ++j) {
+        int argsCount = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
+        for (int j = -1; j < argsCount; ++j) {
             if (j > -1)
                 fputc(' ', out);
             const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType;
@@ -617,7 +619,7 @@ void Generator::generateFunctionParameters(const QList<FunctionDef>& list, const
         }
 
         // Parameter names
-        for (int j = 0; j < f.arguments.count(); ++j) {
+        for (int j = 0; j < argsCount; ++j) {
             const ArgumentDef &arg = f.arguments.at(j);
             fprintf(out, " %4d,", stridx(arg.name));
         }
@@ -1056,12 +1058,19 @@ void Generator::generateStaticMetacall()
                     cdef->classname.constData(), cdef->classname.constData());
             const FunctionDef &f = cdef->constructorList.at(ctorindex);
             int offset = 1;
-            for (int j = 0; j < f.arguments.count(); ++j) {
+
+            int argsCount = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
+            for (int j = 0; j < argsCount; ++j) {
                 const ArgumentDef &a = f.arguments.at(j);
                 if (j)
                     fprintf(out, ",");
                 fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++);
             }
+            if (f.isPrivateSignal) {
+                if (argsCount > 0)
+                    fprintf(out, ", ");
+                fprintf(out, "%s", QByteArray(f.arguments.last().normalizedType + "()").constData());
+            }
             fprintf(out, ");\n");
             fprintf(out, "            if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;\n");
         }
@@ -1098,13 +1107,20 @@ void Generator::generateStaticMetacall()
                 fprintf(out, "%s->", f.inPrivateClass.constData());
             fprintf(out, "%s(", f.name.constData());
             int offset = 1;
-            for (int j = 0; j < f.arguments.count(); ++j) {
+
+            int argsCount = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
+            for (int j = 0; j < argsCount; ++j) {
                 const ArgumentDef &a = f.arguments.at(j);
                 if (j)
                     fprintf(out, ",");
                 fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++);
                 isUsed_a = true;
             }
+            if (f.isPrivateSignal) {
+                if (argsCount > 0)
+                    fprintf(out, ", ");
+                fprintf(out, "%s", QByteArray(f.arguments.last().normalizedType + "()").constData());
+            }
             fprintf(out, ");");
             if (f.normalizedType != "void") {
                 fprintf(out, "\n            if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ",
@@ -1131,12 +1147,19 @@ void Generator::generateStaticMetacall()
             anythingUsed = true;
             fprintf(out, "        {\n");
             fprintf(out, "            typedef %s (%s::*_t)(",f.type.rawName.constData() , cdef->classname.constData());
-            for (int j = 0; j < f.arguments.count(); ++j) {
+
+            int argsCount = f.arguments.count() - (f.isPrivateSignal ? 1 : 0);
+            for (int j = 0; j < argsCount; ++j) {
                 const ArgumentDef &a = f.arguments.at(j);
                 if (j)
                     fprintf(out, ", ");
                 fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData());
             }
+            if (f.isPrivateSignal) {
+                if (argsCount > 0)
+                    fprintf(out, ", ");
+                fprintf(out, "%s", f.arguments.last().normalizedType.constData());
+            }
             if (f.isConst)
                 fprintf(out, ") const;\n");
             else
index c35f27d..0caa124 100644 (file)
@@ -309,6 +309,10 @@ void Moc::parseFunctionArguments(FunctionDef *def)
         if (!until(COMMA))
             break;
     }
+
+    if (!def->arguments.isEmpty()
+        && def->arguments.last().normalizedType == "QPrivateSignal")
+        def->isPrivateSignal = true;
 }
 
 bool Moc::testFunctionAttribute(FunctionDef *def)
index e20e29a..66cc942 100644 (file)
@@ -92,7 +92,7 @@ struct FunctionDef
 {
     FunctionDef(): returnTypeIsVolatile(false), access(Private), isConst(false), isVirtual(false), isStatic(false),
                    inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false),
-                   isScriptable(false), isSlot(false), isSignal(false),
+                   isScriptable(false), isSlot(false), isSignal(false), isPrivateSignal(false),
                    isConstructor(false), isDestructor(false), isAbstract(false), revision(0) {}
     Type type;
     QByteArray normalizedType;
@@ -116,6 +116,7 @@ struct FunctionDef
     bool isScriptable;
     bool isSlot;
     bool isSignal;
+    bool isPrivateSignal;
     bool isConstructor;
     bool isDestructor;
     bool isAbstract;
index d4602a6..a79a5f6 100644 (file)
@@ -543,6 +543,8 @@ private slots:
     void cxx11Enums();
     void returnRefs();
 
+    void privateSignalConnection();
+
 signals:
     void sigWithUnsignedArg(unsigned foo);
     void sigWithSignedArg(signed foo);
@@ -1768,6 +1770,374 @@ void tst_Moc::returnRefs()
     // they used to cause miscompilation of the moc generated file.
 }
 
+class SignalConnectionTester : public QObject
+{
+    Q_OBJECT
+public:
+    SignalConnectionTester(QObject *parent = 0)
+      : QObject(parent), testPassed(false)
+    {
+
+    }
+
+public Q_SLOTS:
+    void testSlot()
+    {
+      testPassed = true;
+    }
+    void testSlotWith1Arg(int i)
+    {
+      testPassed = i == 42;
+    }
+    void testSlotWith2Args(int i, const QString &s)
+    {
+      testPassed = i == 42 && s == "Hello";
+    }
+
+public:
+    bool testPassed;
+};
+
+class ClassWithPrivateSignals : public QObject
+{
+    Q_OBJECT
+public:
+    ClassWithPrivateSignals(QObject *parent = 0)
+      : QObject(parent)
+    {
+
+    }
+
+    void emitPrivateSignals()
+    {
+        emit privateSignal1(QPrivateSignal());
+        emit privateSignalWith1Arg(42, QPrivateSignal());
+        emit privateSignalWith2Args(42, "Hello", QPrivateSignal());
+    }
+
+Q_SIGNALS:
+    void privateSignal1(QPrivateSignal);
+    void privateSignalWith1Arg(int arg1, QPrivateSignal);
+    void privateSignalWith2Args(int arg1, const QString &arg2, QPrivateSignal);
+};
+
+class SubClassFromPrivateSignals : public ClassWithPrivateSignals
+{
+    Q_OBJECT
+public:
+    SubClassFromPrivateSignals(QObject *parent = 0)
+      : ClassWithPrivateSignals(parent)
+    {
+
+    }
+
+    void emitProtectedSignals()
+    {
+      // Compile test: All of this intentionally does not compile:
+//         emit privateSignal1();
+//         emit privateSignalWith1Arg(42);
+//         emit privateSignalWith2Args(42, "Hello");
+//
+//         emit privateSignal1(QPrivateSignal());
+//         emit privateSignalWith1Arg(42, QPrivateSignal());
+//         emit privateSignalWith2Args(42, "Hello", QPrivateSignal());
+//
+//         emit privateSignal1(ClassWithPrivateSignals::QPrivateSignal());
+//         emit privateSignalWith1Arg(42, ClassWithPrivateSignals::QPrivateSignal());
+//         emit privateSignalWith2Args(42, "Hello", ClassWithPrivateSignals::QPrivateSignal());
+    }
+};
+
+void tst_Moc::privateSignalConnection()
+{
+    // Function pointer connects. Matching signals and slots
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignal1, &tester, &SignalConnectionTester::testSlot);
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignal1");
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignal1, &tester, &SignalConnectionTester::testSlot);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignal1");
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg);
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignalWith1Arg", Q_ARG(int, 42));
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignalWith1Arg", Q_ARG(int, 42));
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith2Args, &tester, &SignalConnectionTester::testSlotWith2Args);
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&classWithPrivateSignals, "privateSignalWith2Args", Q_ARG(int, 42), Q_ARG(QString, "Hello"));
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith2Args, &tester, &SignalConnectionTester::testSlotWith2Args);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+        tester.testPassed = false;
+        QMetaObject::invokeMethod(&subClassFromPrivateSignals, "privateSignalWith2Args", Q_ARG(int, 42), Q_ARG(QString, "Hello"));
+        QVERIFY(tester.testPassed);
+    }
+
+
+    // String based connects. Matching signals and slots
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignal1()), &tester, SLOT(testSlot()));
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignal1()), &tester, SLOT(testSlot()));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlotWith1Arg(int)));
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlotWith1Arg(int)));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith2Args(int,QString)));
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith2Args(int,QString)));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+
+    // Function pointer connects. Decayed slot arguments
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot);
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg);
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlotWith1Arg);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot);
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, &ClassWithPrivateSignals::privateSignalWith1Arg, &tester, &SignalConnectionTester::testSlot);
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+
+    // String based connects. Decayed slot arguments
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlot()));
+
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith1Arg(int)), &tester, SLOT(testSlot()));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith1Arg(int)));
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlotWith1Arg(int)));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        ClassWithPrivateSignals classWithPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&classWithPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlot()));
+        QVERIFY(!tester.testPassed);
+
+        classWithPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+    {
+        SubClassFromPrivateSignals subClassFromPrivateSignals;
+        SignalConnectionTester tester;
+        QObject::connect(&subClassFromPrivateSignals, SIGNAL(privateSignalWith2Args(int,QString)), &tester, SLOT(testSlot()));
+
+        QVERIFY(!tester.testPassed);
+
+        subClassFromPrivateSignals.emitPrivateSignals();
+
+        QVERIFY(tester.testPassed);
+    }
+}
+
+
 QTEST_MAIN(tst_Moc)
 #include "tst_moc.moc"