MetaObject: store the QMetaType of the properties

Change-Id: I563e7232b70e94de4184f2c23a581319313dcf5c
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Olivier Goffart 2019-12-05 16:14:14 +01:00
parent a68e4f3b96
commit 46f407126e
13 changed files with 97 additions and 123 deletions

View File

@ -2833,10 +2833,6 @@ QByteArray QMetaEnum::valueToKeys(int value) const
The enum needs to be declared with Q_ENUM. The enum needs to be declared with Q_ENUM.
*/ */
static QByteArray qualifiedName(const QMetaEnum &e)
{
return QByteArray(e.scope()) + "::" + e.name();
}
/*! /*!
\class QMetaProperty \class QMetaProperty
@ -2927,65 +2923,45 @@ const char *QMetaProperty::typeName() const
Returns this property's type. The return value is one Returns this property's type. The return value is one
of the values of the QVariant::Type enumeration. of the values of the QVariant::Type enumeration.
\sa userType(), typeName(), name() \sa userType(), typeName(), name(), metaType()
*/ */
QVariant::Type QMetaProperty::type() const QVariant::Type QMetaProperty::type() const
{ {
if (!mobj) uint type = userType();
return QVariant::Invalid;
int handle = priv(mobj->d.data)->propertyData + 3*idx;
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
uint type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]);
if (type >= QMetaType::User) if (type >= QMetaType::User)
return QVariant::UserType; return QVariant::UserType;
if (type != QMetaType::UnknownType)
return QVariant::Type(type); return QVariant::Type(type);
if (isEnumType()) {
int enumMetaTypeId = QMetaType::type(qualifiedName(menum));
if (enumMetaTypeId == QMetaType::UnknownType)
return QVariant::Int;
}
#ifdef QT_COORD_TYPE
// qreal metatype must be resolved at runtime.
if (strcmp(typeName(), "qreal") == 0)
return QVariant::Type(qMetaTypeId<qreal>());
#endif
return QVariant::UserType;
} }
/*! /*!
\since 4.2 \since 4.2
Returns this property's user type. The return value is one Returns this property's user type. The return value is one
of the values that are registered with QMetaType, or QMetaType::UnknownType if of the values that are registered with QMetaType.
the type is not registered.
\sa type(), QMetaType, typeName() This is equivalent to metaType().id()
\sa type(), QMetaType, typeName(), metaType()
*/ */
int QMetaProperty::userType() const int QMetaProperty::userType() const
{ {
if (!mobj) if (!mobj)
return QMetaType::UnknownType; return QMetaType::UnknownType;
Q_ASSERT(priv(mobj->d.data)->revision >= 7); return QMetaType(mobj->d.metaTypes[idx]).id();
int handle = priv(mobj->d.data)->propertyData + 3*idx;
int type = typeFromTypeInfo(mobj, mobj->d.data[handle + 1]);
if (type != QMetaType::UnknownType)
return type;
if (isEnumType()) {
type = QMetaType::type(qualifiedName(menum));
if (type == QMetaType::UnknownType) {
type = registerPropertyType();
if (type == QMetaType::UnknownType)
return QMetaType::Int; // Match behavior of QMetaType::type()
} }
return type;
} /*!
type = QMetaType::type(typeName()); \since 6.0
if (type != QMetaType::UnknownType)
return type; Returns this property's QMetaType.
return registerPropertyType();
\sa QMetaType
*/
QMetaType QMetaProperty::metaType() const
{
if (!mobj)
return {};
return QMetaType(mobj->d.metaTypes[idx]);
} }
/*! /*!
@ -3096,37 +3072,6 @@ QVariant QMetaProperty::read(const QObject *object) const
if (!object || !mobj) if (!object || !mobj)
return QVariant(); return QVariant();
uint t = QMetaType::Int;
if (isEnumType()) {
/*
try to create a QVariant that can be converted to this enum
type (only works if the enum has already been registered
with QMetaType)
*/
int enumMetaTypeId = QMetaType::type(qualifiedName(menum));
if (enumMetaTypeId != QMetaType::UnknownType)
t = enumMetaTypeId;
} else {
int handle = priv(mobj->d.data)->propertyData + 3*idx;
const char *typeName = nullptr;
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
uint typeInfo = mobj->d.data[handle + 1];
if (!(typeInfo & IsUnresolvedType))
t = typeInfo;
else {
typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask);
t = QMetaType::type(typeName);
}
if (t == QMetaType::UnknownType) {
// Try to register the type and try again before reporting an error.
t = registerPropertyType();
if (t == QMetaType::UnknownType) {
qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property '%s::%s'", typeName, mobj->className(), name());
return QVariant();
}
}
}
// the status variable is changed by qt_metacall to indicate what it did // the status variable is changed by qt_metacall to indicate what it did
// this feature is currently only used by Qt D-Bus and should not be depended // this feature is currently only used by Qt D-Bus and should not be depended
// upon. Don't change it without looking into QDBusAbstractInterface first // upon. Don't change it without looking into QDBusAbstractInterface first
@ -3135,10 +3080,11 @@ QVariant QMetaProperty::read(const QObject *object) const
int status = -1; int status = -1;
QVariant value; QVariant value;
void *argv[] = { nullptr, &value, &status }; void *argv[] = { nullptr, &value, &status };
if (t == QMetaType::QVariant) { QMetaType t(mobj->d.metaTypes[idx]);
if (t == QMetaType::fromType<QVariant>()) {
argv[0] = &value; argv[0] = &value;
} else { } else {
value = QVariant(t, (void*)nullptr); value = QVariant(t, nullptr);
argv[0] = value.data(); argv[0] = value.data();
} }
if (priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static_metacall) { if (priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static_metacall) {
@ -3150,9 +3096,9 @@ QVariant QMetaProperty::read(const QObject *object) const
if (status != -1) if (status != -1)
return value; return value;
if (t != QMetaType::QVariant && argv[0] != value.data()) if (t != QMetaType::fromType<QVariant>() && argv[0] != value.data())
// pointer or reference // pointer or reference
return QVariant((QVariant::Type)t, argv[0]); return QVariant(t, argv[0]);
return value; return value;
} }
@ -3173,9 +3119,10 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const
return false; return false;
QVariant v = value; QVariant v = value;
uint t = QMetaType::UnknownType; QMetaType t(mobj->d.metaTypes[idx]);
if (isEnumType()) { if (t != QMetaType::fromType<QVariant>() && t != v.metaType()) {
if (v.userType() == QMetaType::QString) { if (isEnumType() && !t.metaObject() && v.userType() == QMetaType::QString) {
// Assigning a string to a property of type Q_ENUMS (instead of Q_ENUM)
bool ok; bool ok;
if (isFlagType()) if (isFlagType())
v = QVariant(menum.keysToValue(value.toByteArray(), &ok)); v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
@ -3183,39 +3130,14 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const
v = QVariant(menum.keyToValue(value.toByteArray(), &ok)); v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
if (!ok) if (!ok)
return false; return false;
} else if (v.userType() != QMetaType::Int && v.userType() != QMetaType::UInt) { } else if (!value.isValid()) {
int enumMetaTypeId = QMetaType::type(qualifiedName(menum));
if ((enumMetaTypeId == QMetaType::UnknownType) || (v.userType() != enumMetaTypeId) || !v.constData())
return false;
v = QVariant(*reinterpret_cast<const int *>(v.constData()));
}
v.convert(QMetaType::Int);
} else {
int handle = priv(mobj->d.data)->propertyData + 3*idx;
const char *typeName = nullptr;
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
uint typeInfo = mobj->d.data[handle + 1];
if (!(typeInfo & IsUnresolvedType))
t = typeInfo;
else {
typeName = rawStringData(mobj, typeInfo & TypeNameIndexMask);
t = QMetaType::type(typeName);
if (t == QMetaType::UnknownType)
t = registerPropertyType();
if (t == QMetaType::UnknownType)
return false;
}
if (t != QMetaType::QVariant && int(t) != value.userType()) {
if (!value.isValid()) {
if (isResettable()) if (isResettable())
return reset(object); return reset(object);
v = QVariant(t, nullptr); v = QVariant(t, nullptr);
} else if (!v.convert(t)) { } else if (!v.convert(t.id())) {
return false; return false;
} }
} }
}
// the status variable is changed by qt_metacall to indicate what it did // the status variable is changed by qt_metacall to indicate what it did
// this feature is currently only used by Qt D-Bus and should not be depended // this feature is currently only used by Qt D-Bus and should not be depended
// upon. Don't change it without looking into QDBusAbstractInterface first // upon. Don't change it without looking into QDBusAbstractInterface first
@ -3226,7 +3148,7 @@ bool QMetaProperty::write(QObject *object, const QVariant &value) const
// interception of property writes. // interception of property writes.
int flags = 0; int flags = 0;
void *argv[] = { nullptr, &v, &status, &flags }; void *argv[] = { nullptr, &v, &status, &flags };
if (t == QMetaType::QVariant) if (t == QMetaType::fromType<QVariant>())
argv[0] = &v; argv[0] = &v;
else else
argv[0] = v.data(); argv[0] = v.data();

