summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKent Hansen <kent.hansen@nokia.com>2012-02-02 14:20:24 +0100
committerQt by Nokia <qt-info@nokia.com>2012-02-03 15:09:03 +0100
commitb184dd0a01fda019d5a0960f6d074b6391dba655 (patch)
tree692ac20cda6cf083536040624fe4815605c348be
parent8bbb00e44e2f027c65ff2729f56353abc2b2cbef (diff)
Bring qmetaobjectbuilder in sync with moc
qmetaobjectbuilder should generate meta-objects of the same version as moc; in the future, when the moc version is bumped, QMOB has to be adapted at the same time. QMOB was generating version 4 meta-objects. This patch makes it generate version 6 (the current version). This also fixes a bug with using qt_static_metacall with QMOB (setStaticMetacallFunction()); it was already using the version 6 qt_static_metacall signature, which isn't compatible with version 4. Also add tests that ensure that the QMOB-generated meta-object works with real objects; in particular we want to test the codepaths in Qt that check for version >= 4. Change-Id: I64a151ea5c947a6f8b7a00e85a39866446c735e9 Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com>
-rw-r--r--src/corelib/kernel/qmetaobject_p.h2
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp3
-rw-r--r--src/tools/moc/generator.cpp2
-rw-r--r--tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp368
4 files changed, 360 insertions, 15 deletions
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index 9f87a2a8b8..5fc0555fb5 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -109,6 +109,8 @@ class QMutex;
struct QMetaObjectPrivate
{
+ enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder
+
int revision;
int className;
int classInfoCount, classInfoData;
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index 61ffe74586..529ca22107 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1136,7 +1136,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
}
}
if (buf) {
- pmeta->revision = 4;
+ Q_STATIC_ASSERT_X(QMetaObjectPrivate::OutputRevision == 6, "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.
//pmeta->signalCount is handled in the "output method loop" as an optimization.
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index 3595719672..b4f3d23f4e 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -162,7 +162,7 @@ void Generator::generateCode()
int index = 14;
fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
fprintf(out, "\n // content:\n");
- fprintf(out, " %4d, // revision\n", 6);
+ fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision));
fprintf(out, " %4d, // classname\n", strreg(cdef->qualified));
fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0);
index += cdef->classInfoList.count() * 2;
diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
index 450d42703c..def1b74ea4 100644
--- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
+++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp
@@ -47,7 +47,6 @@ class tst_QMetaObjectBuilder : public QObject
{
Q_OBJECT
private slots:
- void mocVersionCheck();
void create();
void className();
void superClass();
@@ -67,6 +66,14 @@ private slots:
void serialize();
void removeNotifySignal();
+ void usage_signal();
+ void usage_property();
+ void usage_slot();
+ void usage_method();
+ void usage_constructor();
+ void usage_connect();
+ void usage_templateConnect();
+
private:
static bool checkForSideEffects
(const QMetaObjectBuilder& builder,
@@ -130,18 +137,6 @@ signals:
void propChanged(const QString&);
};
-void tst_QMetaObjectBuilder::mocVersionCheck()
-{
- // This test will fail when the moc version number is changed.
- // It is intended as a reminder to also update QMetaObjectBuilder
- // whenenver moc changes. Once QMetaObjectBuilder has been
- // updated, this test can be changed to check for the next version.
- int version = int(QObject::staticMetaObject.d.data[0]);
- QVERIFY(version == 4 || version == 5 || version == 6);
- version = int(staticMetaObject.d.data[0]);
- QVERIFY(version == 4 || version == 5 || version == 6);
-}
-
void tst_QMetaObjectBuilder::create()
{
QMetaObjectBuilder builder;
@@ -1274,6 +1269,353 @@ bool tst_QMetaObjectBuilder::sameMetaObject
return true;
}
+
+// This class is used to test that the meta-object generated by QMOB can be
+// used by a real object.
+// The class manually implements the functions normally generated by moc, and
+// creates the corresponding meta-object using QMOB. The autotests check that
+// this object can be used by QObject/QMetaObject functionality (property
+// access, signals & slots, constructing instances, ...).
+
+class TestObject : public QObject
+{
+ // Manually expanded from Q_OBJECT macro
+public:
+ Q_OBJECT_CHECK
+ virtual const QMetaObject *metaObject() const;
+ virtual void *qt_metacast(const char *);
+ virtual int qt_metacall(QMetaObject::Call, int, void **);
+private:
+ Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData;
+ Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
+
+ //Q_PROPERTY(int intProp READ intProp WRITE setIntProp NOTIFY intPropChanged)
+public:
+ TestObject(QObject *parent = 0); // Q_INVOKABLE
+ ~TestObject();
+
+ // Property accessors
+ int intProp() const;
+ void setIntProp(int v);
+
+ void emitIntPropChanged();
+
+ int voidSlotIntArgument() const;
+
+// Q_INVOKABLE
+ QVariantList listInvokableQRealQString(qreal, const QString &);
+
+//public Q_SLOTS:
+ void voidSlotInt(int);
+
+//Q_SIGNALS:
+ void intPropChanged(int);
+
+private:
+ static QMetaObject *buildMetaObject();
+
+ QMetaObject *m_metaObject;
+ int m_intProp;
+ int m_voidSlotIntArg;
+};
+
+const QMetaObjectExtraData TestObject::staticMetaObjectExtraData = {
+ 0, qt_static_metacall
+};
+
+TestObject::TestObject(QObject *parent)
+ : QObject(parent), m_metaObject(buildMetaObject()),
+ m_intProp(-1), m_voidSlotIntArg(-1)
+{
+}
+
+TestObject::~TestObject()
+{
+ qFree(m_metaObject);
+}
+
+QMetaObject *TestObject::buildMetaObject()
+{
+ QMetaObjectBuilder builder;
+ // NOTE: If you change the meta-object, remember to adapt qt_metacall and
+ // friends below accordingly.
+
+ builder.setClassName("TestObject");
+
+ builder.setStaticMetacallFunction(qt_static_metacall);
+
+ QMetaMethodBuilder intPropChanged = builder.addSignal("intPropChanged(int)");
+ intPropChanged.setParameterNames(QList<QByteArray>() << "newIntPropValue");
+
+ QMetaPropertyBuilder prop = builder.addProperty("intProp", "int");
+ prop.setNotifySignal(intPropChanged);
+
+ QMetaMethodBuilder voidSlotInt = builder.addSlot("voidSlotInt(int)");
+ voidSlotInt.setParameterNames(QList<QByteArray>() << "slotIntArg");
+
+ QMetaMethodBuilder listInvokableQRealQString = builder.addMethod("listInvokableQRealQString(qreal,QString)");
+ listInvokableQRealQString.setReturnType("QVariantList");
+ listInvokableQRealQString.setParameterNames(QList<QByteArray>() << "qrealArg" << "qstringArg");
+
+ builder.addConstructor("TestObject(QObject*)");
+ builder.addConstructor("TestObject()");
+
+ return builder.toMetaObject();
+}
+
+int TestObject::intProp() const
+{
+ return m_intProp;
+}
+
+void TestObject::setIntProp(int value)
+{
+ if (m_intProp != value) {
+ m_intProp = value;
+ emit intPropChanged(value);
+ }
+}
+
+void TestObject::emitIntPropChanged()
+{
+ emit intPropChanged(m_intProp);
+}
+
+QVariantList TestObject::listInvokableQRealQString(qreal r, const QString &s)
+{
+ return QVariantList() << r << s;
+}
+
+void TestObject::voidSlotInt(int value)
+{
+ m_voidSlotIntArg = value;
+}
+
+int TestObject::voidSlotIntArgument() const
+{
+ return m_voidSlotIntArg;
+}
+
+void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+ if (_c == QMetaObject::CreateInstance) {
+ switch (_id) {
+ case 0: { TestObject *_r = new TestObject((*reinterpret_cast< QObject*(*)>(_a[1])));
+ if (_a[0]) *reinterpret_cast<QObject**>(_a[0]) = _r; } break;
+ case 1: { TestObject *_r = new TestObject();
+ 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());
+ }
+ }
+ } else if (_c == QMetaObject::InvokeMetaMethod) {
+ Q_ASSERT(_o->metaObject()->cast(_o));
+ TestObject *_t = static_cast<TestObject *>(_o);
+ switch (_id) {
+ case 0: _t->intPropChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
+ case 1: _t->voidSlotInt((*reinterpret_cast< int(*)>(_a[1]))); break;
+ 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());
+ }
+ }
+ } else if (_c == QMetaObject::IndexOfMethod) {
+ // This code is currently unreachable because it's only used by the
+ // template-based versions of connect() and disconnect(), which don't
+ // work with dynamically generated meta-objects (see test).
+ int *result = reinterpret_cast<int *>(_a[0]);
+ void **func = reinterpret_cast<void **>(_a[1]);
+ {
+ typedef void (TestObject::*_t)(int );
+ if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::intPropChanged)) {
+ *result = 0;
+ }
+ }
+ {
+ typedef void (TestObject::*_t)(int );
+ if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::voidSlotInt)) {
+ *result = 1;
+ }
+ }
+ {
+ typedef QVariantList (TestObject::*_t)(qreal, const QString &);
+ if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&TestObject::listInvokableQRealQString)) {
+ *result = 2;
+ }
+ }
+ qFatal("You forgot to add one or more IndexOfMethod cases");
+ }
+}
+
+const QMetaObject *TestObject::metaObject() const
+{
+ return m_metaObject;
+}
+
+void *TestObject::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, "TestObject"))
+ return static_cast<void*>(const_cast< TestObject*>(this));
+ return QObject::qt_metacast(_clname);
+}
+
+int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QObject::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ int ownMethodCount = m_metaObject->methodCount() - m_metaObject->methodOffset();
+ int ownPropertyCount = m_metaObject->propertyCount() - m_metaObject->propertyOffset();
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ if (_id < ownMethodCount)
+ qt_static_metacall(this, _c, _id, _a);
+ _id -= ownMethodCount;
+ }
+#ifndef QT_NO_PROPERTIES
+ else if (_c == QMetaObject::ReadProperty) {
+ void *_v = _a[0];
+ switch (_id) {
+ case 0: *reinterpret_cast< int*>(_v) = intProp(); break;
+ default: if (_id < ownPropertyCount) {
+ QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id);
+ qFatal("You forgot to add a case for ReadProperty %s", prop.name());
+ }
+ }
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::WriteProperty) {
+ void *_v = _a[0];
+ switch (_id) {
+ case 0: setIntProp(*reinterpret_cast< int*>(_v)); break;
+ default: if (_id < ownPropertyCount) {
+ QMetaProperty prop = m_metaObject->property(m_metaObject->propertyOffset() + _id);
+ qFatal("You forgot to add a case for WriteProperty %s", prop.name());
+ }
+ }
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::ResetProperty) {
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::QueryPropertyDesignable) {
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::QueryPropertyScriptable) {
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::QueryPropertyStored) {
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::QueryPropertyEditable) {
+ _id -= ownPropertyCount;
+ } else if (_c == QMetaObject::QueryPropertyUser) {
+ _id -= ownPropertyCount;
+ }
+#endif // QT_NO_PROPERTIES
+ return _id;
+}
+
+// SIGNAL 0
+void TestObject::intPropChanged(int _t1)
+{
+ void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
+ QMetaObject::activate(this, m_metaObject, 0, _a);
+}
+
+
+void tst_QMetaObjectBuilder::usage_signal()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ QSignalSpy propChangedSpy(testObject.data(), SIGNAL(intPropChanged(int)));
+ testObject->emitIntPropChanged();
+ QCOMPARE(propChangedSpy.count(), 1);
+ QCOMPARE(propChangedSpy.at(0).count(), 1);
+ QCOMPARE(propChangedSpy.at(0).at(0).toInt(), testObject->intProp());
+}
+
+void tst_QMetaObjectBuilder::usage_property()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ QVariant prop = testObject->property("intProp");
+ QCOMPARE(prop.type(), QVariant::Int);
+ QCOMPARE(prop.toInt(), testObject->intProp());
+
+ QSignalSpy propChangedSpy(testObject.data(), SIGNAL(intPropChanged(int)));
+ QVERIFY(testObject->intProp() != 123);
+ testObject->setProperty("intProp", 123);
+ QCOMPARE(propChangedSpy.count(), 1);
+ prop = testObject->property("intProp");
+ QCOMPARE(prop.type(), QVariant::Int);
+ QCOMPARE(prop.toInt(), 123);
+}
+
+void tst_QMetaObjectBuilder::usage_slot()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ int index = testObject->metaObject()->indexOfMethod("voidSlotInt(int)");
+ QVERIFY(index != -1);
+ QMetaMethod voidSlotInt = testObject->metaObject()->method(index);
+
+ QVERIFY(testObject->voidSlotIntArgument() == -1);
+ QVERIFY(voidSlotInt.invoke(testObject.data(), Q_ARG(int, 123)));
+ QCOMPARE(testObject->voidSlotIntArgument(), 123);
+}
+
+void tst_QMetaObjectBuilder::usage_method()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ int index = testObject->metaObject()->indexOfMethod("listInvokableQRealQString(qreal,QString)");
+ QVERIFY(index != -1);
+ QMetaMethod listInvokableQRealQString = testObject->metaObject()->method(index);
+ QVariantList list;
+ QVERIFY(listInvokableQRealQString.invoke(testObject.data(), Q_RETURN_ARG(QVariantList, list),
+ Q_ARG(qreal, 123.0), Q_ARG(QString, "ciao")));
+ QCOMPARE(list.size(), 2);
+ QCOMPARE(list.at(0).type(), QVariant::Type(QMetaType::QReal));
+ QCOMPARE(list.at(0).toDouble(), double(123));
+ QCOMPARE(list.at(1).type(), QVariant::String);
+ QCOMPARE(list.at(1).toString(), QString::fromLatin1("ciao"));
+}
+
+void tst_QMetaObjectBuilder::usage_constructor()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ QCOMPARE(testObject->metaObject()->constructorCount(), 2);
+ QScopedPointer<QObject> testInstance(testObject->metaObject()->newInstance());
+ QVERIFY(testInstance != 0);
+ QScopedPointer<QObject> testInstance2(testObject->metaObject()->newInstance(Q_ARG(QObject*, testInstance.data())));
+ QVERIFY(testInstance2 != 0);
+ QCOMPARE(testInstance2->parent(), testInstance.data());
+}
+
+void tst_QMetaObjectBuilder::usage_connect()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ QVERIFY(QObject::connect(testObject.data(), SIGNAL(intPropChanged(int)),
+ testObject.data(), SLOT(voidSlotInt(int))));
+
+ QVERIFY(testObject->voidSlotIntArgument() == -1);
+ testObject->setProperty("intProp", 123);
+ QCOMPARE(testObject->voidSlotIntArgument(), 123);
+
+ QVERIFY(QObject::disconnect(testObject.data(), SIGNAL(intPropChanged(int)),
+ testObject.data(), SLOT(voidSlotInt(int))));
+}
+
+void tst_QMetaObjectBuilder::usage_templateConnect()
+{
+ QScopedPointer<TestObject> testObject(new TestObject);
+
+ QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in QObject");
+ QMetaObject::Connection con = QObject::connect(testObject.data(), &TestObject::intPropChanged,
+ testObject.data(), &TestObject::voidSlotInt);
+ QEXPECT_FAIL("", "template-based connect() fails because meta-object is deduced at compile-time", Abort);
+ QVERIFY(con);
+}
+
QTEST_MAIN(tst_QMetaObjectBuilder)
#include "tst_qmetaobjectbuilder.moc"