aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Adams <christopher.adams@nokia.com>2011-09-30 11:14:10 +1000
committerQt by Nokia <qt-info@nokia.com>2011-10-06 05:29:00 +0200
commit752cd2aca42f6625f1cfc364937e0d39828cf954 (patch)
treedcb8891d7ff0d99d7bcbf948ed6339c4cce6b257
parent6bd1704c42f564980677682e1d47e91129d94e5c (diff)
Add JavaScript "var" property type to QML
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>
-rw-r--r--doc/src/declarative/basictypes.qdoc59
-rw-r--r--doc/src/declarative/whatsnew.qdoc3
-rw-r--r--src/declarative/qml/qdeclarativecompiler.cpp119
-rw-r--r--src/declarative/qml/qdeclarativeinstruction_p.h5
-rw-r--r--src/declarative/qml/qdeclarativeproperty.cpp16
-rw-r--r--src/declarative/qml/qdeclarativeproperty_p.h1
-rw-r--r--src/declarative/qml/qdeclarativepropertycache_p.h18
-rw-r--r--src/declarative/qml/qdeclarativescript.cpp3
-rw-r--r--src/declarative/qml/qdeclarativescript_p.h4
-rw-r--r--src/declarative/qml/qdeclarativevme.cpp48
-rw-r--r--src/declarative/qml/qdeclarativevmemetaobject.cpp166
-rw-r--r--src/declarative/qml/qdeclarativevmemetaobject_p.h34
-rw-r--r--src/declarative/qml/v8/qv8engine.cpp3
-rw-r--r--src/declarative/qml/v8/qv8qobjectwrapper.cpp5
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent.qml23
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent2.qml26
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent3.qml16
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent4.qml28
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent5.qml7
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarInheritanceComponent.qml22
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarOwnershipComponent.qml37
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml4
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml4
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml4
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml4
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.1.qml22
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.2.qml24
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.3.qml19
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.4.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.5.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.6.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.7.qml18
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.8.qml12
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.9.qml19
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.2.qml26
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.qml44
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.inherit.qml34
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.reparent.qml27
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarCpp.qml17
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarImplicitOwnership.qml26
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.2.qml24
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.qml31
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.type.qml5
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.qml25
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type1.qml23
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type2.qml6
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.qml22
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp1
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/testtypes.h20
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp403
-rw-r--r--tests/auto/declarative/qdeclarativelanguage/data/assignLiteralToVar.qml32
-rw-r--r--tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp52
52 files changed, 1519 insertions, 126 deletions
diff --git a/doc/src/declarative/basictypes.qdoc b/doc/src/declarative/basictypes.qdoc
index 0133ab5efe..1bc13739fa 100644
--- a/doc/src/declarative/basictypes.qdoc
+++ b/doc/src/declarative/basictypes.qdoc
@@ -422,14 +422,69 @@
\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 {
diff --git a/doc/src/declarative/whatsnew.qdoc b/doc/src/declarative/whatsnew.qdoc
index a9a1ecb25f..4efe0da2ff 100644
--- a/doc/src/declarative/whatsnew.qdoc
+++ b/doc/src/declarative/whatsnew.qdoc
@@ -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
diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp
index dc992b5b61..c736c3f98c 100644
--- a/src/declarative/qml/qdeclarativecompiler.cpp
+++ b/src/declarative/qml/qdeclarativecompiler.cpp
@@ -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);
}
diff --git a/src/declarative/qml/qdeclarativeinstruction_p.h b/src/declarative/qml/qdeclarativeinstruction_p.h
index e64ca5d3c7..b6efb1937c 100644
--- a/src/declarative/qml/qdeclarativeinstruction_p.h
+++ b/src/declarative/qml/qdeclarativeinstruction_p.h
@@ -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) \
diff --git a/src/declarative/qml/qdeclarativeproperty.cpp b/src/declarative/qml/qdeclarativeproperty.cpp
index acc2cfb752..6e5e7122b9 100644
--- a/src/declarative/qml/qdeclarativeproperty.cpp
+++ b/src/declarative/qml/qdeclarativeproperty.cpp
@@ -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)
{
diff --git a/src/declarative/qml/qdeclarativeproperty_p.h b/src/declarative/qml/qdeclarativeproperty_p.h
index d05e15558f..190cf2a694 100644
--- a/src/declarative/qml/qdeclarativeproperty_p.h
+++ b/src/declarative/qml/qdeclarativeproperty_p.h
@@ -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 &,
diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h
index fefcf7f6d0..c1610f8db8 100644
--- a/src/declarative/qml/qdeclarativepropertycache_p.h
+++ b/src/declarative/qml/qdeclarativepropertycache_p.h
@@ -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; }
diff --git a/src/declarative/qml/qdeclarativescript.cpp b/src/declarative/qml/qdeclarativescript.cpp
index 8fb423c308..2bc358a579 100644
--- a/src/declarative/qml/qdeclarativescript.cpp
+++ b/src/declarative/qml/qdeclarativescript.cpp
@@ -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]);
diff --git a/src/declarative/qml/qdeclarativescript_p.h b/src/declarative/qml/qdeclarativescript_p.h
index e27024161b..c5b9a7e3f6 100644
--- a/src/declarative/qml/qdeclarativescript_p.h
+++ b/src/declarative/qml/qdeclarativescript_p.h
@@ -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;
diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp
index 8cc8fa0247..8f34fb5dc6 100644
--- a/src/declarative/qml/qdeclarativevme.cpp
+++ b/src/declarative/qml/qdeclarativevme.cpp
@@ -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();
diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp
index bcd46f259e..abf73490d1 100644
--- a/src/declarative/qml/qdeclarativevmemetaobject.cpp
+++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp
@@ -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
{
diff --git a/src/declarative/qml/qdeclarativevmemetaobject_p.h b/src/declarative/qml/qdeclarativevmemetaobject_p.h
index 991c79a4ef..0c02b7632c 100644
--- a/src/declarative/qml/qdeclarativevmemetaobject_p.h
+++ b/src/declarative/qml/qdeclarativevmemetaobject_p.h
@@ -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
diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp
index 5259ab19b4..7012ae14c5 100644
--- a/src/declarative/qml/v8/qv8engine.cpp
+++ b/src/declarative/qml/v8/qv8engine.cpp
@@ -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));
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
index 9481bb54fe..0c0481f1fc 100644
--- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
@@ -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
index 0000000000..36c025401f
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent.qml
@@ -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
index 0000000000..6a49cb9317
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent2.qml
@@ -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
index 0000000000..a90725016e
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent3.qml
@@ -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
index 0000000000..9273a52f54
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent4.qml
@@ -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
index 0000000000..94ef338792
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarCircularComponent5.qml
@@ -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
index 0000000000..b01cf6ed84
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarInheritanceComponent.qml
@@ -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
index 0000000000..c1f73d3bac
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/PropertyVarOwnershipComponent.qml
@@ -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();
+ }
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml
index 9c27653b34..8a06c30d8c 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.1.qml
@@ -18,8 +18,4 @@ Item {
// NOTE: manually add reference from first to second
// in unit test prior reparenting and gc.
}
-
- function performGc() {
- gc();
- }
}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml
index dc196263b4..91edc447e2 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.handle.2.qml
@@ -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/handleReferenceManagement.object.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml
index 4fd1311c29..70e8390677 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.1.qml
@@ -24,8 +24,4 @@ Item {
first = cro;
second = cro;
}
-
- function performGc() {
- gc();
- }
}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml
index 3f8415ce0f..2ddb9253eb 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/handleReferenceManagement.object.2.qml
@@ -25,8 +25,4 @@ Item {
first = cro;
second = cro;
}
-
- 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
index 0000000000..219e61bf91
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.1.qml
@@ -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
index 0000000000..2ac4807ec5
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.2.qml
@@ -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
index 0000000000..cf6a651639
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.3.qml
@@ -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
index 0000000000..82fc225e71
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.4.qml
@@ -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
index 0000000000..a5c7812289
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.5.qml
@@ -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
index 0000000000..5197aeada7
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.6.qml
@@ -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
index 0000000000..1d6c8c0a37
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.7.qml
@@ -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
index 0000000000..a9f73db402
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.8.qml
@@ -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
index 0000000000..f5aca28417
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.9.qml
@@ -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
index 0000000000..93c44afcc9
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.2.qml
@@ -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
index 0000000000..171d7747cd
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.circular.qml
@@ -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
index 0000000000..abd0dd7c04
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.inherit.qml
@@ -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
index 0000000000..7b3df674f1
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVar.reparent.qml
@@ -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
index 0000000000..cd3147f565
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarCpp.qml
@@ -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
index 0000000000..9cebded932
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarImplicitOwnership.qml
@@ -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
index 0000000000..14d4f9fd27
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.2.qml
@@ -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
index 0000000000..d5b449c938
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.qml
@@ -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
index 0000000000..3406553b91
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.3.type.qml
@@ -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
index 0000000000..1eba36ce81
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.qml
@@ -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
index 0000000000..9a29b6e17f
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type1.qml
@@ -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
index 0000000000..f82b8a1c1e
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.4.type2.qml
@@ -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
index 0000000000..7b99c4b6ad
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/propertyVarOwnership.qml
@@ -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;
+ }
+}
+
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
index ac4cb308d1..5bd0287c1b 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp
@@ -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
diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
index 3fd6eaf02d..5357a63678 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
+++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h
@@ -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
{
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index 5739ec9817..d6a2f0a5bd 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -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
index 0000000000..65826dcc87
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativelanguage/data/assignLiteralToVar.qml
@@ -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;
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
index f7e74e80f7..a06be0250b 100644
--- a/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
+++ b/tests/auto/declarative/qdeclarativelanguage/tst_qdeclarativelanguage.cpp
@@ -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()
{