View File

@ -250,6 +250,7 @@ public:
const char *typeName() const; const char *typeName() const;
QVariant::Type type() const; QVariant::Type type() const;
int userType() const; int userType() const;
QMetaType metaType() const;
int propertyIndex() const; int propertyIndex() const;
int relativePropertyIndex() const; int relativePropertyIndex() const;

View File

@ -1196,6 +1196,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
if (!relocatable) meta->d.superdata = d->superClass; if (!relocatable) meta->d.superdata = d->superClass;
meta->d.relatedMetaObjects = nullptr; meta->d.relatedMetaObjects = nullptr;
meta->d.extradata = nullptr; meta->d.extradata = nullptr;
meta->d.metaTypes = nullptr;
meta->d.static_metacall = d->staticMetacallFunction; meta->d.static_metacall = d->staticMetacallFunction;
} }
@ -1474,6 +1475,20 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
size += sizeof(SuperData) * (d->relatedMetaObjects.size() + 1); size += sizeof(SuperData) * (d->relatedMetaObjects.size() + 1);
} }
if (d->properties.size() > 0) {
ALIGN(size, QtPrivate::QMetaTypeInterface *);
auto types = reinterpret_cast<QtPrivate::QMetaTypeInterface **>(buf + size);
if (buf) {
meta->d.metaTypes = types;
for (const auto &prop : d->properties) {
QMetaType mt(QMetaType::type(prop.type));
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
}
}
size += sizeof(QMetaType) * d->properties.size();
}
// Align the final size and return it. // Align the final size and return it.
ALIGN(size, void *); ALIGN(size, void *);
Q_ASSERT(!buf || size == expectedSize); Q_ASSERT(!buf || size == expectedSize);

