Add JavaScript "var" property type to QML
authorChris Adams <christopher.adams@nokia.com>
Fri, 30 Sep 2011 01:14:10 +0000 (11:14 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 6 Oct 2011 03:29:00 +0000 (05:29 +0200)
This commit adds a new syntax which allows "var" type properties
which can have JavaScript objects (as well as other basic types)
assigned to them. Such JavaScript objects cannot be bound to.

Task-number: QMLNG-18
Change-Id: If7f5045f4669e0d5c1b8d0891ed765128d0bc1c6
Reviewed-on: http://codereview.qt-project.org/1466
Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
52 files changed:
doc/src/declarative/basictypes.qdoc
doc/src/declarative/whatsnew.qdoc
src/declarative/qml/qdeclarativecompiler.cpp
src/declarative/qml/qdeclarativeinstruction_p.h
src/declarative/qml/qdeclarativeproperty.cpp
src/declarative/qml/qdeclarativeproperty_p.h
src/declarative/qml/qdeclarativepropertycache_p.h
src/declarative/qml/qdeclarativescript.cpp
src/declarative/qml/qdeclarativescript_p.h
src/declarative/qml/qdeclarativevme.cpp
src/declarative/qml/qdeclarativevmemetaobject.cpp
src/declarative/qml/qdeclarativevmemetaobject_p.h
src/declarative/qml/v8/qv8engine.cpp
src/declarative/qml/v8/qv8qobjectwrapper.cpp
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent3.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent4.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent5.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarInheritanceComponent.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarOwnershipComponent.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml
tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml
tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml
tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.1.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.3.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.4.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.5.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.6.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.7.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.8.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.9.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.inherit.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.reparent.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarCpp.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarImplicitOwnership.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.type.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type1.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type2.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
tests/auto/declarative/qdeclarativeecmascript/testtypes.h
tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
tests/auto/declarative/qdeclarativelanguage/data/assignLiteralToVar.qml [new file with mode: 0644]
tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp

index 0133ab5..1bc1373 100644 (file)
     \sa {QML Basic Types}
 */
 
+ /*!
+    \qmlbasictype var
+    \ingroup qmlbasictypes
+
+    \brief A var type is a generic property type.
+
+    A var is a generic property type capable of storing any data type.
+    It is equivalent to a regular JavaScript variable.
+    For example, var properties can store numbers, strings, objects and
+    arrays:
+
+    \qml
+    Item {
+        property var aNumber: 100
+        property var aBool: false
+        property var aString: "Hello world!"
+        property var anotherString: String("#FF008800")
+        property var aColor: Qt.rgba(0.2, 0.3, 0.4, 0.5)
+        property var aRect: Qt.rect(10, 10, 10, 10)
+        property var aPoint: Qt.point(10, 10)
+        property var aSize: Qt.size(10, 10)
+        property var aVector3d: Qt.vector3d(100, 100, 100)
+        property var anArray: [1, 2, 3, "four", "five"]
+        property var anObject: { "foo": 10, "bar": 20 }
+    }
+    \endqml
+
+    It is important to note that properties of JavaScript objects cannot
+    be bound to:
+
+    \qml
+    Item {
+        property var car: new vehicle(4)
+        property int wheelCount: car.wheels
+
+        function vehicle(wheels) {
+            this.wheels = wheels;
+            this.talk = function() { print("I have " + this.wheels + " wheels!"); }
+        }
+
+        Component.onCompleted: {
+            car.wheels = 6; // wheelCount will _not_ be updated
+        }
+    }
+    \endqml
+
+    \sa {QML Basic Types}
+*/
+
+
 /*!
+    \obsolete
     \qmlbasictype variant
     \ingroup qmlbasictypes
 
     \brief A variant type is a generic property type.
 
-    A variant is a generic property type. A variant type property can hold
-    any of the \l {QML Basic Types}{basic type} values:
+    A variant is a generic property type. It is obsolete and exists only to
+    support old applications; new applications should use "var" type
+    properties instead.
+
+    A variant type property can hold any of the \l {QML Basic Types}{basic type}
+    values:
 
     \qml
     Item {
index a9a1ecb..4efe0da 100644 (file)
@@ -120,6 +120,9 @@ header and footer items).
 ListView section.labelPositioning property added to allow keeping the current section label
 at the start and/or next section label at the end of the view.
 
+A new property type ("var") has been introduced which obsoletes "variant" properties in QML.
+Properties of this type are equivalent to regular JavaScript variables.  See the documentation
+on \l{QML Basic Types} for more information about "var" properties.
 
 \section2 QtQuick 1 is now a separate library and module
 
index dc992b5..c736c3f 100644 (file)
@@ -380,26 +380,54 @@ void QDeclarativeCompiler::genLiteralAssignment(QDeclarativeScript::Property *pr
             if (v->value.isNumber()) {
                 double n = v->value.asNumber();
                 if (double(int(n)) == n) {
-                    Instruction::StoreVariantInteger instr;
+                    if (prop->core.isVMEProperty()) {
+                        Instruction::StoreVarInteger instr;
+                        instr.propertyIndex = prop->index;
+                        instr.value = int(n);
+                        output->addInstruction(instr);
+                    } else {
+                        Instruction::StoreVariantInteger instr;
+                        instr.propertyIndex = prop->index;
+                        instr.value = int(n);
+                        output->addInstruction(instr);
+                    }
+                } else {
+                    if (prop->core.isVMEProperty()) {
+                        Instruction::StoreVarDouble instr;
+                        instr.propertyIndex = prop->index;
+                        instr.value = n;
+                        output->addInstruction(instr);
+                    } else {
+                        Instruction::StoreVariantDouble instr;
+                        instr.propertyIndex = prop->index;
+                        instr.value = n;
+                        output->addInstruction(instr);
+                    }
+                }
+            } else if (v->value.isBoolean()) {
+                if (prop->core.isVMEProperty()) {
+                    Instruction::StoreVarBool instr;
                     instr.propertyIndex = prop->index;
-                    instr.value = int(n);
+                    instr.value = v->value.asBoolean();
                     output->addInstruction(instr);
                 } else {
-                    Instruction::StoreVariantDouble instr;
+                    Instruction::StoreVariantBool instr;
                     instr.propertyIndex = prop->index;
-                    instr.value = n;
+                    instr.value = v->value.asBoolean();
                     output->addInstruction(instr);
                 }
-            } else if(v->value.isBoolean()) {
-                Instruction::StoreVariantBool instr;
-                instr.propertyIndex = prop->index;
-                instr.value = v->value.asBoolean();
-                output->addInstruction(instr);
             } else {
-                Instruction::StoreVariant instr;
-                instr.propertyIndex = prop->index;
-                instr.value = output->indexForString(v->value.asString());
-                output->addInstruction(instr);
+                if (prop->core.isVMEProperty()) {
+                    Instruction::StoreVar instr;
+                    instr.propertyIndex = prop->index;
+                    instr.value = output->indexForString(v->value.asString());
+                    output->addInstruction(instr);
+                } else {
+                    Instruction::StoreVariant instr;
+                    instr.propertyIndex = prop->index;
+                    instr.value = output->indexForString(v->value.asString());
+                    output->addInstruction(instr);
+                }
             }
             }
             break;
@@ -1796,10 +1824,18 @@ void QDeclarativeCompiler::genPropertyAssignment(QDeclarativeScript::Property *p
 
             } else if (prop->type == QMetaType::QVariant) {
 
-                Instruction::StoreVariantObject store;
-                store.line = v->object->location.start.line;
-                store.propertyIndex = prop->index;
-                output->addInstruction(store);
+                if (prop->core.isVMEProperty()) {
+                    Instruction::StoreVarObject store;
+                    store.line = v->object->location.start.line;
+                    store.propertyIndex = prop->index;
+                    output->addInstruction(store);
+                } else {
+                    Instruction::StoreVariantObject store;
+                    store.line = v->object->location.start.line;
+                    store.propertyIndex = prop->index;
+                    output->addInstruction(store);
+                }
+
 
             } else {
 
@@ -2570,11 +2606,16 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
 
     const Object::DynamicProperty *defaultProperty = 0;
     int aliasCount = 0;
+    int varPropCount = 0;
+    int totalPropCount = 0;
+    int firstPropertyVarIndex = 0;
 
     for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
 
         if (p->type == Object::DynamicProperty::Alias)
             aliasCount++;
+        if (p->type == Object::DynamicProperty::Var)
+            varPropCount++;
 
         if (p->isDefaultProperty && 
             (resolveAlias || p->type != Object::DynamicProperty::Alias))
@@ -2628,6 +2669,7 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
         int metaType;
         const char *cppType;
     } builtinTypes[] = {
+        { Object::DynamicProperty::Var, 0, "QVariant" },
         { Object::DynamicProperty::Variant, 0, "QVariant" },
         { Object::DynamicProperty::Int, QMetaType::Int, "int" },
         { Object::DynamicProperty::Bool, QMetaType::Bool, "bool" },
@@ -2706,6 +2748,9 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
                 typeRef = p->typeRef;
             }
 
+            if (p->type == Object::DynamicProperty::Var)
+                continue;
+
             if (buildData) {
                 VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data();
                 vmd->propertyCount++;
@@ -2726,6 +2771,31 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
 
             effectivePropertyIndex++;
         }
+
+        if (varPropCount) {
+            VMD *vmd = (QDeclarativeVMEMetaData *)dynamicData.data();
+            if (buildData)
+                vmd->varPropertyCount = varPropCount;
+            firstPropertyVarIndex = effectivePropertyIndex;
+            totalPropCount = varPropCount + effectivePropertyIndex;
+            for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p; p = obj->dynamicProperties.next(p)) {
+                if (p->type == Object::DynamicProperty::Var) {
+                    QFastMetaBuilder::StringRef typeRef = typeRefs[p->type];
+                    if (buildData) {
+                        vmd->propertyCount++;
+                        (vmd->propertyData() + effectivePropertyIndex)->propertyType = -1;
+                    }
+
+                    builder.setProperty(effectivePropertyIndex, p->nameRef, typeRef, (QMetaType::Type)-1,
+                                        QFastMetaBuilder::Writable, effectivePropertyIndex);
+
+                    p->changedSignatureRef = builder.newString(p->name.utf8length() + strlen("Changed()"));
+                    builder.setSignal(effectivePropertyIndex, p->changedSignatureRef);
+
+                    effectivePropertyIndex++;
+                }
+            }
+        }
         
         if (aliasCount) {
             int aliasIndex = 0;
@@ -2913,9 +2983,19 @@ bool QDeclarativeCompiler::buildDynamicMeta(QDeclarativeScript::Object *obj, Dyn
 
     if (obj->type != -1) {
         QDeclarativePropertyCache *cache = output->types[obj->type].createPropertyCache(engine)->copy();
-        cache->append(engine, &obj->extObject, QDeclarativePropertyCache::Data::NoFlags,
+        cache->append(engine, &obj->extObject,
+                      QDeclarativePropertyCache::Data::NoFlags,
                       QDeclarativePropertyCache::Data::IsVMEFunction, 
                       QDeclarativePropertyCache::Data::IsVMESignal);
+
+        // now we modify the flags appropriately for var properties.
+        int propertyOffset = obj->extObject.propertyOffset();
+        QDeclarativePropertyCache::Data *currPropData = 0;
+        for (int pvi = firstPropertyVarIndex; pvi < totalPropCount; ++pvi) {
+            currPropData = cache->property(pvi + propertyOffset);
+            currPropData->setFlags(currPropData->getFlags() | QDeclarativePropertyCache::Data::IsVMEProperty);
+        }
+
         obj->synthCache = cache;
     }
 
@@ -3205,8 +3285,7 @@ int QDeclarativeCompiler::genValueTypeData(QDeclarativeScript::Property *valueTy
 int QDeclarativeCompiler::genPropertyData(QDeclarativeScript::Property *prop)
 {
     typedef QDeclarativePropertyPrivate QDPP;
-    QByteArray data = QDPP::saveProperty(prop->parent->metaObject(), prop->index, engine);
-
+    QByteArray data = QDPP::saveProperty(&prop->core);
     return output->indexForByteArray(data);
 }
 
index e64ca5d..b6efb19 100644 (file)
@@ -73,6 +73,10 @@ QT_BEGIN_NAMESPACE
     F(StoreVariantInteger, storeInteger) \
     F(StoreVariantDouble, storeDouble) \
     F(StoreVariantBool, storeBool) \
+    F(StoreVar, storeString) \
+    F(StoreVarInteger, storeInteger) \
+    F(StoreVarDouble, storeDouble) \
+    F(StoreVarBool, storeBool) \
     F(StoreString, storeString) \
     F(StoreByteArray, storeByteArray) \
     F(StoreUrl, storeUrl) \
@@ -109,6 +113,7 @@ QT_BEGIN_NAMESPACE
     F(StoreObjectQList, common) \
     F(AssignObjectList, assignObjectList) \
     F(StoreVariantObject, storeObject) \
+    F(StoreVarObject, storeObject) \
     F(StoreInterface, storeObject) \
     F(FetchAttached, fetchAttached) \
     F(FetchQList, fetchQmlList) \
index acc2cfb..6e5e712 100644 (file)
@@ -1327,13 +1327,14 @@ bool QDeclarativePropertyPrivate::writeBinding(const QDeclarativeProperty &that,
     QDeclarativeDeleteWatcher watcher(expression);
 
     QVariant value;
+    bool isVmeProperty = pp->core.isVMEProperty();
 
     if (isUndefined) {
     } else if (that.propertyTypeCategory() == QDeclarativeProperty::List) {
         value = engine->toVariant(result, qMetaTypeId<QList<QObject *> >());
     } else if (result->IsNull() && that.propertyTypeCategory() == QDeclarativeProperty::Object) {
         value = QVariant::fromValue((QObject *)0);
-    } else {
+    } else if (!isVmeProperty) {
         value = engine->toVariant(result, type);
     }
 
@@ -1351,6 +1352,8 @@ bool QDeclarativePropertyPrivate::writeBinding(const QDeclarativeProperty &that,
     } else if (result->IsFunction()) {
         expression->error.setDescription(QLatin1String("Unable to assign a function to a property."));
         return false;
+    } else if (isVmeProperty) {
+        static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(that.object()->metaObject()))->setVMEProperty(that.index(), result);
     } else if (object && !QDeclarativePropertyPrivate::write(that, value, flags)) {
 
         if (watcher.wasDeleted()) 
@@ -1604,6 +1607,17 @@ QByteArray QDeclarativePropertyPrivate::saveProperty(const QMetaObject *metaObje
     return rv;
 }
 
+QByteArray QDeclarativePropertyPrivate::saveProperty(QDeclarativePropertyCache::Data *core)
+{
+    SerializedData sd;
+    memset(&sd, 0, sizeof(sd));
+    sd.isValueType = false;
+    sd.core = *core;
+
+    QByteArray rv((const char *)&sd, sizeof(sd));
+    return rv;
+}
+
 QDeclarativeProperty 
 QDeclarativePropertyPrivate::restore(const QByteArray &data, QObject *object, QDeclarativeContextData *ctxt)
 {
index d05e155..190cf2a 100644 (file)
@@ -116,6 +116,7 @@ public:
                                     QDeclarativeEngine *);
     static QByteArray saveProperty(const QMetaObject *, int, 
                                    QDeclarativeEngine *);
+    static QByteArray saveProperty(QDeclarativePropertyCache::Data *);
 
     static QDeclarativeProperty restore(const QByteArray &, QObject *, QDeclarativeContextData *);
     static QDeclarativeProperty restore(const QDeclarativePropertyCache::Data &,
index fefcf7f..c1610f8 100644 (file)
@@ -95,19 +95,20 @@ public:
                     IsEnumType        = 0x00000100, // Property type is an enum
                     IsQList           = 0x00000200, // Property type is a QML list
                     IsQmlBinding      = 0x00000400, // Property type is a QDeclarativeBinding*
-                    IsQJSValue    = 0x00000800, // Property type is a QScriptValue
+                    IsQJSValue        = 0x00000800, // Property type is a QScriptValue
                     IsV8Handle        = 0x00001000, // Property type is a QDeclarativeV8Handle
+                    IsVMEProperty     = 0x00002000, // Property type is a "var" property of VMEMO
 
                     // Apply only to IsFunctions
-                    IsVMEFunction     = 0x00002000, // Function was added by QML
-                    HasArguments      = 0x00004000, // Function takes arguments
-                    IsSignal          = 0x00008000, // Function is a signal
-                    IsVMESignal       = 0x00010000, // Signal was added by QML
-                    IsV8Function      = 0x00020000, // Function takes QDeclarativeV8Function* args
-                    IsSignalHandler   = 0x00040000, // Function is a signal handler
+                    IsVMEFunction     = 0x00004000, // Function was added by QML
+                    HasArguments      = 0x00008000, // Function takes arguments
+                    IsSignal          = 0x00010000, // Function is a signal
+                    IsVMESignal       = 0x00020000, // Signal was added by QML
+                    IsV8Function      = 0x00040000, // Function takes QDeclarativeV8Function* args
+                    IsSignalHandler   = 0x00080000, // Function is a signal handler
 
                     // Internal QDeclarativePropertyCache flags
-                    NotFullyResolved  = 0x00080000  // True if the type data is to be lazily resolved
+                    NotFullyResolved  = 0x00100000  // True if the type data is to be lazily resolved
         };
         Q_DECLARE_FLAGS(Flags, Flag)
 
@@ -129,6 +130,7 @@ public:
         bool isQmlBinding() const { return flags & IsQmlBinding; }
         bool isQJSValue() const { return flags & IsQJSValue; }
         bool isV8Handle() const { return flags & IsV8Handle; }
+        bool isVMEProperty() const { return flags & IsVMEProperty; }
         bool isVMEFunction() const { return flags & IsVMEFunction; }
         bool hasArguments() const { return flags & HasArguments; }
         bool isSignal() const { return flags & IsSignal; }
index 8fb423c..2bc358a 100644 (file)
@@ -920,7 +920,8 @@ bool ProcessAST::visit(AST::UiPublicMember *node)
         // { "time", strlen("time"), Object::DynamicProperty::Time, "QTime", strlen("QTime") },
         // { "date", strlen("date"), Object::DynamicProperty::Date, "QDate", strlen("QDate") },
         { "date", strlen("date"), Object::DynamicProperty::DateTime, "QDateTime", strlen("QDateTime") },
-        { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") }
+        { "variant", strlen("variant"), Object::DynamicProperty::Variant, "QVariant", strlen("QVariant") },
+        { "var", strlen("var"), Object::DynamicProperty::Var, "QVariant", strlen("QVariant") }
     };
     static const int propTypeNameToTypesCount = sizeof(propTypeNameToTypes) /
                                                 sizeof(propTypeNameToTypes[0]);
index e270241..c5b9a7e 100644 (file)
@@ -385,8 +385,8 @@ public:
     {
         DynamicProperty();
 
-        enum Type { Variant, Int, Bool, Real, String, Url, Color, Time, 
-                    Date, DateTime, Alias, Custom, CustomList };
+        enum Type { Var, Variant, Int, Bool, Real, String, Url, Color,
+                    Time, Date, DateTime, Alias, Custom, CustomList };
 
         bool isDefaultProperty;
         Type type;
index 8cc8fa0..8f34fb5 100644 (file)
@@ -251,6 +251,10 @@ QObject *QDeclarativeVME::run(QList<QDeclarativeError> *errors,
     QDeclarativeEngine *engine = states.at(0).context->engine;
     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine);
 
+    // Need a v8 handle scope and execution context for StoreVar instructions.
+    v8::HandleScope handleScope;
+    v8::Context::Scope contextScope(ep->v8engine()->context());
+
     int status = -1; // needed for dbus
     QDeclarativePropertyPrivate::WriteFlags flags = QDeclarativePropertyPrivate::BypassInterceptor |
                                                     QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite;
@@ -542,6 +546,41 @@ QObject *QDeclarativeVME::run(QList<QDeclarativeError> *errors,
                                   instr.propertyIndex, a);
         QML_END_INSTR(StoreVariantBool)
 
+        QML_BEGIN_INSTR(StoreVar)
+            QObject *target = objects.top();
+            CLEAN_PROPERTY(target, instr.propertyIndex);
+
+            // Note that we don't use QDeclarativeStringConverters::variantFromString() here, which
+            // means that automatic generation of value types from strings doesn't occur.
+            // This is a deliberate behaviour difference to variant properties.
+            v8::Handle<v8::Value> v8Value = ep->v8engine()->fromVariant(PRIMITIVES.at(instr.value));
+            static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(target->metaObject()))->setVMEProperty(instr.propertyIndex, v8Value);
+        QML_END_INSTR(StoreVar)
+
+        QML_BEGIN_INSTR(StoreVarInteger)
+            QObject *target = objects.top();
+            CLEAN_PROPERTY(target, instr.propertyIndex);
+
+            v8::Handle<v8::Value> v8Value = v8::Integer::New(instr.value);
+            static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(target->metaObject()))->setVMEProperty(instr.propertyIndex, v8Value);
+        QML_END_INSTR(StoreVarInteger)
+
+        QML_BEGIN_INSTR(StoreVarDouble)
+            QObject *target = objects.top();
+            CLEAN_PROPERTY(target, instr.propertyIndex);
+
+            v8::Handle<v8::Value> v8Value = v8::Number::New(instr.value);
+            static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(target->metaObject()))->setVMEProperty(instr.propertyIndex, v8Value);
+        QML_END_INSTR(StoreVarDouble)
+
+        QML_BEGIN_INSTR(StoreVarBool)
+            QObject *target = objects.top();
+            CLEAN_PROPERTY(target, instr.propertyIndex);
+
+            v8::Handle<v8::Value> v8Value = v8::Boolean::New(instr.value);
+            static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(target->metaObject()))->setVMEProperty(instr.propertyIndex, v8Value);
+        QML_END_INSTR(StoreVarBool)
+
         QML_BEGIN_INSTR(StoreString)
             QObject *target = objects.top();
             CLEAN_PROPERTY(target, instr.propertyIndex);
@@ -974,6 +1013,15 @@ QObject *QDeclarativeVME::run(QList<QDeclarativeError> *errors,
                                   instr.propertyIndex, a);
         QML_END_INSTR(StoreVariantObject)
 
+        QML_BEGIN_INSTR(StoreVarObject)
+            QObject *assign = objects.pop();
+            QObject *target = objects.top();
+            CLEAN_PROPERTY(target, instr.propertyIndex);
+
+            v8::Handle<v8::Value> v8Value = ep->v8engine()->newQObject(assign);
+            static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(target->metaObject()))->setVMEProperty(instr.propertyIndex, v8Value);
+        QML_END_INSTR(StoreVarObject)
+
         QML_BEGIN_INSTR(StoreInterface)
             QObject *assign = objects.pop();
             QObject *target = objects.top();
index bcd46f2..abf7349 100644 (file)
@@ -382,8 +382,9 @@ QDeclarativeVMEMetaObject::QDeclarativeVMEMetaObject(QObject *obj,
                                                      const QMetaObject *other, 
                                                      const QDeclarativeVMEMetaData *meta,
                                                      QDeclarativeCompiledData *cdata)
-: object(obj), compiledData(cdata), ctxt(QDeclarativeData::get(obj, true)->outerContext),
-  metaData(meta), data(0), v8methods(0), parent(0)
+: QV8GCCallback::Node(GcPrologueCallback), object(obj), compiledData(cdata),
+  ctxt(QDeclarativeData::get(obj, true)->outerContext), metaData(meta), data(0),
+  firstVarPropertyIndex(-1), varPropertiesInitialized(false), v8methods(0), parent(0)
 {
     compiledData->addref();
 
@@ -398,19 +399,23 @@ QDeclarativeVMEMetaObject::QDeclarativeVMEMetaObject(QObject *obj,
     propOffset = QAbstractDynamicMetaObject::propertyOffset();
     methodOffset = QAbstractDynamicMetaObject::methodOffset();
 
-    data = new QDeclarativeVMEVariant[metaData->propertyCount];
+    data = new QDeclarativeVMEVariant[metaData->propertyCount - metaData->varPropertyCount];
 
     aConnected.resize(metaData->aliasCount);
     int list_type = qMetaTypeId<QDeclarativeListProperty<QObject> >();
 
     // ### Optimize
-    for (int ii = 0; ii < metaData->propertyCount; ++ii) {
+    for (int ii = 0; ii < metaData->propertyCount - metaData->varPropertyCount; ++ii) {
         int t = (metaData->propertyData() + ii)->propertyType;
         if (t == list_type) {
             listProperties.append(List(methodOffset + ii));
             data[ii].setValue(listProperties.count() - 1);
         } 
     }
+
+    firstVarPropertyIndex = metaData->propertyCount - metaData->varPropertyCount;
+    if (metaData->varPropertyCount)
+        QV8GCCallback::addGcCallbackNode(this);
 }
 
 QDeclarativeVMEMetaObject::~QDeclarativeVMEMetaObject()
@@ -422,6 +427,9 @@ QDeclarativeVMEMetaObject::~QDeclarativeVMEMetaObject()
     for (int ii = 0; v8methods && ii < metaData->methodCount; ++ii) {
         qPersistentDispose(v8methods[ii]);
     }
+
+    if (metaData->varPropertyCount)
+        qPersistentDispose(varProperties); // if not weak, will not have been cleaned up by the callback.
 }
 
 int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
@@ -468,10 +476,29 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
 
                 if (t == -1) {
 
-                    if (c == QMetaObject::ReadProperty) {
-                        *reinterpret_cast<QVariant *>(a[0]) = readVarPropertyAsVariant(id);
-                    } else if (c == QMetaObject::WriteProperty) {
-                        writeVarProperty(id, *reinterpret_cast<QVariant *>(a[0]));
+                    if (id >= firstVarPropertyIndex) {
+                        // the context can be null if accessing var properties from cpp after re-parenting an item.
+                        QDeclarativeEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QDeclarativeEnginePrivate::get(ctxt->engine);
+                        QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine();
+                        if (v8e) {
+                            v8::HandleScope handleScope;
+                            v8::Context::Scope contextScope(v8e->context());
+                            if (c == QMetaObject::ReadProperty) {
+                                *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
+                            } else if (c == QMetaObject::WriteProperty) {
+                                writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
+                            }
+                        } else if (c == QMetaObject::ReadProperty) {
+                            // if the context was disposed, we just return an invalid variant from read.
+                            *reinterpret_cast<QVariant *>(a[0]) = QVariant();
+                        }
+                    } else {
+                        // don't need to set up v8 scope objects, since not accessing varProperties.
+                        if (c == QMetaObject::ReadProperty) {
+                            *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
+                        } else if (c == QMetaObject::WriteProperty) {
+                            writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
+                        }
                     }
 
                 } else {
@@ -716,54 +743,61 @@ v8::Handle<v8::Function> QDeclarativeVMEMetaObject::method(int index)
     return v8methods[index];
 }
 
-#if 0
-QScriptValue QDeclarativeVMEMetaObject::readVarProperty(int id)
+v8::Handle<v8::Value> QDeclarativeVMEMetaObject::readVarProperty(int id)
 {
-    if (data[id].dataType() == qMetaTypeId<QScriptValue>())
-        return data[id].asQJSValue();
-    else if (data[id].dataType() == QMetaType::QObjectStar) 
-        return QDeclarativeEnginePrivate::get(ctxt->engine)->objectClass->newQObject(data[id].asQObject());
-    else
-        return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueFromVariant(data[id].asQVariant());
+    Q_ASSERT(id >= firstVarPropertyIndex);
+
+    ensureVarPropertiesAllocated();
+    return varProperties->Get(id - firstVarPropertyIndex);
 }
-#endif
 
-QVariant QDeclarativeVMEMetaObject::readVarPropertyAsVariant(int id)
+QVariant QDeclarativeVMEMetaObject::readPropertyAsVariant(int id)
 {
-#if 0
-    if (data[id].dataType() == qMetaTypeId<QScriptValue>())
-        return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueToVariant(data[id].asQJSValue());
-    else 
-#endif
-    if (data[id].dataType() == QMetaType::QObjectStar) 
-        return QVariant::fromValue(data[id].asQObject());
-    else 
-        return data[id].asQVariant();
+    if (id >= firstVarPropertyIndex) {
+        ensureVarPropertiesAllocated();
+        return QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->toVariant(varProperties->Get(id - firstVarPropertyIndex), -1);
+    } else {
+        if (data[id].dataType() == QMetaType::QObjectStar) {
+            return QVariant::fromValue(data[id].asQObject());
+        } else {
+            return data[id].asQVariant();
+        }
+    }
 }
 
-#if 0
-void QDeclarativeVMEMetaObject::writeVarProperty(int id, const QScriptValue &value)
+void QDeclarativeVMEMetaObject::writeVarProperty(int id, v8::Handle<v8::Value> value)
 {
-    data[id].setValue(value);
+    Q_ASSERT(id >= firstVarPropertyIndex);
+
+    ensureVarPropertiesAllocated();
+    varProperties->Set(id - firstVarPropertyIndex, value);
     activate(object, methodOffset + id, 0);
 }
-#endif
 
-void QDeclarativeVMEMetaObject::writeVarProperty(int id, const QVariant &value)
+void QDeclarativeVMEMetaObject::writeProperty(int id, const QVariant &value)
 {
-    bool needActivate = false;
-    if (value.userType() == QMetaType::QObjectStar) {
-        QObject *o = qvariant_cast<QObject *>(value);
-        needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o);
-        data[id].setValue(qvariant_cast<QObject *>(value));
+    if (id >= firstVarPropertyIndex) {
+        ensureVarPropertiesAllocated();
+        QVariant currentValue = readPropertyAsVariant(id);
+        varProperties->Set(id - firstVarPropertyIndex, QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->fromVariant(value));
+        if ((currentValue.userType() != value.userType() || currentValue != value))
+            activate(object, methodOffset + id, 0);
     } else {
-        needActivate = (data[id].dataType() != qMetaTypeId<QVariant>() || 
-                        data[id].asQVariant().userType() != value.userType() || 
-                        data[id].asQVariant() != value);
-        data[id].setValue(value);
+        bool needActivate = false;
+        if (value.userType() == QMetaType::QObjectStar) {
+            QObject *o = qvariant_cast<QObject *>(value);
+            needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o);
+            data[id].setValue(qvariant_cast<QObject *>(value));
+        } else {
+            needActivate = (data[id].dataType() != qMetaTypeId<QVariant>() ||
+                            data[id].asQVariant().userType() != value.userType() ||
+                            data[id].asQVariant() != value);
+            data[id].setValue(value);
+        }
+
+        if (needActivate)
+            activate(object, methodOffset + id, 0);
     }
-    if (needActivate)
-        activate(object, methodOffset + id, 0);
 }
 
 void QDeclarativeVMEMetaObject::listChanged(int id)
@@ -849,8 +883,7 @@ void QDeclarativeVMEMetaObject::setVmeMethod(int index, v8::Persistent<v8::Funct
     v8methods[methodIndex] = value;
 }
 
-#if 0
-QScriptValue QDeclarativeVMEMetaObject::vmeProperty(int index)
+v8::Handle<v8::Value> QDeclarativeVMEMetaObject::vmeProperty(int index)
 {
     if (index < propOffset) {
         Q_ASSERT(parent);
@@ -859,7 +892,7 @@ QScriptValue QDeclarativeVMEMetaObject::vmeProperty(int index)
     return readVarProperty(index - propOffset);
 }
 
-void QDeclarativeVMEMetaObject::setVMEProperty(int index, const QScriptValue &v)
+void QDeclarativeVMEMetaObject::setVMEProperty(int index, v8::Handle<v8::Value> v)
 {
     if (index < propOffset) {
         Q_ASSERT(parent);
@@ -867,7 +900,46 @@ void QDeclarativeVMEMetaObject::setVMEProperty(int index, const QScriptValue &v)
     }
     return writeVarProperty(index - propOffset, v);
 }
-#endif
+
+void QDeclarativeVMEMetaObject::ensureVarPropertiesAllocated()
+{
+    if (!varPropertiesInitialized)
+        allocateVarPropertiesArray();
+}
+
+// see also: QV8GCCallback::garbageCollectorPrologueCallback()
+void QDeclarativeVMEMetaObject::allocateVarPropertiesArray()
+{
+    v8::HandleScope handleScope;
+    v8::Context::Scope cs(QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->context());
+    varProperties = qPersistentNew(v8::Array::New(metaData->varPropertyCount));
+    varProperties.MakeWeak(static_cast<void*>(this), VarPropertiesWeakReferenceCallback);
+    varPropertiesInitialized = true;
+}
+
+/*
+   The "var" properties are stored in a v8::Array which will be strong persistent if the object has cpp-ownership
+   and the root QObject in the parent chain does not have JS-ownership.  In the weak persistent handle case,
+   this callback will dispose the handle when the v8object which owns the lifetime of the var properties array
+   is cleared as a result of all other handles to that v8object being released.
+   See QV8GCCallback::garbageCollectorPrologueCallback() for more information.
+ */
+void QDeclarativeVMEMetaObject::VarPropertiesWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter)
+{
+    QDeclarativeVMEMetaObject *vmemo = static_cast<QDeclarativeVMEMetaObject*>(parameter);
+    Q_ASSERT(vmemo);
+    qPersistentDispose(object);
+    vmemo->varProperties.Clear();
+}
+
+void QDeclarativeVMEMetaObject::GcPrologueCallback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *node)
+{
+    QDeclarativeVMEMetaObject *vmemo = static_cast<QDeclarativeVMEMetaObject*>(node);
+    Q_ASSERT(vmemo);
+    if (!vmemo->varPropertiesInitialized || vmemo->varProperties.IsEmpty())
+        return;
+    r->addRelationship(vmemo->object, vmemo->varProperties);
+}
 
 bool QDeclarativeVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const
 {
index 991c79a..0c02b76 100644 (file)
@@ -69,6 +69,8 @@
 #include "private/qdeclarativecompiler_p.h"
 #include "private/qdeclarativecontext_p.h"
 
+#include "private/qv8gccallback_p.h"
+
 #include <private/qv8_p.h>
 
 QT_BEGIN_NAMESPACE
@@ -77,6 +79,7 @@ QT_BEGIN_NAMESPACE
 
 struct QDeclarativeVMEMetaData
 {
+    short varPropertyCount;
     short propertyCount;
     short aliasCount;
     short signalCount;
@@ -131,9 +134,11 @@ struct QDeclarativeVMEMetaData
     }
 };
 
