1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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 Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4persistent_p.h"
41#include <private/qv4mm_p.h>
42#include "qv4object_p.h"
43#include "qv4qobjectwrapper_p.h"
44#include "PageAllocation.h"
45
46using namespace QV4;
47
48namespace {
49
50struct Page;
51struct Header {
52 WTF::PageAllocation alloc;
53 ExecutionEngine *engine;
54 Page **prev;
55 Page *next;
56 int refCount;
57 int freeList;
58};
59
60static const int kEntriesPerPage = int((WTF::pageSize() - sizeof(Header)) / sizeof(Value));
61
62struct Page {
63 Header header;
64 Value values[1]; // Really kEntriesPerPage, but keep the compiler happy
65};
66
67Page *getPage(Value *val) {
68 return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1)));
69}
70
71QML_NEARLY_ALWAYS_INLINE void insertInFront(PersistentValueStorage *storage, Page *p)
72{
73 p->header.next = reinterpret_cast<Page *>(storage->firstPage);
74 p->header.prev = reinterpret_cast<Page **>(&storage->firstPage);
75 if (p->header.next)
76 p->header.next->header.prev = &p->header.next;
77 storage->firstPage = p;
78}
79
80QML_NEARLY_ALWAYS_INLINE void unlink(Page *p)
81{
82 if (p->header.prev)
83 *p->header.prev = p->header.next;
84 if (p->header.next)
85 p->header.next->header.prev = p->header.prev;
86}
87
88Page *allocatePage(PersistentValueStorage *storage)
89{
90 PageAllocation page = WTF::PageAllocation::allocate(size: WTF::pageSize());
91 Page *p = reinterpret_cast<Page *>(page.base());
92
93 Q_ASSERT(!((quintptr)p & (WTF::pageSize() - 1)));
94
95 p->header.engine = storage->engine;
96 p->header.alloc = page;
97 p->header.refCount = 0;
98 p->header.freeList = 0;
99 insertInFront(storage, p);
100 for (int i = 0; i < kEntriesPerPage - 1; ++i) {
101 p->values[i] = Encode(i + 1);
102 }
103 p->values[kEntriesPerPage - 1] = Encode(-1);
104
105 return p;
106}
107
108
109}
110
111
112PersistentValueStorage::Iterator::Iterator(void *p, int idx)
113 : p(p), index(idx)
114{
115 Page *page = static_cast<Page *>(p);
116 if (page)
117 ++page->header.refCount;
118}
119
120PersistentValueStorage::Iterator::Iterator(const PersistentValueStorage::Iterator &o)
121 : p(o.p), index(o.index)
122{
123 Page *page = static_cast<Page *>(p);
124 if (page)
125 ++page->header.refCount;
126}
127
128PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator=(const PersistentValueStorage::Iterator &o)
129{
130 Page *page = static_cast<Page *>(p);
131 if (page && !--page->header.refCount)
132 freePage(page: p);
133 p = o.p;
134 index = o.index;
135 page = static_cast<Page *>(p);
136 if (page)
137 ++page->header.refCount;
138
139 return *this;
140}
141
142PersistentValueStorage::Iterator::~Iterator()
143{
144 Page *page = static_cast<Page *>(p);
145 if (page && !--page->header.refCount)
146 freePage(page);
147}
148
149PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() {
150 while (p) {
151 while (index < kEntriesPerPage - 1) {
152 ++index;
153 if (!static_cast<Page *>(p)->values[index].isEmpty())
154 return *this;
155 }
156 index = -1;
157 Page *next = static_cast<Page *>(p)->header.next;
158 if (!--static_cast<Page *>(p)->header.refCount)
159 freePage(page: p);
160 p = next;
161 if (next)
162 ++next->header.refCount;
163 }
164 index = 0;
165 return *this;
166}
167
168Value &PersistentValueStorage::Iterator::operator *()
169{
170 return static_cast<Page *>(p)->values[index];
171}
172
173PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine)
174 : engine(engine),
175 firstPage(nullptr)
176{
177}
178
179PersistentValueStorage::~PersistentValueStorage()
180{
181 Page *p = static_cast<Page *>(firstPage);
182 while (p) {
183 for (int i = 0; i < kEntriesPerPage; ++i) {
184 if (!p->values[i].isEmpty())
185 p->values[i] = Encode::undefined();
186 }
187 Page *n = p->header.next;
188 p->header.engine = nullptr;
189 p->header.prev = nullptr;
190 p->header.next = nullptr;
191 Q_ASSERT(p->header.refCount);
192 p = n;
193 }
194}
195
196Value *PersistentValueStorage::allocate()
197{
198 Page *p = static_cast<Page *>(firstPage);
199 while (p) {
200 if (p->header.freeList != -1)
201 break;
202 p = p->header.next;
203 }
204 if (!p)
205 p = allocatePage(storage: this);
206
207 Value *v = p->values + p->header.freeList;
208 p->header.freeList = v->int_32();
209
210 if (p->header.freeList != -1 && p != firstPage) {
211 unlink(p);
212 insertInFront(storage: this, p);
213 }
214
215 ++p->header.refCount;
216
217 v->setRawValue(Encode::undefined());
218
219 return v;
220}
221
222void PersistentValueStorage::free(Value *v)
223{
224 if (!v)
225 return;
226
227 Page *p = getPage(val: v);
228
229 *v = Encode(p->header.freeList);
230 p->header.freeList = v - p->values;
231 if (!--p->header.refCount)
232 freePage(page: p);
233}
234
235void PersistentValueStorage::mark(MarkStack *markStack)
236{
237 Page *p = static_cast<Page *>(firstPage);
238 while (p) {
239 for (int i = 0; i < kEntriesPerPage; ++i) {
240 if (Managed *m = p->values[i].as<Managed>())
241 m->mark(markStack);
242 }
243
244 p = p->header.next;
245 }
246}
247
248ExecutionEngine *PersistentValueStorage::getEngine(Value *v)
249{
250 return getPage(val: v)->header.engine;
251}
252
253void PersistentValueStorage::freePage(void *page)
254{
255 Page *p = static_cast<Page *>(page);
256 unlink(p);
257 p->header.alloc.deallocate();
258}
259
260
261PersistentValue::PersistentValue(const PersistentValue &other)
262 : val(nullptr)
263{
264 if (other.val) {
265 val = other.engine()->memoryManager->m_persistentValues->allocate();
266 *val = *other.val;
267 }
268}
269
270PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value)
271{
272 val = engine->memoryManager->m_persistentValues->allocate();
273 *val = value;
274}
275
276PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value)
277{
278 val = engine->memoryManager->m_persistentValues->allocate();
279 *val = value;
280}
281
282PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object)
283 : val(nullptr)
284{
285 if (!object)
286 return;
287
288 val = engine->memoryManager->m_persistentValues->allocate();
289 *val = object;
290}
291
292PersistentValue::~PersistentValue()
293{
294 PersistentValueStorage::free(v: val);
295}
296
297PersistentValue &PersistentValue::operator=(const PersistentValue &other)
298{
299 if (!val) {
300 if (!other.val)
301 return *this;
302 val = other.engine()->memoryManager->m_persistentValues->allocate();
303 }
304 if (!other.val) {
305 *val = Encode::undefined();
306 return *this;
307 }
308
309 Q_ASSERT(engine() == other.engine());
310
311 *val = *other.val;
312 return *this;
313}
314
315PersistentValue &PersistentValue::operator=(const WeakValue &other)
316{
317 if (!val) {
318 if (!other.valueRef())
319 return *this;
320 val = other.engine()->memoryManager->m_persistentValues->allocate();
321 }
322 if (!other.valueRef()) {
323 *val = Encode::undefined();
324 return *this;
325 }
326
327 Q_ASSERT(engine() == other.engine());
328
329 *val = *other.valueRef();
330 return *this;
331}
332
333PersistentValue &PersistentValue::operator=(Object *object)
334{
335 if (!object) {
336 PersistentValueStorage::free(v: val);
337 return *this;
338 }
339 if (!val)
340 val = object->engine()->memoryManager->m_persistentValues->allocate();
341
342 *val = object;
343 return *this;
344}
345
346void PersistentValue::set(ExecutionEngine *engine, const Value &value)
347{
348 if (!val)
349 val = engine->memoryManager->m_persistentValues->allocate();
350 *val = value;
351}
352
353void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value)
354{
355 if (!val)
356 val = engine->memoryManager->m_persistentValues->allocate();
357 *val = value;
358}
359
360void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj)
361{
362 if (!val)
363 val = engine->memoryManager->m_persistentValues->allocate();
364 *val = obj;
365}
366
367WeakValue::WeakValue(const WeakValue &other)
368 : val(nullptr)
369{
370 if (other.val) {
371 allocVal(engine: other.engine());
372 *val = *other.val;
373 }
374}
375
376WeakValue::WeakValue(ExecutionEngine *engine, const Value &value)
377{
378 allocVal(engine);
379 *val = value;
380}
381
382WeakValue &WeakValue::operator=(const WeakValue &other)
383{
384 if (!val) {
385 if (!other.val)
386 return *this;
387 allocVal(engine: other.engine());
388 }
389 if (!other.val) {
390 *val = Encode::undefined();
391 return *this;
392 }
393
394 Q_ASSERT(engine() == other.engine());
395
396 *val = *other.val;
397 return *this;
398}
399
400WeakValue::~WeakValue()
401{
402 free();
403}
404
405void WeakValue::allocVal(ExecutionEngine *engine)
406{
407 val = engine->memoryManager->m_weakValues->allocate();
408}
409
410void WeakValue::markOnce(MarkStack *markStack)
411{
412 if (!val)
413 return;
414 val->mark(markStack);
415}
416
417void WeakValue::free()
418{
419 if (!val)
420 return;
421
422 ExecutionEngine *e = engine();
423 if (e && val->as<QObjectWrapper>()) {
424 // Some QV4::QObjectWrapper Value will be freed in WeakValue::~WeakValue() before MemoryManager::sweep() is being called,
425 // in this case we will never have a chance to call detroyObject() on those QV4::QObjectWrapper objects.
426 // Here we don't free these Value immediately, instead we keep track of them to free them later in MemoryManager::sweep()
427 e->memoryManager->m_pendingFreedObjectWrapperValue.push_back(t: val);
428 } else {
429 PersistentValueStorage::free(v: val);
430 }
431
432 val = nullptr;
433}
434
435

source code of qtdeclarative/src/qml/jsruntime/qv4persistent.cpp