aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/compiler
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@qt.io>2016-07-14 16:31:19 +0200
committerSimon Hausmann <simon.hausmann@qt.io>2016-07-15 18:51:39 +0000
commitbf5f97e6c6900535a7131c6a26415459bfaca774 (patch)
treea8158d0398faaa1419bbe59c76b45f782566dc96 /src/qml/compiler
parentf1736b9a94a8bce40a26efba766e935ae6865530 (diff)
Fix alias resolution algorithm
The resolution of aliases needs to to be solved in iterations as an alias can refer to another alias, which may not be created yet in the property cache by the time we try to perform the resolution. Therefore the alias resolution code works off a list of pending aliases after the initial pass of resolution. This also requires the ability to detect circular references. Change-Id: Id4e159b9c713aa8a8a095759e22e3fac456a38a0 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/compiler')
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp342
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h12
-rw-r--r--src/qml/compiler/qv4compileddata_p.h4
3 files changed, 252 insertions, 106 deletions
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index 42e31ea7f8..83aaba791b 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -993,137 +993,271 @@ bool QQmlComponentAndAliasResolver::collectIdsAndAliases(int objectIndex)
bool QQmlComponentAndAliasResolver::resolveAliases()
{
- foreach (int objectIndex, _objectsWithAliases) {
- const QmlIR::Object *obj = qmlObjects->at(objectIndex);
+ if (_objectsWithAliases.isEmpty())
+ return true;
+ bool atLeastOneAliasResolved;
+ do {
+ atLeastOneAliasResolved = false;
+ QVector<int> pendingObjects;
- QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex);
- Q_ASSERT(propertyCache);
+ for (int objectIndex: qAsConst(_objectsWithAliases)) {
- int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
- int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+ QQmlCompileError error;
+ const auto result = resolveAliasesInObject(objectIndex, &error);
- int aliasIndex = 0;
- for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) {
- const int idIndex = alias->idIndex;
- const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
- if (targetObjectIndex == -1) {
- recordError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ if (error.isSet()) {
+ recordError(error);
return false;
}
- Q_ASSERT(!(alias->flags & QV4::CompiledData::Alias::Resolved));
- alias->flags |= QV4::CompiledData::Alias::Resolved;
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
- Q_ASSERT(targetObject->id >= 0);
- alias->targetObjectId = targetObject->id;
-
- const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
-
- QStringRef property;
- QStringRef subProperty;
-
- const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
- if (propertySeparator != -1) {
- property = aliasPropertyValue.leftRef(propertySeparator);
- subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
- } else
- property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
-
- int propIdx = -1;
- int type = 0;
- bool writable = false;
- bool resettable = false;
-
- quint32 propertyFlags = QQmlPropertyData::IsAlias;
-
- if (property.isEmpty()) {
- const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
- auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
- Q_ASSERT(typeRef);
-
- if (typeRef->type)
- type = typeRef->type->typeId();
- else
- type = typeRef->compilationUnit->metaTypeId;
-
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
- propertyFlags |= QQmlPropertyData::IsQObjectDerived;
+
+ if (result == AllAliasesResolved) {
+ addAliasesToPropertyCache(objectIndex);
+ atLeastOneAliasResolved = true;
+ } else if (result == SomeAliasesResolved) {
+ atLeastOneAliasResolved = true;
+ pendingObjects.append(objectIndex);
} else {
- QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
- Q_ASSERT(targetCache);
- QmlIR::PropertyResolver resolver(targetCache);
+ pendingObjects.append(objectIndex);
+ }
+ }
+ qSwap(_objectsWithAliases, pendingObjects);
+ } while (!_objectsWithAliases.isEmpty() && atLeastOneAliasResolved);
+
+ if (!atLeastOneAliasResolved && !_objectsWithAliases.isEmpty()) {
+ const QmlIR::Object *obj = qmlObjects->at(_objectsWithAliases.first());
+ for (auto alias = obj->aliasesBegin(), end = obj->aliasesEnd(); alias != end; ++alias) {
+ if (!(alias->flags & QV4::CompiledData::Alias::Resolved)) {
+ recordError(alias->location, tr("Circular alias reference detected"));
+ return false;
+ }
+ }
+ }
- QQmlPropertyData *targetProperty = resolver.property(property.toString());
- if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) {
- recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
- return false;
- }
+ return true;
+}
- propIdx = targetProperty->coreIndex;
- type = targetProperty->propType;
+QQmlComponentAndAliasResolver::AliasResolutionResult QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex, QQmlCompileError *error)
+{
+ const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ if (!obj->aliasCount())
+ return AllAliasesResolved;
- writable = targetProperty->isWritable();
- resettable = targetProperty->isResettable();
+ int numResolvedAliases = 0;
+ bool seenUnresolvedAlias = false;
- if (!subProperty.isEmpty()) {
- const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(type);
- if (!valueTypeMetaObject) {
- recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
+ for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next) {
+ if (alias->flags & QV4::CompiledData::Alias::Resolved)
+ continue;
- int valueTypeIndex =
- valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
- if (valueTypeIndex == -1) {
- recordError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
- return false;
- }
- Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
+ seenUnresolvedAlias = true;
- propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex);
- if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
- type = QVariant::Int;
- else
- type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ const int idIndex = alias->idIndex;
+ const int targetObjectIndex = _idToObjectIndex.value(idIndex, -1);
+ if (targetObjectIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias reference. Unable to find id \"%1\"").arg(stringAt(idIndex)));
+ break;
+ }
- } else {
- if (targetProperty->isEnum()) {
- type = QVariant::Int;
- } else {
- // Copy type flags
- propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+ const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+ Q_ASSERT(targetObject->id >= 0);
+ alias->targetObjectId = targetObject->id;
+ alias->aliasToLocalAlias = false;
- if (targetProperty->isVarProperty())
- propertyFlags |= QQmlPropertyData::IsQVariant;
+ const QString aliasPropertyValue = stringAt(alias->propertyNameIndex);
- if (targetProperty->isQObject())
- alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ QStringRef property;
+ QStringRef subProperty;
+
+ const int propertySeparator = aliasPropertyValue.indexOf(QLatin1Char('.'));
+ if (propertySeparator != -1) {
+ property = aliasPropertyValue.leftRef(propertySeparator);
+ subProperty = aliasPropertyValue.midRef(propertySeparator + 1);
+ } else
+ property = QStringRef(&aliasPropertyValue, 0, aliasPropertyValue.length());
+
+ int propIdx = -1;
+
+ if (property.isEmpty()) {
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ } else {
+ QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QmlIR::PropertyResolver resolver(targetCache);
+
+ QQmlPropertyData *targetProperty = resolver.property(property.toString());
+
+ // If it's an alias that we haven't resolved yet, try again later.
+ if (!targetProperty) {
+ bool aliasPointsToOtherAlias = false;
+ int localAliasIndex = 0;
+ for (auto targetAlias = targetObject->aliasesBegin(), end = targetObject->aliasesEnd(); targetAlias != end; ++targetAlias, ++localAliasIndex) {
+ if (stringAt(targetAlias->nameIndex) == property) {
+ aliasPointsToOtherAlias = true;
+ break;
}
}
+ if (aliasPointsToOtherAlias) {
+ if (targetObjectIndex == objectIndex) {
+ alias->localAliasIndex = localAliasIndex;
+ alias->aliasToLocalAlias = true;
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ ++numResolvedAliases;
+ continue;
+ }
+
+ // Try again later and resolve the target alias first.
+ _objectsWithAliases.append(objectIndex);
+ // restore
+ alias->idIndex = idIndex;
+ break;
+ }
}
- alias->encodedMetaPropertyIndex = propIdx;
+ if (!targetProperty || targetProperty->coreIndex > 0x0000FFFF) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(property.toString()));
+ break;
+ }
- if (!(alias->flags & QV4::CompiledData::Property::IsReadOnly) && writable)
- propertyFlags |= QQmlPropertyData::IsWritable;
- else
- propertyFlags &= ~QQmlPropertyData::IsWritable;
+ propIdx = targetProperty->coreIndex;
- if (resettable)
- propertyFlags |= QQmlPropertyData::IsResettable;
- else
- propertyFlags &= ~QQmlPropertyData::IsResettable;
+ if (!subProperty.isEmpty()) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(targetProperty->propType);
+ if (!valueTypeMetaObject) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
- QString propertyName = stringAt(alias->nameIndex);
+ int valueTypeIndex =
+ valueTypeMetaObject->indexOfProperty(subProperty.toString().toUtf8().constData());
+ if (valueTypeIndex == -1) {
+ *error = QQmlCompileError(alias->referenceLocation, tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ Q_ASSERT(valueTypeIndex <= 0x0000FFFF);
- if (obj->defaultPropertyIsAlias && aliasIndex == obj->indexOfDefaultPropertyOrAlias)
- propertyCache->_defaultPropertyName = propertyName;
+ propIdx = QQmlPropertyData::encodeValueTypePropertyIndex(propIdx, valueTypeIndex);
+ } else {
+ if (targetProperty->isQObject())
+ alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
+ }
+ }
- propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
- type, effectiveSignalIndex++);
+ alias->encodedMetaPropertyIndex = propIdx;
+ alias->flags |= QV4::CompiledData::Alias::Resolved;
+ numResolvedAliases++;
+ }
+ if (numResolvedAliases == 0)
+ return seenUnresolvedAlias ? NoAliasResolved : AllAliasesResolved;
+
+ return SomeAliasesResolved;
+}
+
+void QQmlComponentAndAliasResolver::propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags)
+{
+ const auto objectForId = [this](int objectId) {
+ for (auto idIt = _idToObjectIndex.constBegin(), end = _idToObjectIndex.constEnd(); idIt != end; ++idIt) {
+ const QmlIR::Object *obj = qmlObjects->at(*idIt);
+ if (obj->id == objectId)
+ return *idIt;
+ }
+ return -1;
+ };
+
+ const int targetObjectIndex = objectForId(alias->targetObjectId);
+ Q_ASSERT(targetObjectIndex >= 0);
+
+ const QmlIR::Object *targetObject = qmlObjects->at(targetObjectIndex);
+
+ *type = 0;
+ bool writable = false;
+ bool resettable = false;
+
+ *propertyFlags = QQmlPropertyData::IsAlias;
+
+ if (alias->aliasToLocalAlias) {
+ auto targetAlias = targetObject->firstAlias();
+ for (uint i = 0; i < alias->localAliasIndex; ++i)
+ targetAlias = targetAlias->next;
+ propertyDataForAlias(targetAlias, type, propertyFlags);
+ return;
+ } else if (alias->encodedMetaPropertyIndex == -1) {
+ Q_ASSERT(alias->flags & QV4::CompiledData::Alias::AliasPointsToPointerObject);
+ auto *typeRef = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
+ Q_ASSERT(typeRef);
+
+ if (typeRef->type)
+ *type = typeRef->type->typeId();
+ else
+ *type = typeRef->compilationUnit->metaTypeId;
+
+ *propertyFlags |= QQmlPropertyData::IsQObjectDerived;
+ } else {
+ int coreIndex;
+ int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(alias->encodedMetaPropertyIndex, &coreIndex);
+
+ QQmlPropertyCache *targetCache = propertyCaches.at(targetObjectIndex);
+ Q_ASSERT(targetCache);
+ QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
+ Q_ASSERT(targetProperty);
+
+ *type = targetProperty->propType;
+
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
+ if (valueTypeIndex != -1) {
+ const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(*type);
+ if (valueTypeMetaObject->property(valueTypeIndex).isEnumType())
+ *type = QVariant::Int;
+ else
+ *type = valueTypeMetaObject->property(valueTypeIndex).userType();
+ } else {
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ *propertyFlags |= targetProperty->getFlags() & QQmlPropertyData::PropTypeFlagMask;
+
+ if (targetProperty->isVarProperty())
+ *propertyFlags |= QQmlPropertyData::IsQVariant;
+ }
}
}
- return true;
+
+ if (!(alias->flags & QV4::CompiledData::Property::IsReadOnly) && writable)
+ *propertyFlags |= QQmlPropertyData::IsWritable;
+ else
+ *propertyFlags &= ~QQmlPropertyData::IsWritable;
+
+ if (resettable)
+ *propertyFlags |= QQmlPropertyData::IsResettable;
+ else
+ *propertyFlags &= ~QQmlPropertyData::IsResettable;
+}
+
+void QQmlComponentAndAliasResolver::addAliasesToPropertyCache(int objectIndex)
+{
+ const QmlIR::Object * const obj = qmlObjects->at(objectIndex);
+ QQmlPropertyCache * const propertyCache = propertyCaches.at(objectIndex);
+ Q_ASSERT(propertyCache);
+ int effectiveSignalIndex = propertyCache->signalHandlerIndexCacheStart + propertyCache->propertyIndexCache.count();
+ int effectivePropertyIndex = propertyCache->propertyIndexCacheStart + propertyCache->propertyIndexCache.count();
+
+ int aliasIndex = 0;
+ for (QmlIR::Alias *alias = obj->firstAlias(); alias; alias = alias->next, ++aliasIndex) {
+ int type = 0;
+ quint32 propertyFlags = 0;
+ propertyDataForAlias(alias, &type, &propertyFlags);
+
+ QString propertyName = stringAt(alias->nameIndex);
+
+ if (obj->defaultPropertyIsAlias && aliasIndex == obj->indexOfDefaultPropertyOrAlias)
+ propertyCache->_defaultPropertyName = propertyName;
+
+ propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
+ type, effectiveSignalIndex++);
+ }
}
QQmlDeferredAndCustomParserBindingScanner::QQmlDeferredAndCustomParserBindingScanner(QQmlTypeCompiler *typeCompiler)
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index 93ae7d1fad..0b078fd642 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -266,6 +266,16 @@ protected:
void findAndRegisterImplicitComponents(const QmlIR::Object *obj, QQmlPropertyCache *propertyCache);
bool collectIdsAndAliases(int objectIndex);
bool resolveAliases();
+ void propertyDataForAlias(QmlIR::Alias *alias, int *type, quint32 *propertyFlags);
+
+ enum AliasResolutionResult {
+ NoAliasResolved,
+ SomeAliasesResolved,
+ AllAliasesResolved
+ };
+
+ AliasResolutionResult resolveAliasesInObject(int objectIndex, QQmlCompileError *error);
+ void addAliasesToPropertyCache(int objectIndex);
QQmlEnginePrivate *enginePrivate;
QQmlJS::MemoryPool *pool;
@@ -278,7 +288,7 @@ protected:
int _componentIndex;
QHash<int, int> _idToObjectIndex;
- QList<int> _objectsWithAliases;
+ QVector<int> _objectsWithAliases;
QHash<int, QV4::CompiledData::CompilationUnit::ResolvedTypeReference*> *resolvedTypes;
QQmlPropertyCacheVector propertyCaches;
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1e65be38da..f1cf365b25 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -446,11 +446,13 @@ struct Alias {
};
union {
LEUInt32 idIndex; // string index
- LEUInt32 targetObjectId; // object id index (in QQmlContextData::idValues)
+ QJsonPrivate::qle_bitfield<0, 31> targetObjectId; // object id index (in QQmlContextData::idValues)
+ QJsonPrivate::qle_bitfield<31, 1> aliasToLocalAlias;
};
union {
LEUInt32 propertyNameIndex; // string index
LEInt32 encodedMetaPropertyIndex;
+ LEUInt32 localAliasIndex; // index in list of aliases local to the object (if targetObjectId == objectId)
};
Location location;
Location referenceLocation;