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:
parent
8bbb00e44e
commit
b184dd0a01
@ -109,6 +109,8 @@ class QMutex;
|
||||
|
||||
struct QMetaObjectPrivate
|
||||
{
|
||||
enum { OutputRevision = 6 }; // Used by moc and qmetaobjectbuilder
|
||||
|
||||
int revision;
|
||||
int className;
|
||||
int classInfoCount, classInfoData;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user