From: Aaron Kennedy Date: Wed, 26 Oct 2011 14:04:58 +0000 (+0100) Subject: Readonly QML property support X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=b79ceabb43427ba73fb0d64bd24f46c44e6a9d8d;p=konrad%2Fqtdeclarative.git Readonly QML property support Task-number: QTBUG-15257 Change-Id: I539b6e6a9e0e0172b68e8002aaa3f7c7e6648769 Reviewed-by: Aaron Kennedy --- diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 4788491..782a20e 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -208,7 +208,7 @@ bool QDeclarativeCompiler::testLiteralAssignment(QDeclarativeScript::Property *p { const QDeclarativeScript::Variant &value = v->value; - if (!prop->core.isWritable()) + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); if (prop->core.isEnum()) { @@ -2004,7 +2004,7 @@ bool QDeclarativeCompiler::buildGroupedProperty(QDeclarativeScript::Property *pr } } - if (!obj->metaObject()->property(prop->index).isWritable()) { + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) { COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); } @@ -2082,8 +2082,7 @@ bool QDeclarativeCompiler::buildValueTypeProperty(QObject *type, bool isEnumAssignment = false; if (prop->core.isEnum()) - COMPILE_CHECK(testQualifiedEnumAssignment(obj->metatype->property(prop->index), obj, - value, &isEnumAssignment)); + COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, value, &isEnumAssignment)); if (isEnumAssignment) { value->type = Value::Literal; @@ -2222,7 +2221,7 @@ bool QDeclarativeCompiler::buildPropertyObjectAssignment(QDeclarativeScript::Pro Q_ASSERT(prop->index != -1); Q_ASSERT(v->object->type != -1); - if (!obj->metaObject()->property(prop->index).isWritable()) + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); if (QDeclarativeMetaType::isInterface(prop->type)) { @@ -2303,7 +2302,9 @@ bool QDeclarativeCompiler::buildPropertyOnAssignment(QDeclarativeScript::Propert Q_ASSERT(prop->index != -1); Q_ASSERT(v->object->type != -1); - if (!obj->metaObject()->property(prop->index).isWritable()) + Q_UNUSED(obj); + + if (!prop->core.isWritable()) COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); @@ -2350,8 +2351,7 @@ bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeScript::Pr //optimization for . enum assignments if (prop->core.isEnum()) { bool isEnumAssignment = false; - COMPILE_CHECK(testQualifiedEnumAssignment(obj->metaObject()->property(prop->index), obj, - v, &isEnumAssignment)); + COMPILE_CHECK(testQualifiedEnumAssignment(prop, obj, v, &isEnumAssignment)); if (isEnumAssignment) { v->type = Value::Literal; return true; @@ -2372,17 +2372,19 @@ bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeScript::Pr return true; } -bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop, +bool QDeclarativeCompiler::testQualifiedEnumAssignment(QDeclarativeScript::Property *prop, QDeclarativeScript::Object *obj, QDeclarativeScript::Value *v, bool *isAssignment) { *isAssignment = false; - if (!prop.isEnumType()) + if (!prop->core.isEnum()) return true; - if (!prop.isWritable()) - COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(QString::fromUtf8(prop.name()))); + QMetaProperty mprop = obj->metaObject()->property(prop->index); + + if (!prop->core.isWritable() && !prop->isReadOnlyDeclaration) + COMPILE_EXCEPTION(v, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); QString string = v->value.asString(); if (!string.at(0).isUpper()) @@ -2413,10 +2415,10 @@ bool QDeclarativeCompiler::testQualifiedEnumAssignment(const QMetaProperty &prop if (objTypeName == type->qmlTypeName()) { // When these two match, we can short cut the search - if (prop.isFlagType()) { - value = prop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); + if (mprop.isFlagType()) { + value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok); } else { - value = prop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); + value = mprop.enumerator().keyToValue(enumValue.toUtf8().constData(), &ok); } } else { // Otherwise we have to search the whole type @@ -2571,7 +2573,8 @@ bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeScript::Object *obj) bool QDeclarativeCompiler::mergeDynamicMetaProperties(QDeclarativeScript::Object *obj) { - for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) { + for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; + p = obj->dynamicProperties.next(p)) { if (!p->defaultValue || p->type == Object::DynamicProperty::Alias) continue; @@ -2585,6 +2588,9 @@ bool QDeclarativeCompiler::mergeDynamicMetaProperties(QDeclarativeScript::Object COMPILE_EXCEPTION(property, tr("Property value set multiple times")); } + if (p->isReadOnly) + property->isReadOnlyDeclaration = true; + if (property->value) COMPILE_EXCEPTION(property, tr("Invalid property nesting")); @@ -2755,6 +2761,9 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn if (p->type == Object::DynamicProperty::Var) continue; + if (p->isReadOnly) + readonly = true; + if (buildData) { VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data(); vmd->propertyCount++; @@ -2790,8 +2799,10 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn (vmd->propertyData() + effectivePropertyIndex)->propertyType = -1; } - builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, (QMetaType::Type)-1, - QFastMetaBuilder::Writable, effectivePropertyIndex); + builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, + (QMetaType::Type)-1, + p->isReadOnly?QFastMetaBuilder::None:QFastMetaBuilder::Writable, + effectivePropertyIndex); p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()")); builder.setSignal(effectivePropertyIndex, p->changedSignatureRef); @@ -3098,8 +3109,8 @@ bool QDeclarativeCompiler::compileAlias(QFastMetaBuilder &builder, if (!aliasProperty.isScriptable()) COMPILE_EXCEPTION(prop.defaultValue, tr("Invalid alias location")); - writable = aliasProperty.isWritable(); - resettable = aliasProperty.isResettable(); + writable = aliasProperty.isWritable() && !prop.isReadOnly; + resettable = aliasProperty.isResettable() && !prop.isReadOnly; if (aliasProperty.type() < QVariant::UserType) type = aliasProperty.type(); @@ -3175,7 +3186,7 @@ bool QDeclarativeCompiler::buildBinding(QDeclarativeScript::Value *value, Q_ASSERT(prop->parent); Q_ASSERT(prop->parent->metaObject()); - if (!prop->core.isWritable() && !prop->core.isQList()) + if (!prop->core.isWritable() && !prop->core.isQList() && !prop->isReadOnlyDeclaration) COMPILE_EXCEPTION(prop, tr("Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString())); BindingReference *reference = pool->New(); diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index 2615109..f6229bc 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -336,7 +336,7 @@ private: bool doesPropertyExist(QDeclarativeScript::Property *prop, QDeclarativeScript::Object *obj); bool testLiteralAssignment(QDeclarativeScript::Property *prop, QDeclarativeScript::Value *value); - bool testQualifiedEnumAssignment(const QMetaProperty &prop, + bool testQualifiedEnumAssignment(QDeclarativeScript::Property *prop, QDeclarativeScript::Object *obj, QDeclarativeScript::Value *value, bool *isAssignment); diff --git a/src/declarative/qml/qdeclarativescript.cpp b/src/declarative/qml/qdeclarativescript.cpp index fb4f26a..9d18cb3 100644 --- a/src/declarative/qml/qdeclarativescript.cpp +++ b/src/declarative/qml/qdeclarativescript.cpp @@ -182,7 +182,7 @@ Property *QDeclarativeScript::Object::getProperty(const QString &name, bool crea } QDeclarativeScript::Object::DynamicProperty::DynamicProperty() -: isDefaultProperty(false), type(Variant), defaultValue(0), nextProperty(0), +: isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0), resolvedCustomTypeName(0) { } @@ -225,8 +225,8 @@ int QDeclarativeScript::Object::DynamicSlot::parameterNamesLength() const QDeclarativeScript::Property::Property() : parent(0), type(0), index(-1), value(0), isDefault(true), isDeferred(false), - isValueTypeSubProperty(false), isAlias(false), scriptStringScope(-1), - nextMainProperty(0), nextProperty(0) + isValueTypeSubProperty(false), isAlias(false), isReadOnlyDeclaration(false), + scriptStringScope(-1), nextMainProperty(0), nextProperty(0) { } @@ -1028,18 +1028,9 @@ bool ProcessAST::visit(AST::UiPublicMember *node) return false; } - if (node->isReadonlyMember) { - QDeclarativeError error; - error.setDescription(QCoreApplication::translate("QDeclarativeParser","Readonly not yet supported")); - error.setLine(node->readonlyToken.startLine); - error.setColumn(node->readonlyToken.startColumn); - _parser->_errors << error; - return false; - - } - Object::DynamicProperty *property = _parser->_pool.New(); property->isDefaultProperty = node->isDefaultMember; + property->isReadOnly = node->isReadonlyMember; property->type = type; if (type >= Object::DynamicProperty::Custom) { QDeclarativeScript::TypeReference *typeRef = diff --git a/src/declarative/qml/qdeclarativescript_p.h b/src/declarative/qml/qdeclarativescript_p.h index dbf94b2..a83cd9f 100644 --- a/src/declarative/qml/qdeclarativescript_p.h +++ b/src/declarative/qml/qdeclarativescript_p.h @@ -279,6 +279,8 @@ public: // True if this property is a property alias. Set by the // QDeclarativeCompiler bool isAlias; + // True if this is a readonly property declaration + bool isReadOnlyDeclaration; // Used for scriptStringProperties int scriptStringScope; @@ -388,7 +390,9 @@ public: enum Type { Var, Variant, Int, Bool, Real, String, Url, Color, Time, Date, DateTime, Alias, Custom, CustomList }; - bool isDefaultProperty; + quint32 isDefaultProperty:1; + quint32 isReadOnly:1; + Type type; QHashedStringRef customType; diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/readonlyDeclaration.qml b/tests/auto/declarative/qdeclarativeecmascript/data/readonlyDeclaration.qml new file mode 100644 index 0000000..5377d2d --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/readonlyDeclaration.qml @@ -0,0 +1,45 @@ +import QtQuick 2.0 + +QtObject { + property int dummy: 13 + + readonly property int test1: 19 + readonly property int test2: dummy * 49 + readonly property alias test3: other.test + + property bool test: false + + property var dummyObj: QtObject { + id: other + property int test: 9 + } + + Component.onCompleted: { + if (test1 != 19) return; + if (test2 != 637) return; + if (test3 != 9) return; + + var caught = false; + + caught = false; + try { test1 = 13 } catch (e) { caught = true; } + if (!caught) return; + + caught = false; + try { test2 = 13 } catch (e) { caught = true; } + if (!caught) return; + + caught = false; + try { test3 = 13 } catch (e) { caught = true; } + if (!caught) return; + + other.test = 13; + dummy = 9; + + if (test1 != 19) return; + if (test2 != 441) return; + if (test3 != 13) return; + + test = true; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index 31aefd2..c92dc80 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -169,7 +169,7 @@ private slots: void booleanConversion(); void handleReferenceManagement(); void stringArg(); - + void readonlyDeclaration(); void bug1(); void bug2(); void dynamicCreationCrash(); @@ -4036,6 +4036,18 @@ void tst_qdeclarativeecmascript::stringArg() delete object; } +void tst_qdeclarativeecmascript::readonlyDeclaration() +{ + QDeclarativeComponent component(&engine, TEST_FILE("readonlyDeclaration.qml")); + + QObject *object = component.create(); + QVERIFY(object != 0); + + QCOMPARE(object->property("test").toBool(), true); + + delete object; +} + // Test that assigning a null object works // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 void tst_qdeclarativeecmascript::nullObjectBinding() diff --git a/tests/auto/declarative/qdeclarativelanguage/data/ReadOnlyType.qml b/tests/auto/declarative/qdeclarativelanguage/data/ReadOnlyType.qml new file mode 100644 index 0000000..456ac76 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelanguage/data/ReadOnlyType.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +QtObject { + readonly property int readOnlyProperty: 19 +} diff --git a/tests/auto/declarative/qdeclarativelanguage/data/property.5.errors.txt b/tests/auto/declarative/qdeclarativelanguage/data/property.5.errors.txt deleted file mode 100644 index 32a8dc1..0000000 --- a/tests/auto/declarative/qdeclarativelanguage/data/property.5.errors.txt +++ /dev/null @@ -1 +0,0 @@ -4:5:Readonly not yet supported diff --git a/tests/auto/declarative/qdeclarativelanguage/data/property.5.qml b/tests/auto/declarative/qdeclarativelanguage/data/property.5.qml deleted file mode 100644 index a1401d2..0000000 --- a/tests/auto/declarative/qdeclarativelanguage/data/property.5.qml +++ /dev/null @@ -1,6 +0,0 @@ -import QtQuick 2.0 - -QtObject { - readonly property int a: value -} - diff --git a/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.errors.txt b/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.errors.txt index baf4766..e71ae44 100644 --- a/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.errors.txt +++ b/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.errors.txt @@ -1 +1 @@ -3:27:Invalid property assignment: "readOnlyEnumProperty" is a read-only property +2:23:Invalid property assignment: "readOnlyProperty" is a read-only property diff --git a/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.qml b/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.qml index 422d13d..d80b27a 100644 --- a/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.qml +++ b/tests/auto/declarative/qdeclarativelanguage/data/readOnly.5.qml @@ -1,4 +1,3 @@ -import Test 1.0 -MyTypeObject { - readOnlyEnumProperty: MyTypeObject.EnumValue1 +ReadOnlyType { + readOnlyProperty: 13 } diff --git a/tests/auto/declarative/qdeclarativelanguage/data/readonly.qml b/tests/auto/declarative/qdeclarativelanguage/data/readonly.qml new file mode 100644 index 0000000..493a9ad --- /dev/null +++ b/tests/auto/declarative/qdeclarativelanguage/data/readonly.qml @@ -0,0 +1,17 @@ +import Test 1.0 + +MyQmlObject { + property int testData: 9 + property alias testData2: myObject.test1 + + readonly property int test1: 10 + readonly property int test2: testData + 9 + readonly property alias test3: myObject.test1 + + + property variant dummy: MyQmlObject { + id: myObject + property int test1: 13 + } +} + diff --git a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp index 538ebbb..64ab5d8 100644 --- a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp +++ b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp @@ -156,6 +156,7 @@ private slots: void inlineAssignmentsOverrideBindings(); void nestedComponentRoots(); void registrationOrder(); + void readonly(); void basicRemote_data(); void basicRemote(); @@ -364,7 +365,6 @@ void tst_qdeclarativelanguage::errors_data() QTest::newRow("property.2") << "property.2.qml" << "property.2.errors.txt" << false; QTest::newRow("property.3") << "property.3.qml" << "property.3.errors.txt" << false; QTest::newRow("property.4") << "property.4.qml" << "property.4.errors.txt" << false; - QTest::newRow("property.5") << "property.5.qml" << "property.5.errors.txt" << false; QTest::newRow("property.6") << "property.6.qml" << "property.6.errors.txt" << false; QTest::newRow("property.7") << "property.7.qml" << "property.7.errors.txt" << false; @@ -2142,6 +2142,40 @@ void tst_qdeclarativelanguage::registrationOrder() delete o; } +void tst_qdeclarativelanguage::readonly() +{ + QDeclarativeComponent component(&engine, TEST_FILE("readonly.qml")); + + QObject *o = component.create(); + QVERIFY(o != 0); + + QCOMPARE(o->property("test1").toInt(), 10); + QCOMPARE(o->property("test2").toInt(), 18); + QCOMPARE(o->property("test3").toInt(), 13); + + o->setProperty("testData", 13); + + QCOMPARE(o->property("test1").toInt(), 10); + QCOMPARE(o->property("test2").toInt(), 22); + QCOMPARE(o->property("test3").toInt(), 13); + + o->setProperty("testData2", 2); + + QCOMPARE(o->property("test1").toInt(), 10); + QCOMPARE(o->property("test2").toInt(), 22); + QCOMPARE(o->property("test3").toInt(), 2); + + o->setProperty("test1", 11); + o->setProperty("test2", 11); + o->setProperty("test3", 11); + + QCOMPARE(o->property("test1").toInt(), 10); + QCOMPARE(o->property("test2").toInt(), 22); + QCOMPARE(o->property("test3").toInt(), 2); + + delete o; +} + // QTBUG-18268 void tst_qdeclarativelanguage::remoteLoadCrash() {