+class QV8QObjectWrapper;
 class QDeclarativeVMEVariant;
 class QDeclarativeRefCount;
-class QDeclarativeVMEMetaObject : public QAbstractDynamicMetaObject
+class Q_AUTOTEST_EXPORT QDeclarativeVMEMetaObject : public QAbstractDynamicMetaObject,
+                                                    public QV8GCCallback::Node
 {
 public:
     QDeclarativeVMEMetaObject(QObject *obj, const QMetaObject *other, const QDeclarativeVMEMetaData *data,
@@ -145,10 +150,8 @@ public:
     v8::Handle<v8::Function> vmeMethod(int index);
     int vmeMethodLineNumber(int index);
     void setVmeMethod(int index, v8::Persistent<v8::Function>);
-#if 0
-    QScriptValue vmeProperty(int index);
-    void setVMEProperty(int index, const QScriptValue &);
-#endif
+    v8::Handle<v8::Value> vmeProperty(int index);
+    void setVMEProperty(int index, v8::Handle<v8::Value> v);
 
     void connectAliasSignal(int index);
 
@@ -166,6 +169,14 @@ private:
 
     QDeclarativeVMEVariant *data;
 
+    v8::Persistent<v8::Array> varProperties;
+    int firstVarPropertyIndex;
+    bool varPropertiesInitialized;
+    static void VarPropertiesWeakReferenceCallback(v8::Persistent<v8::Value> object, void* parameter);
+    static void GcPrologueCallback(QV8GCCallback::Referencer *r, QV8GCCallback::Node *node);
+    inline void allocateVarPropertiesArray();
+    inline void ensureVarPropertiesAllocated();
+
     void connectAlias(int aliasId);
     QBitArray aConnected;
     QBitArray aInterceptors;
@@ -174,12 +185,10 @@ private:
     v8::Persistent<v8::Function> *v8methods;
     v8::Handle<v8::Function> method(int);
 
-#if 0
-    QScriptValue readVarProperty(int);
-    void writeVarProperty(int, const QScriptValue &);
-#endif
-    QVariant readVarPropertyAsVariant(int);
-    void writeVarProperty(int, const QVariant &);
+    v8::Handle<v8::Value> readVarProperty(int);
+    void writeVarProperty(int, v8::Handle<v8::Value>);
+    QVariant readPropertyAsVariant(int);
+    void writeProperty(int, const QVariant &);
 
     QAbstractDynamicMetaObject *parent;
 
@@ -196,6 +205,9 @@ private:
     static int list_count(QDeclarativeListProperty<QObject> *);
     static QObject *list_at(QDeclarativeListProperty<QObject> *, int);
     static void list_clear(QDeclarativeListProperty<QObject> *);
+
+    friend class QV8GCCallback;
+    friend class QV8QObjectWrapper;
 };
 
 QT_END_NAMESPACE
index 5259ab1..7012ae1 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "qv8engine_p.h"
 
+#include "qv8gccallback_p.h"
 #include "qv8contextwrapper_p.h"
 #include "qv8valuetypewrapper_p.h"
 #include "qv8gccallback_p.h"
@@ -135,6 +136,8 @@ QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
     m_variantWrapper.init(this);
     m_valueTypeWrapper.init(this);
 
+    QV8GCCallback::registerGcPrologueCallback();
+
     {
     v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames"));
     m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v));
