aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/declarative/qdeclarativeecmascript
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 /tests/auto/declarative/qdeclarativeecmascript
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>
Diffstat (limited to 'tests/auto/declarative/qdeclarativeecmascript')
-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
36 files changed, 1043 insertions, 34 deletions
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;