aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-12-04 14:24:24 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-12-05 10:06:08 +0100
commit32d05752e00218a40516d43069fca2bee0d9894e (patch)
treea5078b1a864aa872f0762e4369ced45768a984c9
parent268f4615dcf19aad3603833af83ba28eca886aa5 (diff)
Fix dynamic properties in QQmlPropertyMap not always being visible in QML
QQmlPropertyMap is a fully dynamic class that can add properties at any point in time. In order for these properties to be visible inside QML, we must disable the property cache (instead of trying to unsuccessfully re-fresh it). What happened in this particular case is that the QQmlPropertyMap derived type was instantiated and the VME instruction for creating it would also assign the property cache the compiler determined. There's no way for QQmlPropertyMap itself to access this property cache instance (stored in output->types[id].typePropertyCache) or invalidate it, so instead don't use the compiler's property cache when instantiating the type. This patch also disallows the adding properties to QQmlPropertyMap when it is used as base type for a new QML type, as we cannot provide the derived type to the QQmlPropertyMap constructor - this is only possible in C++. Task-number: QTBUG-35233 Change-Id: I7fa9e4a2224ccfdd7ccb3fd9f73919ecd46058a8 Reviewed-by: Alberto Mardegan <mardy@users.sourceforge.net> Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/qml/qml/qqmlcompileddata.cpp22
-rw-r--r--src/qml/qml/qqmlcompiler.cpp25
-rw-r--r--src/qml/qml/qqmlcompiler_p.h6
-rw-r--r--tests/auto/qml/qqmlpropertymap/tst_qqmlpropertymap.cpp81
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"