View File

@ -2764,6 +2764,11 @@ QMetaType QMetaType::fromType()
return QMetaType(QtPrivate::qMetaTypeIntefaceForType<T>()); return QMetaType(QtPrivate::qMetaTypeIntefaceForType<T>());
} }
template<typename... T>
QtPrivate::QMetaTypeInterface *const qt_metaTypeArray[] = {
QtPrivate::qMetaTypeIntefaceForType<T>()...
};
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QMETATYPE_H #endif // QMETATYPE_H

View File

@ -101,6 +101,10 @@ class QMetaEnum;
class QMetaProperty; class QMetaProperty;
class QMetaClassInfo; class QMetaClassInfo;
namespace QtPrivate {
class QMetaTypeInterface;
}
struct QMethodRawArguments struct QMethodRawArguments
{ {
void **arguments; void **arguments;
@ -428,6 +432,7 @@ struct Q_CORE_EXPORT QMetaObject
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall; StaticMetacallFunction static_metacall;
const SuperData *relatedMetaObjects; const SuperData *relatedMetaObjects;
QtPrivate::QMetaTypeInterface *const *metaTypes;
void *extradata; //reserved for future use void *extradata; //reserved for future use
} d; } d;

View File

@ -521,6 +521,9 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
offset += methodParametersDataSize; offset += methodParametersDataSize;
Q_ASSERT(offset == header->propertyData); Q_ASSERT(offset == header->propertyData);
QMetaType *metaTypes = new QMetaType[properties.count()];
int propertyId = 0;
// add each property // add each property
signatureOffset = header->propertyDBusData; signatureOffset = header->propertyDBusData;
for (QMap<QByteArray, Property>::ConstIterator it = properties.constBegin(); for (QMap<QByteArray, Property>::ConstIterator it = properties.constBegin();
@ -535,6 +538,8 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
idata[signatureOffset++] = strings.enter(mp.signature); idata[signatureOffset++] = strings.enter(mp.signature);
idata[signatureOffset++] = mp.type; idata[signatureOffset++] = mp.type;
metaTypes[propertyId++] = QMetaType(mp.type);
} }
Q_ASSERT(offset == header->propertyDBusData); Q_ASSERT(offset == header->propertyDBusData);
@ -553,6 +558,7 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj)
obj->d.extradata = nullptr; obj->d.extradata = nullptr;
obj->d.stringdata = reinterpret_cast<const uint *>(string_data); obj->d.stringdata = reinterpret_cast<const uint *>(string_data);
obj->d.superdata = &QDBusAbstractInterface::staticMetaObject; obj->d.superdata = &QDBusAbstractInterface::staticMetaObject;
obj->d.metaTypes = reinterpret_cast<QtPrivate::QMetaTypeInterface *const *>(metaTypes);
} }
#if 0 #if 0

