diff options
-rw-r--r-- | src/qml/qml/qqmlcompileddata.cpp | 22 | ||||
-rw-r--r-- | src/qml/qml/qqmlcompiler.cpp | 25 | ||||
-rw-r--r-- | src/qml/qml/qqmlcompiler_p.h | 6 | ||||
-rw-r--r-- | tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp | 81 |
4 files changed, 134 insertions, 0 deletions
diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 9fcef176ad..76bf24fe6b 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -45,6 +45,7 @@ #include "qqmlcomponent_p.h" #include "qqmlcontext.h" #include "qqmlcontext_p.h" +#include "qqmlpropertymap.h" #ifdef QML_THREADED_VME_INTERPRETER #include "qqmlvme_p.h" #endif @@ -173,6 +174,27 @@ QQmlPropertyCache *QQmlCompiledData::TypeReference::createPropertyCache(QQmlEngi } } +template <typename T> +bool qtTypeInherits(const QMetaObject *mo) { + while (mo) { + if (mo == &T::staticMetaObject) + return true; + mo = mo->superClass(); + } + return false; +} + +void QQmlCompiledData::TypeReference::doDynamicTypeCheck() +{ + const QMetaObject *mo = 0; + if (typePropertyCache) + mo = typePropertyCache->firstCppMetaObject(); + else if (type) + mo = type->metaObject(); + else + mo = component->rootPropertyCache->firstCppMetaObject(); + isFullyDynamicType = qtTypeInherits<QQmlPropertyMap>(mo); +} void QQmlCompiledData::dumpInstructions() { diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 675f242f39..2b925b24ec 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -848,6 +848,8 @@ bool QQmlCompiler::compile(QQmlEngine *engine, } } + ref.doDynamicTypeCheck(); + out->types << ref; } @@ -859,6 +861,19 @@ bool QQmlCompiler::compile(QQmlEngine *engine, if (componentStats) dumpStats(); Q_ASSERT(out->rootPropertyCache); + + // Any QQmlPropertyMap instances for example need to have their property cache removed, + // because the class is too dynamic and allows adding properties at any point at run-time. + for (int i = 0; i < output->types.count(); ++i) { + QQmlCompiledData::TypeReference &tr = output->types[i]; + if (!tr.typePropertyCache) + continue; + + if (tr.isFullyDynamicType) { + tr.typePropertyCache->release(); + tr.typePropertyCache = 0; + } + } } else { reset(out); } @@ -1227,6 +1242,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper) // Setup the synthesized meta object if necessary if (!obj->synthdata.isEmpty()) { + Q_ASSERT(!output->types.at(obj->type).isFullyDynamicType); Instruction::StoreMetaObject meta; meta.aliasData = output->indexForByteArray(obj->synthdata); meta.propertyCache = output->propertyCaches.count(); @@ -2705,6 +2721,15 @@ int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, c // Ensures that the dynamic meta specification on obj is valid bool QQmlCompiler::checkDynamicMeta(QQmlScript::Object *obj) { + if (output->types[obj->type].isFullyDynamicType) { + if (!obj->dynamicProperties.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new properties.")); + if (!obj->dynamicSignals.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully dynamic types cannot declare new signals.")); + if (!obj->dynamicSlots.isEmpty()) + COMPILE_EXCEPTION(obj, tr("Fully Dynamic types cannot declare new functions.")); + } + bool seenDefaultProperty = false; // We use a coarse grain, 31 bit hash to check if there are duplicates. diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index 443a80d95c..75f404a7d5 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -105,6 +105,7 @@ public: : type(0), typePropertyCache(0), component(0) , majorVersion(0) , minorVersion(0) + , isFullyDynamicType(false) {} QQmlType *type; @@ -113,9 +114,14 @@ public: int majorVersion; int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; QQmlPropertyCache *propertyCache() const; QQmlPropertyCache *createPropertyCache(QQmlEngine *); + + void doDynamicTypeCheck(); }; // --- old compiler: QList<TypeReference> types; diff --git a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp index ca212d333b..17f54508a3 100644 --- a/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp +++ b/tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp @@ -55,6 +55,8 @@ public: tst_QQmlPropertyMap() {} private slots: + void initTestCase(); + void insert(); void operatorInsert(); void operatorValue(); @@ -68,8 +70,39 @@ private slots: void metaObjectAccessibility(); void QTBUG_31226(); void QTBUG_29836(); + void QTBUG_35233(); + void disallowExtending(); +}; + +class LazyPropertyMap : public QQmlPropertyMap, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(int someFixedProperty READ someFixedProperty WRITE setSomeFixedProperty) +public: + LazyPropertyMap() + : QQmlPropertyMap(this, /*parent*/0) + , value(0) + {} + + virtual void classBegin() {} + virtual void componentComplete() { + insert(QStringLiteral("lateProperty"), QStringLiteral("lateValue")); + } + + int someFixedProperty() const { return value; } + void setSomeFixedProperty(int v) { value = v; } + +private: + int value; }; +void tst_QQmlPropertyMap::initTestCase() +{ + qmlRegisterType<LazyPropertyMap>("QTBUG_35233", 1, 0, "LazyPropertyMap"); +} + void tst_QQmlPropertyMap::insert() { QQmlPropertyMap map; @@ -369,6 +402,54 @@ void tst_QQmlPropertyMap::QTBUG_29836() } +void tst_QQmlPropertyMap::QTBUG_35233() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "QtObject {\n" + " property QtObject testMap: LazyPropertyMap {\n" + " id: map\n" + " }\n" + " property QtObject sibling: QtObject {\n" + " objectName: \"sibling\"\n" + " property string testValue: map.lateProperty\n" + " }\n" + "}", QUrl()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + QObject *sibling = obj->findChild<QObject*>("sibling"); + QVERIFY(sibling); + QCOMPARE(sibling->property("testValue").toString(), QString("lateValue")); +} + +void tst_QQmlPropertyMap::disallowExtending() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "LazyPropertyMap {\n" + " id: blah\n" + " someFixedProperty: 42\n" + "}\n", QUrl()); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + + component.setData("import QtQml 2.0\n" + "import QTBUG_35233 1.0\n" + "LazyPropertyMap {\n" + " id: blah\n" + " property int someNewProperty;\n" + "}\n", QUrl()); + obj.reset(component.create()); + QVERIFY(obj.isNull()); + QCOMPARE(component.errors().count(), 1); + QCOMPARE(component.errors().at(0).toString(), QStringLiteral("<Unknown File>: Fully dynamic types cannot declare new properties.")); +} + QTEST_MAIN(tst_QQmlPropertyMap) #include "tst_qqmlpropertymap.moc" |