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 | |
10 | using namespace QV4; |
11 | |
12 | namespace { |
13 | |
14 | struct Page; |
15 | struct { |
16 | WTF::PageAllocation ; |
17 | ExecutionEngine *; |
18 | Page **; |
19 | Page *; |
20 | int ; |
21 | int ; |
22 | }; |
23 | |
24 | static const int kEntriesPerPage = int((WTF::pageSize() - sizeof(Header)) / sizeof(Value)); |
25 | |
26 | struct Page { |
27 | Header ; |
28 | Value values[1]; // Really kEntriesPerPage, but keep the compiler happy |
29 | }; |
30 | |
31 | Page *getPage(const Value *val) { |
32 | return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1))); |
33 | } |
34 | |
35 | QML_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 | |
44 | QML_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 | |
52 | Page *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 | |
76 | PersistentValueStorage::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 | |
84 | PersistentValueStorage::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 | |
92 | PersistentValueStorage::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 | |
106 | PersistentValueStorage::Iterator::~Iterator() |
107 | { |
108 | Page *page = static_cast<Page *>(p); |
109 | if (page && !--page->header.refCount) |
110 | freePage(page); |
111 | } |
112 | |
113 | PersistentValueStorage::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 | |
132 | Value &PersistentValueStorage::Iterator::operator *() |
133 | { |
134 | return static_cast<Page *>(p)->values[index]; |
135 | } |
136 | |
137 | PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine) |
138 | : engine(engine), |
139 | firstPage(nullptr) |
140 | { |
141 | } |
142 | |
143 | PersistentValueStorage::~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 | |
160 | Value *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 | |
186 | void 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 | |
197 | void 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 | |
210 | ExecutionEngine *PersistentValueStorage::getEngine(const Value *v) |
211 | { |
212 | return getPage(val: v)->header.engine; |
213 | } |
214 | |
215 | void PersistentValueStorage::freePage(void *page) |
216 | { |
217 | Page *p = static_cast<Page *>(page); |
218 | unlink(p); |
219 | p->header.alloc.deallocate(); |
220 | } |
221 | |
222 | |
223 | PersistentValue::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 | |
232 | PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value) |
233 | { |
234 | val = engine->memoryManager->m_persistentValues->allocate(); |
235 | *val = value; |
236 | } |
237 | |
238 | PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value) |
239 | { |
240 | val = engine->memoryManager->m_persistentValues->allocate(); |
241 | *val = value; |
242 | } |
243 | |
244 | PersistentValue::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 | |
254 | PersistentValue &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 | |
272 | PersistentValue &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 | |
290 | PersistentValue &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 | |
303 | void PersistentValue::set(ExecutionEngine *engine, const Value &value) |
304 | { |
305 | if (!val) |
306 | val = engine->memoryManager->m_persistentValues->allocate(); |
307 | *val = value; |
308 | } |
309 | |
310 | void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value) |
311 | { |
312 | if (!val) |
313 | val = engine->memoryManager->m_persistentValues->allocate(); |
314 | *val = value; |
315 | } |
316 | |
317 | void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) |
318 | { |
319 | if (!val) |
320 | val = engine->memoryManager->m_persistentValues->allocate(); |
321 | *val = obj; |
322 | } |
323 | |
324 | WeakValue::WeakValue(const WeakValue &other) |
325 | : val(nullptr) |
326 | { |
327 | if (other.val) { |
328 | allocVal(engine: other.engine()); |
329 | *val = *other.val; |
330 | } |
331 | } |
332 | |
333 | WeakValue::WeakValue(ExecutionEngine *engine, const Value &value) |
334 | { |
335 | allocVal(engine); |
336 | *val = value; |
337 | } |
338 | |
339 | WeakValue &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 | |
357 | WeakValue::~WeakValue() |
358 | { |
359 | free(); |
360 | } |
361 | |
362 | void WeakValue::allocVal(ExecutionEngine *engine) |
363 | { |
364 | val = engine->memoryManager->m_weakValues->allocate(); |
365 | } |
366 | |
367 | void WeakValue::markOnce(MarkStack *markStack) |
368 | { |
369 | if (!val) |
370 | return; |
371 | val->mark(markStack); |
372 | } |
373 | |
374 | void 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 | |