View File

@ -77,6 +77,7 @@ struct Q_DBUS_EXPORT QDBusMetaObject: public QMetaObject
{ {
delete [] reinterpret_cast<const char *>(d.stringdata); delete [] reinterpret_cast<const char *>(d.stringdata);
delete [] d.data; delete [] d.data;
delete [] reinterpret_cast<const QMetaType *>(d.metaTypes);
} }
// methods (slots & signals): // methods (slots & signals):

View File

@ -83,6 +83,7 @@ class QDBusTrayIcon: public QPlatformSystemTrayIcon
Q_PROPERTY(QString attentionIconName READ attentionIconName NOTIFY attention) Q_PROPERTY(QString attentionIconName READ attentionIconName NOTIFY attention)
Q_PROPERTY(QIcon attentionIcon READ attentionIcon NOTIFY attention) Q_PROPERTY(QIcon attentionIcon READ attentionIcon NOTIFY attention)
Q_PROPERTY(QDBusPlatformMenu *menu READ menu NOTIFY menuChanged) Q_PROPERTY(QDBusPlatformMenu *menu READ menu NOTIFY menuChanged)
Q_MOC_INCLUDE("qdbusplatformmenu_p.h")
public: public:
QDBusTrayIcon(); QDBusTrayIcon();

View File

@ -552,6 +552,18 @@ void Generator::generateCode()
fprintf(out, " nullptr,\n"); fprintf(out, " nullptr,\n");
else else
fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData()); fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData());
if (cdef->propertyList.isEmpty()) {
fprintf(out, " nullptr,\n");
} else {
fprintf(out, "qt_metaTypeArray<\n");
for (int i = 0; i < cdef->propertyList.count(); ++i) {
const PropertyDef &p = cdef->propertyList.at(i);
fprintf(out, "%s%s", i == 0 ? "" : ", ", p.type.data());
}
fprintf(out, ">,\n");
}
fprintf(out, " nullptr\n} };\n\n"); fprintf(out, " nullptr\n} };\n\n");
if (!cdef->hasQObject) if (!cdef->hasQObject)

View File

@ -1446,13 +1446,15 @@ void tst_QMetaObject::customPropertyType()
QMetaProperty prop = metaObject()->property(metaObject()->indexOfProperty("value3")); QMetaProperty prop = metaObject()->property(metaObject()->indexOfProperty("value3"));
QCOMPARE(prop.type(), QVariant::UserType); QCOMPARE(prop.type(), QVariant::UserType);
QCOMPARE(prop.userType(), 0); QCOMPARE(prop.userType(), QMetaType::fromType<MyStruct>().id());
QCOMPARE(prop.metaType(), QMetaType::fromType<MyStruct>());
qRegisterMetaType<MyStruct>("MyStruct"); qRegisterMetaType<MyStruct>("MyStruct");
QCOMPARE(prop.userType(), QMetaType::type("MyStruct")); QCOMPARE(prop.userType(), QMetaType::type("MyStruct"));
prop = metaObject()->property(metaObject()->indexOfProperty("value4")); prop = metaObject()->property(metaObject()->indexOfProperty("value4"));
QCOMPARE(prop.type(), QVariant::List); QCOMPARE(prop.type(), QVariant::List);
QCOMPARE(prop.metaType(), QMetaType::fromType<QList<QVariant>>());
prop = metaObject()->property(metaObject()->indexOfProperty("value5")); prop = metaObject()->property(metaObject()->indexOfProperty("value5"));
QCOMPARE(prop.type(), QVariant::List); QCOMPARE(prop.type(), QVariant::List);

View File

