Key required properties by object and property data

Property data alone is not enough as the same property can be required
in multiple objects.

Fixes: QTBUG-108291
Change-Id: I3b1c899e24bb2967d05372701f9b5d0927b3c711
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2022-11-24 15:15:13 +01:00
parent 6acf343acb
commit b6ba7e9c90
6 changed files with 63 additions and 11 deletions

View File

@ -1036,7 +1036,7 @@ QObject *QQmlComponentPrivate::beginCreate(QQmlRefPointer<QQmlContextData> conte
state.ensureRequiredPropertyStorage(); state.ensureRequiredPropertyStorage();
RequiredPropertyInfo info; RequiredPropertyInfo info;
info.propertyName = propertyData->name(rv); info.propertyName = propertyData->name(rv);
state.addPendingRequiredProperty(propertyData, info); state.addPendingRequiredProperty(rv, propertyData, info);
} }
} }
} }
@ -1147,7 +1147,7 @@ QQmlProperty QQmlComponentPrivate::removePropertyFromRequired(
Q_ASSERT(data && data->propertyCache); Q_ASSERT(data && data->propertyCache);
targetProp = data->propertyCache->property(targetProp->coreIndex()); targetProp = data->propertyCache->property(targetProp->coreIndex());
} }
auto it = requiredProperties->find(targetProp); auto it = requiredProperties->find({createdComponent, targetProp});
if (it != requiredProperties->end()) { if (it != requiredProperties->end()) {
if (wasInRequiredProperties) if (wasInRequiredProperties)
*wasInRequiredProperties = true; *wasInRequiredProperties = true;

View File

@ -110,7 +110,9 @@ public:
inline void ensureRequiredPropertyStorage(); inline void ensureRequiredPropertyStorage();
inline RequiredProperties *requiredProperties(); inline RequiredProperties *requiredProperties();
inline void addPendingRequiredProperty(const QQmlPropertyData *propData, const RequiredPropertyInfo &info); inline void addPendingRequiredProperty(
const QObject *object, const QQmlPropertyData *propData,
const RequiredPropertyInfo &info);
inline bool hasUnsetRequiredProperties() const; inline bool hasUnsetRequiredProperties() const;
inline void clearRequiredProperties(); inline void clearRequiredProperties();
@ -193,10 +195,11 @@ inline RequiredProperties *QQmlComponentPrivate::ConstructionState::requiredProp
return m_creatorOrRequiredProperties.asT2(); return m_creatorOrRequiredProperties.asT2();
} }
inline void QQmlComponentPrivate::ConstructionState::addPendingRequiredProperty(const QQmlPropertyData *propData, const RequiredPropertyInfo &info) inline void QQmlComponentPrivate::ConstructionState::addPendingRequiredProperty(
const QObject *object, const QQmlPropertyData *propData, const RequiredPropertyInfo &info)
{ {
Q_ASSERT(requiredProperties()); Q_ASSERT(requiredProperties());
requiredProperties()->insert(propData, info); requiredProperties()->insert({object, propData}, info);
} }
inline bool QQmlComponentPrivate::ConstructionState::hasUnsetRequiredProperties() const { inline bool QQmlComponentPrivate::ConstructionState::hasUnsetRequiredProperties() const {

View File

@ -694,8 +694,9 @@ void QQmlObjectCreator::setupBindings(BindingSetupFlags mode)
QQmlData *data = QQmlData::get(targetObject); QQmlData *data = QQmlData::get(targetObject);
Q_ASSERT(data && data->propertyCache); Q_ASSERT(data && data->propertyCache);
targetProperty = data->propertyCache->property(targetIndex.coreIndex()); targetProperty = data->propertyCache->property(targetIndex.coreIndex());
sharedState->requiredProperties.remove({targetObject, targetProperty});
} }
sharedState->requiredProperties.remove(targetProperty); sharedState->requiredProperties.remove({_bindingTarget, property});
} }
@ -1563,7 +1564,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
postHocRequired.erase(postHocIt); postHocRequired.erase(postHocIt);
if (isContextObject) if (isContextObject)
sharedState->hadTopLevelRequiredProperties = true; sharedState->hadTopLevelRequiredProperties = true;
sharedState->requiredProperties.insert(propertyData, sharedState->requiredProperties.insert({_qobject, propertyData},
RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}}); RequiredPropertyInfo {compilationUnit->stringAt(property->nameIndex), compilationUnit->finalUrl(), property->location, {}});
} }
@ -1623,7 +1624,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
if (isContextObject) if (isContextObject)
sharedState->hadTopLevelRequiredProperties = true; sharedState->hadTopLevelRequiredProperties = true;
sharedState->requiredProperties.insert( sharedState->requiredProperties.insert(
propertyData, {_qobject, propertyData},
RequiredPropertyInfo { RequiredPropertyInfo {
name, compilationUnit->finalUrl(), _compiledObject->location, {} }); name, compilationUnit->finalUrl(), _compiledObject->location, {} });
} }
@ -1654,7 +1655,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
if (isContextObject) if (isContextObject)
sharedState->hadTopLevelRequiredProperties = true; sharedState->hadTopLevelRequiredProperties = true;
sharedState->requiredProperties.insert( sharedState->requiredProperties.insert(
propertyData, {_qobject, propertyData},
RequiredPropertyInfo { RequiredPropertyInfo {
name, compilationUnit->finalUrl(), _compiledObject->location, {} }); name, compilationUnit->finalUrl(), _compiledObject->location, {} });
} }
@ -1687,7 +1688,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
const QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(coreIndex); const QQmlPropertyData *const targetProperty = targetDData->propertyCache->property(coreIndex);
if (!targetProperty) if (!targetProperty)
continue; continue;
auto it = sharedState->requiredProperties.find(targetProperty); auto it = sharedState->requiredProperties.find({target, targetProperty});
if (it != sharedState->requiredProperties.end()) if (it != sharedState->requiredProperties.end())
it->aliasesToRequired.push_back( it->aliasesToRequired.push_back(
AliasToRequiredInfo { AliasToRequiredInfo {

View File

@ -53,7 +53,30 @@ struct RequiredPropertyInfo
QVector<AliasToRequiredInfo> aliasesToRequired; QVector<AliasToRequiredInfo> aliasesToRequired;
}; };
class RequiredProperties : public QHash<const QQmlPropertyData *, RequiredPropertyInfo> {}; struct RequiredPropertyKey
{
RequiredPropertyKey() = default;
RequiredPropertyKey(const QObject *object, const QQmlPropertyData *data)
: object(object)
, data(data)
{}
const QObject *object = nullptr;
const QQmlPropertyData *data = nullptr;
private:
friend size_t qHash(const RequiredPropertyKey &key, size_t seed = 0)
{
return qHashMulti(seed, key.object, key.data);
}
friend bool operator==(const RequiredPropertyKey &a, const RequiredPropertyKey &b)
{
return a.object == b.object && a.data == b.data;
}
};
class RequiredProperties : public QHash<RequiredPropertyKey, RequiredPropertyInfo> {};
struct DeferredQPropertyBinding { struct DeferredQPropertyBinding {
QObject *target = nullptr; QObject *target = nullptr;

View File

@ -0,0 +1,12 @@
import QtQml
QtObject {
component AccessibleButton : QtObject {
required property string description
objectName: description
}
property AccessibleButton a: AccessibleButton {}
property AccessibleButton b: AccessibleButton {
description: "b"
}
}

View File

@ -401,6 +401,7 @@ private slots:
void functionSignatureEnforcement(); void functionSignatureEnforcement();
void importPrecedence(); void importPrecedence();
void nullIsNull(); void nullIsNull();
void multiRequired();
private: private:
QQmlEngine engine; QQmlEngine engine;
@ -7740,6 +7741,18 @@ void tst_qqmllanguage::nullIsNull()
QTRY_COMPARE(o->property("someProperty").value<QObject*>(), nullptr); QTRY_COMPARE(o->property("someProperty").value<QObject*>(), nullptr);
} }
void tst_qqmllanguage::multiRequired()
{
QQmlEngine engine;
const QUrl url = testFileUrl("multiRequired.qml");
QQmlComponent c(&engine, url);
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(o.isNull());
QCOMPARE(c.errorString(),
qPrintable(url.toString() + ":5 Required property description was not initialized\n"));
}
QTEST_MAIN(tst_qqmllanguage) QTEST_MAIN(tst_qqmllanguage)
#include "tst_qqmllanguage.moc" #include "tst_qqmllanguage.moc"