{
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()) {
}
}
- 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()));
}
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;
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)) {
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()));
//optimization for <Type>.<EnumValue> 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;
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())
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
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;
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"));
if (p->type == Object::DynamicProperty::Var)
continue;
+ if (p->isReadOnly)
+ readonly = true;
+
if (buildData) {
VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data();
vmd->propertyCount++;
(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);
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();
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<BindingReference>();
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);
}
QDeclarativeScript::Object::DynamicProperty::DynamicProperty()
-: isDefaultProperty(false), type(Variant), defaultValue(0), nextProperty(0),
+: isDefaultProperty(false), isReadOnly(false), type(Variant), defaultValue(0), nextProperty(0),
resolvedCustomTypeName(0)
{
}
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)
{
}
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<Object::DynamicProperty>();
property->isDefaultProperty = node->isDefaultMember;
+ property->isReadOnly = node->isReadonlyMember;
property->type = type;
if (type >= Object::DynamicProperty::Custom) {
QDeclarativeScript::TypeReference *typeRef =
// 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;
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;
--- /dev/null
+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;
+ }
+}
void booleanConversion();
void handleReferenceManagement();
void stringArg();
-
+ void readonlyDeclaration();
void bug1();
void bug2();
void dynamicCreationCrash();
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()
--- /dev/null
+import QtQuick 2.0
+
+QtObject {
+ readonly property int readOnlyProperty: 19
+}
+++ /dev/null
-4:5:Readonly not yet supported
+++ /dev/null
-import QtQuick 2.0
-
-QtObject {
- readonly property int a: value
-}
-
-3:27:Invalid property assignment: "readOnlyEnumProperty" is a read-only property
+2:23:Invalid property assignment: "readOnlyProperty" is a read-only property
-import Test 1.0
-MyTypeObject {
- readOnlyEnumProperty: MyTypeObject.EnumValue1
+ReadOnlyType {
+ readOnlyProperty: 13
}
--- /dev/null
+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
+ }
+}
+
void inlineAssignmentsOverrideBindings();
void nestedComponentRoots();
void registrationOrder();
+ void readonly();
void basicRemote_data();
void basicRemote();
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;
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()
{