qmltyperegistrar: Fix handling of default properties

Default properties are always local. There is no way to declare a
default property for a foreign type as the default property is queried
directly from the classinfo at runtime.

Change-Id: I30efb6fba190957ac2a4ad86da437f209cd1f3ad
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2021-02-02 14:11:24 +01:00
parent ecd54e68b9
commit 1b93a1d865
3 changed files with 120 additions and 26 deletions

View File

@ -113,7 +113,7 @@ void QmlTypesClassDescription::collectLocalAnonymous(
for (const QJsonValue &classInfo : classInfos) {
const QJsonObject obj = classInfo.toObject();
if (obj[QStringLiteral("name")].toString() == QStringLiteral("DefaultProperty"))
defaultProp = obj[obj[QStringLiteral("value")].toString()].toString();
defaultProp = obj[QStringLiteral("value")].toString();
}
collectInterfaces(classDef);
@ -129,6 +129,7 @@ void QmlTypesClassDescription::collect(
const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
const QString classDefName = classDef->value(QLatin1String("className")).toString();
QString foreignTypeName;
for (const QJsonValue &classInfo : classInfos) {
const QJsonObject obj = classInfo.toObject();
const QString name = obj[QLatin1String("name")].toString();
@ -173,36 +174,44 @@ void QmlTypesClassDescription::collect(
if (value == QLatin1String("true"))
isSingleton = true;
} else if (name == QLatin1String("QML.Foreign")) {
if (const QJsonObject *other = findType(foreign, value)) {
classDef = other;
// Foreign type can have a default property or an attached types
const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
for (const QJsonValue &classInfo : classInfos) {
const QJsonObject obj = classInfo.toObject();
const QString foreignName = obj[QLatin1String("name")].toString();
const QString foreignValue = obj[QLatin1String("value")].toString();
if (defaultProp.isEmpty() && foreignName == QLatin1String("DefaultProperty")) {
defaultProp = foreignValue;
} else if (foreignName == QLatin1String("QML.Attached")) {
attachedType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
} else if (foreignName == QLatin1String("QML.Extended")) {
extensionType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
} else if (foreignName == QLatin1String("QML.Sequence")) {
sequenceValueType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
}
}
} else {
className = value;
classDef = nullptr;
}
foreignTypeName = value;
} else if (name == QLatin1String("QML.Root")) {
isRootClass = true;
}
}
if (!foreignTypeName.isEmpty()) {
if (const QJsonObject *other = findType(foreign, foreignTypeName)) {
classDef = other;
// Default properties are always local.
defaultProp.clear();
// Foreign type can have a default property or an attached types
const auto classInfos = classDef->value(QLatin1String("classInfos")).toArray();
for (const QJsonValue &classInfo : classInfos) {
const QJsonObject obj = classInfo.toObject();
const QString foreignName = obj[QLatin1String("name")].toString();
const QString foreignValue = obj[QLatin1String("value")].toString();
if (defaultProp.isEmpty() && foreignName == QLatin1String("DefaultProperty")) {
defaultProp = foreignValue;
} else if (foreignName == QLatin1String("QML.Attached")) {
attachedType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
} else if (foreignName == QLatin1String("QML.Extended")) {
extensionType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
} else if (foreignName == QLatin1String("QML.Sequence")) {
sequenceValueType = foreignValue;
collectRelated(foreignValue, types, foreign, defaultRevision);
}
}
} else {
className = foreignTypeName;
classDef = nullptr;
}
}
if (classDef) {
if (mode == RelatedType || !elementName.isEmpty()) {
collectExtraVersions(classDef, QString::fromLatin1("properties"), revisions);

View File

@ -171,4 +171,38 @@ void tst_qmltyperegistrar::multiExtensions()
QVERIFY(qmltypesData.contains("interfaces: [\"Interface3\"]"));
}
void tst_qmltyperegistrar::localDefault()
{
QQmlEngine engine;
{
QQmlComponent c(&engine);
c.setData("import QmlTypeRegistrarTest\n"
"import QtQml\n"
"ForeignWithoutDefault { QtObject {} }", QUrl());
QVERIFY(c.isError());
QVERIFY(c.errorString().contains(
QStringLiteral("Cannot assign to non-existent default property")));
}
{
QQmlComponent c(&engine);
c.setData("import QmlTypeRegistrarTest\n"
"import QtQml\n"
"Local { QtObject {} }", QUrl());
QVERIFY(c.isReady());
}
QCOMPARE(qmltypesData.count("name: \"LocalWithDefault\""), 1);
QCOMPARE(qmltypesData.count("name: \"ForeignWithoutDefault\""), 1);
QCOMPARE(qmltypesData.count("defaultProperty: \"d\""), 1);
const int local = qmltypesData.indexOf("name: \"LocalWithDefault\"");
const int foreign = qmltypesData.indexOf("name: \"ForeignWithoutDefault\"");
const int defaultProp = qmltypesData.indexOf("defaultProperty: \"d\"");
// We assume that name is emitted before defaultProperty.
// Then this proves that the default property does not belong to ForeignWithoutDefault.
QVERIFY(local < defaultProp);
QVERIFY(foreign > defaultProp || foreign < local);
}
QTEST_MAIN(tst_qmltyperegistrar)

View File

@ -116,11 +116,61 @@ public:
void setWidth(int width) { v.setWidth(width); }
};
class ForeignWithoutDefault : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject *d READ d WRITE setD NOTIFY dChanged)
public:
QObject *d() const { return m_d; }
void setD(QObject *d)
{
if (m_d != d) {
m_d = d;
emit dChanged();
}
}
signals:
void dChanged();
private:
QObject *m_d = nullptr;
};
class LocalWithDefault : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject *d READ d WRITE setD NOTIFY dChanged)
QML_NAMED_ELEMENT(ForeignWithoutDefault)
QML_FOREIGN(ForeignWithoutDefault)
Q_CLASSINFO("DefaultProperty", "d")
public:
LocalWithDefault(QObject *parent = nullptr) : QObject(parent) {}
QObject *d() const { return m_d; }
void setD(QObject *d)
{
if (m_d != d) {
m_d = d;
emit dChanged();
}
}
signals:
void dChanged();
private:
QObject *m_d = nullptr;
};
class Local : public Foreign
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int someProperty MEMBER someProperty BINDABLE bindableSomeProperty)
QML_EXTENDED(LocalWithDefault)
public:
enum Flag {
Flag1 = 0x1,
@ -216,6 +266,7 @@ private slots:
void derivedFromForeign();
void metaTypesRegistered();
void multiExtensions();
void localDefault();
private:
QByteArray qmltypesData;