diff options
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit.cpp | 9 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4executablecompilationunit_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycachecreator_p.h | 89 | ||||
-rw-r--r-- | src/qml/qml/qqmltypecompiler_p.h | 9 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml | 24 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml | 8 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml | 5 | ||||
-rw-r--r-- | tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 27 |
8 files changed, 138 insertions, 34 deletions
diff --git a/src/qml/jsruntime/qv4executablecompilationunit.cpp b/src/qml/jsruntime/qv4executablecompilationunit.cpp index e71568b2d8..c0c5f341e5 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit.cpp +++ b/src/qml/jsruntime/qv4executablecompilationunit.cpp @@ -501,6 +501,15 @@ int ExecutableCompilationUnit::totalObjectCount() const { return inlineComponentData[*icRootName].totalObjectCount; } +ResolvedTypeReference *ExecutableCompilationUnit::resolvedType(QMetaType type) const +{ + for (ResolvedTypeReference *ref : std::as_const(resolvedTypes)) { + if (ref->type().typeId() == type) + return ref; + } + return nullptr; +} + int ExecutableCompilationUnit::totalParserStatusCount() const { if (!icRootName) return m_totalParserStatusCount; diff --git a/src/qml/jsruntime/qv4executablecompilationunit_p.h b/src/qml/jsruntime/qv4executablecompilationunit_p.h index f7a491c740..5ad3838658 100644 --- a/src/qml/jsruntime/qv4executablecompilationunit_p.h +++ b/src/qml/jsruntime/qv4executablecompilationunit_p.h @@ -144,6 +144,7 @@ public: QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; ResolvedTypeReference *resolvedType(int id) const { return resolvedTypes.value(id); } + ResolvedTypeReference *resolvedType(QMetaType type) const; bool verifyChecksum(const CompiledData::DependentTypesHasher &dependencyHasher) const; diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 05e669a856..efd897ad4e 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -887,53 +887,74 @@ inline QQmlError QQmlPropertyCacheAliasCreator<ObjectContainer>::propertyDataFor const QQmlPropertyData *targetProperty = targetCache->property(coreIndex); Q_ASSERT(targetProperty); + const QMetaType targetPropType = targetProperty->propType(); + + const auto resolveType = [](QMetaType targetPropType) { + if (targetPropType.flags() & QMetaType::IsEnumeration) + return targetPropType.underlyingType(); + else + return targetPropType; + }; + + const auto populateWithPropertyData = [&](const QQmlPropertyData *property) { + *type = resolveType(property->propType()); + writable = property->isWritable(); + resettable = property->isResettable(); + bindable = property->isBindable(); + + // Copy type flags + propertyFlags->copyPropertyTypeFlags(property->flags()); + if (property->isVarProperty()) + propertyFlags->setType(QQmlPropertyData::Flags::QVariantType); + }; + // for deep aliases, valueTypeIndex is always set - if (!QQmlMetaType::isValueType(targetProperty->propType()) && valueTypeIndex != -1) { + if (!QQmlMetaType::isValueType(targetPropType) && valueTypeIndex != -1) { // deep alias property - *type = targetProperty->propType(); - QQmlPropertyCache::ConstPtr typeCache = QQmlMetaType::propertyCacheForType(*type); - Q_ASSERT(typeCache); - const QQmlPropertyData *typeProperty = typeCache->property(valueTypeIndex); - if (typeProperty == nullptr) { - return qQmlCompileError(alias.referenceLocation, - QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + QQmlPropertyCache::ConstPtr typeCache + = QQmlMetaType::propertyCacheForType(targetPropType); + + if (!typeCache) { + // See if it's a half-resolved composite type + if (const QV4::ResolvedTypeReference *typeRef + = objectContainer->resolvedType(targetPropType)) { + typeCache = typeRef->typePropertyCache(); + } } - *type = typeProperty->propType(); - writable = typeProperty->isWritable(); - resettable = typeProperty->isResettable(); - bindable = typeProperty->isBindable(); + const QQmlPropertyData *typeProperty = typeCache + ? typeCache->property(valueTypeIndex) + : nullptr; + if (typeProperty == nullptr) { + return qQmlCompileError( + alias.referenceLocation, + QQmlPropertyCacheCreatorBase::tr("Invalid alias target")); + } + populateWithPropertyData(typeProperty); } else { // value type or primitive type or enum - *type = targetProperty->propType(); - - writable = targetProperty->isWritable(); - resettable = targetProperty->isResettable(); - bindable = targetProperty->isBindable(); + populateWithPropertyData(targetProperty); if (valueTypeIndex != -1) { - const QMetaObject *valueTypeMetaObject = QQmlMetaType::metaObjectForValueType(*type); - if (valueTypeMetaObject->property(valueTypeIndex).isEnumType()) - *type = QMetaType::fromType<int>(); - else - *type = valueTypeMetaObject->property(valueTypeIndex).metaType(); - } else { - if (targetProperty->isEnum()) { - *type = QMetaType::fromType<int>(); - } else { - // Copy type flags - propertyFlags->copyPropertyTypeFlags(targetProperty->flags()); - - if (targetProperty->isVarProperty()) - propertyFlags->setType(QQmlPropertyData::Flags::QVariantType); - } + const QMetaObject *valueTypeMetaObject + = QQmlMetaType::metaObjectForValueType(*type); + const QMetaProperty valueTypeMetaProperty + = valueTypeMetaObject->property(valueTypeIndex); + *type = resolveType(valueTypeMetaProperty.metaType()); + + // We can only write or reset the value type property if we can write + // the value type itself. + resettable = writable && valueTypeMetaProperty.isResettable(); + writable = writable && valueTypeMetaProperty.isWritable(); + + bindable = valueTypeMetaProperty.isBindable(); } } } - propertyFlags->setIsWritable(!(alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly)) - && writable); + propertyFlags->setIsWritable( + writable && !alias.hasFlag(QV4::CompiledData::Alias::IsReadOnly)); propertyFlags->setIsResettable(resettable); propertyFlags->setIsBindable(bindable); return QQmlError(); diff --git a/src/qml/qml/qqmltypecompiler_p.h b/src/qml/qml/qqmltypecompiler_p.h index a379946664..2e183ed837 100644 --- a/src/qml/qml/qqmltypecompiler_p.h +++ b/src/qml/qml/qqmltypecompiler_p.h @@ -108,6 +108,15 @@ public: return resolvedTypes->value(id); } + QV4::ResolvedTypeReference *resolvedType(QMetaType type) const + { + for (QV4::ResolvedTypeReference *ref : std::as_const(*resolvedTypes)) { + if (ref->type().typeId() == type) + return ref; + } + return nullptr; + } + QQmlType qmlTypeForComponent(const QString &inlineComponentName = QString()) const; private: diff --git a/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml new file mode 100644 index 0000000000..250d42f691 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/DeepAliasOnIC.qml @@ -0,0 +1,24 @@ +import QtQml + +QtObject { + id: root + + component ObjectWithColor: QtObject { + property string color + property var varvar + } + + property ObjectWithColor border: ObjectWithColor { + color: root.trueBorderColor + varvar: root.trueBorderVarvar + } + + readonly property rect readonlyRect: ({x: 12, y: 13, width: 14, height: 15}) + + property alias borderColor: root.border.color + property alias borderVarvar: root.border.varvar + property alias readonlyRectX: root.readonlyRect.x + + property string trueBorderColor: "green" + property var trueBorderVarvar: 1234 +} diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml new file mode 100644 index 0000000000..ef6001cd89 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnICUser.qml @@ -0,0 +1,8 @@ +import QtQml + +DeepAliasOnIC { + borderColor: "black" + borderVarvar: "mauve" +} + + diff --git a/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml new file mode 100644 index 0000000000..f5ae62406b --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/deepAliasOnReadonly.qml @@ -0,0 +1,5 @@ +import QtQml + +DeepAliasOnIC { + readonlyRectX: 55 +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 740f10e212..f6b4b2f9b8 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -436,6 +436,7 @@ private slots: void corpseInQmlList(); void objectInQmlListAndGc(); void asCastToInlineComponent(); + void deepAliasOnICOrReadonly(); private: QQmlEngine engine; @@ -8402,6 +8403,32 @@ void tst_qqmllanguage::asCastToInlineComponent() QCOMPARE(o->objectName(), QLatin1String("value: 20")); } +void tst_qqmllanguage::deepAliasOnICOrReadonly() +{ + QQmlEngine engine; + QQmlComponent c(&engine, testFileUrl("deepAliasOnICUser.qml")); + QVERIFY2(c.isReady(), qPrintable(c.errorString())); + QScopedPointer<QObject> o(c.create()); + QVERIFY(!o.isNull()); + + // We are mostly testing that it doesn't crash here. The actual bug is fixed separately. + + QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue); + QCOMPARE(o->property("borderColor").toString(), QLatin1String("black")); + + const QVariant var = o->property("borderVarvar"); + QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue); + QCOMPARE(var.metaType(), QMetaType::fromType<QString>()); + QEXPECT_FAIL("", "QTBUG-115579 is not fixed yet", Continue); + QCOMPARE(var.toString(), QLatin1String("mauve")); + + QQmlComponent c2(&engine, testFileUrl("deepAliasOnReadonly.qml")); + QVERIFY(c2.isError()); + QVERIFY(c2.errorString().contains( + QLatin1String( + "Invalid property assignment: \"readonlyRectX\" is a read-only property"))); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" |