Add a function to QQmlPropertyCache to calculate the meta-object sizes

This will be used later for calculating checksums of the meta-object
data.

Change-Id: Iba925eae298cbfc7b89196f4dd6fb2854ce75e2e
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Simon Hausmann 2016-07-28 17:40:11 +02:00
parent 7ddb47f3ae
commit 897eb7f129
3 changed files with 293 additions and 37 deletions

View File

@ -1246,6 +1246,173 @@ void QQmlPropertyCache::toMetaObjectBuilder(QMetaObjectBuilder &builder)
}
}
namespace {
template <typename StringVisitor, typename TypeInfoVisitor>
int visitMethods(const QMetaObject &mo, int methodOffset, int methodCount,
StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
{
const int intsPerMethod = 5;
int fieldsForParameterData = 0;
bool hasRevisionedMethods = false;
for (int i = 0; i < methodCount; ++i) {
const int handle = methodOffset + i * intsPerMethod;
const uint flags = mo.d.data[handle + 4];
if (flags & MethodRevisioned)
hasRevisionedMethods = true;
visitString(mo.d.data[handle + 0]); // name
visitString(mo.d.data[handle + 3]); // tag
const int argc = mo.d.data[handle + 1];
const int paramIndex = mo.d.data[handle + 2];
fieldsForParameterData += argc * 2; // type and name
fieldsForParameterData += 1; // + return type
// return type + args
for (int i = 0; i < 1 + argc; ++i) {
// type name (maybe)
visitTypeInfo(mo.d.data[paramIndex + i]);
// parameter name
if (i > 0)
visitString(mo.d.data[paramIndex + argc + i]);
}
}
int fieldsForRevisions = 0;
if (hasRevisionedMethods)
fieldsForRevisions = methodCount;
return methodCount * intsPerMethod + fieldsForRevisions + fieldsForParameterData;
}
template <typename StringVisitor, typename TypeInfoVisitor>
int visitProperties(const QMetaObject &mo, StringVisitor visitString, TypeInfoVisitor visitTypeInfo)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
const int intsPerProperty = 3;
bool hasRevisionedProperties = false;
bool hasNotifySignals = false;
for (int i = 0; i < priv->propertyCount; ++i) {
const int handle = priv->propertyData + i * intsPerProperty;
const auto flags = mo.d.data[handle + 2];
if (flags & Revisioned) {
hasRevisionedProperties = true;
}
if (flags & Notify)
hasNotifySignals = true;
visitString(mo.d.data[handle]); // name
visitTypeInfo(mo.d.data[handle + 1]);
}
int fieldsForPropertyRevisions = 0;
if (hasRevisionedProperties)
fieldsForPropertyRevisions = priv->propertyCount;
int fieldsForNotifySignals = 0;
if (hasNotifySignals)
fieldsForNotifySignals = priv->propertyCount;
return priv->propertyCount * intsPerProperty + fieldsForPropertyRevisions
+ fieldsForNotifySignals;
}
template <typename StringVisitor>
int visitClassInfo(const QMetaObject &mo, StringVisitor visitString)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
const int intsPerClassInfo = 2;
for (int i = 0; i < priv->classInfoCount; ++i) {
const int handle = priv->classInfoData + i * intsPerClassInfo;
visitString(mo.d.data[handle]); // key
visitString(mo.d.data[handle + 1]); // value
}
return priv->classInfoCount * intsPerClassInfo;
}
template <typename StringVisitor>
int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
const int intsPerEnumerator = 4;
int fieldCount = priv->enumeratorCount * intsPerEnumerator;
for (int i = 0; i < priv->enumeratorCount; ++i) {
const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator;
const uint keyCount = enumeratorData[2];
fieldCount += keyCount * 2;
visitString(enumeratorData[0]); // name
const uint keyOffset = enumeratorData[3];
for (uint j = 0; j < keyCount; ++j) {
visitString(mo.d.data[keyOffset + 2 * j]);
}
}
return fieldCount;
}
template <typename StringVisitor>
int countMetaObjectFields(const QMetaObject &mo, StringVisitor stringVisitor)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
const auto typeInfoVisitor = [&stringVisitor](uint typeInfo) {
if (typeInfo & IsUnresolvedType)
stringVisitor(typeInfo & TypeNameIndexMask);
};
int fieldCount = MetaObjectPrivateFieldCount;
fieldCount += visitMethods(mo, priv->methodData, priv->methodCount, stringVisitor,
typeInfoVisitor);
fieldCount += visitMethods(mo, priv->constructorData, priv->constructorCount, stringVisitor,
typeInfoVisitor);
fieldCount += visitProperties(mo, stringVisitor, typeInfoVisitor);
fieldCount += visitClassInfo(mo, stringVisitor);
fieldCount += visitEnumerations(mo, stringVisitor);
return fieldCount;
}
} // anonymous namespace
bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount,
int *stringCount)
{
const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
if (priv->revision != 7) {
return false;
}
uint highestStringIndex = 0;
const auto stringIndexVisitor = [&highestStringIndex](uint index) {
highestStringIndex = qMax(highestStringIndex, index);
};
*fieldCount = countMetaObjectFields(mo, stringIndexVisitor);
*stringCount = highestStringIndex + 1;
return true;
}
/*! \internal
\a index MUST be in the signal index range (see QObjectPrivate::signalIndex()).
This is different from QMetaMethod::methodIndex().

View File

@ -366,6 +366,8 @@ public:
void toMetaObjectBuilder(QMetaObjectBuilder &);
static bool determineMetaObjectSizes(const QMetaObject &mo, int *fieldCount, int *stringCount);
protected:
virtual void destroy();
virtual void clear();

View File

@ -30,6 +30,7 @@
#include <private/qqmlpropertycache_p.h>
#include <QtQml/qqmlengine.h>
#include <private/qv8engine_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include "../../shared/util.h"
class tst_qqmlpropertycache : public QObject
@ -45,6 +46,8 @@ private slots:
void methodsDerived();
void signalHandlers();
void signalHandlersDerived();
void metaObjectSize_data();
void metaObjectSize();
private:
QQmlEngine engine;
@ -105,16 +108,16 @@ void tst_qqmlpropertycache::properties()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "propertyA"));
QVERIFY((data = cacheProperty(cache, "propertyA")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
QVERIFY(data = cacheProperty(cache, "propertyB"));
QVERIFY((data = cacheProperty(cache, "propertyB")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
QVERIFY(data = cacheProperty(cache, "propertyC"));
QVERIFY((data = cacheProperty(cache, "propertyC")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
QVERIFY(data = cacheProperty(cache, "propertyD"));
QVERIFY((data = cacheProperty(cache, "propertyD")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
}
@ -129,16 +132,16 @@ void tst_qqmlpropertycache::propertiesDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "propertyA"));
QVERIFY((data = cacheProperty(cache, "propertyA")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyA"));
QVERIFY(data = cacheProperty(cache, "propertyB"));
QVERIFY((data = cacheProperty(cache, "propertyB")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyB"));
QVERIFY(data = cacheProperty(cache, "propertyC"));
QVERIFY((data = cacheProperty(cache, "propertyC")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyC"));
QVERIFY(data = cacheProperty(cache, "propertyD"));
QVERIFY((data = cacheProperty(cache, "propertyD")));
QCOMPARE(data->coreIndex, metaObject->indexOfProperty("propertyD"));
}
@ -152,28 +155,28 @@ void tst_qqmlpropertycache::methods()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "slotA"));
QVERIFY((data = cacheProperty(cache, "slotA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
QVERIFY(data = cacheProperty(cache, "slotB"));
QVERIFY((data = cacheProperty(cache, "slotB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
QVERIFY(data = cacheProperty(cache, "signalA"));
QVERIFY((data = cacheProperty(cache, "signalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
QVERIFY(data = cacheProperty(cache, "signalB"));
QVERIFY((data = cacheProperty(cache, "signalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@ -188,28 +191,28 @@ void tst_qqmlpropertycache::methodsDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "slotA"));
QVERIFY((data = cacheProperty(cache, "slotA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotA()"));
QVERIFY(data = cacheProperty(cache, "slotB"));
QVERIFY((data = cacheProperty(cache, "slotB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("slotB()"));
QVERIFY(data = cacheProperty(cache, "signalA"));
QVERIFY((data = cacheProperty(cache, "signalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
QVERIFY(data = cacheProperty(cache, "signalB"));
QVERIFY((data = cacheProperty(cache, "signalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
QVERIFY(data = cacheProperty(cache, "propertyAChanged"));
QVERIFY((data = cacheProperty(cache, "propertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyBChanged"));
QVERIFY((data = cacheProperty(cache, "propertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyCChanged"));
QVERIFY((data = cacheProperty(cache, "propertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
QVERIFY(data = cacheProperty(cache, "propertyDChanged"));
QVERIFY((data = cacheProperty(cache, "propertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@ -223,22 +226,22 @@ void tst_qqmlpropertycache::signalHandlers()
QQmlRefPointer<QQmlPropertyCache> cache(new QQmlPropertyCache(v4, metaObject));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "onSignalA"));
QVERIFY((data = cacheProperty(cache, "onSignalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
QVERIFY(data = cacheProperty(cache, "onSignalB"));
QVERIFY((data = cacheProperty(cache, "onSignalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
@ -253,25 +256,109 @@ void tst_qqmlpropertycache::signalHandlersDerived()
QQmlRefPointer<QQmlPropertyCache> cache(parentCache->copyAndAppend(object.metaObject()));
QQmlPropertyData *data;
QVERIFY(data = cacheProperty(cache, "onSignalA"));
QVERIFY((data = cacheProperty(cache, "onSignalA")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalA()"));
QVERIFY(data = cacheProperty(cache, "onSignalB"));
QVERIFY((data = cacheProperty(cache, "onSignalB")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("signalB()"));
QVERIFY(data = cacheProperty(cache, "onPropertyAChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyAChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyAChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyBChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyBChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyBChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyCChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyCChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyCChanged()"));
QVERIFY(data = cacheProperty(cache, "onPropertyDChanged"));
QVERIFY((data = cacheProperty(cache, "onPropertyDChanged")));
QCOMPARE(data->coreIndex, metaObject->indexOfMethod("propertyDChanged()"));
}
QTEST_MAIN(tst_qqmlpropertycache)
class TestClass : public QObject
{
Q_OBJECT
Q_PROPERTY(int prop READ prop WRITE setProp NOTIFY propChanged)
int m_prop;
public:
enum MyEnum {
First, Second
};
Q_ENUM(MyEnum)
Q_CLASSINFO("Foo", "Bar")
TestClass() {}
int prop() const
{
return m_prop;
}
public slots:
void setProp(int prop)
{
if (m_prop == prop)
return;
m_prop = prop;
emit propChanged(prop);
}
signals:
void propChanged(int prop);
};
class TestClassWithParameters : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void slotWithArguments(int firstArg) {
Q_UNUSED(firstArg);
}
};
class TestClassWithClassInfo : public QObject
{
Q_OBJECT
Q_CLASSINFO("Key", "Value")
};
#include "tst_qqmlpropertycache.moc"
#define ARRAY_SIZE(arr) \
int(sizeof(arr) / sizeof(arr[0]))
#define TEST_CLASS(Class) \
QTest::newRow(#Class) << &Class::staticMetaObject << ARRAY_SIZE(qt_meta_data_##Class) << ARRAY_SIZE(qt_meta_stringdata_##Class.data)
Q_DECLARE_METATYPE(const QMetaObject*);
void tst_qqmlpropertycache::metaObjectSize_data()
{
QTest::addColumn<const QMetaObject*>("metaObject");
QTest::addColumn<int>("expectedFieldCount");
QTest::addColumn<int>("expectedStringCount");
TEST_CLASS(TestClass);
TEST_CLASS(TestClassWithParameters);
TEST_CLASS(TestClassWithClassInfo);
}
void tst_qqmlpropertycache::metaObjectSize()
{
QFETCH(const QMetaObject *, metaObject);
QFETCH(int, expectedFieldCount);
QFETCH(int, expectedStringCount);
int size = 0;
int stringDataSize = 0;
bool valid = QQmlPropertyCache::determineMetaObjectSizes(*metaObject, &size, &stringDataSize);
QVERIFY(valid);
QCOMPARE(size, expectedFieldCount - 1); // Remove trailing zero field until fixed in moc.
QCOMPARE(stringDataSize, expectedStringCount);
}
QTEST_MAIN(tst_qqmlpropertycache)