From d4f044533111fcfb34fe3a785eeb7af7fdbefbdd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Nov 2019 16:20:44 +0100 Subject: 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 --- src/corelib/kernel/qmetaobject.cpp | 15 ++++++++ src/corelib/kernel/qmetaobject.h | 1 + src/corelib/kernel/qmetaobject_p.h | 1 + src/corelib/kernel/qobjectdefs.h | 4 ++- src/tools/moc/generator.cpp | 72 +++++++++++++++++++++++++++----------- src/tools/moc/moc.cpp | 69 ++++++++++++++++++++++++++++++++---- src/tools/moc/moc.h | 5 +++ 7 files changed, 140 insertions(+), 27 deletions(-) (limited to 'src') 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 @@ -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 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(_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 *>(_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 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 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 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 signalList, slotList, methodList, publicList; QVector nonClassSignalList; QVector propertyList; + QSet 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); -- cgit v1.2.3