Skip to content

Commit

Permalink
Add support for exposing public QProperty members in the meta-object …
Browse files Browse the repository at this point in the history
…system

At the moment this makes the type as well as the setter/getter available
through the meta-call as well as the ability to register observers and
bindings. Only QProperty members that are annotated with
Q_PROPERTY(type name) are made public through the meta-object.

Change-Id: I16b98fd318122c722b85ce61e39975284e0c2404
Reviewed-by: Ulf Hermann <[email protected]>
  • Loading branch information
tronical committed Mar 18, 2020
1 parent b5f6a85 commit d4f0445
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 30 deletions.
15 changes: 15 additions & 0 deletions src/corelib/kernel/qmetaobject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3505,6 +3505,21 @@ bool QMetaProperty::isRequired() const
return flags & Required;
}

/*!
\since 6.0
Returns \c true if the property is implemented using a QProperty member; otherwise returns \c false.
This can be used to detect the availability of QProperty related meta-call types ahead of
performing the call itself.
*/
bool QMetaProperty::isQProperty() const
{
if (!mobj)
return false;
int flags = mobj->d.data[handle + 2];
return flags & IsQProperty;
}

/*!
\obsolete
Expand Down
1 change: 1 addition & 0 deletions src/corelib/kernel/qmetaobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ class Q_CORE_EXPORT QMetaProperty
bool isConstant() const;
bool isFinal() const;
bool isRequired() const;
bool isQProperty() const;

bool isFlagType() const;
bool isEnumType() const;
Expand Down
1 change: 1 addition & 0 deletions src/corelib/kernel/qmetaobject_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ enum PropertyFlags {
Notify = 0x00400000,
Revisioned = 0x00800000,
Required = 0x01000000,
IsQProperty = 0x02000000
};

enum MethodFlags {
Expand Down
4 changes: 3 additions & 1 deletion src/corelib/kernel/qobjectdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ struct Q_CORE_EXPORT QMetaObject
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType
RegisterMethodArgumentMetaType,
RegisterQPropertyObserver,
SetQPropertyBinding
};

int static_metacall(Call, int, void **) const;
Expand Down
72 changes: 52 additions & 20 deletions src/tools/moc/generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,9 @@ void Generator::generateProperties()
if (p.required)
flags |= Required;

if (p.isQProperty)
flags |= IsQProperty;

fprintf(out, " %4d, ", stridx(p.name));
generateTypeInfo(p.type);
fprintf(out, ", 0x%.8x,\n", flags);
Expand Down Expand Up @@ -1017,7 +1020,9 @@ void Generator::generateMetacall()
fprintf(out, "else ");
fprintf(out,
"if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n"
" || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {\n"
" || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType\n"
" || _c == QMetaObject::RegisterQPropertyObserver\n"
" || _c == QMetaObject::SetQPropertyBinding) {\n"
" qt_static_metacall(this, _c, _id, _a);\n"
" _id -= %d;\n }", cdef->propertyList.count());

Expand Down Expand Up @@ -1354,6 +1359,7 @@ void Generator::generateStaticMetacall()
bool needTempVarForGet = false;
bool needSet = false;
bool needReset = false;
bool haveQProperties = false;
for (int i = 0; i < cdef->propertyList.size(); ++i) {
const PropertyDef &p = cdef->propertyList.at(i);
needGet |= !p.read.isEmpty() || !p.member.isEmpty();
Expand All @@ -1363,13 +1369,15 @@ void Generator::generateStaticMetacall()

needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant);
needReset |= !p.reset.isEmpty();
haveQProperties |= p.isQProperty;
}
fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");

if (needElse)
fprintf(out, "else ");
fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n");
if (needGet) {

auto setupMemberAccess = [this]() {
if (cdef->hasQObject) {
#ifndef QT_NO_DEBUG
fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
Expand All @@ -1379,6 +1387,10 @@ void Generator::generateStaticMetacall()
fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
}
fprintf(out, " Q_UNUSED(_t)\n");
};

if (needGet) {
setupMemberAccess();
if (needTempVarForGet)
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
Expand Down Expand Up @@ -1416,15 +1428,7 @@ void Generator::generateStaticMetacall()
fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n");

if (needSet) {
if (cdef->hasQObject) {
#ifndef QT_NO_DEBUG
fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
#endif
fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData());
} else {
fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
}
fprintf(out, " Q_UNUSED(_t)\n");
setupMemberAccess();
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
Expand Down Expand Up @@ -1472,15 +1476,7 @@ void Generator::generateStaticMetacall()
fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n");
if (needReset) {
if (cdef->hasQObject) {
#ifndef QT_NO_DEBUG
fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
#endif
fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
} else {
fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
}
fprintf(out, " Q_UNUSED(_t)\n");
setupMemberAccess();
fprintf(out, " switch (_id) {\n");
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
Expand All @@ -1497,6 +1493,42 @@ void Generator::generateStaticMetacall()
fprintf(out, " }\n");
}
fprintf(out, " }");

fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::RegisterQPropertyObserver) {\n");
if (haveQProperties) {
setupMemberAccess();
fprintf(out, " QPropertyObserver *observer = reinterpret_cast<QPropertyObserver *>(_a[0]);\n");
fprintf(out, " switch (_id) {\n");
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (!p.isQProperty)
continue;
fprintf(out, " case %d: observer->setSource(_t->%s); break;\n",
propindex, p.name.constData());
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
}
fprintf(out, " }");

fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::SetQPropertyBinding) {\n");
if (haveQProperties) {
setupMemberAccess();
fprintf(out, " switch (_id) {\n");
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (!p.isQProperty)
continue;
fprintf(out, " case %d: _t->%s.setBinding(*reinterpret_cast<QPropertyBinding<%s> *>(_a[0])); break;\n",
propindex, p.name.constData(), p.type.constData());
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
}
fprintf(out, " }");

fprintf(out, "\n#endif // QT_NO_PROPERTIES");
needElse = true;
}
Expand Down
69 changes: 63 additions & 6 deletions src/tools/moc/moc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,32 @@ bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
return true;
}


// Try to parse QProperty<MyType> propertName; members
bool Moc::parseMaybeQProperty(ClassDef *def)
{
if (!test(IDENTIFIER))
return false;

if (lexem() != "QProperty")
return false;

if (!test(LANGLE))
return false;

until(RANGLE);

next();
const auto propName = lexem();

if (!test(SEMIC))
return false;

def->qPropertyMembers.insert(propName);

return true;
}

void Moc::parse()
{
QVector<NamespaceDef> namespaceList;
Expand Down Expand Up @@ -909,7 +935,9 @@ void Moc::parse()
}
}
} else {
index = rewind;
index = rewind - 1;
if (!parseMaybeQProperty(&def))
index = rewind;
}
}
}
Expand Down Expand Up @@ -1198,11 +1226,14 @@ void Moc::parseSignals(ClassDef *def)

void Moc::createPropertyDef(PropertyDef &propDef)
{
propDef.location = index;

QByteArray type = parseType().name;
if (type.isEmpty())
error();
propDef.designable = propDef.scriptable = propDef.stored = "true";
propDef.user = "false";

/*
The Q_PROPERTY construct cannot contain any commas, since
commas separate macro arguments. We therefore expect users
Expand Down Expand Up @@ -1234,6 +1265,17 @@ void Moc::createPropertyDef(PropertyDef &propDef)

next();
propDef.name = lexem();

// Could be Q_PROPERTY(type field) and later QProperty<int> field; -- to be resolved later.
if (lookup() == RPAREN) {
propDef.isQProperty = true;
propDef.designable = propDef.scriptable = propDef.stored = "true";
propDef.user = "false";
propDef.read = propDef.name + ".value";
propDef.write = propDef.name + ".setValue";
return;
}

while (test(IDENTIFIER)) {
const QByteArray l = lexem();
if (l[0] == 'C' && l == "CONSTANT") {
Expand Down Expand Up @@ -1320,11 +1362,6 @@ void Moc::createPropertyDef(PropertyDef &propDef)
error(2);
}
}
if (propDef.read.isNull() && propDef.member.isNull()) {
const QByteArray msg = "Property declaration " + propDef.name
+ " has no READ accessor function or associated MEMBER variable. The property will be invalid.";
warning(msg.constData());
}
if (propDef.constant && !propDef.write.isNull()) {
const QByteArray msg = "Property declaration " + propDef.name
+ " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
Expand Down Expand Up @@ -1777,6 +1814,25 @@ void Moc::checkProperties(ClassDef *cdef)
}
definedProperties.insert(p.name);

const auto skipProperty = [&](const QByteArray &msg) {
const int rewind = index;
if (p.location >= 0)
index = p.location;
warning(msg.constData());
index = rewind;
cdef->propertyList.removeAt(i);
--i;
};

if (p.isQProperty) {
if (!cdef->qPropertyMembers.contains(p.name)) {
QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member"
", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.";
skipProperty(msg);
break;
}
}

for (int j = 0; j < cdef->publicList.count(); ++j) {
const FunctionDef &f = cdef->publicList.at(j);
if (f.name != p.read)
Expand Down Expand Up @@ -1989,6 +2045,7 @@ QJsonObject PropertyDef::toJson() const
prop[QLatin1String("constant")] = constant;
prop[QLatin1String("final")] = final;
prop[QLatin1String("required")] = required;
prop[QLatin1String("isQProperty")] = isQProperty;

if (revision > 0)
prop[QLatin1String("revision")] = revision;
Expand Down
5 changes: 5 additions & 0 deletions src/tools/moc/moc.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ struct PropertyDef
bool constant = false;
bool final = false;
bool required = false;
bool isQProperty = false;

int location = -1; // token index, used for error reporting

QJsonObject toJson() const;
};
Expand Down Expand Up @@ -188,6 +191,7 @@ struct ClassDef : BaseDef {
QVector<FunctionDef> signalList, slotList, methodList, publicList;
QVector<QByteArray> nonClassSignalList;
QVector<PropertyDef> propertyList;
QSet<QByteArray> qPropertyMembers;
int notifyableProperties = 0;
int revisionedMethods = 0;
int revisionedProperties = 0;
Expand Down Expand Up @@ -247,6 +251,7 @@ class Moc : public Parser

bool parseFunction(FunctionDef *def, bool inMacro = false);
bool parseMaybeFunction(const ClassDef *cdef, FunctionDef *def);
bool parseMaybeQProperty(ClassDef *def);

void parseSlots(ClassDef *def, FunctionDef::Access access);
void parseSignals(ClassDef *def);
Expand Down
Loading

0 comments on commit d4f0445

Please sign in to comment.