Permit value types with metatype IDs >= QMetaType::User
authorMatthew Vogt <matthew.vogt@nokia.com>
Wed, 1 Aug 2012 00:27:17 +0000 (10:27 +1000)
committerQt by Nokia <qt-info@nokia.com>
Fri, 10 Aug 2012 03:40:49 +0000 (05:40 +0200)
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 <michael.brasser@nokia.com>
21 files changed:
src/qml/qml/qqmlabstractbinding.cpp
src/qml/qml/qqmlabstractbinding_p.h
src/qml/qml/qqmlcompiler.cpp
src/qml/qml/qqmlengine_p.h
src/qml/qml/qqmlinstruction_p.h
src/qml/qml/qqmlproperty.cpp
src/qml/qml/qqmlproperty_p.h
src/qml/qml/qqmlpropertycache_p.h
src/qml/qml/qqmlvaluetype.cpp
src/qml/qml/qqmlvaluetype_p.h
src/qml/qml/qqmlvaluetypeproxybinding.cpp
src/qml/qml/qqmlvme.cpp
src/qml/qml/qqmlvmemetaobject.cpp
src/qml/qml/qqmlvmemetaobject_p.h
src/qml/qml/v4/qv4bindings.cpp
src/qml/qml/v4/qv4bindings_p.h
src/qml/qml/v8/qv8engine.cpp
src/qml/qml/v8/qv8qobjectwrapper.cpp
src/qml/qml/v8/qv8valuetypewrapper.cpp
tests/auto/qml/qqmlvaluetypeproviders/data/userType.qml [new file with mode: 0644]
tests/auto/qml/qqmlvaluetypeproviders/tst_qqmlvaluetypeproviders.cpp

index 1e5ce8d..f663d9a 100644 (file)
@@ -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);
             }
index b9f8ecd..ae92077 100644 (file)
@@ -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); }
index af84480..ef8e44f 100644 (file)
@@ -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()
index f62e99b..fb7109f 100644 (file)
@@ -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 {
index 5cd06c2..db19627 100644 (file)
@@ -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;
index c034dbe..a239b45 100644 (file)
@@ -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<QQmlValueTypeProxyBinding *>(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<QQmlValueTypeProxyBinding *>(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<QQmlValueTypeProxyBinding *>(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<QVariant>()) {
-        writeValueProperty(object, engine, core, QVariant(), context, flags);
+        writeValueProperty(object, core, QVariant(), context, flags);
     } else if (type == qMetaTypeId<QJSValue>()) {
         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;
 }
 
index 0131e7e..501ab33 100644 (file)
@@ -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);
index b62d34c..99c6671 100644 (file)
@@ -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 *
index 165024a..9be48ae 100644 (file)
 
 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<int, QQmlValueType *> 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<QQmlEasingValueType>(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<int, QQmlValueType *>::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<QQmlEasingValueType>(uri, versionMajor, versionMinor, "Easing");
+}
+
+
 QQmlValueType::QQmlValueType(int userType, QObject *parent)
 : QObject(parent), m_userType(userType)
 {
 }
 
-
 QQmlPointFValueType::QQmlPointFValueType(QObject *parent)
     : QQmlValueTypeBase<QPointF>(QMetaType::QPointF, parent)
 {
index 153037b..dd3c919 100644 (file)
@@ -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<QPointF>
index f8d54a6..09e5b27 100644 (file)
@@ -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();
 
index f764b60..60e911d 100644 (file)
@@ -203,7 +203,7 @@ inline bool fastHasBinding(QObject *o, int index)
 {
     QQmlData *ddata = static_cast<QQmlData *>(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<QQmlError> *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<QQmlError> *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)
index ce57487..902a607 100644 (file)
@@ -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());
index 5751989..4216e09 100644 (file)
@@ -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;
         }
     };
     
index 9b74c2a..02f2bfc 100644 (file)
@@ -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<quint16>::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<const QV4Bindings::Binding *>(_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<v8::Value>(ep->v8engine()->valueTypeWrapper()->newValueType(tmp, vt));
             V8HANDLE_REGISTER(instr->unaryop.output);
         }
index 0c92cc4..dd63f6d 100644 (file)
@@ -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;
index 7eabd96..7972e85 100644 (file)
@@ -397,11 +397,8 @@ v8::Handle<v8::Value> 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<QQmlListReference>()) {
             typedef QQmlListReferencePrivate QDLRP;
@@ -435,6 +432,9 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
         v8::Handle<v8::Value> 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:
index bee176f..14694a5 100644 (file)
@@ -440,20 +440,17 @@ static v8::Handle<v8::Value> 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);
index fe58546..9187515 100644 (file)
@@ -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 (file)
index 0000000..d2f748c
--- /dev/null
@@ -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
+    }
+}
index 20cc93b..7c40a73 100644 (file)
@@ -42,7 +42,9 @@
 #include <qtest.h>
 #include <QQmlEngine>
 #include <QQmlComponent>
+#include <QQmlContext>
 #include <QDebug>
+#include <private/qqmlglobal_p.h>
 #include <private/qquickvaluetypes_p.h>
 #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<TestValue>
+{
+    Q_OBJECT
+    Q_PROPERTY(int property1 READ property1 WRITE setProperty1)
+    Q_PROPERTY(double property2 READ property2 WRITE setProperty2)
+public:
+    TestValueType(QObject *parent = 0) : QQmlValueTypeBase<TestValue>(qMetaTypeId<TestValue>(), parent) {}
+
+    virtual QString toString() const { return QString::number(property1()) + QLatin1Char(',') + QString::number(property2()); }
+    virtual bool isEqual(const QVariant &other) const { return (other.userType() == qMetaTypeId<TestValue>()) && (v == other.value<TestValue>()); }
+
+    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<TestValue>()) {
+            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<TestValue>() >= QMetaType::User);
+
+    qRegisterMetaType<TestValue>();
+    qmlRegisterType<TestValueExporter>("Test", 1, 0, "TestValueExporter");
+
+    TestValueExporter exporter;
+
+    QQmlEngine e;
+    e.rootContext()->setContextProperty("testValueExporter", &exporter);
+
+    QQmlComponent component(&e, testFileUrl("userType.qml"));
+    QScopedPointer<QObject> obj(component.create());
+    QVERIFY(obj != 0);
+    QCOMPARE(obj->property("success").toBool(), true);
+}
+
 QTEST_MAIN(tst_qqmlvaluetypeproviders)
 
 #include "tst_qqmlvaluetypeproviders.moc"