Skip to content

Commit

Permalink
Readonly QML property support
Browse files Browse the repository at this point in the history
Task-number: QTBUG-15257
Change-Id: I539b6e6a9e0e0172b68e8002aaa3f7c7e6648769
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
  • Loading branch information
Aaron Kennedy authored and Qt by Nokia committed Oct 26, 2011
1 parent a927dc9 commit b79ceab
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 49 deletions.
53 changes: 32 additions & 21 deletions src/declarative/qml/qdeclarativecompiler.cpp
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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()));
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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()));


Expand Down Expand Up @@ -2350,8 +2351,7 @@ bool QDeclarativeCompiler::buildPropertyLiteralAssignment(QDeclarativeScript::Pr
//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;
Expand All @@ -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())
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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"));

Expand Down Expand Up @@ -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++;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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<BindingReference>();
Expand Down
2 changes: 1 addition & 1 deletion src/declarative/qml/qdeclarativecompiler_p.h
Expand Up @@ -336,7 +336,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiler
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);
Expand Down
17 changes: 4 additions & 13 deletions src/declarative/qml/qdeclarativescript.cpp
Expand Up @@ -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)
{
}
Expand Down Expand Up @@ -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)
{
}

Expand Down Expand Up @@ -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<Object::DynamicProperty>();
property->isDefaultProperty = node->isDefaultMember;
property->isReadOnly = node->isReadonlyMember;
property->type = type;
if (type >= Object::DynamicProperty::Custom) {
QDeclarativeScript::TypeReference *typeRef =
Expand Down
6 changes: 5 additions & 1 deletion src/declarative/qml/qdeclarativescript_p.h
Expand Up @@ -279,6 +279,8 @@ class Property : public QDeclarativePool::POD
// 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;
Expand Down Expand Up @@ -388,7 +390,9 @@ class Object : public QDeclarativePool::Class
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;
Expand Down
@@ -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;
}
}
Expand Up @@ -169,7 +169,7 @@ private slots:
void booleanConversion();
void handleReferenceManagement();
void stringArg();

void readonlyDeclaration();
void bug1();
void bug2();
void dynamicCreationCrash();
Expand Down Expand Up @@ -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()
Expand Down
@@ -0,0 +1,5 @@
import QtQuick 2.0

QtObject {
readonly property int readOnlyProperty: 19
}

This file was deleted.

This file was deleted.

@@ -1 +1 @@
3:27:Invalid property assignment: "readOnlyEnumProperty" is a read-only property
2:23:Invalid property assignment: "readOnlyProperty" is a read-only property
@@ -1,4 +1,3 @@
import Test 1.0
MyTypeObject {
readOnlyEnumProperty: MyTypeObject.EnumValue1
ReadOnlyType {
readOnlyProperty: 13
}
17 changes: 17 additions & 0 deletions 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
}
}

0 comments on commit b79ceab

Please sign in to comment.