1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 basysKom GmbH. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include <qtest.h> |
30 | #include <QQmlEngine> |
31 | #include <QLoggingCategory> |
32 | #include <QQmlComponent> |
33 | |
34 | #include <private/qv4mm_p.h> |
35 | #include <private/qv4qobjectwrapper_p.h> |
36 | #include <private/qjsvalue_p.h> |
37 | |
38 | #include "../../shared/util.h" |
39 | |
40 | #include <memory> |
41 | |
42 | class tst_qv4mm : public QQmlDataTest |
43 | { |
44 | Q_OBJECT |
45 | |
46 | private slots: |
47 | void gcStats(); |
48 | void multiWrappedQObjects(); |
49 | void accessParentOnDestruction(); |
50 | void clearICParent(); |
51 | }; |
52 | |
53 | void tst_qv4mm::gcStats() |
54 | { |
55 | QLoggingCategory::setFilterRules("qt.qml.gc.*=true" ); |
56 | QQmlEngine engine; |
57 | engine.collectGarbage(); |
58 | } |
59 | |
60 | void tst_qv4mm::multiWrappedQObjects() |
61 | { |
62 | QV4::ExecutionEngine engine1; |
63 | QV4::ExecutionEngine engine2; |
64 | { |
65 | QObject object; |
66 | for (int i = 0; i < 10; ++i) |
67 | QV4::QObjectWrapper::wrap(engine: i % 2 ? &engine1 : &engine2, object: &object); |
68 | |
69 | QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
70 | QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
71 | { |
72 | QV4::WeakValue value; |
73 | value.set(engine: &engine1, value: QV4::QObjectWrapper::wrap(engine: &engine1, object: &object)); |
74 | } |
75 | |
76 | QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1); |
77 | QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
78 | |
79 | // The additional WeakValue from m_multiplyWrappedQObjects hasn't been moved |
80 | // to m_pendingFreedObjectWrapperValue yet. It's still alive after all. |
81 | engine1.memoryManager->runGC(); |
82 | QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 1); |
83 | |
84 | // engine2 doesn't own the object as engine1 was the first to wrap it above. |
85 | // Therefore, no effect here. |
86 | engine2.memoryManager->runGC(); |
87 | QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
88 | } |
89 | |
90 | // Clears m_pendingFreedObjectWrapperValue. Now it's really dead. |
91 | engine1.memoryManager->runGC(); |
92 | QCOMPARE(engine1.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
93 | |
94 | engine2.memoryManager->runGC(); |
95 | QCOMPARE(engine2.memoryManager->m_pendingFreedObjectWrapperValue.size(), 0); |
96 | } |
97 | |
98 | void tst_qv4mm::accessParentOnDestruction() |
99 | { |
100 | QLoggingCategory::setFilterRules("qt.qml.gc.*=false" ); |
101 | QQmlEngine engine; |
102 | QQmlComponent component(&engine, testFileUrl(fileName: "createdestroy.qml" )); |
103 | std::unique_ptr<QObject> obj(component.create()); |
104 | QVERIFY(obj); |
105 | QPointer<QObject> timer = qvariant_cast<QObject *>(v: obj->property(name: "timer" )); |
106 | QVERIFY(timer); |
107 | QTRY_VERIFY(!timer->property("running" ).toBool()); |
108 | QCOMPARE(obj->property("iterations" ).toInt(), 100); |
109 | QCOMPARE(obj->property("creations" ).toInt(), 100); |
110 | QCOMPARE(obj->property("destructions" ).toInt(), 100); |
111 | } |
112 | |
113 | void tst_qv4mm::clearICParent() |
114 | { |
115 | QV4::ExecutionEngine engine; |
116 | QV4::Scope scope(engine.rootContext()); |
117 | QV4::ScopedObject object(scope, engine.newObject()); |
118 | |
119 | // Keep identifiers in a separate array so that we don't have to allocate them in the loop that |
120 | // should test the GC on InternalClass allocations. |
121 | QV4::ScopedArrayObject identifiers(scope, engine.newArrayObject()); |
122 | for (uint i = 0; i < 16 * 1024; ++i) { |
123 | QV4::Scope scope(&engine); |
124 | QV4::ScopedString s(scope); |
125 | s = engine.newIdentifier(text: QString::fromLatin1(str: "key%1" ).arg(a: i)); |
126 | identifiers->push_back(v: s); |
127 | |
128 | QV4::ScopedValue v(scope); |
129 | v->setDouble(i); |
130 | object->insertMember(s, v); |
131 | } |
132 | |
133 | // When allocating the InternalClass objects required for deleting properties, the GC should |
134 | // eventually run and remove all but the last two. |
135 | // If we ever manage to avoid allocating the InternalClasses in the first place we will need |
136 | // to change this test. |
137 | for (uint i = 0; i < 16 * 1024; ++i) { |
138 | QV4::Scope scope(&engine); |
139 | QV4::ScopedString s(scope, identifiers->get(idx: i)); |
140 | QV4::Scoped<QV4::InternalClass> ic(scope, object->internalClass()); |
141 | QVERIFY(ic->d()->parent != nullptr); |
142 | object->deleteProperty(id: s->toPropertyKey()); |
143 | QVERIFY(object->internalClass() != ic->d()); |
144 | QCOMPARE(object->internalClass()->parent, ic->d()); |
145 | if (ic->d()->parent == nullptr) |
146 | return; |
147 | } |
148 | QFAIL("Garbage collector was not triggered by large amount of InternalClasses" ); |
149 | } |
150 | |
151 | QTEST_MAIN(tst_qv4mm) |
152 | |
153 | #include "tst_qv4mm.moc" |
154 | |