aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/qml/qqmlpropertycachecreator_p.h76
-rw-r--r--src/qml/qml/qqmltypecompiler.cpp51
-rw-r--r--src/qml/qml/qqmltypedata.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp33
-rw-r--r--tests/auto/qml/qqmllanguage/data/alias.17.qml31
-rw-r--r--tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp63
6 files changed, 201 insertions, 55 deletions
diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h
index 94bf3cbdc3..39778aa328 100644
--- a/src/qml/qml/qqmlpropertycachecreator_p.h
+++ b/src/qml/qml/qqmlpropertycachecreator_p.h
@@ -585,13 +585,13 @@ public:
QQmlPropertyCacheAliasCreator(QQmlPropertyCacheVector *propertyCaches, const ObjectContainer *objectContainer);
- void appendAliasPropertiesToMetaObjects();
+ void appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv);
- QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex);
+ QQmlJS::DiagnosticMessage appendAliasesToPropertyCache(const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv);
private:
- void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex);
- QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags);
+ void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv);
+ QQmlJS::DiagnosticMessage propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv);
void collectObjectsWithAliasesRecursively(int objectIndex, QVector<int> *objectsWithAliases) const;
@@ -610,7 +610,7 @@ inline QQmlPropertyCacheAliasCreator<ObjectContainer>::QQmlPropertyCacheAliasCre
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects()
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesToMetaObjects(QQmlEnginePrivate *enginePriv)
{
// skip the root object (index 0) as that one does not have a first object index originating
// from a binding.
@@ -620,15 +620,15 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
continue;
const auto rootBinding = component.bindingsBegin();
- appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex);
+ appendAliasPropertiesInMetaObjectsWithinComponent(component, rootBinding->value.objectIndex, enginePriv);
}
const int rootObjectIndex = 0;
- appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex);
+ appendAliasPropertiesInMetaObjectsWithinComponent(*objectContainer->objectAt(rootObjectIndex), rootObjectIndex, enginePriv);
}
template <typename ObjectContainer>
-inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex)
+inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex, QQmlEnginePrivate *enginePriv)
{
QVector<int> objectsWithAliases;
collectObjectsWithAliasesRecursively(firstObjectIndex, &objectsWithAliases);
@@ -668,7 +668,7 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasPropertie
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (allAliasTargetsExist(object)) {
- appendAliasesToPropertyCache(component, objectIndex);
+ appendAliasesToPropertyCache(component, objectIndex, enginePriv);
} else {
pendingObjects.append(objectIndex);
}
@@ -702,9 +702,8 @@ inline void QQmlPropertyCacheAliasCreator<ObjectContainer>::collectObjectsWithAl
}
template <typename ObjectContainer>
-inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(
- const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
- QQmlPropertyData::Flags *propertyFlags)
+inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion,
+ QQmlPropertyData::Flags *propertyFlags, QQmlEnginePrivate *enginePriv)
{
*type = 0;
bool writable = false;
@@ -736,7 +735,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
lastAlias = targetAlias;
} while (lastAlias->aliasToLocalAlias);
- return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags);
+ return propertyDataForAlias(component, *lastAlias, type, minorVersion, propertyFlags, enginePriv);
}
const int targetObjectIndex = objectForId(component, alias.targetObjectId);
@@ -768,29 +767,46 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
QQmlPropertyCache *targetCache = propertyCaches->at(targetObjectIndex);
Q_ASSERT(targetCache);
+
QQmlPropertyData *targetProperty = targetCache->property(coreIndex);
Q_ASSERT(targetProperty);
- *type = targetProperty->propType();
+ // for deep aliases, valueTypeIndex is always set
+ if (!QQmlValueTypeFactory::isValueType(targetProperty->propType()) && valueTypeIndex != -1) {
+ // deep alias property
+ *type = targetProperty->propType();
+ targetCache = enginePriv->propertyCacheForType(*type);
+ Q_ASSERT(targetCache);
+ targetProperty = targetCache->property(valueTypeIndex);
- 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();
+ *type = targetProperty->propType();
+ writable = targetProperty->isWritable();
+ resettable = targetProperty->isResettable();
+
} else {
- if (targetProperty->isEnum()) {
- *type = QVariant::Int;
+ // value type or primitive type or enum
+ *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 {
- // Copy type flags
- propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
+ if (targetProperty->isEnum()) {
+ *type = QVariant::Int;
+ } else {
+ // Copy type flags
+ propertyFlags->copyPropertyTypeFlags(targetProperty->flags());
- if (targetProperty->isVarProperty())
- propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ if (targetProperty->isVarProperty())
+ propertyFlags->type = QQmlPropertyData::Flags::QVariantType;
+ }
}
}
}
@@ -802,7 +818,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
template <typename ObjectContainer>
inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>::appendAliasesToPropertyCache(
- const CompiledObject &component, int objectIndex)
+ const CompiledObject &component, int objectIndex, QQmlEnginePrivate *enginePriv)
{
const CompiledObject &object = *objectContainer->objectAt(objectIndex);
if (!object.aliasCount())
@@ -823,7 +839,7 @@ inline QQmlJS::DiagnosticMessage QQmlPropertyCacheAliasCreator<ObjectContainer>:
int type = 0;
int minorVersion = 0;
QQmlPropertyData::Flags propertyFlags;
- QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags);
+ QQmlJS::DiagnosticMessage error = propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags, enginePriv);
if (error.isValid())
return error;
diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp
index bbeaf7be9a..9ff0e3fb9e 100644
--- a/src/qml/qml/qqmltypecompiler.cpp
+++ b/src/qml/qml/qqmltypecompiler.cpp
@@ -1016,7 +1016,7 @@ bool QQmlComponentAndAliasResolver::resolveAliases(int componentIndex)
}
if (result == AllAliasesResolved) {
- QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex);
+ QQmlJS::DiagnosticMessage error = aliasCacheCreator.appendAliasesToPropertyCache(*qmlObjects->at(componentIndex), objectIndex, enginePrivate);
if (error.isValid()) {
recordError(error);
return false;
@@ -1143,23 +1143,42 @@ QQmlComponentAndAliasResolver::resolveAliasesInObject(int objectIndex,
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;
- }
+ // could be a deep alias
+ bool isDeepAlias = subProperty.at(0).isLower();
+ if (isDeepAlias) {
+ isDeepAlias = false;
+ for (auto it = targetObject->bindingsBegin(); it != targetObject->bindingsEnd(); ++it) {
+ auto binding = *it;
+ if (compiler->stringAt(binding.propertyNameIndex) == property) {
+ resolver = QQmlPropertyResolver(propertyCaches.at(binding.value.objectIndex));
+ QQmlPropertyData *actualProperty = resolver.property(subProperty.toString());
+ if (actualProperty) {
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), actualProperty->coreIndex());
+ isDeepAlias = true;
+ }
+ }
+ }
+ }
+ if (!isDeepAlias) {
+ *error = qQmlCompileError(
+ alias->referenceLocation,
+ tr("Invalid alias target location: %1").arg(subProperty.toString()));
+ break;
+ }
+ } else {
- 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);
+ 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);
- propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ propIdx = QQmlPropertyIndex(propIdx.coreIndex(), valueTypeIndex);
+ }
} else {
if (targetProperty->isQObject())
alias->flags |= QV4::CompiledData::Alias::AliasPointsToPointerObject;
diff --git a/src/qml/qml/qqmltypedata.cpp b/src/qml/qml/qqmltypedata.cpp
index 99fe069685..cfdcf6aad5 100644
--- a/src/qml/qml/qqmltypedata.cpp
+++ b/src/qml/qml/qqmltypedata.cpp
@@ -209,7 +209,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(
QQmlPropertyCacheAliasCreator<QV4::ExecutableCompilationUnit> aliasCreator(
&m_compiledData->propertyCaches, m_compiledData.data());
- aliasCreator.appendAliasPropertiesToMetaObjects();
+ aliasCreator.appendAliasPropertiesToMetaObjects(engine);
pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
}
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index b9d8fed243..abdc686ec2 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -164,8 +164,19 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
QQmlData *targetDData = QQmlData::get(target, /*create*/false);
if (!targetDData)
return;
- int coreIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex).coreIndex();
+ QQmlPropertyIndex encodedIndex = QQmlPropertyIndex::fromEncoded(aliasData->encodedMetaPropertyIndex);
+ int coreIndex = encodedIndex.coreIndex();
+ int valueTypeIndex = encodedIndex.valueTypeIndex();
const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
+ if (pd && valueTypeIndex != -1 && !QQmlValueTypeFactory::valueType(pd->propType())) {
+ // deep alias
+ QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(metaObject->compilationUnit->engine->qmlEngine());
+ auto const *newPropertyCache = enginePriv->propertyCacheForType(pd->propType());
+ void *argv[1] = { &target };
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ Q_ASSERT(newPropertyCache);
+ pd = newPropertyCache->property(valueTypeIndex);
+ }
if (!pd)
return;
@@ -858,17 +869,23 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void *
if (!targetDData->propertyCache)
return -1;
const QQmlPropertyData *pd = targetDData->propertyCache->property(coreIndex);
- // Value type property
+ // Value type property or deep alias
QQmlValueType *valueType = QQmlValueTypeFactory::valueType(pd->propType());
- Q_ASSERT(valueType);
+ if (valueType) {
- valueType->read(target, coreIndex);
- int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
+ valueType->read(target, coreIndex);
+ int rv = QMetaObject::metacall(valueType, c, valueTypePropertyIndex, a);
- if (c == QMetaObject::WriteProperty)
- valueType->write(target, coreIndex, nullptr);
+ if (c == QMetaObject::WriteProperty)
+ valueType->write(target, coreIndex, nullptr);
- return rv;
+ return rv;
+ } else {
+ // deep alias
+ void *argv[1] = { &target };
+ QMetaObject::metacall(target, QMetaObject::ReadProperty, coreIndex, argv);
+ return QMetaObject::metacall(target, c, valueTypePropertyIndex, a);
+ }
} else {
return QMetaObject::metacall(target, c, coreIndex, a);
diff --git a/tests/auto/qml/qqmllanguage/data/alias.17.qml b/tests/auto/qml/qqmllanguage/data/alias.17.qml
new file mode 100644
index 0000000000..a76dd120b6
--- /dev/null
+++ b/tests/auto/qml/qqmllanguage/data/alias.17.qml
@@ -0,0 +1,31 @@
+import QtQuick 2.12
+
+Item {
+ id: root
+ anchors.fill: parent
+ width: 100
+ height: 100
+ property bool success: checkValue === aliasUser.topMargin
+ property int checkValue: 42
+ Rectangle {
+ id: myItem
+ objectName: "myItem"
+ color: "blue"
+ anchors.topMargin: root.checkValue
+ width: 50
+ height: 50
+ Text {text: "source:\n" + myItem.anchors.topMargin}
+ }
+
+ Rectangle {
+ property alias topMargin: myItem.anchors.topMargin
+ id: aliasUser
+ objectName: "aliasUser"
+ color: "red"
+ anchors.left: myItem.right
+ width: 50
+ height: 50
+ Text {objectName: "myText"; text: "alias:\n" + aliasUser.topMargin}
+ }
+}
+
diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
index ad96b1438f..ecf87c51d6 100644
--- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
+++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp
@@ -1969,6 +1969,69 @@ void tst_qqmllanguage::aliasProperties()
QScopedPointer<QObject> object(component.create());
QVERIFY(!object.isNull());
}
+
+ // Alias to grouped property
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QVERIFY(object->property("success").toBool());
+ }
+
+ // Alias to grouped property updates
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty checkValueProp(object.get(), "checkValue");
+ QVERIFY(checkValueProp.isValid());
+ checkValueProp.write(777);
+ QCOMPARE(object->property("checkValue").toInt(), 777);
+ QCOMPARE(aliasUser->property("topMargin").toInt(), 777);
+ }
+
+ // Write to alias to grouped property
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty topMarginProp {aliasUser, "topMargin"};
+ QVERIFY(topMarginProp.isValid());
+ topMarginProp.write(777);
+ QObject *myItem = object->findChild<QObject*>(QLatin1String("myItem"));
+ QVERIFY(myItem);
+ auto anchors = myItem->property("anchors").value<QObject*>();
+ QVERIFY(anchors);
+ QCOMPARE(anchors->property("topMargin").toInt(), 777);
+ }
+
+ // Binding to alias to grouped property gets updated
+ {
+ QQmlComponent component(&engine, testFileUrl("alias.17.qml"));
+ VERIFY_ERRORS(0);
+
+ QScopedPointer<QObject> object(component.create());
+ QVERIFY(!object.isNull());
+ QObject *aliasUser = object->findChild<QObject*>(QLatin1String("aliasUser"));
+ QVERIFY(aliasUser);
+ QQmlProperty topMarginProp {aliasUser, "topMargin"};
+ QVERIFY(topMarginProp.isValid());
+ topMarginProp.write(20);
+ QObject *myText = object->findChild<QObject*>(QLatin1String("myText"));
+ QVERIFY(myText);
+ auto text = myText->property("text").toString();
+ QCOMPARE(text, "alias:\n20");
+ }
}
// QTBUG-13374 Test that alias properties and signals can coexist