QtQml: Correctly resolve aliases to aliases when loading .qmlc files

We need to loop the objects until all property caches are resolved. We
cannot assume the property caches to be complete right away.

Amends commit 2d7fe23b41.

Pick-to: 6.6 6.5
Fixes: QTBUG-116148
Change-Id: I407675cd1ebf8067d1b387542b4ecca8100d9b34
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Sami Shalayel <sami.shalayel@qt.io>
This commit is contained in:
Ulf Hermann 2023-08-17 12:18:29 +02:00
parent 5774d9b13b
commit 487bd23ee8
5 changed files with 88 additions and 20 deletions

View File

@ -53,7 +53,8 @@ private:
void allocateNamedObjects(CompiledObject *object) const;
void setObjectId(int index) const;
[[nodiscard]] bool markAsComponent(int index) const;
[[nodiscard]] AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlError *error);
[[nodiscard]] AliasResolutionResult resolveAliasesInObject(
const CompiledObject &component, int objectIndex, QQmlError *error);
[[nodiscard]] bool wrapImplicitComponent(CompiledBinding *binding);
[[nodiscard]] QQmlError findAndRegisterImplicitComponents(
@ -381,13 +382,14 @@ QQmlError QQmlComponentAndAliasResolver<ObjectContainer>::resolveAliases(int com
for (int objectIndex: std::as_const(m_objectsWithAliases)) {
QQmlError error;
const auto result = resolveAliasesInObject(objectIndex, &error);
const auto &component = *m_compiler->objectAt(componentIndex);
const auto result = resolveAliasesInObject(component, objectIndex, &error);
if (error.isValid())
return error;
if (result == AllAliasesResolved) {
QQmlError error = aliasCacheCreator.appendAliasesToPropertyCache(
*m_compiler->objectAt(componentIndex), objectIndex, m_enginePrivate);
component, objectIndex, m_enginePrivate);
if (error.isValid())
return error;
atLeastOneAliasResolved = true;

View File

@ -735,6 +735,18 @@ inline QMetaType QQmlPropertyCacheCreator<ObjectContainer>::metaTypeForParameter
: compilationUnit->qmlType.typeId();
}
template <typename ObjectContainer, typename CompiledObject>
int objectForId(const ObjectContainer *objectContainer, const CompiledObject &component, int id)
{
for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
const int candidateIndex = component.namedObjectsInComponentTable()[i];
const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
if (candidate.objectId() == id)
return candidateIndex;
}
return -1;
}
template <typename ObjectContainer>
class QQmlPropertyCacheAliasCreator
{
@ -751,7 +763,6 @@ private:
const CompiledObject &component, const QV4::CompiledData::Alias &alias, QMetaType *type,
QTypeRevision *version, QQmlPropertyData::Flags *propertyFlags,
QQmlEnginePrivate *enginePriv);
int objectForId(const CompiledObject &component, int id) const;
QQmlPropertyCacheVector *propertyCaches;
const ObjectContainer *objectContainer;
@ -783,7 +794,8 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias});
do {
const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId());
const int targetObjectIndex = objectForId(
objectContainer, component, lastAlias->targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex);
Q_ASSERT(targetObject);
@ -806,7 +818,7 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor
component, *lastAlias, type, version, propertyFlags, enginePriv);
}
const int targetObjectIndex = objectForId(component, alias.targetObjectId());
const int targetObjectIndex = objectForId(objectContainer, component, alias.targetObjectId());
Q_ASSERT(targetObjectIndex >= 0);
const CompiledObject &targetObject = *objectContainer->objectAt(targetObjectIndex);
@ -938,18 +950,6 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesTo
return QQmlError();
}
template <typename ObjectContainer>
inline int QQmlPropertyCacheAliasCreator<ObjectContainer>::objectForId(const CompiledObject &component, int id) const
{
for (quint32 i = 0, count = component.namedObjectsInComponentCount(); i < count; ++i) {
const int candidateIndex = component.namedObjectsInComponentTable()[i];
const CompiledObject &candidate = *objectContainer->objectAt(candidateIndex);
if (candidate.objectId() == id)
return candidateIndex;
}
return -1;
}
QT_END_NAMESPACE
#endif // QQMLPROPERTYCACHECREATOR_P_H

View File

@ -811,8 +811,10 @@ bool QQmlComponentAndAliasResolver<QQmlTypeCompiler>::wrapImplicitComponent(QmlI
template<>
typename QQmlComponentAndAliasResolver<QQmlTypeCompiler>::AliasResolutionResult
QQmlComponentAndAliasResolver<QQmlTypeCompiler>::resolveAliasesInObject(
int objectIndex, QQmlError *error)
const CompiledObject &component, int objectIndex, QQmlError *error)
{
Q_UNUSED(component);
const QmlIR::Object * const obj = m_compiler->objectAt(objectIndex);
if (!obj->aliasCount())
return AllAliasesResolved;

View File

@ -198,7 +198,7 @@ void QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::setObjectId(
template<>
typename QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::AliasResolutionResult
QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::resolveAliasesInObject(
int objectIndex, QQmlError *error)
const CompiledObject &component, int objectIndex, QQmlError *error)
{
const CompiledObject *obj = m_compiler->objectAt(objectIndex);
for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
@ -206,6 +206,20 @@ QQmlComponentAndAliasResolver<QV4::ExecutableCompilationUnit>::resolveAliasesInO
*error = qQmlCompileError( alias->referenceLocation, tr("Unresolved alias found"));
return NoAliasResolved;
}
if (alias->isAliasToLocalAlias() || alias->encodedMetaPropertyIndex == -1)
continue;
const int targetObjectIndex
= objectForId(m_compiler, component, alias->targetObjectId());
const int coreIndex
= QQmlPropertyIndex::fromEncoded(alias->encodedMetaPropertyIndex).coreIndex();
QQmlPropertyCache::ConstPtr targetCache = m_propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
if (!targetCache->property(coreIndex))
return SomeAliasesResolved;
}
return AllAliasesResolved;

View File

@ -34,6 +34,7 @@ private slots:
void recompileAfterDirectoryChange();
void fileSelectors();
void localAliases();
void aliasToAlias();
void cacheResources();
void stableOrderOfDependentCompositeTypes();
void singletonDependency();
@ -675,6 +676,55 @@ void tst_qmldiskcache::localAliases()
}
}
void tst_qmldiskcache::aliasToAlias()
{
QQmlEngine engine;
TestCompiler testCompiler(&engine);
QVERIFY(testCompiler.tempDir.isValid());
const QByteArray contents = QByteArrayLiteral(R"(
import QML
QtObject {
id: foo
readonly property alias myAlias: bar.prop
property QtObject o: QtObject {
id: bar
property QtObject o: QtObject {
id: baz
readonly property int value: 100
}
readonly property alias prop: baz.value
}
}
)");
{
testCompiler.clearCache();
QVERIFY2(testCompiler.compile(contents), qPrintable(testCompiler.lastErrorString));
QVERIFY2(testCompiler.verify(), qPrintable(testCompiler.lastErrorString));
}
{
CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("myAlias").toInt(), 100);
}
engine.clearComponentCache();
{
CleanlyLoadingComponent component(&engine, testCompiler.testFilePath);
QScopedPointer<QObject> obj(component.create());
QVERIFY(!obj.isNull());
QCOMPARE(obj->property("myAlias").toInt(), 100);
}
}
static QSet<QString> entrySet(const QDir &dir)
{
const auto &list = dir.entryList(QDir::Files | QDir::NoDotAndDotDot);