summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2019-11-08 16:20:44 +0100
committerSimon Hausmann <simon.hausmann@qt.io>2020-03-18 15:42:58 +0100
commitd4f044533111fcfb34fe3a785eeb7af7fdbefbdd (patch)
tree1b2ef0c8695655e6ee4c1ae5f13a731738d1da96 /src
parentb5f6a85d2745ab6ac97f593664d255906923e737 (diff)
Add support for exposing public QProperty members in the meta-object 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 <ulf.hermann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/kernel/qmetaobject.cpp15
-rw-r--r--src/corelib/kernel/qmetaobject.h1
-rw-r--r--src/corelib/kernel/qmetaobject_p.h1
-rw-r--r--src/corelib/kernel/qobjectdefs.h4
-rw-r--r--src/tools/moc/generator.cpp72
-rw-r--r--src/tools/moc/moc.cpp69
-rw-r--r--src/tools/moc/moc.h5
7 files changed, 140 insertions, 27 deletions
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 9be99d8c6a..e715093127 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -3506,6 +3506,21 @@ bool QMetaProperty::isRequired() const
}
/*!
+ \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
Returns \c true if the property is editable for the given \a object;
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index 08adc495e0..96f851a0e1 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -265,6 +265,7 @@ public:
bool isConstant() const;
bool isFinal() const;
bool isRequired() const;
+ bool isQProperty() const;
bool isFlagType() const;
bool isEnumType() const;
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index 277109dac4..49c43e3d79 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -87,6 +87,7 @@ enum PropertyFlags {
Notify = 0x00400000,
Revisioned = 0x00800000,
Required = 0x01000000,
+ IsQProperty = 0x02000000
};
enum MethodFlags {
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index fd7c081e88..5ae4c47259 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -390,7 +390,9 @@ struct Q_CORE_EXPORT QMetaObject
CreateInstance,
IndexOfMethod,
RegisterPropertyMetaType,
- RegisterMethodArgumentMetaType
+ RegisterMethodArgumentMetaType,
+ RegisterQPropertyObserver,
+ SetQPropertyBinding
};
int static_metacall(Call, int, void **) const;
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index 078eea257d..c0e1dca748 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -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);
@@ -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());
@@ -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();
@@ -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");
@@ -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");
@@ -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) {
@@ -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);
@@ -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;
}
diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp
index 03976771e5..56db54b457 100644
--- a/src/tools/moc/moc.cpp
+++ b/src/tools/moc/moc.cpp
@@ -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;
@@ -909,7 +935,9 @@ void Moc::parse()
}
}
} else {
- index = rewind;
+ index = rewind - 1;
+ if (!parseMaybeQProperty(&def))
+ index = rewind;
}
}
}
@@ -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
@@ -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") {
@@ -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.";
@@ -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)
@@ -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;
diff --git a/src/tools/moc/moc.h b/src/tools/moc/moc.h
index 743749433f..210b6c7c2a 100644
--- a/src/tools/moc/moc.h
+++ b/src/tools/moc/moc.h
@@ -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;
};
@@ -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;
@@ -247,6 +251,7 @@ public:
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);