index 9481bb5..0c0481f 100644 (file)
@@ -522,6 +522,9 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject
             ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
     }
 
+    if (result->isVMEProperty())
+        return static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject*>(object->metaObject()))->vmeProperty(result->coreIndex);
+
     if (result->isDirect())  {
         return LoadPropertyDirect(engine, object, *result);
     } else {
@@ -589,6 +592,8 @@ static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativ
         PROPERTY_STORE(double, double(value->ToNumber()->Value()));
     } else if (property->propType == QMetaType::QString && value->IsString()) {
         PROPERTY_STORE(QString, engine->toString(value->ToString()));
+    } else if (property->isVMEProperty()) {
+        static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
     } else {
         QVariant v;
         if (property->isQList()) 
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent.qml
new file mode 100644 (file)
index 0000000..36c0254
--- /dev/null
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: first
+    property var vp: Item {
+        id: second
+        property var vp: Item {
+            id: third
+            property var vp: Item {
+                id: fourth
+                property var vp: Item {
+                    id: fifth
+                    property int fifthCanary: 5
+                    property var circ: third.vp
+                    property MyScarceResourceObject srp;
+                    srp: MyScarceResourceObject { id: scarceResourceProvider }
+                    property variant memoryHog: scarceResourceProvider.newScarceResource()
+                }
+            }
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent2.qml
new file mode 100644 (file)
index 0000000..6a49cb9
--- /dev/null
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+// Similar to PVCC.qml except that it has another var property
+// It will have a different metaobject.
+Item {
+    id: first
+    property var anotherVp: 6
+    property var vp: Item {
+        id: second
+        property var vp: Item {
+            id: third
+            property var vp: Item {
+                id: fourth
+                property var vp: Item {
+                    id: fifth
+                    property int fifthCanary: 5
+                    property var circ: third.vp
+                    property MyScarceResourceObject srp;
+                    srp: MyScarceResourceObject { id: scarceResourceProvider }
+                    property variant memoryHog2: scarceResourceProvider.newScarceResource()
+                }
+            }
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent3.qml
new file mode 100644 (file)
index 0000000..a907250
--- /dev/null
@@ -0,0 +1,16 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: rectangle      // will have JS ownership
+    objectName: "rectangle"
+    width: 10
+    height: 10
+    property var rectCanary: 5
+
+    Text {
+        id: text // will have Eventual-JS ownership
+        objectName: "text"
+        property var vp: rectangle
+        property var textCanary: 10
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent4.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent4.qml
new file mode 100644 (file)
index 0000000..9273a52
--- /dev/null
@@ -0,0 +1,28 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: rectangle      // will have JS ownership
+    objectName: "rectangle"
+    width: 10
+    height: 10
+    property var rectCanary: 5
+
+    Text {
+        id: text // will have Eventual-JS ownership
+        objectName: "text"
+        property var vp
+        property var textCanary: 10
+
+        // The varProperties array of "text" is weak
+        // (due to eventual JS ownership since parent is JS owned)
+        // but nonetheless, the reference to the created QObject
+        // should cause that QObject to NOT be collected.
+        function constructQObject() {
+            var component = Qt.createComponent("PropertyVarCircularComponent5.qml");
+            if (component.status == Component.Ready) {
+                text.vp = component.createObject(null); // has JavaScript ownership
+            }
+            gc();
+        }
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent5.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent5.qml
new file mode 100644 (file)
index 0000000..94ef338
--- /dev/null
@@ -0,0 +1,7 @@
+import QtQuick 2.0
+
+Image {
+    id: image
+    objectName: "image"
+    property var imageCanary: 13
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarInheritanceComponent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarInheritanceComponent.qml
new file mode 100644 (file)
index 0000000..b01cf6e
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+PropertyVarCircularComponent {
+    id: inheritanceComponent
+    property int inheritanceIntProperty: 6
+    property var inheritanceVarProperty
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarCircularComponent2.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    Component.onCompleted: {
+        inheritanceVarProperty = constructGarbage();
+        gc();
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarOwnershipComponent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarOwnershipComponent.qml
new file mode 100644 (file)
index 0000000..c1f73d3
--- /dev/null
@@ -0,0 +1,37 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: rectangle      // will have JS ownership
+    objectName: "rectangle"
+    width: 10
+    height: 10
+    property var rectCanary: 5
+
+    Text {
+        id: textOne // will have Eventual-JS ownership
+        objectName: "textOne"
+        property var textCanary: 11
+        property var vp
+    }
+
+    Text {
+        id: textTwo
+        objectName: "textTwo"
+        property var textCanary: 12
+        property var vp
+
+        function constructQObject() {
+            var component = Qt.createComponent("PropertyVarCircularComponent5.qml");
+            if (component.status == Component.Ready) {
+                textTwo.vp = component.createObject(null); // has JavaScript ownership
+            }
+            gc();
+        }
+
+        function deassignVp() {
+            textTwo.textCanary = 22;
+            textTwo.vp = textTwo.textCanary;
+            gc();
+        }
+    }
+}
index 9c27653..8a06c30 100644 (file)
@@ -18,8 +18,4 @@ Item {
         // NOTE: manually add reference from first to second
         // in unit test prior reparenting and gc.
     }
-
-    function performGc() {
-        gc();
-    }
 }
index dc19626..91edc44 100644 (file)
@@ -19,8 +19,4 @@ Item {
         // note: must manually reparent in unit test
         // after setting the handle references.
     }
-
-    function performGc() {
-        gc();
-    }
 }
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.1.qml
new file mode 100644 (file)
index 0000000..219e61b
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property bool test: false
+
+    property var car: new vehicle(4);
+    property int wheelCount: car.wheels
+
+    function vehicle(wheels) {
+        this.wheels = wheels;
+    }
+
+    Component.onCompleted: {
+        car.wheels = 6;  // not bindable, wheelCount shouldn't update
+
+        if (car.wheels != 6) return;
+        if (wheelCount != 4) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.2.qml
new file mode 100644 (file)
index 0000000..2ac4807
--- /dev/null
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property bool test: false
+
+    property var truck: new vehicle(8);
+    property int wheelCount: truck.wheels
+
+    function vehicle(wheels) {
+        this.wheels = wheels;
+    }
+
+    Component.onCompleted: {
+        if (wheelCount != 8) return;
+
+        // not bindable, but wheelCount will update because truck itself changed.
+        truck = new vehicle(12); 
+
+        if (wheelCount != 12) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.3.qml
new file mode 100644 (file)
index 0000000..cf6a651
--- /dev/null
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    property bool test: false
+
+    property var jsint: 4
+    property int bound: jsint + 5
+
+    Component.onCompleted: {
+        if (bound != 9) return;
+
+        jsint = jsint + 1;
+
+        if (bound != 10) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.4.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.4.qml
new file mode 100644 (file)
index 0000000..82fc225
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    property bool test: false
+
+    property var items: [1, 2, 3, "four", "five"]
+    property int bound: items[0]
+
+    Component.onCompleted: {
+        if (bound != 1) return;
+
+        items[0] = 10      // bound should remain 1
+
+        if (bound != 1) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.5.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.5.qml
new file mode 100644 (file)
index 0000000..a5c7812
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    property bool test: false
+
+    property var attributes: { 'color': 'red', 'width': 100 }
+    property int bound: attributes.width
+
+    Component.onCompleted: {
+        if (bound != 100) return;
+
+        attributes.width = 200   // bound should remain 100
+
+        if (bound != 100) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.6.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.6.qml
new file mode 100644 (file)
index 0000000..5197aea
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    property bool test: false
+
+    property var items: [1, 2, 3, "four", "five"]
+    property int bound: items[0]
+
+    Component.onCompleted: {
+        if (bound != 1) return false;
+
+        items = [10, 2, 3, "four", "five"]  // bound should now be 10
+
+        if (bound != 10) return false;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.7.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.7.qml
new file mode 100644 (file)
index 0000000..1d6c8c0
--- /dev/null
@@ -0,0 +1,18 @@
+import QtQuick 2.0
+
+Item {
+    property bool test: false
+
+    property var attributes: { 'color': 'red', 'width': 100 }
+    property int bound: attributes.width
+
+    Component.onCompleted: {
+        if (bound != 100) return;
+
+        attributes = { 'color': 'blue', 'width': 200 }   // bound should now be 200
+
+        if (bound != 200) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.8.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.8.qml
new file mode 100644 (file)
index 0000000..a9f73db
--- /dev/null
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+
+Item {
+    property bool test: false
+
+    property var literalValue: 6
+
+    Component.onCompleted: {
+        if (literalValue != 6) return;
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.9.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.9.qml
new file mode 100644 (file)
index 0000000..f5aca28
--- /dev/null
@@ -0,0 +1,19 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    property bool test: false
+
+    MyQmlObject {
+        id: qmlobject
+        intProperty: 5
+    }
+    property var qobjectVar: qmlobject
+    property int bound: qobjectVar.intProperty
+
+    Component.onCompleted: {
+        if (bound != 5) return;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.2.qml
new file mode 100644 (file)
index 0000000..93c44af
--- /dev/null
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    objectName: "separateRootObject"
+    property var vp
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarCircularComponent3.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    function assignCircular() {
+        vp = constructGarbage();
+        gc();
+    }
+
+    function deassignCircular() {
+        vp = 2;
+        gc();
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.qml
new file mode 100644 (file)
index 0000000..171d774
--- /dev/null
@@ -0,0 +1,44 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: testCircular
+
+    property var varProperty
+    property variant canaryResource
+    property int canaryInt
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarCircularComponent.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    function deassignCanaryResource() {
+        canaryResource = 1;
+        gc();
+    }
+
+    function assignCircular() {
+        varProperty = constructGarbage();
+        canaryResource = varProperty.vp.vp.vp.vp.memoryHog;
+        canaryInt = varProperty.vp.vp.vp.vp.fifthCanary; // == 5
+        gc();
+    }
+
+    function deassignCircular() {
+        canaryInt = 2;
+        varProperty = 2;
+        gc();
+    }
+
+    function assignThenDeassign() {
+        varProperty = constructGarbage();
+        varProperty = 2;
+        gc();
+    }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.inherit.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.inherit.qml
new file mode 100644 (file)
index 0000000..abd0dd7
--- /dev/null
@@ -0,0 +1,34 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: testInheritance
+
+    property var varProperty
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarInheritanceComponent.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    function assignCircular() {
+        varProperty = constructGarbage();
+        gc();
+    }
+
+    function deassignCircular() {
+        varProperty = 2;
+        gc();
+    }
+
+    function assignThenDeassign() {
+        varProperty = constructGarbage();
+        varProperty = 2;
+        gc();
+    }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.reparent.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.reparent.qml
new file mode 100644 (file)
index 0000000..7b3df67
--- /dev/null
@@ -0,0 +1,27 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    objectName: "separateRootObject"
+    property var vp
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarOwnershipComponent.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    function assignVarProp() {
+        vp = constructGarbage();
+        gc();
+    }
+
+    function deassignVarProp() {
+        vp = 2;
+        gc();
+    }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarCpp.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarCpp.qml
new file mode 100644 (file)
index 0000000..cd3147f
--- /dev/null
@@ -0,0 +1,17 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: testOwnership
+    property int intProperty: 10
+    property var varProperty: intProperty
+    property var varProperty2: false
+    property var varBound: varProperty + 5
+    property int intBound: varProperty + 5
+    property var jsobject: new vehicle(4)
+
+    function vehicle(wheels) {
+        this.wheels = wheels;
+    }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarImplicitOwnership.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarImplicitOwnership.qml
new file mode 100644 (file)
index 0000000..9cebded
--- /dev/null
@@ -0,0 +1,26 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+    objectName: "separateRootObject"
+    property var vp
+
+    function constructGarbage() {
+        var retn = 1;
+        var component = Qt.createComponent("PropertyVarCircularComponent4.qml");
+        if (component.status == Component.Ready) {
+            retn = component.createObject(null); // has JavaScript ownership
+        }
+        return retn;
+    }
+
+    function assignCircular() {
+        vp = constructGarbage();
+        gc();
+    }
+
+    function deassignCircular() {
+        vp = 2;
+        gc();
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.2.qml
new file mode 100644 (file)
index 0000000..14d4f9f
--- /dev/null
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: testOwnership
+    property bool test: false
+
+    property int dummyProperty // Tests for non-interference of other properties
+    property var varProperty
+
+    function runTest() {
+        if (varProperty != undefined) return;
+        varProperty = { a: 10, b: 11 }
+        if (varProperty.a != 10) return;
+
+        gc(); // Shouldn't collect
+
+        if (varProperty.a != 10) return;
+
+        test = true;
+    } 
+}
+
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.qml
new file mode 100644 (file)
index 0000000..d5b449c
--- /dev/null
@@ -0,0 +1,31 @@
+import QtQuick 2.0
+
+Item {
+    property var object
+
+    property bool test1: false
+    property bool test2: false
+
+    // Test methods are executed in sequential order
+
+    function runTest() {
+        var c = Qt.createComponent("propertyVarOwnership.3.type.qml");
+        object = c.createObject();
+
+        if (object.dummy != 10) return;
+        test1 = true;
+    }
+
+    // Run gc() from C++ 
+
+    function runTest2() {
+        if (object.dummy != 10) return;
+
+        object = undefined;
+        if (object != undefined) return;
+
+        test2 = true;
+    }
+
+    // Run gc() from C++ - QObject should be collected
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.type.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.type.qml
new file mode 100644 (file)
index 0000000..3406553
--- /dev/null
@@ -0,0 +1,5 @@
+import QtQuick 2.0
+
+QtObject {
+    property int dummy: 10
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.qml
new file mode 100644 (file)
index 0000000..1eba36c
--- /dev/null
@@ -0,0 +1,25 @@
+import QtQuick 2.0
+
+Item {
+    id: root
+
+    property var object
+
+    property bool test: false
+
+    Component.onCompleted: {
+        var c = Qt.createComponent("propertyVarOwnership.4.type1.qml");
+        object = c.createObject();
+
+        if (object.dummy != 10) return;
+        if (object.test != true) return;
+
+        object.creatorRef = root;
+
+        test = true;
+    }
+
+    function runTest() {
+        object = null;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type1.qml
new file mode 100644 (file)
index 0000000..9a29b6e
--- /dev/null
@@ -0,0 +1,23 @@
+import QtQuick 2.0
+
+// Has a self reference in selfRef, and a reference to propertyVarOwnership.4.qml in creatorRef
+Item {
+    id: root
+
+    property var creatorRef
+    property var selfRef
+    property var object
+
+    property int dummy: 10
+    property bool test: false
+
+    Component.onCompleted: {
+        selfRef = root;
+
+        var c = Qt.createComponent("propertyVarOwnership.4.type2.qml");
+        object = c.createObject();
+        object.creatorRef = root;
+
+        test = true;
+    }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type2.qml
new file mode 100644 (file)
index 0000000..f82b8a1
--- /dev/null
@@ -0,0 +1,6 @@
+import QtQuick 2.0
+
+// Has a reference to propertyVarOwnership.4.type1.qml in creatorRef
+Item {
+    property var creatorRef
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.qml b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.qml
new file mode 100644 (file)
index 0000000..7b99c4b
--- /dev/null
@@ -0,0 +1,22 @@
+import QtQuick 2.0
+import Qt.test 1.0
+
+Item {
+    id: testOwnership
+    property bool test: false
+
+    property var varProperty
+
+    function runTest() {
+        if (varProperty != undefined) return;
+        varProperty = { a: 10, b: 11 }
+        if (varProperty.a != 10) return;
+
+        gc(); // Shouldn't collect
+
+        if (varProperty.a != 10) return;
+
+        test = true;
+    } 
+}
+
index ac4cb30..5bd0287 100644 (file)
@@ -164,6 +164,7 @@ void registerTypes()
     qmlRegisterType<MyRevisionedClass,1>("Qt.test",1,1,"MyRevisionedClass");
 
     // test scarce resource property binding post-evaluation optimisation
+    // and for testing memory usage in property var circular reference test
     qmlRegisterType<ScarceResourceObject>("Qt.test", 1,0, "MyScarceResourceObject");
 
     // Register the uncreatable base class
index 3fd6eaf..5357a63 100644 (file)
@@ -98,9 +98,10 @@ class MyQmlObject : public QObject
     Q_PROPERTY(int resettableProperty READ resettableProperty WRITE setResettableProperty RESET resetProperty)
     Q_PROPERTY(QRegExp regExp READ regExp WRITE setRegExp)
     Q_PROPERTY(int nonscriptable READ nonscriptable WRITE setNonscriptable SCRIPTABLE false)
+    Q_PROPERTY(int intProperty READ intProperty WRITE setIntProperty NOTIFY intChanged)
 
 public:
-    MyQmlObject(): myinvokableObject(0), m_methodCalled(false), m_methodIntCalled(false), m_object(0), m_value(0), m_resetProperty(13) {}
+    MyQmlObject(): myinvokableObject(0), m_methodCalled(false), m_methodIntCalled(false), m_object(0), m_value(0), m_resetProperty(13), m_intProperty(0) {}
 
     enum MyEnum { EnumValue1 = 0, EnumValue2 = 1 };
     enum MyEnum2 { EnumValue3 = 2, EnumValue4 = 3 };
@@ -161,6 +162,9 @@ public:
         int value;
     };
     QVariant variant() const { return m_variant; }
+
+    int intProperty() const { return m_intProperty; }
+    void setIntProperty(int i) { m_intProperty = i; emit intChanged(); }
     
 signals:
     void basicSignal();
@@ -171,6 +175,7 @@ signals:
     void thirdBasicSignal();
     void signalWithUnknownType(const MyQmlObject::MyType &arg);
     void signalWithVariant(const QVariant &arg);
+    void intChanged();
 
 public slots:
     void deleteMe() { delete this; }
@@ -192,6 +197,7 @@ private:
     int m_resetProperty;
     QRegExp m_regExp;
     QVariant m_variant;
+    int m_intProperty;
 };
 
 QML_DECLARE_TYPEINFO(MyQmlObject, QML_HAS_ATTACHED_PROPERTIES)
@@ -928,12 +934,24 @@ public:
 
     bool scarceResourceIsDetached() const { return m_value.isDetached(); }
 
+    // this particular one returns a new one each time
+    // this means that every Scarce Resource Copy will
+    // consume resources (so that we can track disposal
+    // of v8 handles with circular references).
+    Q_INVOKABLE QPixmap newScarceResource() const
+    {
+        QPixmap retn(800, 600);
+        retn.fill(QColor(100, 110, 120, 45));
+        return retn;
+    }
+
 signals:
     void scarceResourceChanged();
 
 private:
     QPixmap m_value;
 };
+QML_DECLARE_TYPE(ScarceResourceObject)
 
 class testQObjectApi : public QObject
 {
index 5739ec9..d6a2f0a 100644 (file)
@@ -49,6 +49,8 @@
 #include <QtCore/qdir.h>
 #include <QtCore/qnumeric.h>
 #include <private/qdeclarativeengine_p.h>
+#include <private/qv8gccallback_p.h>
+#include <private/qdeclarativevmemetaobject_p.h>
 #include "testtypes.h"
 #include "testhttpserver.h"
 #include "../../../shared/util.h"
@@ -151,6 +153,17 @@ private slots:
     void importScripts();
     void scarceResources();
     void propertyChangeSlots();
+    void propertyVar_data();
+    void propertyVar();
+    void propertyVarCpp();
+    void propertyVarOwnership();
+    void propertyVarImplicitOwnership();
+    void propertyVarReparent();
+    void propertyVarReparentNullContext();
+    void propertyVarCircular();
+    void propertyVarCircular2();
+    void propertyVarInheritance();
+    void propertyVarInheritance2();
     void elementAssign();
     void objectPassThroughSignals();
     void objectConversion();
@@ -205,6 +218,7 @@ private slots:
     void automaticSemicolon();
 
 private:
+    static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
     QDeclarativeEngine engine;
 };
 
@@ -3337,6 +3351,370 @@ void tst_qdeclarativeecmascript::propertyChangeSlots()
     delete object;
 }
 
+void tst_qdeclarativeecmascript::propertyVar_data()
+{
+    QTest::addColumn<QUrl>("qmlFile");
+
+    // valid
+    QTest::newRow("non-bindable object subproperty changed") << TEST_FILE("propertyVar.1.qml");
+    QTest::newRow("non-bindable object changed") << TEST_FILE("propertyVar.2.qml");
+    QTest::newRow("primitive changed") << TEST_FILE("propertyVar.3.qml");
+    QTest::newRow("javascript array modification") << TEST_FILE("propertyVar.4.qml");
+    QTest::newRow("javascript map modification") << TEST_FILE("propertyVar.5.qml");
+    QTest::newRow("javascript array assignment") << TEST_FILE("propertyVar.6.qml");
+    QTest::newRow("javascript map assignment") << TEST_FILE("propertyVar.7.qml");
+    QTest::newRow("literal property assignment") << TEST_FILE("propertyVar.8.qml");
+    QTest::newRow("qobject property assignment") << TEST_FILE("propertyVar.9.qml");
+}
+
+void tst_qdeclarativeecmascript::propertyVar()
+{
+    QFETCH(QUrl, qmlFile);
+
+    QDeclarativeComponent component(&engine, qmlFile);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    delete object;
+}
+
+// Tests that we can write QVariant values to var properties from C++
+void tst_qdeclarativeecmascript::propertyVarCpp()
+{
+    QObject *object = 0;
+
+    // ensure that writing to and reading from a var property from cpp works as required.
+    // Literal values stored in var properties can be read and written as QVariants
+    // of a specific type, whereas object values are read as QVariantMaps.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarCpp.qml"));
+    object = component.create();
+    QVERIFY(object != 0);
+    // assign int to property var that currently has int assigned
+    QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
+    QCOMPARE(object->property("varBound"), QVariant(15));
+    QCOMPARE(object->property("intBound"), QVariant(15));
+    QCOMPARE(object->property("varProperty").userType(), (int)QVariant::Int);
+    QCOMPARE(object->property("varBound").userType(), (int)QVariant::Int);
+    // assign string to property var that current has bool assigned
+    QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
+    QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
+    QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
+    QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
+    // now enforce behaviour when accessing JavaScript objects from cpp.
+    QCOMPARE(object->property("jsobject").userType(), (int)QVariant::Map);
+    delete object;
+}
+
+static void gc(QDeclarativeEngine &engine)
+{
+    engine.collectGarbage();
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+}
+
+void tst_qdeclarativeecmascript::propertyVarOwnership()
+{
+    // Referenced JS objects are not collected
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("test").toBool(), false);
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test").toBool(), true);
+    delete object;
+    }
+    // Referenced JS objects are not collected
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QCOMPARE(object->property("test").toBool(), false);
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test").toBool(), true);
+    delete object;
+    }
+    // Qt objects are not collected until they've been dereferenced
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.3.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test2").toBool(), false);
+    QCOMPARE(object->property("test2").toBool(), false);
+
+    QMetaObject::invokeMethod(object, "runTest");
+    QCOMPARE(object->property("test1").toBool(), true);
+
+    QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
+    QVERIFY(!referencedObject.isNull());
+    gc(engine);
+    QVERIFY(!referencedObject.isNull());
+
+    QMetaObject::invokeMethod(object, "runTest2");
+    QCOMPARE(object->property("test2").toBool(), true);
+    gc(engine);
+    QVERIFY(referencedObject.isNull());
+
+    delete object;
+    }
+    // Self reference does not prevent Qt object collection
+    {
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarOwnership.4.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test").toBool(), true);
+
+    QPointer<QObject> referencedObject = object->property("object").value<QObject*>();
+    QVERIFY(!referencedObject.isNull());
+    gc(engine);
+    QVERIFY(!referencedObject.isNull());
+
+    QMetaObject::invokeMethod(object, "runTest");
+    gc(engine);
+    QVERIFY(referencedObject.isNull());
+
+    delete object;
+    }
+}
+
+void tst_qdeclarativeecmascript::propertyVarImplicitOwnership()
+{
+    // The childObject has a reference to a different QObject.  We want to ensure
+    // that the different item will not be cleaned up until required.  IE, the childObject
+    // has implicit ownership of the constructed QObject.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVarImplicitOwnership.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    QMetaObject::invokeMethod(childObject, "constructQObject");    // creates a reference to a constructed QObject.
+    QWeakPointer<QObject> qobjectGuard(childObject->property("vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
+    QVERIFY(!qobjectGuard.isNull());
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!qobjectGuard.isNull());
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(qobjectGuard.isNull());                                // should have been collected now.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarReparent()
+{
+    // ensure that nothing breaks if we re-parent objects
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignVarProp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rect = object->property("vp").value<QObject*>();
+    QObject *text = rect->findChild<QObject*>("textOne");
+    QObject *text2 = rect->findChild<QObject*>("textTwo");
+    QWeakPointer<QObject> rectGuard(rect);
+    QWeakPointer<QObject> textGuard(text);
+    QWeakPointer<QObject> text2Guard(text2);
+    QVERIFY(!rectGuard.isNull());
+    QVERIFY(!textGuard.isNull());
+    QVERIFY(!text2Guard.isNull());
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 12);
+    // now construct an image which we will reparent.
+    QMetaObject::invokeMethod(text2, "constructQObject");
+    QObject *image = text2->property("vp").value<QObject*>();
+    QWeakPointer<QObject> imageGuard(image);
+    QVERIFY(!imageGuard.isNull());
+    QCOMPARE(image->property("imageCanary").toInt(), 13);
+    // now reparent the "Image" object (currently, it has JS ownership)
+    image->setParent(text);                                        // shouldn't be collected after deassignVp now, since has a parent.
+    QMetaObject::invokeMethod(text2, "deassignVp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 22);
+    QVERIFY(!imageGuard.isNull());                                 // should still be alive.
+    QCOMPARE(image->property("imageCanary").toInt(), 13);          // still able to access var properties
+    QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(imageGuard.isNull());                                  // should now have been deleted, due to parent being deleted.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarReparentNullContext()
+{
+    // sometimes reparenting can cause problems
+    // (eg, if the ctxt is collected, varproperties are no longer available)
+    // this test ensures that no crash occurs in that situation.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.reparent.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignVarProp");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rect = object->property("vp").value<QObject*>();
+    QObject *text = rect->findChild<QObject*>("textOne");
+    QObject *text2 = rect->findChild<QObject*>("textTwo");
+    QWeakPointer<QObject> rectGuard(rect);
+    QWeakPointer<QObject> textGuard(text);
+    QWeakPointer<QObject> text2Guard(text2);
+    QVERIFY(!rectGuard.isNull());
+    QVERIFY(!textGuard.isNull());
+    QVERIFY(!text2Guard.isNull());
+    QCOMPARE(text->property("textCanary").toInt(), 11);
+    QCOMPARE(text2->property("textCanary").toInt(), 12);
+    // now construct an image which we will reparent.
+    QMetaObject::invokeMethod(text2, "constructQObject");
+    QObject *image = text2->property("vp").value<QObject*>();
+    QWeakPointer<QObject> imageGuard(image);
+    QVERIFY(!imageGuard.isNull());
+    QCOMPARE(image->property("imageCanary").toInt(), 13);
+    // now reparent the "Image" object (currently, it has JS ownership)
+    image->setParent(object);                                      // reparented to base object.  after deassignVarProp, the ctxt will be invalid.
+    QMetaObject::invokeMethod(object, "deassignVarProp");          // now deassign the root-object's vp, causing gc of rect+text+text2
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!imageGuard.isNull());                                 // should still be alive.
+    QVERIFY(!image->property("imageCanary").isValid());            // but varProperties won't be available (null context).
+    delete object;
+    QVERIFY(imageGuard.isNull());                                  // should now be dead.
+}
+
+void tst_qdeclarativeecmascript::propertyVarCircular()
+{
+    // enforce behaviour regarding circular references - ensure qdvmemo deletion.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(object->property("canaryInt"), QVariant(5));
+    QVariant canaryResourceVariant = object->property("canaryResource");
+    QVERIFY(canaryResourceVariant.isValid());
+    QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
+    canaryResourceVariant = QVariant();                            // invalidate it to remove one copy of the pixmap from memory.
+    QMetaObject::invokeMethod(object, "deassignCanaryResource");   // remove one copy of the pixmap from memory
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(!canaryResourcePixmap.isDetached());                   // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
+    QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QCOMPARE(object->property("canaryInt"), QVariant(2));
+    QCOMPARE(object->property("canaryResource"), QVariant(1));
+    QVERIFY(canaryResourcePixmap.isDetached());                    // now detached, since orig copy was member of qdvmemo which was deleted.
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarCircular2()
+{
+    // track deletion of JS-owned parent item with Cpp-owned child
+    // where the child has a var property referencing its parent.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QWeakPointer<QObject> rootObjectTracker(rootObject);
+    QVERIFY(!rootObjectTracker.isNull());
+    QWeakPointer<QObject> childObjectTracker(childObject);
+    QVERIFY(!childObjectTracker.isNull());
+    gc(engine);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(rootObjectTracker.isNull());                           // should have been collected
+    QVERIFY(childObjectTracker.isNull());                          // should have been collected
+    delete object;
+}
+
+void tst_qdeclarativeecmascript::propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter)
+{
+    *(int*)(parameter) += 1;
+    qPersistentDispose(object);
+}
+
+void tst_qdeclarativeecmascript::propertyVarInheritance()
+{
+    int propertyVarWeakRefCallbackCount = 0;
+
+    // enforce behaviour regarding element inheritance - ensure handle disposal.
+    // The particular component under test here has a chain of references.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.inherit.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");           // cause assignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    // we want to be able to track when the varProperties array of the last metaobject is disposed
+    QObject *cco5 = object->property("varProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
+    QObject *ico5 = object->property("varProperty").value<QObject*>()->property("inheritanceVarProperty").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>()->property("vp").value<QObject*>();
+    QDeclarativeVMEMetaObject *icovmemo = ((QDeclarativeVMEMetaObject *)(ico5->metaObject()));
+    QDeclarativeVMEMetaObject *ccovmemo = ((QDeclarativeVMEMetaObject *)(cco5->metaObject()));
+    v8::Persistent<v8::Value> icoCanaryHandle;
+    v8::Persistent<v8::Value> ccoCanaryHandle;
+    {
+        v8::HandleScope hs;
+        // XXX NOTE: this is very implementation dependent.  QDVMEMO->vmeProperty() is the only
+        // public function which can return us a handle to something in the varProperties array.
+        icoCanaryHandle = qPersistentNew(icovmemo->vmeProperty(41));
+        ccoCanaryHandle = qPersistentNew(ccovmemo->vmeProperty(41));
+        // we make them weak and invoke the gc, but we should not hit the weak-callback yet
+        // as the varproperties array of each vmemo still references the resource.
+        icoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        ccoCanaryHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        gc(engine);
+        QVERIFY(propertyVarWeakRefCallbackCount == 0);
+    }
+    // now we deassign the var prop, which should trigger collection of item subtrees.
+    QMetaObject::invokeMethod(object, "deassignCircular");         // cause deassignment and gc
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    // ensure that there are only weak handles to the underlying varProperties array remaining.
+    gc(engine);
+    QCOMPARE(propertyVarWeakRefCallbackCount, 2);                  // should have been called for both, since all refs should be weak.
+    delete object;
+    // since there are no parent vmemo's to keep implicit references alive, and the only handles
+    // to what remains are weak, all varProperties arrays must have been collected.
+}
+
+void tst_qdeclarativeecmascript::propertyVarInheritance2()
+{
+    int propertyVarWeakRefCallbackCount = 0;
+
+    // The particular component under test here does NOT have a chain of references; the
+    // only link between rootObject and childObject is that rootObject is the parent of childObject.
+    QDeclarativeComponent component(&engine, TEST_FILE("propertyVar.circular.2.qml"));
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+    QMetaObject::invokeMethod(object, "assignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QObject *rootObject = object->property("vp").value<QObject*>();
+    QVERIFY(rootObject != 0);
+    QObject *childObject = rootObject->findChild<QObject*>("text");
+    QVERIFY(childObject != 0);
+    QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
+    QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    v8::Persistent<v8::Value> childObjectVarArrayValueHandle;
+    {
+        v8::HandleScope hs;
+        propertyVarWeakRefCallbackCount = 0;                           // reset callback count.
+        childObjectVarArrayValueHandle = qPersistentNew(((QDeclarativeVMEMetaObject *)(childObject->metaObject()))->vmeProperty(58));
+        childObjectVarArrayValueHandle.MakeWeak(&propertyVarWeakRefCallbackCount, propertyVarWeakRefCallback);
+        gc(engine);
+        QVERIFY(propertyVarWeakRefCallbackCount == 0);                 // should not have been collected yet.
+        QCOMPARE(childObject->property("textCanary").toInt(), 10);
+    }
+    QMetaObject::invokeMethod(object, "deassignCircular");
+    QCoreApplication::processEvents(QEventLoop::DeferredDeletion); // process deleteLater() events from QV8QObjectWrapper.
+    QVERIFY(propertyVarWeakRefCallbackCount == 1);                 // should have been collected now.
+    delete object;
+}
+
 // Ensure that QObject type conversion works on binding assignment
 void tst_qdeclarativeecmascript::elementAssign()
 {
@@ -3412,8 +3790,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
         cro->setDtorCount(&dtorCount);
         QMetaObject::invokeMethod(object, "createReference");
-        QMetaObject::invokeMethod(object, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
         delete object;
         hrmEngine.collectGarbage();
@@ -3431,8 +3808,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro");
         cro->setDtorCount(&dtorCount);
         QMetaObject::invokeMethod(object, "circularReference");
-        QMetaObject::invokeMethod(object, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
         delete object;
         hrmEngine.collectGarbage();
@@ -3459,8 +3835,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         // now we have to reparent second and make second owned by JS.
         second->setParent(0);
         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
-        QMetaObject::invokeMethod(object, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 0); // due to reference from first to second, second shouldn't be collected.
         delete object;
         hrmEngine.collectGarbage();
@@ -3490,8 +3865,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         second->setParent(0);
         QDeclarativeEngine::setObjectOwnership(first, QDeclarativeEngine::JavaScriptOwnership);
         QDeclarativeEngine::setObjectOwnership(second, QDeclarativeEngine::JavaScriptOwnership);
-        QMetaObject::invokeMethod(object, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 2); // despite circular references, both will be collected.
         delete object;
         hrmEngine.collectGarbage();
@@ -3530,8 +3904,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         // now we have to reparent second2 and make second2 owned by JS.
         second2->setParent(0);
         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
-        QMetaObject::invokeMethod(object1, "performGc");
-        QMetaObject::invokeMethod(object2, "performGc");
+        gc(engine);
         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
         QCOMPARE(dtorCount, 0); // due to reference from first1 to second2, second2 shouldn't be collected.
         delete object1;
@@ -3582,8 +3955,7 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
-        QMetaObject::invokeMethod(object1, "performGc");
-        QMetaObject::invokeMethod(object2, "performGc");
+        gc(engine);
         QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
         QCOMPARE(dtorCount, 4); // circular references shouldn't keep them alive.
         delete object1;
@@ -3632,13 +4004,10 @@ void tst_qdeclarativeecmascript::handleReferenceManagement()
         QDeclarativeEngine::setObjectOwnership(second1, QDeclarativeEngine::JavaScriptOwnership);
         QDeclarativeEngine::setObjectOwnership(first2, QDeclarativeEngine::JavaScriptOwnership);
         QDeclarativeEngine::setObjectOwnership(second2, QDeclarativeEngine::JavaScriptOwnership);
-        QMetaObject::invokeMethod(object1, "performGc");
-        QMetaObject::invokeMethod(object2, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 0);
         delete hrmEngine2;
-        QMetaObject::invokeMethod(object1, "performGc");
-        QCoreApplication::processEvents(QEventLoop::DeferredDeletion);
+        gc(engine);
         QCOMPARE(dtorCount, 0);
         delete object1;
         delete object2;
diff --git a/tests/auto/declarative/qdeclarativelanguage/data/assignLiteralToVar.qml b/tests/auto/declarative/qdeclarativelanguage/data/assignLiteralToVar.qml
new file mode 100644 (file)
index 0000000..65826dc
--- /dev/null
@@ -0,0 +1,32 @@
+// This tests assigning literals to "var" properties.
+// These properties store JavaScript object references.
+
+import QtQuick 1.0
+
+QtObject {
+    property var test1: 1
+    property var test2: 1.7
+    property var test3: "Hello world!"
+    property var test4: "#FF008800"
+    property var test5: "10,10,10x10"
+    property var test6: "10,10"
+    property var test7: "10x10"
+    property var test8: "100,100,100"
+    property var test9: String("#FF008800")
+    property var test10: true
+    property var test11: false
+
+    property variant variantTest1Bound: test1 + 4 // 1 + 4 + 4 = 9
+
+    property var test12: Qt.rgba(0.2, 0.3, 0.4, 0.5)
+    property var test13: Qt.rect(10, 10, 10, 10)
+    property var test14: Qt.point(10, 10)
+    property var test15: Qt.size(10, 10)
+    property var test16: Qt.vector3d(100, 100, 100)
+
+    property var test1Bound: test1 + 6 // 1 + 4 + 6 = 11
+
+    Component.onCompleted: {
+        test1 = test1 + 4;
+    }
+}
index f7e74e8..a06be02 100644 (file)
@@ -121,6 +121,7 @@ private slots:
     void assignTypeExtremes();
     void assignCompositeToType();
     void assignLiteralToVariant();
+    void assignLiteralToVar();
     void customParserTypes();
     void rootAsQmlComponent();
     void inlineQmlComponents();
@@ -648,6 +649,57 @@ void tst_qdeclarativelanguage::assignLiteralToVariant()
     delete object;
 }
 
+// Test that literals are stored correctly in "var" properties
+// Note that behaviour differs from "variant" properties in that
+// no conversion from "special strings" to QVariants is performed.
+void tst_qdeclarativelanguage::assignLiteralToVar()
+{
+    QDeclarativeComponent component(&engine, TEST_FILE("assignLiteralToVar.qml"));
+    VERIFY_ERRORS(0);
+    QObject *object = component.create();
+    QVERIFY(object != 0);
+
+    QCOMPARE(object->property("test1").userType(), (int)QMetaType::Int);
+    QCOMPARE(object->property("test2").userType(), (int)QMetaType::Double);
+    QCOMPARE(object->property("test3").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test4").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test5").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test6").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test7").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test8").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test9").userType(), (int)QVariant::String);
+    QCOMPARE(object->property("test10").userType(), (int)QVariant::Bool);
+    QCOMPARE(object->property("test11").userType(), (int)QVariant::Bool);
+    QCOMPARE(object->property("test12").userType(), (int)QVariant::Color);
+    QCOMPARE(object->property("test13").userType(), (int)QVariant::RectF);
+    QCOMPARE(object->property("test14").userType(), (int)QVariant::PointF);
+    QCOMPARE(object->property("test15").userType(), (int)QVariant::SizeF);
+    QCOMPARE(object->property("test16").userType(), (int)QVariant::Vector3D);
+    QCOMPARE(object->property("variantTest1Bound").userType(), (int)QMetaType::Int);
+    QCOMPARE(object->property("test1Bound").userType(), (int)QMetaType::Int);
+
+    QCOMPARE(object->property("test1"), QVariant(5));
+    QCOMPARE(object->property("test2"), QVariant((double)1.7));
+    QCOMPARE(object->property("test3"), QVariant(QString(QLatin1String("Hello world!"))));
+    QCOMPARE(object->property("test4"), QVariant(QString(QLatin1String("#FF008800"))));
+    QCOMPARE(object->property("test5"), QVariant(QString(QLatin1String("10,10,10x10"))));
+    QCOMPARE(object->property("test6"), QVariant(QString(QLatin1String("10,10"))));
+    QCOMPARE(object->property("test7"), QVariant(QString(QLatin1String("10x10"))));
+    QCOMPARE(object->property("test8"), QVariant(QString(QLatin1String("100,100,100"))));
+    QCOMPARE(object->property("test9"), QVariant(QString(QLatin1String("#FF008800"))));
+    QCOMPARE(object->property("test10"), QVariant(bool(true)));
+    QCOMPARE(object->property("test11"), QVariant(bool(false)));
+    QCOMPARE(object->property("test12"), QVariant(QColor::fromRgbF(0.2, 0.3, 0.4, 0.5)));
+    QCOMPARE(object->property("test13"), QVariant(QRectF(10, 10, 10, 10)));
+    QCOMPARE(object->property("test14"), QVariant(QPointF(10, 10)));
+    QCOMPARE(object->property("test15"), QVariant(QSizeF(10, 10)));
+    QCOMPARE(object->property("test16"), QVariant(QVector3D(100, 100, 100)));
+    QCOMPARE(object->property("variantTest1Bound"), QVariant(9));
+    QCOMPARE(object->property("test1Bound"), QVariant(11));
+
+    delete object;
+}
+
 // Tests that custom parser types can be instantiated
 void tst_qdeclarativelanguage::customParserTypes()
 {