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>
This commit is contained in:
Kent Hansen 2012-02-02 14:20:24 +01:00 committed by Qt by Nokia
parent 8bbb00e44e
commit b184dd0a01
4 changed files with 360 additions and 15 deletions

View File

@ -109,6 +109,8 @@ class QMutex;
struct QMetaObjectPrivate
{
enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder
int revision;
int className;
int classInfoCount, classInfoData;

View File

@ -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.

View File

@ -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;

View File

@ -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"