@ -138,6 +138,7 @@ void tst_QMetaProperty::gadget()
const QMetaObject *mo = &MyGadget::staticMetaObject; const QMetaObject *mo = &MyGadget::staticMetaObject;
QMetaProperty valueProp = mo->property(mo->indexOfProperty("value")); QMetaProperty valueProp = mo->property(mo->indexOfProperty("value"));
QVERIFY(valueProp.isValid()); QVERIFY(valueProp.isValid());
QCOMPARE(valueProp.metaType(), QMetaType::fromType<QString>());
{ {
MyGadget g; MyGadget g;
QString hello = QLatin1String("hello"); QString hello = QLatin1String("hello");

View File

@ -1914,7 +1914,7 @@ void tst_QObject::property()
property = mo->property(mo->indexOfProperty("alpha")); property = mo->property(mo->indexOfProperty("alpha"));
QVERIFY(property.isEnumType()); QVERIFY(property.isEnumType());
QCOMPARE(property.typeName(), "Alpha"); QCOMPARE(property.typeName(), "Alpha");
QCOMPARE(property.type(), QVariant::Int); QCOMPARE(property.userType(), QMetaType::fromType<PropertyObject::Alpha>().id());
QVariant var = object.property("alpha"); QVariant var = object.property("alpha");
QVERIFY(!var.isNull()); QVERIFY(!var.isNull());
@ -1925,7 +1925,8 @@ void tst_QObject::property()
QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha2)); QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha2));
QVERIFY(object.setProperty("alpha", "Alpha1")); QVERIFY(object.setProperty("alpha", "Alpha1"));
QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1)); QCOMPARE(object.property("alpha").toInt(), int(PropertyObject::Alpha1));
QVERIFY(!object.setProperty("alpha", QVariant())); QVERIFY(object.setProperty("alpha", QVariant()));
QCOMPARE(object.property("alpha").toInt(), 0);
QVERIFY(mo->indexOfProperty("number") != -1); QVERIFY(mo->indexOfProperty("number") != -1);
QCOMPARE(object.property("number").toInt(), 0); QCOMPARE(object.property("number").toInt(), 0);
@ -1995,7 +1996,7 @@ void tst_QObject::property()
property = mo->property(mo->indexOfProperty("priority")); property = mo->property(mo->indexOfProperty("priority"));
QVERIFY(property.isEnumType()); QVERIFY(property.isEnumType());
QCOMPARE(property.typeName(), "Priority"); QCOMPARE(property.typeName(), "Priority");
QCOMPARE(property.type(), QVariant::Int); QCOMPARE(property.userType(), QMetaType::fromType<PropertyObject::Priority>().id());
var = object.property("priority"); var = object.property("priority");
QVERIFY(!var.isNull()); QVERIFY(!var.isNull());
@ -2006,7 +2007,8 @@ void tst_QObject::property()
QCOMPARE(object.property("priority").toInt(), int(PropertyObject::VeryHigh)); QCOMPARE(object.property("priority").toInt(), int(PropertyObject::VeryHigh));
QVERIFY(object.setProperty("priority", "High")); QVERIFY(object.setProperty("priority", "High"));
QCOMPARE(object.property("priority").toInt(), int(PropertyObject::High)); QCOMPARE(object.property("priority").toInt(), int(PropertyObject::High));
QVERIFY(!object.setProperty("priority", QVariant())); QVERIFY(object.setProperty("priority", QVariant()));
QCOMPARE(object.property("priority").toInt(), 0);
// now it's registered, so it works as expected // now it's registered, so it works as expected
int priorityMetaTypeId = qRegisterMetaType<PropertyObject::Priority>("PropertyObject::Priority"); int priorityMetaTypeId = qRegisterMetaType<PropertyObject::Priority>("PropertyObject::Priority");
@ -2028,7 +2030,8 @@ void tst_QObject::property()
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::VeryHigh); QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::VeryHigh);
QVERIFY(object.setProperty("priority", "High")); QVERIFY(object.setProperty("priority", "High"));
QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::High); QCOMPARE(qvariant_cast<PropertyObject::Priority>(object.property("priority")), PropertyObject::High);
QVERIFY(!object.setProperty("priority", QVariant())); QVERIFY(object.setProperty("priority", QVariant()));
QCOMPARE(object.property("priority").toInt(), 0);
var = object.property("priority"); var = object.property("priority");
QCOMPARE(qvariant_cast<PropertyObject::Priority>(var), PropertyObject::High); QCOMPARE(qvariant_cast<PropertyObject::Priority>(var), PropertyObject::High);

View File

@ -3945,7 +3945,7 @@ void tst_Moc::testQNamespace()
EnumFromNamespaceClass obj; EnumFromNamespaceClass obj;
const QVariant prop = obj.property("prop"); const QVariant prop = obj.property("prop");
QCOMPARE(prop.type(), QMetaType::Int); QCOMPARE(prop.userType(), QMetaType::fromType<FooNamespace::Enum1>().id());
QCOMPARE(prop.toInt(), int(FooNamespace::Enum1::Key2)); QCOMPARE(prop.toInt(), int(FooNamespace::Enum1::Key2));
} }