From 4350877d6deb58f36df24164c6edde3302a3f1a3 Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Wed, 1 Aug 2012 10:27:17 +1000 Subject: Permit value types with metatype IDs >= QMetaType::User Remove the assumption that value types must be types defined by Qt, having metatype IDs below QMetaType::User. Task-number: QTBUG-26352 Change-Id: Ib5a56ff2e7892e82adf17a3a1e7517a0c9fe0534 Reviewed-by: Michael Brasser --- src/qml/qml/qqmlabstractbinding.cpp | 8 +- src/qml/qml/qqmlabstractbinding_p.h | 3 +- src/qml/qml/qqmlcompiler.cpp | 33 +++--- src/qml/qml/qqmlengine_p.h | 2 - src/qml/qml/qqmlinstruction_p.h | 3 +- src/qml/qml/qqmlproperty.cpp | 110 +++++++------------ src/qml/qml/qqmlproperty_p.h | 2 +- src/qml/qml/qqmlpropertycache_p.h | 10 +- src/qml/qml/qqmlvaluetype.cpp | 89 +++++++++++++--- src/qml/qml/qqmlvaluetype_p.h | 22 +--- src/qml/qml/qqmlvaluetypeproxybinding.cpp | 2 +- src/qml/qml/qqmlvme.cpp | 28 ++--- src/qml/qml/qqmlvmemetaobject.cpp | 12 +-- src/qml/qml/qqmlvmemetaobject_p.h | 9 +- src/qml/qml/v4/qv4bindings.cpp | 35 +++--- src/qml/qml/v4/qv4bindings_p.h | 10 +- src/qml/qml/v8/qv8engine.cpp | 10 +- src/qml/qml/v8/qv8qobjectwrapper.cpp | 15 ++- src/qml/qml/v8/qv8valuetypewrapper.cpp | 5 +- .../qml/qqmlvaluetypeproviders/data/userType.qml | 88 +++++++++++++++ .../tst_qqmlvaluetypeproviders.cpp | 118 +++++++++++++++++++++ 21 files changed, 407 insertions(+), 207 deletions(-) create mode 100644 tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml diff --git a/src/qml/qml/qqmlabstractbinding.cpp b/src/qml/qml/qqmlabstractbinding.cpp index 1e5ce8d64a..f663d9a0e0 100644 --- a/src/qml/qml/qqmlabstractbinding.cpp +++ b/src/qml/qml/qqmlabstractbinding.cpp @@ -92,10 +92,10 @@ void QQmlAbstractBinding::addToObject() QQmlData *data = QQmlData::get(obj, true); - if (index & 0xFF000000) { + if (index & 0xFFFF0000) { // Value type - int coreIndex = index & 0xFFFFFF; + int coreIndex = index & 0x0000FFFF; // Find the value type proxy (if there is one) QQmlValueTypeProxyBinding *proxy = 0; @@ -141,11 +141,11 @@ void QQmlAbstractBinding::removeFromObject() QQmlData *data = QQmlData::get(obj, false); Q_ASSERT(data); - if (index & 0xFF000000) { + if (index & 0xFFFF0000) { // Find the value type binding QQmlAbstractBinding *vtbinding = data->bindings; - while (vtbinding->propertyIndex() != (index & 0xFFFFFF)) { + while (vtbinding->propertyIndex() != (index & 0x0000FFFF)) { vtbinding = vtbinding->nextBinding(); Q_ASSERT(vtbinding); } diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index b9f8ecda5d..ae92077add 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -87,8 +87,9 @@ public: // Should return the encoded property index for the binding. Should return this value // even if the binding is not enabled or added to an object. - // Encoding is: coreIndex | (valueTypeIndex << 24) + // Encoding is: coreIndex | (valueTypeIndex << 16) int propertyIndex() const { return vtable()->propertyIndex(this); } + // Should return the object for the binding. Should return this object even if the // binding is not enabled or added to the object. QObject *object() const { return vtable()->object(this); } diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index af8448001e..ef8e44f697 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -2108,8 +2108,8 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, Q_ASSERT(prop->index != -1); if (QQmlValueTypeFactory::isValueType(prop->type)) { - if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) { - + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(prop->type); + if (prop->type >= 0 && valueType) { if (!prop->values.isEmpty()) { // Only error if we are assigning values, and not e.g. a property interceptor for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) { @@ -2133,8 +2133,7 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, } } - COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type], - prop->value, obj, ctxt.incr())); + COMPILE_CHECK(buildValueTypeProperty(valueType, prop->value, obj, ctxt.incr())); // When building a value type where sub components are declared, this // code path is followed from buildProperty, even if there is a previous @@ -2154,11 +2153,9 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop, } obj->addValueTypeProperty(prop); - } else { COMPILE_EXCEPTION(prop, tr("Invalid grouped property access")); } - } else { // Load the nested property's meta type prop->value->metatype = enginePrivate->propertyCacheForType(prop->type); @@ -3243,6 +3240,7 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias reference. Unable to find id \"%1\"").arg(alias.at(0))); int propIdx = -1; + int propType = 0; int notifySignal = -1; int flags = 0; int type = 0; @@ -3254,7 +3252,7 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) if (alias.count() == 2 || alias.count() == 3) { QQmlPropertyData *property = this->property(idObject, alias.at(1)); - if (!property || property->coreIndex > 0xFFFF) + if (!property || property->coreIndex > 0x0000FFFF) COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); propIdx = property->coreIndex; @@ -3265,16 +3263,17 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) notifySignal = property->notifyIndex; if (alias.count() == 3) { - QQmlValueType *valueType = enginePrivate->valueTypes[type]; // XXX threadsafe? + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); if (!valueType) COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - propIdx |= ((unsigned int)type) << 24; + propType = type; + int valueTypeIndex = valueType->metaObject()->indexOfProperty(alias.at(2).toUtf8().constData()); if (valueTypeIndex == -1) COMPILE_EXCEPTION(p->defaultValue, tr("Invalid alias location")); - Q_ASSERT(valueTypeIndex <= 0xFF); + Q_ASSERT(valueTypeIndex <= 0x0000FFFF); propIdx |= (valueTypeIndex << 16); if (valueType->metaObject()->property(valueTypeIndex).isEnumType()) @@ -3309,7 +3308,7 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj) propertyFlags |= QQmlPropertyData::IsQObjectDerived; } - QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, flags, notifySignal }; + QQmlVMEMetaData::AliasData aliasData = { idObject->idIndex, propIdx, propType, flags, notifySignal }; typedef QQmlVMEMetaData VMD; VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); @@ -3498,12 +3497,12 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding, store.owner = js.bindingContext.owner; store.isAlias = prop->isAlias; if (valueTypeProperty) { - store.property = (valueTypeProperty->index & 0xFFFF) | - ((valueTypeProperty->type & 0xFF)) << 16 | - ((prop->index & 0xFF) << 24); + store.property = ((prop->index << 16) | valueTypeProperty->index); + store.propType = valueTypeProperty->type; store.isRoot = (compileState->root == valueTypeProperty->parent); } else { store.property = prop->index; + store.propType = 0; store.isRoot = (compileState->root == obj); } store.line = binding->location.start.line; @@ -3592,9 +3591,9 @@ QQmlPropertyData QQmlCompiler::genValueTypeData(QQmlScript::Property *valueTypeProp, QQmlScript::Property *prop) { - typedef QQmlPropertyPrivate QDPP; - return QDPP::saveValueType(prop->core, enginePrivate->valueTypes[prop->type]->metaObject(), - valueTypeProp->index, engine); + QQmlValueType *vt = QQmlValueTypeFactory::valueType(prop->type); + Q_ASSERT(vt); + return QQmlPropertyPrivate::saveValueType(prop->core, vt->metaObject(), valueTypeProp->index, engine); } bool QQmlCompiler::completeComponentBuild() diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index f62e99b3ce..fb7109f507 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -200,8 +200,6 @@ public: return uniqueId++; } - QQmlValueTypeFactory valueTypes; - // Unfortunate workaround to avoid a circular dependency between // qqmlengine_p.h and qqmlincubator_p.h struct Incubator { diff --git a/src/qml/qml/qqmlinstruction_p.h b/src/qml/qml/qqmlinstruction_p.h index 5cd06c2e3d..db196276d5 100644 --- a/src/qml/qml/qqmlinstruction_p.h +++ b/src/qml/qml/qqmlinstruction_p.h @@ -238,7 +238,8 @@ union QQmlInstruction }; struct instr_assignV4Binding { QML_INSTR_HEADER - unsigned int property; + int property; // ((value type sub-property index << 16) | property index) + int propType; int value; int fallbackValue; short context; diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index c034dbe59b..a239b458f3 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -224,8 +224,6 @@ QQmlProperty::QQmlProperty(QObject *obj, const QString &name, QQmlEngine *engine if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } } -Q_GLOBAL_STATIC(QQmlValueTypeFactory, qmlValueTypes); - QQmlPropertyPrivate::QQmlPropertyPrivate() : context(0), engine(0), object(0), isNameCached(false) { @@ -293,9 +291,8 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) return; // Not an object property if (ii == (path.count() - 2) && QQmlValueTypeFactory::isValueType(property->propType)) { - // We're now at a value type property. We can use a global valuetypes array as we - // never actually use the objects, just look up their properties. - QObject *typeObject = (*qmlValueTypes())[property->propType]; + // We're now at a value type property + QObject *typeObject = QQmlValueTypeFactory::valueType(property->propType); if (!typeObject) return; // Not a value type int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); @@ -303,16 +300,14 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QMetaProperty vtProp = typeObject->metaObject()->property(idx); - typedef QQmlPropertyData PCD; - - Q_ASSERT(PCD::flagsForProperty(vtProp) <= PCD::ValueTypeFlagMask); - Q_ASSERT(vtProp.userType() <= 0xFF); - Q_ASSERT(idx <= 0xFF); + Q_ASSERT(QQmlPropertyData::flagsForProperty(vtProp) <= QQmlPropertyData::ValueTypeFlagMask); + Q_ASSERT(vtProp.userType() <= 0x0000FFFF); + Q_ASSERT(idx <= 0x0000FFFF); object = currentObject; core = *property; - core.setFlags(core.getFlags() | PCD::IsValueTypeVirtual); - core.valueTypeFlags = PCD::flagsForProperty(vtProp); + core.setFlags(core.getFlags() | QQmlPropertyData::IsValueTypeVirtual); + core.valueTypeFlags = QQmlPropertyData::flagsForProperty(vtProp); core.valueTypePropType = vtProp.userType(); core.valueTypeCoreIndex = idx; @@ -462,9 +457,9 @@ QQmlPropertyPrivate::propertyTypeCategory() const return QQmlProperty::List; else return QQmlProperty::Normal; - } else { - return QQmlProperty::InvalidCategory; } + + return QQmlProperty::InvalidCategory; } /*! @@ -476,18 +471,9 @@ const char *QQmlProperty::propertyTypeName() const if (!d) return 0; if (d->isValueType()) { - - QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0; - QQmlValueType *valueType = 0; - if (ep) valueType = ep->valueTypes[d->core.propType]; - else valueType = QQmlValueTypeFactory::valueType(d->core.propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType); Q_ASSERT(valueType); - - const char *rv = valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName(); - - if (!ep) delete valueType; - - return rv; + return valueType->metaObject()->property(d->core.valueTypeCoreIndex).typeName(); } else if (d->object && type() & Property && d->core.isValid()) { return d->object->metaObject()->property(d->core.coreIndex).typeName(); } else { @@ -664,17 +650,12 @@ QString QQmlProperty::name() const } else if (d->isValueType()) { QString rv = d->core.name(d->object) + QLatin1Char('.'); - QQmlEnginePrivate *ep = d->engine?QQmlEnginePrivate::get(d->engine):0; - QQmlValueType *valueType = 0; - if (ep) valueType = ep->valueTypes[d->core.propType]; - else valueType = QQmlValueTypeFactory::valueType(d->core.propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->core.propType); Q_ASSERT(valueType); const char *vtName = valueType->metaObject()->property(d->core.valueTypeCoreIndex).name(); rv += QString::fromUtf8(vtName); - if (!ep) delete valueType; - d->nameCache = rv; } else if (type() & SignalProperty) { QString name = QLatin1String("on") + d->core.name(d->object); @@ -761,8 +742,8 @@ QQmlPropertyPrivate::setBinding(const QQmlProperty &that, QObject *object = newBinding->object(); int pi = newBinding->propertyIndex(); - int core = pi & 0xFFFFFF; - int vt = (pi & 0xFF000000)?(pi >> 24):-1; + int core = pi & 0x0000FFFF; + int vt = (pi & 0xFFFF0000)?(pi >> 16):-1; return setBinding(object, core, vt, newBinding, flags); } else { @@ -803,7 +784,7 @@ QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) if (binding && valueTypeIndex != -1) { if (binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) { - int index = coreIndex | (valueTypeIndex << 24); + int index = coreIndex | (valueTypeIndex << 16); binding = static_cast(binding)->binding(index); } } @@ -814,9 +795,8 @@ QQmlPropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, QObject **targetObject, int *targetBindingIndex) { - int coreIndex = bindingIndex & 0xFFFFFF; - int valueTypeIndex = bindingIndex >> 24; - if (valueTypeIndex == 0) valueTypeIndex = -1; + int coreIndex = bindingIndex & 0x0000FFFF; + int valueTypeIndex = (bindingIndex & 0xFFFF0000)?(bindingIndex >> 16):-1; QQmlData *data = QQmlData::get(object, false); if (data) { @@ -832,9 +812,9 @@ void QQmlPropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, int aBindingIndex = aCoreIndex; if (aValueTypeIndex != -1) - aBindingIndex |= aValueTypeIndex << 24; + aBindingIndex |= aValueTypeIndex << 16; else if (valueTypeIndex != -1) - aBindingIndex |= valueTypeIndex << 24; + aBindingIndex |= valueTypeIndex << 16; findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); return; @@ -881,7 +861,7 @@ QQmlPropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeInd int index = coreIndex; if (valueTypeIndex != -1) - index |= (valueTypeIndex << 24); + index |= (valueTypeIndex << 16); if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) binding = static_cast(binding)->binding(index); @@ -940,7 +920,7 @@ QQmlPropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valu int index = coreIndex; if (valueTypeIndex != -1) - index |= (valueTypeIndex << 24); + index |= (valueTypeIndex << 16); if (binding && valueTypeIndex != -1 && binding->bindingType() == QQmlAbstractBinding::ValueTypeProxy) binding = static_cast(binding)->binding(index); @@ -980,8 +960,8 @@ QQmlAbstractBinding *QQmlPropertyPrivate::activateSharedBinding(QQmlContextData QObject *object = newBinding->object(); int pi = newBinding->propertyIndex(); - int core = pi & 0xFFFFFF; - int vt = (pi & 0xFF000000)?(pi >> 24):-1; + int core = pi & 0x0000FFFF; + int vt = (pi & 0xFFFF0000)?(pi >> 16):-1; return setBinding(object, core, vt, newBinding, flags); } @@ -1137,18 +1117,10 @@ QVariant QQmlPropertyPrivate::readValueProperty() { if (isValueType()) { - QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; - QQmlValueType *valueType = 0; - if (ep) valueType = ep->valueTypes[core.propType]; - else valueType = QQmlValueTypeFactory::valueType(core.propType); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(core.propType); Q_ASSERT(valueType); - valueType->read(object, core.coreIndex); - - QVariant rv = valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType); - - if (!ep) delete valueType; - return rv; + return valueType->metaObject()->property(core.valueTypeCoreIndex).read(valueType); } else if (core.isQList()) { @@ -1262,14 +1234,14 @@ bool QQmlPropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, bool QQmlPropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) { - return writeValueProperty(object, engine, core, value, effectiveContext(), flags); + return writeValueProperty(object, core, value, effectiveContext(), flags); } bool -QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlEngine *engine, - const QQmlPropertyData &core, - const QVariant &value, - QQmlContextData *context, WriteFlags flags) +QQmlPropertyPrivate::writeValueProperty(QObject *object, + const QQmlPropertyData &core, + const QVariant &value, + QQmlContextData *context, WriteFlags flags) { // Remove any existing bindings on this property if (!(flags & DontRemoveBinding) && object) { @@ -1281,15 +1253,8 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlEngine *engine, bool rv = false; if (core.isValueTypeVirtual()) { - QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; - - QQmlValueType *writeBack = 0; - if (ep) { - writeBack = ep->valueTypes[core.propType]; - } else { - writeBack = QQmlValueTypeFactory::valueType(core.propType); - } + QQmlValueType *writeBack = QQmlValueTypeFactory::valueType(core.propType); writeBack->read(object, core.coreIndex); QQmlPropertyData data = core; @@ -1300,7 +1265,6 @@ QQmlPropertyPrivate::writeValueProperty(QObject *object, QQmlEngine *engine, rv = write(writeBack, data, value, context, flags); writeBack->write(object, core.coreIndex, flags); - if (!ep) delete writeBack; } else { @@ -1603,14 +1567,14 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object, void *args[] = { 0 }; QMetaObject::metacall(object, QMetaObject::ResetProperty, core.coreIndex, args); } else if (isUndefined && type == qMetaTypeId()) { - writeValueProperty(object, engine, core, QVariant(), context, flags); + writeValueProperty(object, core, QVariant(), context, flags); } else if (type == qMetaTypeId()) { if (!result.IsEmpty() && result->IsFunction() && !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) { expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); return false; } - writeValueProperty(object, engine, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags); + writeValueProperty(object, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags); } else if (isUndefined) { QString errorStr = QLatin1String("Unable to assign [undefined] to "); if (!QMetaType::typeName(type)) @@ -1625,7 +1589,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object, else expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var.")); return false; - } else if (!writeValueProperty(object, engine, core, value, context, flags)) { + } else if (!writeValueProperty(object, core, value, context, flags)) { if (watcher.wasDeleted()) return true; @@ -1842,8 +1806,8 @@ int QQmlPropertyPrivate::valueTypeCoreIndex(const QQmlProperty &that) } /*! - Returns the "property index" for use in bindings. The top 8 bits are the value type - offset, and 0 otherwise. The bottom 24-bits are the regular property index. + Returns the "property index" for use in bindings. The top 16 bits are the value type + offset, and 0 otherwise. The bottom 16 bits are the regular property index. */ int QQmlPropertyPrivate::bindingIndex(const QQmlProperty &that) { @@ -1856,7 +1820,7 @@ int QQmlPropertyPrivate::bindingIndex(const QQmlPropertyData &that) { int rv = that.coreIndex; if (rv != -1 && that.isValueTypeVirtual()) - rv = rv | (that.valueTypeCoreIndex << 24); + rv = rv | (that.valueTypeCoreIndex << 16); return rv; } diff --git a/src/qml/qml/qqmlproperty_p.h b/src/qml/qml/qqmlproperty_p.h index 0131e7e1a4..501ab33112 100644 --- a/src/qml/qml/qqmlproperty_p.h +++ b/src/qml/qml/qqmlproperty_p.h @@ -104,7 +104,7 @@ public: static QQmlMetaObject rawMetaObjectForType(QQmlEnginePrivate *, int); static bool writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags); - static bool writeValueProperty(QObject *, QQmlEngine *, + static bool writeValueProperty(QObject *, const QQmlPropertyData &, const QVariant &, QQmlContextData *, WriteFlags flags = 0); diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index b62d34cefb..99c667127b 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -165,7 +165,7 @@ public: inline int getValueTypeCoreIndex() const; // Returns the "encoded" index for use with bindings. Encoding is: - // coreIndex | (valueTypeCoreIndex << 24) + // coreIndex | (valueTypeCoreIndex << 16) inline int encodedIndex() const; union { @@ -189,10 +189,10 @@ public: struct { // When IsValueTypeVirtual quint16 valueTypeFlags; // flags of the access property on the value type proxy // object - quint8 valueTypePropType; // The QVariant::Type of access property on the value - // type proxy object - quint8 valueTypeCoreIndex; // The prop index of the access property on the value + quint16 valueTypePropType; // The QVariant::Type of access property on the value // type proxy object + quint16 valueTypeCoreIndex; // The prop index of the access property on the value + // type proxy object }; struct { // When !IsValueTypeVirtual @@ -433,7 +433,7 @@ int QQmlPropertyRawData::getValueTypeCoreIndex() const int QQmlPropertyRawData::encodedIndex() const { - return isValueTypeVirtual()?(coreIndex | (valueTypeCoreIndex << 24)):coreIndex; + return isValueTypeVirtual()?(coreIndex | (valueTypeCoreIndex << 16)):coreIndex; } QQmlPropertyData * diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index 165024adfe..9be48ae9c7 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -47,21 +47,40 @@ QT_BEGIN_NAMESPACE -QQmlValueTypeFactory::QQmlValueTypeFactory() +namespace { + +struct QQmlValueTypeFactoryImpl +{ + QQmlValueTypeFactoryImpl(); + ~QQmlValueTypeFactoryImpl(); + + bool isValueType(int idx); + + QQmlValueType *createValueType(int); + QQmlValueType *valueType(int); + + QQmlValueType *valueTypes[QVariant::UserType]; + QHash userTypes; + QMutex mutex; +}; + +QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl() { for (unsigned int ii = 0; ii < QVariant::UserType; ++ii) valueTypes[ii] = 0; } -QQmlValueTypeFactory::~QQmlValueTypeFactory() +QQmlValueTypeFactoryImpl::~QQmlValueTypeFactoryImpl() { - for (unsigned int ii = 0; ii < QVariant::UserType; ++ii) - delete valueTypes[ii]; + qDeleteAll(valueTypes, valueTypes + QVariant::UserType); + qDeleteAll(userTypes); } -bool QQmlValueTypeFactory::isValueType(int idx) +bool QQmlValueTypeFactoryImpl::isValueType(int idx) { - if ((uint)idx < QVariant::UserType + if (idx >= QVariant::UserType) { + return (valueType(idx) != 0); + } else if (idx >= 0 && idx != QVariant::StringList && idx != QMetaType::QObjectStar && idx != QMetaType::QWidgetStar @@ -69,15 +88,11 @@ bool QQmlValueTypeFactory::isValueType(int idx) && idx != QMetaType::QVariant) { return true; } - return false; -} -void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, int versionMinor) -{ - qmlRegisterValueTypeEnums(uri, versionMajor, versionMinor, "Easing"); + return false; } -QQmlValueType *QQmlValueTypeFactory::valueType(int t) +QQmlValueType *QQmlValueTypeFactoryImpl::createValueType(int t) { QQmlValueType *rv = 0; @@ -112,12 +127,60 @@ QQmlValueType *QQmlValueTypeFactory::valueType(int t) return rv; } +QQmlValueType *QQmlValueTypeFactoryImpl::valueType(int idx) +{ + if (idx >= (int)QVariant::UserType) { + // Protect the hash with a mutex + mutex.lock(); + + QHash::iterator it = userTypes.find(idx); + if (it == userTypes.end()) { + it = userTypes.insert(idx, createValueType(idx)); + } + + mutex.unlock(); + return *it; + } + + QQmlValueType *rv = valueTypes[idx]; + if (!rv) { + // No need for mutex protection - the most we can lose is a valueType instance + + // TODO: Investigate the performance/memory characteristics of + // removing the preallocated array + if ((rv = createValueType(idx))) { + valueTypes[idx] = rv; + } + } + + return rv; +} + +} + +Q_GLOBAL_STATIC(QQmlValueTypeFactoryImpl, factoryImpl); + +bool QQmlValueTypeFactory::isValueType(int idx) +{ + return factoryImpl()->isValueType(idx); +} + +QQmlValueType *QQmlValueTypeFactory::valueType(int idx) +{ + return factoryImpl()->valueType(idx); +} + +void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, int versionMinor) +{ + qmlRegisterValueTypeEnums(uri, versionMajor, versionMinor, "Easing"); +} + + QQmlValueType::QQmlValueType(int userType, QObject *parent) : QObject(parent), m_userType(userType) { } - QQmlPointFValueType::QQmlPointFValueType(QObject *parent) : QQmlValueTypeBase(QMetaType::QPointF, parent) { diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 153037b248..dd3c9196d3 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -161,30 +161,10 @@ protected: class Q_QML_PRIVATE_EXPORT QQmlValueTypeFactory { public: - QQmlValueTypeFactory(); - ~QQmlValueTypeFactory(); static bool isValueType(int); - static QQmlValueType *valueType(int); + static QQmlValueType *valueType(int idx); static void registerValueTypes(const char *uri, int versionMajor, int versionMinor); - - QQmlValueType *operator[](int idx) const { - if (idx >= (int)QVariant::UserType) - return 0; - - QQmlValueType *rv = valueTypes[idx]; - if (!rv) { - // Table update is not thread-safe, but the potential for leaks is - // so small that the cost of protection is unwarranted - if ((rv = valueType(idx))) { - valueTypes[idx] = rv; - } - } - return rv; - } - -private: - mutable QQmlValueType *valueTypes[QVariant::UserType]; }; class Q_QML_PRIVATE_EXPORT QQmlPointFValueType : public QQmlValueTypeBase diff --git a/src/qml/qml/qqmlvaluetypeproxybinding.cpp b/src/qml/qml/qqmlvaluetypeproxybinding.cpp index f8d54a6815..09e5b27c02 100644 --- a/src/qml/qml/qqmlvaluetypeproxybinding.cpp +++ b/src/qml/qml/qqmlvaluetypeproxybinding.cpp @@ -131,7 +131,7 @@ void QQmlValueTypeProxyBinding::removeBindings(quint32 mask) QQmlAbstractBinding *lastBinding = 0; while (binding) { - if (mask & (1 << (binding->propertyIndex() >> 24))) { + if (mask & (1 << (binding->propertyIndex() >> 16))) { QQmlAbstractBinding *remove = binding; binding = remove->nextBinding(); diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index f764b60701..60e911d935 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -203,7 +203,7 @@ inline bool fastHasBinding(QObject *o, int index) { QQmlData *ddata = static_cast(QObjectPrivate::get(o)->declarativeData); - index &= 0xFFFFFF; // To handle value types + index &= 0x0000FFFF; // To handle value types return ddata && (ddata->bindingBitsSize > index) && (ddata->bindingBits[index / 32] & (1 << (index % 32))); @@ -211,9 +211,8 @@ inline bool fastHasBinding(QObject *o, int index) static void removeBindingOnProperty(QObject *o, int index) { - int coreIndex = index & 0xFFFFFF; - int valueTypeIndex = index & 0xFF000000; - if (!valueTypeIndex) valueTypeIndex = -1; + int coreIndex = index & 0x0000FFFF; + int valueTypeIndex = (index & 0xFFFF0000 ? index >> 16 : -1); QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); if (binding) binding->destroy(); @@ -842,29 +841,29 @@ QObject *QQmlVME::run(QList *errors, QObject *scope = objects.at(objects.count() - 1 - instr.context); - int property = instr.property; - if (instr.isRoot && BINDINGSKIPLIST.testBit(property & 0xFFFF)) + int propertyIdx = (instr.property & 0x0000FFFF); + + if (instr.isRoot && BINDINGSKIPLIST.testBit(propertyIdx)) QML_NEXT_INSTR(StoreV4Binding); QQmlAbstractBinding *binding = - CTXT->v4bindings->configBinding(instr.value, instr.fallbackValue, target, scope, property, - instr.line, instr.column); + CTXT->v4bindings->configBinding(instr.value, instr.fallbackValue, target, scope, instr.property, + instr.propType, instr.line, instr.column); bindValues.push(binding); binding->m_mePtr = &bindValues.top(); if (instr.isAlias) { - int valueTypeIndex = (property & 0x00FF0000) ? (property >> 24) : -1; QQmlAbstractBinding *old = QQmlPropertyPrivate::setBindingNoEnable(target, - property & 0xFFFF, - valueTypeIndex, + propertyIdx, + instr.propType ? (instr.property >> 16) : -1, binding); if (old) { old->destroy(); } } else { - Q_ASSERT(binding->propertyIndex() == (property & 0xFF00FFFF)); + Q_ASSERT(binding->propertyIndex() == instr.property); Q_ASSERT(binding->object() == target); - CLEAN_PROPERTY(target, property & 0xFF00FFFF); + CLEAN_PROPERTY(target, instr.property); binding->addToObject(); } @@ -1054,7 +1053,8 @@ QObject *QQmlVME::run(QList *errors, } } - QQmlValueType *valueHandler = ep->valueTypes[instr.type]; + QQmlValueType *valueHandler = QQmlValueTypeFactory::valueType(instr.type); + Q_ASSERT(valueHandler); valueHandler->read(target, instr.property); objects.push(valueHandler); QML_END_INSTR(FetchValueType) diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index ce574875e3..902a6073b8 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -627,10 +627,7 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) if (type != QVariant::Invalid) { if (valueIndex != -1) { - QQmlEnginePrivate *ep = ctxt?QQmlEnginePrivate::get(ctxt->engine):0; - QQmlValueType *valueType = 0; - if (ep) valueType = ep->valueTypes[type]; - else valueType = QQmlValueTypeFactory::valueType(type); + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type); Q_ASSERT(valueType); // @@ -683,9 +680,6 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) updated = true; } - if (!ep) - delete valueType; - if (updated) return -1; } else { @@ -880,9 +874,7 @@ int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) if (d->isValueTypeAlias()) { // Value type property - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); - - QQmlValueType *valueType = ep->valueTypes[d->valueType()]; + QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType()); Q_ASSERT(valueType); valueType->read(target, d->propertyIndex()); diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index 5751989b0c..4216e09733 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -90,6 +90,7 @@ struct QQmlVMEMetaData struct AliasData { int contextIdx; int propertyIdx; + int propType; int flags; int notifySignal; @@ -97,19 +98,19 @@ struct QQmlVMEMetaData return propertyIdx == -1; } bool isPropertyAlias() const { - return !isObjectAlias() && !(propertyIdx & 0xFF000000); + return !isObjectAlias() && !(propertyIdx & 0xFFFF0000); } bool isValueTypeAlias() const { - return !isObjectAlias() && (propertyIdx & 0xFF000000); + return !isObjectAlias() && (propertyIdx & 0xFFFF0000); } int propertyIndex() const { return propertyIdx & 0x0000FFFF; } int valueTypeIndex() const { - return (propertyIdx & 0x00FF0000) >> 16; + return (propertyIdx & 0xFFFF0000) >> 16; } int valueType() const { - return ((unsigned int)propertyIdx) >> 24; + return (propertyIdx & 0xFFFF0000) ? propType : 0; } }; diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index 9b74c2aa72..02f2bfc863 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -298,15 +298,17 @@ QV4Bindings::~QV4Bindings() delete [] subscriptions; subscriptions = 0; } -QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target, - QObject *scope, int property, - int line, int column) +QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target, QObject *scope, + int property, int propType, int line, int column) { + Q_ASSERT(propType <= std::numeric_limits::max()); + Binding *rv = bindings + index; rv->index = index; rv->fallbackIndex = fallbackIndex; rv->property = property; + rv->propType = propType; rv->target = target; rv->scope = scope; rv->line = line; @@ -352,8 +354,7 @@ int QV4Bindings::Binding::propertyIndex(const QQmlAbstractBinding *_This) const QV4Bindings::Binding *This = static_cast(_This); if (This->target.hasValue()) return This->target.constValue()->targetProperty; - //mask out the type information set for value types - else return This->property & 0xFF00FFFF; + else return This->property; } QObject *QV4Bindings::Binding::object(const QQmlAbstractBinding *_This) @@ -421,15 +422,13 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) if (binding->updating) { QString name; - if (binding->property & 0xFFFF0000) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - - QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + if (binding->propType) { + QQmlValueType *vt = QQmlValueTypeFactory::valueType(binding->propType); Q_ASSERT(vt); - name = QLatin1String(binding->target->metaObject()->property(binding->property & 0xFFFF).name()); + name = QLatin1String(binding->target->metaObject()->property(binding->property & 0x0000FFFF).name()); name.append(QLatin1Char('.')); - name.append(QLatin1String(vt->metaObject()->property(binding->property >> 24).name())); + name.append(QLatin1String(vt->metaObject()->property(binding->property >> 16).name())); } else { name = QLatin1String(binding->target->metaObject()->property(binding->property).name()); } @@ -441,18 +440,16 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) bool *inv = (binding->fallbackIndex != -1) ? &invalidated : 0; binding->updating = true; - if (binding->property & 0xFFFF0000) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - - QQmlValueType *vt = ep->valueTypes[(binding->property >> 16) & 0xFF]; + if (binding->propType) { + QQmlValueType *vt = QQmlValueTypeFactory::valueType(binding->propType); Q_ASSERT(vt); - vt->read(*binding->target, binding->property & 0xFFFF); + vt->read(*binding->target, binding->property & 0x0000FFFF); QObject *target = vt; run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv); if (!invalidated) { - vt->write(*binding->target, binding->property & 0xFFFF, flags); + vt->write(*binding->target, binding->property & 0x0000FFFF, flags); } } else { QQmlData *data = QQmlData::get(*binding->target); @@ -1525,7 +1522,7 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); QV8Engine *v8engine = ep->v8engine(); - QQmlValueType *vt = ep->valueTypes[QMetaType::QColor]; + QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor); v8::HandleScope handle_scope; v8::Context::Scope scope(v8engine->context()); new (output.getjsvalueptr()) QJSValue(v8engine->scriptValueFromInternal( @@ -1583,7 +1580,7 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); - QQmlValueType *vt = ep->valueTypes[QMetaType::QColor]; + QQmlValueType *vt = QQmlValueTypeFactory::valueType(QMetaType::QColor); new (output.gethandleptr()) v8::Handle(ep->v8engine()->valueTypeWrapper()->newValueType(tmp, vt)); V8HANDLE_REGISTER(instr->unaryop.output); } diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h index 0c92cc4b45..dd63f6d9f5 100644 --- a/src/qml/qml/v4/qv4bindings_p.h +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -72,8 +72,8 @@ public: virtual ~QV4Bindings(); QQmlAbstractBinding *configBinding(int index, int fallbackIndex, QObject *target, - QObject *scope, int property, - int line, int column); + QObject *scope, int property, int propType, + int line, int column); #ifdef QML_THREADED_INTERPRETER static void **getDecodeInstrTable(); @@ -81,7 +81,7 @@ public: struct Binding : public QQmlAbstractBinding, public QQmlDelayedError { Binding() : QQmlAbstractBinding(V4), index(-1), fallbackIndex(-1), enabled(false), - updating(0), property(0), scope(0), target(0), executedBlocks(0), parent(0) {} + updating(0), property(0), propType(0), scope(0), target(0), executedBlocks(0), parent(0) {} // Inherited from QQmlAbstractBinding static void destroy(QQmlAbstractBinding *); @@ -101,9 +101,11 @@ public: bool enabled:1; bool updating:1; - // Encoding of property is coreIndex | (propType << 16) | (valueTypeIndex << 24) + // Encoding of property is: coreIndex | (valueTypeIndex << 16) // propType and valueTypeIndex are only set if the property is a value type property int property; + quint16 propType; + QObject *scope; int line; int column; diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 7eabd96745..7972e85315 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -397,11 +397,8 @@ v8::Handle QV8Engine::fromVariant(const QVariant &variant) break; } - if (m_engine) { - if (QQmlValueType *vt = QQmlEnginePrivate::get(m_engine)->valueTypes[type]) - return m_valueTypeWrapper.newValueType(variant, vt); - } - + if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type)) + return m_valueTypeWrapper.newValueType(variant, vt); } else { if (type == qMetaTypeId()) { typedef QQmlListReferencePrivate QDLRP; @@ -435,6 +432,9 @@ v8::Handle QV8Engine::fromVariant(const QVariant &variant) v8::Handle retn = m_sequenceWrapper.fromVariant(variant, &succeeded); if (succeeded) return retn; + + if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type)) + return m_valueTypeWrapper.newValueType(variant, vt); } // XXX TODO: To be compatible, we still need to handle: diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp index bee176f829..14694a52fd 100644 --- a/src/qml/qml/v8/qv8qobjectwrapper.cpp +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -440,20 +440,17 @@ static v8::Handle LoadProperty(QV8Engine *engine, QObject *object, } else if (property.isQVariant()) { QVariant v; ReadFunction(object, property, &v, notifier); - if (QQmlValueTypeFactory::isValueType(v.userType()) && engine->engine()) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); - QQmlValueType *valueType = ep->valueTypes[v.userType()]; - if (valueType) + + if (QQmlValueTypeFactory::isValueType(v.userType())) { + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType())) return engine->newValueType(object, property.coreIndex, valueType); // VariantReference value-type. } + return engine->fromVariant(v); - } else if (QQmlValueTypeFactory::isValueType((uint)property.propType) - && engine->engine()) { + } else if (QQmlValueTypeFactory::isValueType(property.propType)) { Q_ASSERT(notifier == 0); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); - QQmlValueType *valueType = ep->valueTypes[property.propType]; - if (valueType) + if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType)) return engine->newValueType(object, property.coreIndex, valueType); } else { Q_ASSERT(notifier == 0); diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp index fe58546522..91875150ec 100644 --- a/src/qml/qml/v8/qv8valuetypewrapper.cpp +++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp @@ -164,9 +164,8 @@ static bool readReferenceValue(QV8ValueTypeReferenceResource *reference) // overwritten with a different type in the meantime. // We need to modify this reference to the updated value type, if // possible, or return false if it is not a value type. - QQmlEngine *e = reference->engine->engine(); - if (QQmlValueTypeFactory::isValueType(variantReferenceType) && e) { - reference->type = QQmlEnginePrivate::get(e)->valueTypes[variantReferenceType]; + if (QQmlValueTypeFactory::isValueType(variantReferenceType)) { + reference->type = QQmlValueTypeFactory::valueType(variantReferenceType); if (!reference->type) { return false; } diff --git a/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml b/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml new file mode 100644 index 0000000000..d2f748c4c4 --- /dev/null +++ b/tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml @@ -0,0 +1,88 @@ +import QtQuick 2.0 +import Test 1.0 + +Item { + property bool success: false + + // Test user value type stored as both var and variant + property var testValue1 + property variant testValue2 + property variant testValue3 + property var testValue4 + + TestValueExporter { + id: assignmentValueType + testValue.property1: 1 + testValue.property2: 3.1415927 + } + + TestValueExporter { + id: v4BindingValueType + testValue.property1: 1 + 2 + testValue.property2: 3.1415927 / 2.0 + } + + TestValueExporter { + id: v8BindingValueType + testValue.property1: if (true) 1 + 2 + testValue.property2: if (true) 3.1415927 / 2.0 + } + + function numberEqual(lhs, rhs) { + var d = (lhs - rhs) + return (Math.abs(d) < 0.0001) + } + + Component.onCompleted: { + // Poperties assigned the result of Q_INVOKABLE: + testValue1 = testValueExporter.getTestValue() + testValue2 = testValueExporter.getTestValue() + + if (testValue1.property1 != 333) return + if (!numberEqual(testValue1.property2, 666.999)) return + + if (testValue2.property1 != 333) return + if (!numberEqual(testValue2.property2, 666.999)) return + + if (testValue1 != testValue2) return + + // Write to the properties of the value type + testValue1.property1 = 1 + testValue1.property2 = 3.1415927 + + testValue2.property1 = 1 + testValue2.property2 = 3.1415927 + + if (testValue1.property1 != 1) return + if (!numberEqual(testValue1.property2, 3.1415927)) return + + if (testValue2.property1 != 1) return + if (!numberEqual(testValue2.property2, 3.1415927)) return + + if (testValue1 != testValue2) return + + // Assignment of value type properties + testValue3 = testValue1 + testValue4 = testValue2 + + if (testValue3.property1 != 1) return + if (!numberEqual(testValue3.property2, 3.1415927)) return + + if (testValue4.property1 != 1) return + if (!numberEqual(testValue4.property2, 3.1415927)) return + + if (testValue3 != testValue4) return + + // Access a value-type property of a QObject + var vt = testValueExporter.testValue + if (vt.property1 != 0) return + if (!numberEqual(vt.property2, 0.0)) return + + testValueExporter.testValue = testValue4 + + if (vt.property1 != 1) return + if (!numberEqual(vt.property2, 3.1415927)) return + + success = true + } +} diff --git a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp index 20cc93bb7b..7c40a73812 100644 --- a/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp +++ b/tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp @@ -42,7 +42,9 @@ #include #include #include +#include #include +#include #include #include "../../shared/util.h" #include "testtypes.h" @@ -71,6 +73,7 @@ private slots: void cppIntegration(); void jsObjectConversion(); void invokableFunctions(); + void userType(); }; void tst_qqmlvaluetypeproviders::initTestCase() @@ -182,6 +185,121 @@ void tst_qqmlvaluetypeproviders::invokableFunctions() delete object; } +namespace { + +// A value-type class to export to QML +class TestValue +{ +public: + TestValue() : m_p1(0), m_p2(0.0) {} + TestValue(int p1, double p2) : m_p1(p1), m_p2(p2) {} + TestValue(const TestValue &other) : m_p1(other.m_p1), m_p2(other.m_p2) {} + ~TestValue() {} + + TestValue &operator=(const TestValue &other) { m_p1 = other.m_p1; m_p2 = other.m_p2; return *this; } + + int property1() const { return m_p1; } + void setProperty1(int p1) { m_p1 = p1; } + + double property2() const { return m_p2; } + void setProperty2(double p2) { m_p2 = p2; } + + bool operator==(const TestValue &other) const { return (m_p1 == other.m_p1) && (m_p2 == other.m_p2); } + bool operator!=(const TestValue &other) const { return !operator==(other); } + +private: + int m_p1; + double m_p2; +}; + +} + +Q_DECLARE_METATYPE(TestValue); + +namespace { + +class TestValueType : public QQmlValueTypeBase +{ + Q_OBJECT + Q_PROPERTY(int property1 READ property1 WRITE setProperty1) + Q_PROPERTY(double property2 READ property2 WRITE setProperty2) +public: + TestValueType(QObject *parent = 0) : QQmlValueTypeBase(qMetaTypeId(), parent) {} + + virtual QString toString() const { return QString::number(property1()) + QLatin1Char(',') + QString::number(property2()); } + virtual bool isEqual(const QVariant &other) const { return (other.userType() == qMetaTypeId()) && (v == other.value()); } + + int property1() const { return v.property1(); } + void setProperty1(int p1) { v.setProperty1(p1); } + + double property2() const { return v.property2(); } + void setProperty2(double p2) { v.setProperty2(p2); } +}; + +class TestValueTypeProvider : public QQmlValueTypeProvider +{ +public: + bool create(int type, QQmlValueType *&v) + { + if (type == qMetaTypeId()) { + v = new TestValueType; + return true; + } + + return false; + } + +}; + +TestValueTypeProvider *getValueTypeProvider() +{ + static TestValueTypeProvider valueTypeProvider; + return &valueTypeProvider; +} + +bool initializeProviders() +{ + QQml_addValueTypeProvider(getValueTypeProvider()); + return true; +} + +const bool initialized = initializeProviders(); + +class TestValueExporter : public QObject +{ + Q_OBJECT + Q_PROPERTY(TestValue testValue READ testValue WRITE setTestValue) +public: + TestValue testValue() const { return m_testValue; } + void setTestValue(const TestValue &v) { m_testValue = v; } + + Q_INVOKABLE TestValue getTestValue() const { return TestValue(333, 666.999); } + +private: + TestValue m_testValue; +}; + +} + +void tst_qqmlvaluetypeproviders::userType() +{ + Q_ASSERT(initialized); + Q_ASSERT(qMetaTypeId() >= QMetaType::User); + + qRegisterMetaType(); + qmlRegisterType("Test", 1, 0, "TestValueExporter"); + + TestValueExporter exporter; + + QQmlEngine e; + e.rootContext()->setContextProperty("testValueExporter", &exporter); + + QQmlComponent component(&e, testFileUrl("userType.qml")); + QScopedPointer obj(component.create()); + QVERIFY(obj != 0); + QCOMPARE(obj->property("success").toBool(), true); +} + QTEST_MAIN(tst_qqmlvaluetypeproviders) #include "tst_qqmlvaluetypeproviders.moc" -- cgit v1.2.3