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

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