From 9643635f867b67e263d1c615965a93a1579f8b34 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 14 Sep 2017 14:38:36 +0200 Subject: Allow exported signal handlers for signals with revision Properties of QML objects or alias properties to QML objects have to know about the revision of the QML type. If the property is used as a grouped property and a signal or property is assigned. Without this patch this is not working with signals that have a revision. To get this working we store the minor version of the QML type in QQmlPropertyData and retrieve the QQmlPropertyCache with the correct AllowedRevisionCache using this minor version. Task-number: QTCREATORBUG-18820 Change-Id: I1e20169e0d5a2ae11059a951aa83a5c94106accb Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlpropertycachecreator.cpp | 2 +- src/qml/compiler/qqmlpropertycachecreator_p.h | 19 ++++++---- src/qml/compiler/qqmlpropertyvalidator.cpp | 5 +-- src/qml/compiler/qqmltypecompiler.cpp | 2 +- src/qml/qml/qqmlengine.cpp | 8 +++-- src/qml/qml/qqmlengine_p.h | 2 +- src/qml/qml/qqmlpropertycache.cpp | 3 +- src/qml/qml/qqmlpropertycache_p.h | 41 ++++++++++++++++++---- .../data/GroupedPropertiesRevisionComponent1.qml | 10 ++++++ .../data/GroupedPropertiesRevisionComponent2.qml | 10 ++++++ .../data/testGroupedPropertiesRevision.1.qml | 7 ++++ .../data/testGroupedPropertiesRevision.2.qml | 7 ++++ tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 12 +++++++ 13 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml create mode 100644 tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml create mode 100644 tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml create mode 100644 tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml diff --git a/src/qml/compiler/qqmlpropertycachecreator.cpp b/src/qml/compiler/qqmlpropertycachecreator.cpp index f8d63ec634..4f8b48f52e 100644 --- a/src/qml/compiler/qqmlpropertycachecreator.cpp +++ b/src/qml/compiler/qqmlpropertycachecreator.cpp @@ -64,7 +64,7 @@ QQmlBindingInstantiationContext::QQmlBindingInstantiationContext(int referencing Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; - instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision); + instantiatingProperty = QmlIR::PropertyResolver(referencingObjectPropertyCache).property(instantiatingPropertyName, ¬InRevision, QmlIR::PropertyResolver::IgnoreRevision); } } diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 5d6a5c177a..0a525e418c 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -184,7 +184,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator::propertyCac { if (context.instantiatingProperty) { if (context.instantiatingProperty->isQObject()) { - return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType()); + return enginePrivate->rawPropertyCacheForType(context.instantiatingProperty->propType(), context.instantiatingProperty->typeMinorVersion()); } else if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(context.instantiatingProperty->propType())) { return enginePrivate->cache(vtmo); } @@ -476,6 +476,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj pend = obj->propertiesEnd(); for ( ; p != pend; ++p, ++propertyIdx) { int propertyType = 0; + int propertTypeMinorVersion = 0; QQmlPropertyData::Flags propertyFlags; if (p->type == QV4::CompiledData::Property::Var) { @@ -513,6 +514,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj } else { if (p->type == QV4::CompiledData::Property::Custom) { propertyType = qmltype.typeId(); + propertTypeMinorVersion = qmltype.minorVersion(); } else { propertyType = qmltype.qListTypeId(); } @@ -532,7 +534,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator::createMetaObj if (!obj->defaultPropertyIsAlias && propertyIdx == obj->indexOfDefaultPropertyOrAlias) cache->_defaultPropertyName = propertyName; cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - propertyType, effectiveSignalIndex); + propertyType, propertTypeMinorVersion, effectiveSignalIndex); effectiveSignalIndex++; } @@ -555,7 +557,7 @@ public: private: void appendAliasPropertiesInMetaObjectsWithinComponent(const CompiledObject &component, int firstObjectIndex); - void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, QQmlPropertyRawData::Flags *propertyFlags); + void propertyDataForAlias(const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *rev, QQmlPropertyRawData::Flags *propertyFlags); void collectObjectsWithAliasesRecursively(int objectIndex, QVector *objectsWithAliases) const; @@ -667,7 +669,7 @@ inline void QQmlPropertyCacheAliasCreator::collectObjectsWithAl template inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias( - const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, + const CompiledObject &component, const QV4::CompiledData::Alias &alias, int *type, int *minorVersion, QQmlPropertyData::Flags *propertyFlags) { const int targetObjectIndex = objectForId(component, alias.targetObjectId); @@ -685,7 +687,7 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias auto targetAlias = targetObject.aliasesBegin(); for (uint i = 0; i < alias.localAliasIndex; ++i) ++targetAlias; - propertyDataForAlias(component, *targetAlias, type, propertyFlags); + propertyDataForAlias(component, *targetAlias, type, minorVersion, propertyFlags); return; } else if (alias.encodedMetaPropertyIndex == -1) { Q_ASSERT(alias.flags & QV4::CompiledData::Alias::AliasPointsToPointerObject); @@ -697,6 +699,8 @@ inline void QQmlPropertyCacheAliasCreator::propertyDataForAlias else *type = typeRef->compilationUnit->metaTypeId; + *minorVersion = typeRef->minorVersion; + propertyFlags->type = QQmlPropertyData::Flags::QObjectDerivedType; } else { int coreIndex = QQmlPropertyIndex::fromEncoded(alias.encodedMetaPropertyIndex).coreIndex(); @@ -756,8 +760,9 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasesToPrope Q_ASSERT(alias->flags & QV4::CompiledData::Alias::Resolved); int type = 0; + int minorVersion = 0; QQmlPropertyData::Flags propertyFlags; - propertyDataForAlias(component, *alias, &type, &propertyFlags); + propertyDataForAlias(component, *alias, &type, &minorVersion, &propertyFlags); const QString propertyName = objectContainer->stringAt(alias->nameIndex); @@ -765,7 +770,7 @@ inline void QQmlPropertyCacheAliasCreator::appendAliasesToPrope propertyCache->_defaultPropertyName = propertyName; propertyCache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, - type, effectiveSignalIndex++); + type, minorVersion, effectiveSignalIndex++); } } diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 7ea89b378d..9fde6848bb 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -665,10 +665,11 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * } else if (property->propType() == qMetaTypeId()) { return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected")); } else { - // We want to raw metaObject here as the raw metaobject is the + // We want to use the raw metaObject here as the raw metaobject is the // actual property type before we applied any extensions that might // effect the properties on the type, but don't effect assignability - QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType()); + // Using -1 for the minor version ensures that we get the raw metaObject. + QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1); // Will be true if the assgned type inherits propertyMetaObject bool isAssignable = false; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 97ca597953..836aa8c416 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -823,7 +823,7 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI if (!pd || !pd->isQObject()) continue; - QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType()); + QQmlPropertyCache *pc = enginePrivate->rawPropertyCacheForType(pd->propType(), pd->typeMinorVersion()); const QMetaObject *mo = pc ? pc->firstCppMetaObject() : 0; while (mo) { if (mo == &QQmlComponent::staticMetaObject) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index c47d8a0a81..a66bcaa9bd 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2342,7 +2342,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t) } } -QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) +QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVersion) { Locker locker(this); auto iter = m_compositeTypes.constFind(t); @@ -2351,7 +2351,11 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) } else { QQmlType type = QQmlMetaType::qmlType(t); locker.unlock(); - return type.isValid() ? cache(type.baseMetaObject()) : 0; + + if (minorVersion >= 0) + return type.isValid() ? cache(type, minorVersion) : 0; + else + return type.isValid() ? cache(type.baseMetaObject()) : 0; } } diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 791660cac7..74e232fd84 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -220,7 +220,7 @@ public: QQmlMetaObject rawMetaObjectForType(int) const; QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); - QQmlPropertyCache *rawPropertyCacheForType(int); + QQmlPropertyCache *rawPropertyCacheForType(int, int minorVersion = -1); void registerInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); void unregisterInternalCompositeType(QV4::CompiledData::CompilationUnit *compilationUnit); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 7178dffa8b..bce81c1504 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -321,13 +321,14 @@ QQmlPropertyCache *QQmlPropertyCache::copyAndReserve(int propertyCount, int meth This is different from QMetaMethod::methodIndex(). */ void QQmlPropertyCache::appendProperty(const QString &name, QQmlPropertyData::Flags flags, - int coreIndex, int propType, int notifyIndex) + int coreIndex, int propType, int minorVersion, int notifyIndex) { QQmlPropertyData data; data.setPropType(propType); data.setCoreIndex(coreIndex); data.setNotifyIndex(notifyIndex); data.setFlags(flags); + data.setTypeMinorVersion(minorVersion); QQmlPropertyData *old = findNamedProperty(name); if (old) diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 6cdb82bd46..e032d4f4c7 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -218,12 +218,38 @@ public: _coreIndex = qint16(idx); } - int revision() const { return _revision; } - void setRevision(int rev) + quint8 revision() const { return _revision; } + void setRevision(quint8 rev) { - Q_ASSERT(rev >= std::numeric_limits::min()); - Q_ASSERT(rev <= std::numeric_limits::max()); - _revision = qint16(rev); + Q_ASSERT(rev >= std::numeric_limits::min()); + Q_ASSERT(rev <= std::numeric_limits::max()); + _revision = quint8(rev); + } + + /* If a property is a C++ type, then we store the minor + * version of this type. + * This is required to resolve property or signal revisions + * if this property is used as a grouped property. + * + * Test.qml + * property TextEdit someTextEdit: TextEdit {} + * + * Test { + * someTextEdit.preeditText: "test" //revision 7 + * someTextEdit.onEditingFinished: console.log("test") //revision 6 + * } + * + * To determine if these properties with revisions are available we need + * the minor version of TextEdit as imported in Test.qml. + * + */ + + quint8 typeMinorVersion() const { return _typeMinorVersion; } + void setTypeMinorVersion(quint8 rev) + { + Q_ASSERT(rev >= std::numeric_limits::min()); + Q_ASSERT(rev <= std::numeric_limits::max()); + _typeMinorVersion = quint8(rev); } QQmlPropertyCacheMethodArguments *arguments() const { return _arguments; } @@ -257,7 +283,8 @@ private: qint16 _notifyIndex; qint16 _overrideIndex; - qint16 _revision; + quint8 _revision; + quint8 _typeMinorVersion; qint16 _metaObjectOffset; QQmlPropertyCacheMethodArguments *_arguments; @@ -390,7 +417,7 @@ public: QQmlPropertyCache *copyAndReserve(int propertyCount, int methodCount, int signalCount, int enumCount); void appendProperty(const QString &, QQmlPropertyRawData::Flags flags, int coreIndex, - int propType, int notifyIndex); + int propType, int revision, int notifyIndex); void appendSignal(const QString &, QQmlPropertyRawData::Flags, int coreIndex, const int *types = 0, const QList &names = QList()); void appendMethod(const QString &, QQmlPropertyData::Flags flags, int coreIndex, diff --git a/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml new file mode 100644 index 0000000000..1047926750 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent1.qml @@ -0,0 +1,10 @@ +import QtQuick 2.8 + +Item { + property alias textEdit: textEdit + + TextEdit { + id: textEdit + + } +} diff --git a/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml new file mode 100644 index 0000000000..552c6c3791 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/GroupedPropertiesRevisionComponent2.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 + +Item { + property alias textEdit: textEdit + + TextEdit { + id: textEdit + + } +} diff --git a/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml new file mode 100644 index 0000000000..39bd01fe40 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.1.qml @@ -0,0 +1,7 @@ +import QtQuick 2.8 + +Item { + GroupedPropertiesRevisionComponent1 { + textEdit.onEditingFinished: console.log("test") + } +} diff --git a/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml new file mode 100644 index 0000000000..bb9ba79b01 --- /dev/null +++ b/tests/auto/qml/qqmlengine/data/testGroupedPropertiesRevision.2.qml @@ -0,0 +1,7 @@ +import QtQuick 2.8 + +Item { + GroupedPropertiesRevisionComponent2 { + textEdit.onEditingFinished: console.log("test") + } +} diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index b332ab2194..d6d7506c48 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -75,6 +75,7 @@ private slots: void urlInterceptor(); void qmlContextProperties(); void testGCCorruption(); + void testGroupedPropertyRevisions(); public slots: QObject *createAQObjectForOwnershipTest () @@ -873,6 +874,17 @@ void tst_qqmlengine::testGCCorruption() QVERIFY2(o, qPrintable(c.errorString())); } +void tst_qqmlengine::testGroupedPropertyRevisions() +{ + QQmlEngine e; + + QQmlComponent c(&e, testFileUrl("testGroupedPropertiesRevision.1.qml")); + QScopedPointer object(c.create()); + QVERIFY2(object.data(), qPrintable(c.errorString())); + QQmlComponent c2(&e, testFileUrl("testGroupedPropertiesRevision.2.qml")); + QVERIFY(!c2.errorString().isEmpty()); +} + QTEST_MAIN(tst_qqmlengine) #include "tst_qqmlengine.moc" -- cgit v1.2.3