From 6f43de5c1173277465f13eed9e904928a54bb9fc Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Thu, 23 Jul 2009 11:21:54 +1000 Subject: Add prototype value type support --- src/declarative/qml/qml.pri | 6 +- src/declarative/qml/qmlcompiler.cpp | 141 ++++++++++++-- src/declarative/qml/qmlcompiler_p.h | 15 +- src/declarative/qml/qmlengine.cpp | 143 ++++++++++++-- src/declarative/qml/qmlengine_p.h | 24 +++ src/declarative/qml/qmlinstruction.cpp | 6 + src/declarative/qml/qmlinstruction_p.h | 9 +- src/declarative/qml/qmlmetaproperty.cpp | 216 +++++++++++++++------ src/declarative/qml/qmlmetaproperty.h | 9 +- src/declarative/qml/qmlmetaproperty_p.h | 15 +- src/declarative/qml/qmlparser.cpp | 8 + src/declarative/qml/qmlparser_p.h | 2 + src/declarative/qml/qmlvaluetype.cpp | 131 +++++++++++++ src/declarative/qml/qmlvaluetype_p.h | 110 +++++++++++ src/declarative/qml/qmlvme.cpp | 47 +++-- .../qmlparser/fakeDotProperty.errors.txt | 2 +- tests/auto/declarative/qmlparser/testtypes.h | 17 +- tests/auto/declarative/qmlparser/tst_qmlparser.cpp | 26 +++ tests/auto/declarative/qmlparser/valueTypes.txt | 12 ++ 19 files changed, 813 insertions(+), 126 deletions(-) create mode 100644 src/declarative/qml/qmlvaluetype.cpp create mode 100644 src/declarative/qml/qmlvaluetype_p.h create mode 100644 tests/auto/declarative/qmlparser/valueTypes.txt diff --git a/src/declarative/qml/qml.pri b/src/declarative/qml/qml.pri index b3fa063..da3574c 100644 --- a/src/declarative/qml/qml.pri +++ b/src/declarative/qml/qml.pri @@ -27,7 +27,8 @@ SOURCES += qml/qmlparser.cpp \ qml/qmlscriptparser.cpp \ qml/qmlenginedebug.cpp \ qml/qmlrewrite.cpp \ - qml/qmlbasicscript.cpp + qml/qmlbasicscript.cpp \ + qml/qmlvaluetype.cpp HEADERS += qml/qmlparser_p.h \ qml/qmlinstruction_p.h \ @@ -69,7 +70,8 @@ HEADERS += qml/qmlparser_p.h \ qml/qmlbasicscript_p.h \ qml/qmlenginedebug_p.h \ qml/qmlrewrite_p.h \ - qml/qpodvector_p.h + qml/qpodvector_p.h \ + qml/qmlvaluetype_p.h # for qtscript debugger contains(QT_CONFIG, scripttools):QT += scripttools diff --git a/src/declarative/qml/qmlcompiler.cpp b/src/declarative/qml/qmlcompiler.cpp index 477e6a5..407314a 100644 --- a/src/declarative/qml/qmlcompiler.cpp +++ b/src/declarative/qml/qmlcompiler.cpp @@ -63,6 +63,7 @@ #include #include "parser/qmljsast_p.h" #include +#include "qmlmetaproperty_p.h" #include "qmlscriptparser_p.h" @@ -76,7 +77,7 @@ using namespace QmlParser; Instantiate a new QmlCompiler. */ QmlCompiler::QmlCompiler() -: output(0) +: output(0), engine(0) { } @@ -571,6 +572,7 @@ bool QmlCompiler::compile(QmlEngine *engine, Object *root = unit->data.tree(); Q_ASSERT(root); + this->engine = engine; compileTree(root); if (!isError()) { @@ -583,6 +585,7 @@ bool QmlCompiler::compile(QmlEngine *engine, compileState = ComponentCompileState(); savedCompileStates.clear(); output = 0; + this->engine = 0; return !isError(); } @@ -877,6 +880,27 @@ void QmlCompiler::genObjectBody(QmlParser::Object *obj) pop.line = prop->location.start.line; output->bytecode << pop; } + + foreach(Property *prop, obj->valueTypeProperties) { + QmlInstruction fetch; + fetch.type = QmlInstruction::FetchValueType; + fetch.fetchValue.property = prop->index; + fetch.fetchValue.type = prop->type; + fetch.line = prop->location.start.line; + + output->bytecode << fetch; + + foreach(Property *vprop, prop->value->valueProperties) { + genPropertyAssignment(vprop, prop->value, prop); + } + + QmlInstruction pop; + pop.type = QmlInstruction::PopValueType; + pop.fetchValue.property = prop->index; + pop.fetchValue.type = prop->type; + pop.line = prop->location.start.line; + output->bytecode << pop; + } } void QmlCompiler::genComponent(QmlParser::Object *obj) @@ -1273,7 +1297,8 @@ void QmlCompiler::genListProperty(QmlParser::Property *prop, } void QmlCompiler::genPropertyAssignment(QmlParser::Property *prop, - QmlParser::Object *obj) + QmlParser::Object *obj, + QmlParser::Property *valueTypeProperty) { for (int ii = 0; ii < prop->values.count(); ++ii) { QmlParser::Value *v = prop->values.at(ii); @@ -1314,12 +1339,19 @@ void QmlCompiler::genPropertyAssignment(QmlParser::Property *prop, QmlInstruction store; store.type = QmlInstruction::StoreValueSource; store.line = v->object->location.start.line; - store.assignValueSource.property = prop->index; + if (valueTypeProperty) { + store.assignValueSource.property = QmlMetaPropertyPrivate::saveValueType(valueTypeProperty->index, prop->index); + store.assignValueSource.owner = 1; + } else { + store.assignValueSource.property = + QmlMetaPropertyPrivate::saveProperty(prop->index); + store.assignValueSource.owner = 0; + } output->bytecode << store; } else if (v->type == Value::PropertyBinding) { - genBindingAssignment(v, prop, obj); + genBindingAssignment(v, prop, obj, valueTypeProperty); } else if (v->type == Value::Literal) { @@ -1419,18 +1451,89 @@ bool QmlCompiler::buildGroupedProperty(QmlParser::Property *prop, Q_ASSERT(prop->type != 0); Q_ASSERT(prop->index != -1); - // Load the nested property's meta type - prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); - if (!prop->value->metatype) - COMPILE_EXCEPTION(prop, "Cannot nest non-QObject property" << prop->name); + if (prop->type < QVariant::UserType) { + QmlEnginePrivate *ep = + static_cast(QObjectPrivate::get(engine)); + if (ep->valueTypes[prop->type]) { + COMPILE_CHECK(buildValueTypeProperty(ep->valueTypes[prop->type], + prop->value, ctxt.incr())); + obj->addValueTypeProperty(prop); + } else { + COMPILE_EXCEPTION(prop, "Invalid property access"); + } - obj->addGroupedProperty(prop); + } else { + // Load the nested property's meta type + prop->value->metatype = QmlMetaType::metaObjectForType(prop->type); + if (!prop->value->metatype) + COMPILE_EXCEPTION(prop, "Cannot nest non-QObject property" << + prop->name); - COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + obj->addGroupedProperty(prop); + + COMPILE_CHECK(buildSubObject(prop->value, ctxt.incr())); + } return true; } +bool QmlCompiler::buildValueTypeProperty(QObject *type, + QmlParser::Object *obj, + const BindingContext &ctxt) +{ + if (obj->defaultProperty) + COMPILE_EXCEPTION(obj, "Invalid property use"); + obj->metatype = type->metaObject(); + + foreach (Property *prop, obj->properties) { + int idx = type->metaObject()->indexOfProperty(prop->name.constData()); + if (idx == -1) + COMPILE_EXCEPTION(prop, "Cannot assign to non-existant property"); + QMetaProperty p = type->metaObject()->property(idx); + prop->index = idx; + prop->type = p.userType(); + + if (prop->value || prop->values.count() != 1) + COMPILE_EXCEPTION(prop, "Invalid property use"); + + Value *value = prop->values.at(0); + + if (value->object) { + const QMetaObject *c = + output->types.at(value->object->type).metaObject(); + bool isPropertyValue = false; + while (c && !isPropertyValue) { + isPropertyValue = + (c == &QmlPropertyValueSource::staticMetaObject); + c = c->superClass(); + } + + if (!isPropertyValue) { + COMPILE_EXCEPTION(prop, "Invalid property use"); + } else { + COMPILE_CHECK(buildObject(value->object, ctxt)); + value->type = Value::ValueSource; + } + + } else if (value->value.isScript()) { + // ### Check for writability + BindingReference reference; + reference.expression = value->value; + reference.property = prop; + reference.value = value; + reference.bindingContext = ctxt; + reference.bindingContext.owner++; + addBindingReference(reference); + value->type = Value::PropertyBinding; + } else { + COMPILE_CHECK(testLiteralAssignment(p, value)); + value->type = Value::Literal; + } + obj->addValueProperty(prop); + } + + return true; +} // Build assignments to QML lists. QML lists are properties of type // QList * and QmlList *. @@ -1951,14 +2054,13 @@ bool QmlCompiler::buildBinding(QmlParser::Value *value, void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, QmlParser::Property *prop, - QmlParser::Object *obj) + QmlParser::Object *obj, + QmlParser::Property *valueTypeProperty) { Q_ASSERT(compileState.bindings.contains(binding)); const BindingReference &ref = compileState.bindings.value(binding); - QMetaProperty mp = obj->metaObject()->property(prop->index); - QmlInstruction store; int dataRef; if (ref.compiledData.isEmpty()) { @@ -1969,10 +2071,19 @@ void QmlCompiler::genBindingAssignment(QmlParser::Value *binding, store.type = QmlInstruction::StoreCompiledBinding; } - store.assignBinding.property = prop->index; + Q_ASSERT(ref.bindingContext.owner == 0 || + (ref.bindingContext.owner != 0 && valueTypeProperty)); + if (ref.bindingContext.owner) { + store.assignBinding.property = + QmlMetaPropertyPrivate::saveValueType(valueTypeProperty->index, + prop->index); + } else { + store.assignBinding.property = + QmlMetaPropertyPrivate::saveProperty(prop->index); + } store.assignBinding.value = dataRef; - store.assignBinding.category = QmlMetaProperty::propertyCategory(mp); store.assignBinding.context = ref.bindingContext.stack; + store.assignBinding.owner = ref.bindingContext.owner; output->bytecode << store; } diff --git a/src/declarative/qml/qmlcompiler_p.h b/src/declarative/qml/qmlcompiler_p.h index 86e6590..094c05a 100644 --- a/src/declarative/qml/qmlcompiler_p.h +++ b/src/declarative/qml/qmlcompiler_p.h @@ -141,9 +141,9 @@ private: struct BindingContext { BindingContext() - : stack(0), object(0) {} + : stack(0), owner(0), object(0) {} BindingContext(QmlParser::Object *o) - : stack(0), object(o) {} + : stack(0), owner(0), object(o) {} BindingContext incr() const { BindingContext rv(object); rv.stack = stack + 1; @@ -151,6 +151,7 @@ private: } bool isSubContext() const { return stack != 0; } int stack; + int owner; QmlParser::Object *object; }; @@ -171,6 +172,9 @@ private: bool buildGroupedProperty(QmlParser::Property *prop, QmlParser::Object *obj, const BindingContext &ctxt); + bool buildValueTypeProperty(QObject *type, + QmlParser::Object *obj, + const BindingContext &ctxt); bool buildListProperty(QmlParser::Property *prop, QmlParser::Object *obj, const BindingContext &ctxt); @@ -208,12 +212,14 @@ private: void genValueProperty(QmlParser::Property *prop, QmlParser::Object *obj); void genListProperty(QmlParser::Property *prop, QmlParser::Object *obj); void genPropertyAssignment(QmlParser::Property *prop, - QmlParser::Object *obj); + QmlParser::Object *obj, + QmlParser::Property *valueTypeProperty = 0); void genLiteralAssignment(const QMetaProperty &prop, QmlParser::Value *value); void genBindingAssignment(QmlParser::Value *binding, QmlParser::Property *prop, - QmlParser::Object *obj); + QmlParser::Object *obj, + QmlParser::Property *valueTypeProperty = 0); int componentTypeRef(); @@ -257,6 +263,7 @@ private: QList exceptions; QmlCompiledData *output; + QmlEngine *engine; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 8b21290..75ff284 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -148,6 +148,7 @@ void QmlEnginePrivate::init() scriptEngine.installTranslatorFunctions(); contextClass = new QmlContextScriptClass(q); objectClass = new QmlObjectScriptClass(q); + valueTypeClass = new QmlValueTypeScriptClass(q); rootContext = new QmlContext(q,true); #ifdef QT_SCRIPTTOOLS_LIB if (qmlDebugger()){ @@ -189,7 +190,7 @@ Q_GLOBAL_STATIC(FunctionCache, functionCache); QScriptClass::QueryFlags QmlEnginePrivate::queryObject(const QString &propName, - uint *id, QObject *obj) + uint *id, QObject *obj) { QScriptClass::QueryFlags rv = 0; @@ -223,8 +224,15 @@ QmlEnginePrivate::queryObject(const QString &propName, return rv; } +struct QmlValueTypeReference { + QmlValueType *type; + QGuard object; + int property; +}; +Q_DECLARE_METATYPE(QmlValueTypeReference); + QScriptValue QmlEnginePrivate::propertyObject(const QScriptString &propName, - QObject *obj, uint id) + QObject *obj, uint id) { if (id == QmlScriptClass::FunctionId) { QScriptValue sobj = scriptEngine.newQObject(obj); @@ -236,10 +244,20 @@ QScriptValue QmlEnginePrivate::propertyObject(const QScriptString &propName, if (!prop.isValid()) return QScriptValue(); - QVariant var = prop.read(); if (prop.needsChangedNotifier()) capturedProperties << CapturedProperty(prop); - QObject *varobj = QmlMetaType::toQObject(var); + + int propType = prop.propertyType(); + if (propType < QVariant::UserType && valueTypes[propType]) { + QmlValueTypeReference ref; + ref.type = valueTypes[propType]; + ref.object = obj; + ref.property = prop.coreIndex(); + return scriptEngine.newObject(valueTypeClass, scriptEngine.newVariant(QVariant::fromValue(ref))); + } + + QVariant var = prop.read(); + QObject *varobj = (propType < QVariant::UserType)?0:QmlMetaType::toQObject(var); if (!varobj) varobj = qvariant_cast(var); if (varobj) { @@ -679,6 +697,32 @@ QmlScriptClass::QmlScriptClass(QmlEngine *bindengine) { } +QVariant QmlScriptClass::toVariant(QmlEngine *engine, const QScriptValue &val) +{ + QmlEnginePrivate *ep = + static_cast(QObjectPrivate::get(engine)); + + QScriptClass *sc = val.scriptClass(); + if (!sc) { + return val.toVariant(); + } else if (sc == ep->contextClass) { + return QVariant(); + } else if (sc == ep->objectClass) { + return QVariant::fromValue(val.data().toQObject()); + } else if (sc == ep->valueTypeClass) { + QmlValueTypeReference ref = + qvariant_cast(val.data().toVariant()); + + if (!ref.object) + return QVariant(); + + QMetaProperty p = ref.object->metaObject()->property(ref.property); + return p.read(ref.object); + } + + return QVariant(); +} + ///////////////////////////////////////////////////////////// /* The QmlContextScriptClass handles property access for a QmlContext @@ -805,19 +849,88 @@ void QmlContextScriptClass::setProperty(QScriptValue &object, QmlMetaProperty prop; prop.restore(id, obj); - QVariant v; - QObject *data = value.data().toQObject(); - if (data) { - v = QVariant::fromValue(data); - } else { - v = value.toVariant(); - } + QVariant v = QmlScriptClass::toVariant(engine, value); prop.write(v); scriptEngine->currentContext()->setActivationObject(oldact); } ///////////////////////////////////////////////////////////// +QmlValueTypeScriptClass::QmlValueTypeScriptClass(QmlEngine *bindEngine) +: QmlScriptClass(bindEngine) +{ +} + +QmlValueTypeScriptClass::~QmlValueTypeScriptClass() +{ +} + +QmlValueTypeScriptClass::QueryFlags +QmlValueTypeScriptClass::queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id) +{ + QmlValueTypeReference ref = + qvariant_cast(object.data().toVariant()); + + if (!ref.object) + return 0; + + QByteArray propName = name.toString().toUtf8(); + + int idx = ref.type->metaObject()->indexOfProperty(propName.constData()); + if (idx == -1) + return 0; + *id = idx; + + QMetaProperty prop = ref.object->metaObject()->property(idx); + + QmlValueTypeScriptClass::QueryFlags rv = + QmlValueTypeScriptClass::HandlesReadAccess; + if (prop.isWritable()) + rv |= QmlValueTypeScriptClass::HandlesWriteAccess; + + return rv; +} + +QScriptValue QmlValueTypeScriptClass::property(const QScriptValue &object, + const QScriptString &name, + uint id) +{ + QmlValueTypeReference ref = + qvariant_cast(object.data().toVariant()); + + if (!ref.object) + return QScriptValue(); + + ref.type->read(ref.object, ref.property); + + QMetaProperty p = ref.type->metaObject()->property(id); + QVariant rv = p.read(ref.type); + + return static_cast(QObjectPrivate::get(engine))->scriptEngine.newVariant(rv); +} + +void QmlValueTypeScriptClass::setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value) +{ + QmlValueTypeReference ref = + qvariant_cast(object.data().toVariant()); + + if (!ref.object) + return; + + QVariant v = QmlScriptClass::toVariant(engine, value); + + ref.type->read(ref.object, ref.property); + QMetaProperty p = ref.type->metaObject()->property(id); + p.write(ref.type, v); + ref.type->write(ref.object, ref.property); +} + +///////////////////////////////////////////////////////////// /* The QmlObjectScriptClass handles property access for QObjects via QtScript. It is also used to provide a more useful API in @@ -914,13 +1027,7 @@ void QmlObjectScriptClass::setProperty(QScriptValue &object, QmlMetaProperty prop; prop.restore(id, obj); - QVariant v; - QObject *data = value.data().toQObject(); - if (data) { - v = QVariant::fromValue(data); - } else { - v = value.toVariant(); - } + QVariant v = QmlScriptClass::toVariant(engine, value); prop.write(v); scriptEngine->currentContext()->setActivationObject(oldact); diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index 47d2397..af561e7 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,7 @@ class QmlExpression; class QmlBasicScriptNodeCache; class QmlContextScriptClass; class QmlObjectScriptClass; +class QmlValueTypeScriptClass; class QScriptEngineDebugger; class QNetworkReply; class QNetworkAccessManager; @@ -120,6 +122,7 @@ public: QmlContextScriptClass *contextClass; QmlObjectScriptClass *objectClass; + QmlValueTypeScriptClass *valueTypeClass; QmlContext *setCurrentBindContext(QmlContext *); QStack activeContexts; @@ -167,6 +170,8 @@ public: quint32 getUniqueId() const { return uniqueId++; } + + QmlValueTypeFactory valueTypes; }; class QmlScriptClass : public QScriptClass @@ -187,6 +192,7 @@ public: QmlScriptClass(QmlEngine *); + static QVariant toVariant(QmlEngine *, const QScriptValue &); protected: QmlEngine *engine; }; @@ -230,6 +236,24 @@ public: const QScriptValue &value); }; +class QmlValueTypeScriptClass : public QmlScriptClass +{ +public: + QmlValueTypeScriptClass(QmlEngine *); + ~QmlValueTypeScriptClass(); + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, + uint id); + virtual void setProperty(QScriptValue &object, + const QScriptString &name, + uint id, + const QScriptValue &value); +}; + QT_END_NAMESPACE #endif // QMLENGINE_P_H diff --git a/src/declarative/qml/qmlinstruction.cpp b/src/declarative/qml/qmlinstruction.cpp index 83fb18b..761903c 100644 --- a/src/declarative/qml/qmlinstruction.cpp +++ b/src/declarative/qml/qmlinstruction.cpp @@ -176,12 +176,18 @@ void QmlCompiledData::dump(QmlInstruction *instr, int idx) case QmlInstruction::FetchObject: qWarning() << idx << "\t" << line << "\t" << "FETCH\t\t\t" << instr->fetch.property; break; + case QmlInstruction::FetchValueType: + qWarning() << idx << "\t" << line << "\t" << "FETCH_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type; + break; case QmlInstruction::PopFetchedObject: qWarning() << idx << "\t" << line << "\t" << "POP"; break; case QmlInstruction::PopQList: qWarning() << idx << "\t" << line << "\t" << "POP_QLIST"; break; + case QmlInstruction::PopValueType: + qWarning() << idx << "\t" << line << "\t" << "POP_VALUE\t\t" << instr->fetchValue.property << "\t" << instr->fetchValue.type; + break; case QmlInstruction::Defer: qWarning() << idx << "\t" << line << "\t" << "DEFER" << "\t\t" << instr->defer.deferCount; break; diff --git a/src/declarative/qml/qmlinstruction_p.h b/src/declarative/qml/qmlinstruction_p.h index a7221c0..dc118b6 100644 --- a/src/declarative/qml/qmlinstruction_p.h +++ b/src/declarative/qml/qmlinstruction_p.h @@ -139,6 +139,7 @@ public: FetchQmlList, /* fetchQmlList */ FetchQList, /* fetch */ FetchObject, /* fetch */ + FetchValueType, /* fetchValue */ // // Stack manipulation @@ -147,6 +148,7 @@ public: // PopQList - Remove a list from the list stack PopFetchedObject, PopQList, + PopValueType, /* fetchValue */ // // Deferred creation @@ -177,12 +179,13 @@ public: } setId; struct { int property; + int owner; } assignValueSource; struct { int property; int value; short context; - short category; + short owner; } assignBinding; struct { int property; @@ -190,6 +193,10 @@ public: struct { int property; int type; + } fetchValue; + struct { + int property; + int type; } fetchQmlList; struct { int castValue; diff --git a/src/declarative/qml/qmlmetaproperty.cpp b/src/declarative/qml/qmlmetaproperty.cpp index daa4242..60b4ac6 100644 --- a/src/declarative/qml/qmlmetaproperty.cpp +++ b/src/declarative/qml/qmlmetaproperty.cpp @@ -49,6 +49,8 @@ #include "qmlboundsignal_p.h" #include #include +#include +#include Q_DECLARE_METATYPE(QList); @@ -162,16 +164,13 @@ void QmlMetaProperty::initDefault(QObject *obj) \internal Creates a QmlMetaProperty for the property at index \a idx of \a obj. - - The QmlMetaProperty is assigned category \a cat. */ -QmlMetaProperty::QmlMetaProperty(QObject *obj, int idx, PropertyCategory cat, QmlContext *ctxt) +QmlMetaProperty::QmlMetaProperty(QObject *obj, int idx, QmlContext *ctxt) : d(new QmlMetaPropertyPrivate) { d->context = ctxt; d->object = obj; d->type = Property; - d->category = cat; QMetaPropertyEx p(obj->metaObject()->property(idx)); d->propType = p.propertyType; d->coreIdx = idx; @@ -309,36 +308,6 @@ QmlMetaPropertyPrivate::propertyCategory() const } /*! - Returns the property category of \a prop. -*/ -QmlMetaProperty::PropertyCategory -QmlMetaProperty::propertyCategory(const QMetaProperty &prop) -{ - if (prop.name()) { - int type = 0; - if (prop.type() == QVariant::LastType) - type = qMetaTypeId(); - else if (prop.type() == QVariant::UserType) - type = prop.userType(); - else - type = prop.type(); - - if (type == qMetaTypeId()) - return Bindable; - else if (QmlMetaType::isList(type)) - return List; - else if (QmlMetaType::isQmlList(type)) - return QmlList; - else if (QmlMetaType::isObject(type)) - return Object; - else - return Normal; - } else { - return InvalidProperty; - } -} - -/*! Returns the type name of the property, or 0 if the property has no type name. */ @@ -426,12 +395,15 @@ QObject *QmlMetaProperty::object() const QmlMetaProperty &QmlMetaProperty::operator=(const QmlMetaProperty &other) { d->name = other.d->name; - d->propType = other.d->propType; - d->type = other.d->type; d->signal = other.d->signal; + d->context = other.d->context; d->coreIdx = other.d->coreIdx; + d->valueTypeIdx = other.d->valueTypeIdx; + d->valueTypeId = other.d->valueTypeId; + d->type = other.d->type; d->attachedFunc = other.d->attachedFunc; d->object = other.d->object; + d->propType = other.d->propType; d->category = other.d->category; return *this; } @@ -615,10 +587,27 @@ QVariant QmlMetaProperty::read() const return sig->expression(); } } else if (type() & Property) { - if (type() & Attached) + if (type() & Attached) { return QVariant::fromValue(d->attachedObject()); - else + } else if(type() & ValueTypeProperty) { + QmlEnginePrivate *ep = d->context?static_cast(QObjectPrivate::get(d->context->engine())):0; + QmlValueType *valueType = 0; + if (ep) + valueType = ep->valueTypes[d->valueTypeId]; + else + valueType = QmlValueTypeFactory::valueType(d->valueTypeId); + Q_ASSERT(valueType); + + valueType->read(object(), d->coreIdx); + QVariant rv = + valueType->metaObject()->property(d->valueTypeIdx).read(valueType); + if (!ep) + delete valueType; + return rv; + + } else { return d->object->metaObject()->property(d->coreIdx).read(object()); + } } return QVariant(); } @@ -649,7 +638,33 @@ void QmlMetaPropertyPrivate::writeSignalProperty(const QVariant &value) void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) { + QObject *object = this->object; + int coreIdx = this->coreIdx; + + QmlValueType *writeBack = 0; + QObject *writeBackObj = 0; + int writeBackIdx = -1; + bool deleteWriteBack = false; + + if (type & QmlMetaProperty::ValueTypeProperty) { + QmlEnginePrivate *ep = context?static_cast(QObjectPrivate::get(context->engine())):0; + + if (ep) { + writeBack = ep->valueTypes[valueTypeId]; + } else { + writeBack = QmlValueTypeFactory::valueType(valueTypeId); + deleteWriteBack = true; + } + + writeBackObj = this->object; + writeBackIdx = this->coreIdx; + writeBack->read(writeBackObj, writeBackIdx); + object = writeBack; + coreIdx = valueTypeIdx; + } + QMetaProperty prop = object->metaObject()->property(coreIdx); + if (prop.isEnumType()) { QVariant v = value; if (value.type() == QVariant::Double) { //enum values come through the script engine as doubles @@ -659,6 +674,11 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) v.convert(QVariant::Int); } prop.write(object, v); + + if (writeBack) { + writeBack->write(writeBackObj, writeBackIdx); + if (deleteWriteBack) delete writeBack; + } return; } @@ -757,18 +777,18 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) } } else if (category == QmlMetaProperty::Normal) { + bool found = false; switch(t) { case QVariant::Double: { double d; - bool found = true; if (vt == QVariant::Int) { d = value.toInt(); + found = true; } else if (vt == QVariant::UInt) { d = value.toUInt(); - } else { - found = false; - } + found = true; + } if (found) { void *a[1]; @@ -776,7 +796,6 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - return; } } break; @@ -784,14 +803,13 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) case QVariant::Int: { int i; - bool found = true; if (vt == QVariant::Double) { i = (int)value.toDouble(); + found = true; } else if (vt == QVariant::UInt) { i = (int)value.toUInt(); - } else { - found = false; - } + found = true; + } if (found) { void *a[1]; @@ -799,7 +817,6 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - return; } } break; @@ -807,12 +824,10 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) case QVariant::String: { QString s; - bool found = true; if (vt == QVariant::ByteArray) { s = QLatin1String(value.toByteArray()); - } else { - found = false; - } + found = true; + } if (found) { void *a[1]; @@ -820,7 +835,6 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); - return; } } break; @@ -833,15 +847,20 @@ void QmlMetaPropertyPrivate::writeValueProperty(const QVariant &value) if (con) { QVariant v = con(value.toString()); prop.write(object, v); - return; + found = true; } } } break; } - prop.write(object, value); + if (!found) + prop.write(object, value); } + if (writeBack) { + writeBack->write(writeBackObj, writeBackIdx); + if (deleteWriteBack) delete writeBack; + } } /*! @@ -936,6 +955,8 @@ int QmlMetaProperty::coreIndex() const return d->coreIdx; } +Q_GLOBAL_STATIC(QmlValueTypeFactory, qmlValueTypes); + /*! Returns the property information serialized into a single integer. QmlMetaProperty uses the bottom 24 bits only. @@ -949,10 +970,35 @@ quint32 QmlMetaProperty::save() const rv = d->coreIdx; } - Q_ASSERT(rv <= 0xFFFF); - Q_ASSERT(type() <= 0xFF); - rv |= (type() << 16); + Q_ASSERT(rv <= 0x7FF); + Q_ASSERT(type() <= 0x3F); + Q_ASSERT(d->valueTypeIdx <= 0x7F); + + rv |= (type() << 18); + if (type() & ValueTypeProperty) + rv |= (d->valueTypeIdx << 11); + + return rv; +} + +quint32 QmlMetaPropertyPrivate::saveValueType(int core, int valueType) +{ + Q_ASSERT(core <= 0x7FF); + Q_ASSERT(valueType <= 0x7F); + quint32 rv = 0; + rv = (QmlMetaProperty::ValueTypeProperty | QmlMetaProperty::Property) << 18; + rv |= core; + rv |= valueType << 11; + return rv; +} + +quint32 QmlMetaPropertyPrivate::saveProperty(int core) +{ + Q_ASSERT(core <= 0x7FF); + quint32 rv = 0; + rv = (QmlMetaProperty::Property) << 18; + rv |= core; return rv; } @@ -962,17 +1008,34 @@ quint32 QmlMetaProperty::save() const to QmlMetaProperty::save(). Only the bottom 24-bits are used, the high bits can be set to any value. */ -void QmlMetaProperty::restore(quint32 id, QObject *obj) +void QmlMetaProperty::restore(quint32 id, QObject *obj, QmlContext *ctxt) { - *this = QmlMetaProperty(); d->object = obj; + d->context = ctxt; id &= 0xFFFFFF; - d->type = id >> 16; + d->type = id >> 18; id &= 0xFFFF; if (d->type & Attached) { d->attachedFunc = id; + } else if (d->type & ValueTypeProperty) { + int coreIdx = id & 0x7FF; + int valueTypeIdx = id >> 11; + + QMetaProperty p(obj->metaObject()->property(coreIdx)); + Q_ASSERT(p.type() < QVariant::UserType); + + QmlValueType *valueType = qmlValueTypes()->valueTypes[p.type()]; + + QMetaPropertyEx p2(valueType->metaObject()->property(valueTypeIdx)); + + d->name = QLatin1String(p2.name()); + d->propType = p2.propertyType; + d->coreIdx = coreIdx; + d->valueTypeIdx = valueTypeIdx; + d->valueTypeId = p.type(); + } else if (d->type & Property) { QMetaPropertyEx p(obj->metaObject()->property(id)); d->name = QLatin1String(p.name()); @@ -981,7 +1044,9 @@ void QmlMetaProperty::restore(quint32 id, QObject *obj) } else if (d->type & SignalProperty) { d->signal = obj->metaObject()->method(id); d->coreIdx = id; - } + } else { + *this = QmlMetaProperty(); + } } /*! @@ -1000,7 +1065,8 @@ QMetaMethod QmlMetaProperty::method() const the QmlMetaProperty(QObject*, QString) constructor, this static function will correctly handle dot properties. */ -QmlMetaProperty QmlMetaProperty::createProperty(QObject *obj, const QString &name) +QmlMetaProperty QmlMetaProperty::createProperty(QObject *obj, + const QString &name) { QStringList path = name.split(QLatin1Char('.')); @@ -1009,6 +1075,30 @@ QmlMetaProperty QmlMetaProperty::createProperty(QObject *obj, const QString &nam for (int jj = 0; jj < path.count() - 1; ++jj) { const QString &pathName = path.at(jj); QmlMetaProperty prop(object, pathName); + + if (jj == path.count() - 2 && + prop.propertyType() < QVariant::UserType && + qmlValueTypes()->valueTypes[prop.propertyType()]) { + // We're now at a value type property + QObject *typeObject = + qmlValueTypes()->valueTypes[prop.propertyType()]; + int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); + if (idx == -1) + return QmlMetaProperty(); + + QmlMetaProperty p; + p.d->name = pathName + QLatin1String(".") + path.last(); + p.d->context = 0; + p.d->coreIdx = prop.coreIndex(); + p.d->valueTypeIdx = idx; + p.d->valueTypeId = prop.propertyType(); + p.d->type = QmlMetaProperty::ValueTypeProperty | + QmlMetaProperty::Property; + p.d->object = obj; + p.d->propType = typeObject->metaObject()->property(idx).userType(); + return p; + } + QObject *objVal = QmlMetaType::toQObject(prop.read()); if (!objVal) return QmlMetaProperty(); diff --git a/src/declarative/qml/qmlmetaproperty.h b/src/declarative/qml/qmlmetaproperty.h index fdcf9be..2470d5d 100644 --- a/src/declarative/qml/qmlmetaproperty.h +++ b/src/declarative/qml/qmlmetaproperty.h @@ -57,6 +57,7 @@ class QStringList; class QVariant; struct QMetaObject; class QmlContext; +class QmlEngine; class QmlMetaPropertyPrivate; class Q_DECLARATIVE_EXPORT QmlMetaProperty @@ -78,7 +79,7 @@ public: QmlMetaProperty(QObject *, const QString &, QmlContext *); QmlMetaProperty(const QmlMetaProperty &); QmlMetaProperty &operator=(const QmlMetaProperty &); - QmlMetaProperty(QObject *, int, PropertyCategory = Unknown, QmlContext * = 0); + QmlMetaProperty(QObject *, int, QmlContext * = 0); ~QmlMetaProperty(); static QStringList properties(QObject *); @@ -93,7 +94,7 @@ public: bool connectNotifier(QObject *dest, int method) const; quint32 save() const; - void restore(quint32, QObject *); + void restore(quint32, QObject *, QmlContext * = 0); QMetaMethod method() const; @@ -101,7 +102,8 @@ public: Property = 0x01, SignalProperty = 0x02, Default = 0x08, - Attached = 0x10 }; + Attached = 0x10, + ValueTypeProperty = 0x20 }; Type type() const; bool isProperty() const; @@ -112,7 +114,6 @@ public: QObject *object() const; PropertyCategory propertyCategory() const; - static PropertyCategory propertyCategory(const QMetaProperty &); int propertyType() const; const char *propertyTypeName() const; diff --git a/src/declarative/qml/qmlmetaproperty_p.h b/src/declarative/qml/qmlmetaproperty_p.h index 69fd5c3..7efdb6e 100644 --- a/src/declarative/qml/qmlmetaproperty_p.h +++ b/src/declarative/qml/qmlmetaproperty_p.h @@ -62,18 +62,22 @@ class QmlMetaPropertyPrivate { public: QmlMetaPropertyPrivate() - : context(0), coreIdx(-1), type(QmlMetaProperty::Invalid), attachedFunc(-1), + : context(0), coreIdx(-1), valueTypeIdx(-1), valueTypeId(0), + type(QmlMetaProperty::Invalid), attachedFunc(-1), object(0), propType(-1), category(QmlMetaProperty::Unknown) {} QmlMetaPropertyPrivate(const QmlMetaPropertyPrivate &other) : name(other.name), signal(other.signal), context(other.context), - coreIdx(other.coreIdx), type(other.type), attachedFunc(other.attachedFunc), - object(other.object), propType(other.propType), - category(other.category) {} + coreIdx(other.coreIdx), valueTypeIdx(other.valueTypeIdx), + valueTypeId(other.valueTypeId), type(other.type), + attachedFunc(other.attachedFunc), object(other.object), + propType(other.propType), category(other.category) {} QString name; QMetaMethod signal; QmlContext *context; int coreIdx; + int valueTypeIdx; + int valueTypeId; uint type; int attachedFunc; QObject *object; @@ -89,6 +93,9 @@ public: void writeSignalProperty(const QVariant &); void writeValueProperty(const QVariant &); + + static quint32 saveValueType(int, int); + static quint32 saveProperty(int); }; QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlparser.cpp b/src/declarative/qml/qmlparser.cpp index 9eed3f1..2a67c92 100644 --- a/src/declarative/qml/qmlparser.cpp +++ b/src/declarative/qml/qmlparser.cpp @@ -78,6 +78,8 @@ QmlParser::Object::~Object() prop->release(); foreach(Property *prop, groupedProperties) prop->release(); + foreach(Property *prop, valueTypeProperties) + prop->release(); } const QMetaObject *Object::metaObject() const @@ -121,6 +123,12 @@ void QmlParser::Object::addGroupedProperty(Property *p) groupedProperties << p; } +void QmlParser::Object::addValueTypeProperty(Property *p) +{ + p->addref(); + valueTypeProperties << p; +} + Property *QmlParser::Object::getProperty(const QByteArray &name, bool create) { if (!properties.contains(name)) { diff --git a/src/declarative/qml/qmlparser_p.h b/src/declarative/qml/qmlparser_p.h index d23b4ea..927c9e6 100644 --- a/src/declarative/qml/qmlparser_p.h +++ b/src/declarative/qml/qmlparser_p.h @@ -147,10 +147,12 @@ namespace QmlParser void addSignalProperty(Property *); void addAttachedProperty(Property *); void addGroupedProperty(Property *); + void addValueTypeProperty(Property *); QList valueProperties; QList signalProperties; QList attachedProperties; QList groupedProperties; + QList valueTypeProperties; // The bytes to cast instances by to get to the QmlParserStatus // interface. -1 indicates the type doesn't support this interface. diff --git a/src/declarative/qml/qmlvaluetype.cpp b/src/declarative/qml/qmlvaluetype.cpp new file mode 100644 index 0000000..a01b4a7 --- /dev/null +++ b/src/declarative/qml/qmlvaluetype.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmlvaluetype_p.h" + +QT_BEGIN_NAMESPACE + +QmlValueTypeFactory::QmlValueTypeFactory() +{ + // ### Optimize + for (int ii = 0; ii < (QVariant::UserType - 1); ++ii) + valueTypes[ii] = valueType(ii); +} + +QmlValueTypeFactory::~QmlValueTypeFactory() +{ + for (int ii = 0; ii < (QVariant::UserType - 1); ++ii) + delete valueTypes[ii]; +} + +QmlValueType *QmlValueTypeFactory::valueType(int t) +{ + switch (t) { + case QVariant::Rect: + return new QmlRectValueType; + default: + return 0; + } +} + +QmlValueType::QmlValueType(QObject *parent) +: QObject(parent) +{ +} + +QmlRectValueType::QmlRectValueType(QObject *parent) +: QmlValueType(parent) +{ +} + +void QmlRectValueType::read(QObject *obj, int idx) +{ + void *a[] = { &rect, 0 }; + QMetaObject::metacall(obj, QMetaObject::ReadProperty, idx, a); +} + +void QmlRectValueType::write(QObject *obj, int idx) +{ + void *a[] = { &rect, 0 }; + QMetaObject::metacall(obj, QMetaObject::WriteProperty, idx, a); +} + +int QmlRectValueType::x() const +{ + return rect.x(); +} + +int QmlRectValueType::y() const +{ + return rect.y(); +} + +void QmlRectValueType::setX(int x) +{ + rect.moveLeft(x); +} + +void QmlRectValueType::setY(int y) +{ + rect.moveTop(y); +} + +int QmlRectValueType::width() const +{ + return rect.width(); +} + +int QmlRectValueType::height() const +{ + return rect.height(); +} + +void QmlRectValueType::setWidth(int w) +{ + rect.setWidth(w); +} + +void QmlRectValueType::setHeight(int h) +{ + rect.setHeight(h); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlvaluetype_p.h b/src/declarative/qml/qmlvaluetype_p.h new file mode 100644 index 0000000..5581df7 --- /dev/null +++ b/src/declarative/qml/qmlvaluetype_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMLVALUETYPE_P_H +#define QMLVALUETYPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QmlValueType : public QObject +{ + Q_OBJECT +public: + QmlValueType(QObject *parent = 0); + virtual void read(QObject *, int) = 0; + virtual void write(QObject *, int) = 0; +}; + +class QmlValueTypeFactory +{ +public: + QmlValueTypeFactory(); + ~QmlValueTypeFactory(); + static QmlValueType *valueType(int); + + QmlValueType *valueTypes[QVariant::UserType - 1]; + QmlValueType *operator[](int idx) const { return valueTypes[idx]; } +}; + +class QmlRectValueType : public QmlValueType +{ + Q_PROPERTY(int x READ x WRITE setX); + Q_PROPERTY(int y READ y WRITE setY); + Q_PROPERTY(int width READ width WRITE setWidth); + Q_PROPERTY(int height READ height WRITE setHeight); + Q_OBJECT +public: + QmlRectValueType(QObject *parent = 0); + + virtual void read(QObject *, int); + virtual void write(QObject *, int); + + int x() const; + int y() const; + void setX(int); + void setY(int); + + int width() const; + int height() const; + void setWidth(int); + void setHeight(int); + +private: + QRect rect; +}; +QT_END_NAMESPACE + +#endif // QMLVALUETYPE_P_H diff --git a/src/declarative/qml/qmlvme.cpp b/src/declarative/qml/qmlvme.cpp index 58b8689..ebf0a73 100644 --- a/src/declarative/qml/qmlvme.cpp +++ b/src/declarative/qml/qmlvme.cpp @@ -142,9 +142,8 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData QStack qliststack; - QStack pushedProperties; - vmeErrors.clear(); + QmlEnginePrivate *ep = ctxt->engine()->d_func(); for (int ii = start; !isError() && ii < (start + count); ++ii) { QmlInstruction &instr = comp->bytecode[ii]; @@ -533,12 +532,13 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData case QmlInstruction::StoreCompiledBinding: { - QObject *target = stack.top(); + QObject *target = + stack.at(stack.count() - 1 - instr.assignBinding.owner); QObject *context = stack.at(stack.count() - 1 - instr.assignBinding.context); - QmlMetaProperty mp(target, instr.assignBinding.property, - (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); + QmlMetaProperty mp; + mp.restore(instr.assignBinding.property, target, ctxt); QmlBinding *bind = new QmlBinding((void *)datas.at(instr.assignBinding.value).constData(), comp, context, ctxt, 0); bindValues.append(bind); @@ -553,12 +553,13 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData case QmlInstruction::StoreBinding: { - QObject *target = stack.top(); + QObject *target = + stack.at(stack.count() - 1 - instr.assignBinding.owner); QObject *context = stack.at(stack.count() - 1 - instr.assignBinding.context); - QmlMetaProperty mp(target, instr.assignBinding.property, - (QmlMetaProperty::PropertyCategory)instr.assignBinding.category); + QmlMetaProperty mp; + mp.restore(instr.assignBinding.property, target, ctxt); QmlBinding *bind = new QmlBinding(primitives.at(instr.assignBinding.value), context, ctxt); bindValues.append(bind); @@ -574,12 +575,14 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData case QmlInstruction::StoreValueSource: { - QObject *assign = stack.pop(); QmlPropertyValueSource *vs = - static_cast(assign); - QObject *target = stack.top(); + static_cast(stack.pop()); + QObject *target = + stack.at(stack.count() - 1 - instr.assignValueSource.owner); + QmlMetaProperty prop; + prop.restore(instr.assignValueSource.property, target, ctxt); vs->setParent(target); - vs->setTarget(QmlMetaProperty(target, instr.assignValueSource.property)); + vs->setTarget(prop); } break; @@ -762,6 +765,25 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData } break; + case QmlInstruction::FetchValueType: + { + QObject *target = stack.top(); + QmlValueType *valueHandler = + ep->valueTypes[instr.fetchValue.type]; + valueHandler->read(target, instr.fetchValue.property); + stack.push(valueHandler); + } + break; + + case QmlInstruction::PopValueType: + { + QmlValueType *valueHandler = + static_cast(stack.pop()); + QObject *target = stack.top(); + valueHandler->write(target, instr.fetchValue.property); + } + break; + default: qFatal("QmlCompiledData: Internal error - unknown instruction %d", instr.type); break; @@ -778,7 +800,6 @@ QObject *QmlVME::run(QStack &stack, QmlContext *ctxt, QmlCompiledData return 0; } - QmlEnginePrivate *ep = ctxt->engine()->d_func(); if (bindValues.count) ep->bindValues << bindValues; if (parserStatus.count) diff --git a/tests/auto/declarative/qmlparser/fakeDotProperty.errors.txt b/tests/auto/declarative/qmlparser/fakeDotProperty.errors.txt index 25f22ce..989d0e6 100644 --- a/tests/auto/declarative/qmlparser/fakeDotProperty.errors.txt +++ b/tests/auto/declarative/qmlparser/fakeDotProperty.errors.txt @@ -1 +1 @@ -2:5:Cannot nest non-QObject property "value" +2:5:Invalid property access diff --git a/tests/auto/declarative/qmlparser/testtypes.h b/tests/auto/declarative/qmlparser/testtypes.h index 7528331..34f3616 100644 --- a/tests/auto/declarative/qmlparser/testtypes.h +++ b/tests/auto/declarative/qmlparser/testtypes.h @@ -128,7 +128,8 @@ class MyTypeObject : public QObject Q_PROPERTY(QPointF pointFProperty READ pointFProperty WRITE setPointFProperty); Q_PROPERTY(QSize sizeProperty READ sizeProperty WRITE setSizeProperty); Q_PROPERTY(QSizeF sizeFProperty READ sizeFProperty WRITE setSizeFProperty); - Q_PROPERTY(QRect rectProperty READ rectProperty WRITE setRectProperty); + Q_PROPERTY(QRect rectProperty READ rectProperty WRITE setRectProperty NOTIFY rectPropertyChanged); + Q_PROPERTY(QRect rectProperty2 READ rectProperty2 WRITE setRectProperty2); Q_PROPERTY(QRectF rectFProperty READ rectFProperty WRITE setRectFProperty); Q_PROPERTY(bool boolProperty READ boolProperty WRITE setBoolProperty); Q_PROPERTY(QVariant variantProperty READ variantProperty WRITE setVariantProperty); @@ -290,6 +291,15 @@ public: } void setRectProperty(const QRect &v) { rectPropertyValue = v; + emit rectPropertyChanged(); + } + + QRect rectPropertyValue2; + QRect rectProperty2() const { + return rectPropertyValue2; + } + void setRectProperty2(const QRect &v) { + rectPropertyValue2 = v; } QRectF rectFPropertyValue; @@ -315,6 +325,11 @@ public: void setVariantProperty(const QVariant &v) { variantPropertyValue = v; } + + void doAction() { emit action(); } +signals: + void action(); + void rectPropertyChanged(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(MyTypeObject::MyFlags) QML_DECLARE_TYPE(MyTypeObject); diff --git a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp index f722ca3..eaa8267 100644 --- a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp +++ b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp @@ -44,6 +44,7 @@ private slots: void attachedProperties(); void dynamicObjects(); void customVariantTypes(); + void valueTypes(); // regression tests for crashes void crash1(); @@ -431,6 +432,31 @@ void tst_qmlparser::customVariantTypes() QCOMPARE(object->customType().a, 10); } +void tst_qmlparser::valueTypes() +{ + QmlComponent component(&engine, TEST_FILE("valueTypes.txt")); + MyTypeObject *object = qobject_cast(component.create()); + QVERIFY(object != 0); + + QCOMPARE(object->rectProperty(), QRect(10, 11, 12, 13)); + QCOMPARE(object->rectProperty2(), QRect(10, 11, 12, 13)); + QCOMPARE(object->intProperty(), 10); + object->doAction(); + QCOMPARE(object->rectProperty(), QRect(12, 11, 14, 13)); + QCOMPARE(object->rectProperty2(), QRect(12, 11, 14, 13)); + QCOMPARE(object->intProperty(), 12); + + QmlMetaProperty p = QmlMetaProperty::createProperty(object, "rectProperty.x"); + QCOMPARE(p.read(), QVariant(12)); + p.write(13); + QCOMPARE(p.read(), QVariant(13)); + + quint32 r = p.save(); + QmlMetaProperty p2; + p2.restore(r, object); + QCOMPARE(p2.read(), QVariant(13)); +} + void tst_qmlparser::crash1() { QmlComponent component(&engine, "Component {}"); diff --git a/tests/auto/declarative/qmlparser/valueTypes.txt b/tests/auto/declarative/qmlparser/valueTypes.txt new file mode 100644 index 0000000..0d670af --- /dev/null +++ b/tests/auto/declarative/qmlparser/valueTypes.txt @@ -0,0 +1,12 @@ +MyTypeObject { + rectProperty.x: 10 + rectProperty.y: 11 + rectProperty.width: rectProperty.x + 2 + rectProperty.height: 13 + + intProperty: rectProperty.x + + onAction: { var a = rectProperty; a.x = 12; } + + rectProperty2: rectProperty +} -- cgit v0.12