| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtQml module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qv4persistent_p.h" |
| #include <private/qv4mm_p.h> |
| #include "qv4object_p.h" |
| #include "qv4qobjectwrapper_p.h" |
| #include "PageAllocation.h" |
| |
| using namespace QV4; |
| |
| namespace { |
| |
| struct Page; |
| struct Header { |
| WTF::PageAllocation alloc; |
| ExecutionEngine *engine; |
| Page **prev; |
| Page *next; |
| int refCount; |
| int freeList; |
| }; |
| |
| static const int kEntriesPerPage = int((WTF::pageSize() - sizeof(Header)) / sizeof(Value)); |
| |
| struct Page { |
| Header header; |
| Value values[1]; // Really kEntriesPerPage, but keep the compiler happy |
| }; |
| |
| Page *getPage(Value *val) { |
| return reinterpret_cast<Page *>(reinterpret_cast<quintptr>(val) & ~((quintptr)(WTF::pageSize() - 1))); |
| } |
| |
| QML_NEARLY_ALWAYS_INLINE void insertInFront(PersistentValueStorage *storage, Page *p) |
| { |
| p->header.next = reinterpret_cast<Page *>(storage->firstPage); |
| p->header.prev = reinterpret_cast<Page **>(&storage->firstPage); |
| if (p->header.next) |
| p->header.next->header.prev = &p->header.next; |
| storage->firstPage = p; |
| } |
| |
| QML_NEARLY_ALWAYS_INLINE void unlink(Page *p) |
| { |
| if (p->header.prev) |
| *p->header.prev = p->header.next; |
| if (p->header.next) |
| p->header.next->header.prev = p->header.prev; |
| } |
| |
| Page *allocatePage(PersistentValueStorage *storage) |
| { |
| PageAllocation page = WTF::PageAllocation::allocate(WTF::pageSize()); |
| Page *p = reinterpret_cast<Page *>(page.base()); |
| |
| Q_ASSERT(!((quintptr)p & (WTF::pageSize() - 1))); |
| |
| p->header.engine = storage->engine; |
| p->header.alloc = page; |
| p->header.refCount = 0; |
| p->header.freeList = 0; |
| insertInFront(storage, p); |
| for (int i = 0; i < kEntriesPerPage - 1; ++i) { |
| p->values[i] = Encode(i + 1); |
| } |
| p->values[kEntriesPerPage - 1] = Encode(-1); |
| |
| return p; |
| } |
| |
| |
| } |
| |
| |
| PersistentValueStorage::Iterator::Iterator(void *p, int idx) |
| : p(p), index(idx) |
| { |
| Page *page = static_cast<Page *>(p); |
| if (page) |
| ++page->header.refCount; |
| } |
| |
| PersistentValueStorage::Iterator::Iterator(const PersistentValueStorage::Iterator &o) |
| : p(o.p), index(o.index) |
| { |
| Page *page = static_cast<Page *>(p); |
| if (page) |
| ++page->header.refCount; |
| } |
| |
| PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator=(const PersistentValueStorage::Iterator &o) |
| { |
| Page *page = static_cast<Page *>(p); |
| if (page && !--page->header.refCount) |
| freePage(p); |
| p = o.p; |
| index = o.index; |
| page = static_cast<Page *>(p); |
| if (page) |
| ++page->header.refCount; |
| |
| return *this; |
| } |
| |
| PersistentValueStorage::Iterator::~Iterator() |
| { |
| Page *page = static_cast<Page *>(p); |
| if (page && !--page->header.refCount) |
| freePage(page); |
| } |
| |
| PersistentValueStorage::Iterator &PersistentValueStorage::Iterator::operator++() { |
| while (p) { |
| while (index < kEntriesPerPage - 1) { |
| ++index; |
| if (!static_cast<Page *>(p)->values[index].isEmpty()) |
| return *this; |
| } |
| index = -1; |
| Page *next = static_cast<Page *>(p)->header.next; |
| if (!--static_cast<Page *>(p)->header.refCount) |
| freePage(p); |
| p = next; |
| if (next) |
| ++next->header.refCount; |
| } |
| index = 0; |
| return *this; |
| } |
| |
| Value &PersistentValueStorage::Iterator::operator *() |
| { |
| return static_cast<Page *>(p)->values[index]; |
| } |
| |
| PersistentValueStorage::PersistentValueStorage(ExecutionEngine *engine) |
| : engine(engine), |
| firstPage(nullptr) |
| { |
| } |
| |
| PersistentValueStorage::~PersistentValueStorage() |
| { |
| Page *p = static_cast<Page *>(firstPage); |
| while (p) { |
| for (int i = 0; i < kEntriesPerPage; ++i) { |
| if (!p->values[i].isEmpty()) |
| p->values[i] = Encode::undefined(); |
| } |
| Page *n = p->header.next; |
| p->header.engine = nullptr; |
| p->header.prev = nullptr; |
| p->header.next = nullptr; |
| Q_ASSERT(p->header.refCount); |
| p = n; |
| } |
| } |
| |
| Value *PersistentValueStorage::allocate() |
| { |
| Page *p = static_cast<Page *>(firstPage); |
| while (p) { |
| if (p->header.freeList != -1) |
| break; |
| p = p->header.next; |
| } |
| if (!p) |
| p = allocatePage(this); |
| |
| Value *v = p->values + p->header.freeList; |
| p->header.freeList = v->int_32(); |
| |
| if (p->header.freeList != -1 && p != firstPage) { |
| unlink(p); |
| insertInFront(this, p); |
| } |
| |
| ++p->header.refCount; |
| |
| v->setRawValue(Encode::undefined()); |
| |
| return v; |
| } |
| |
| void PersistentValueStorage::free(Value *v) |
| { |
| if (!v) |
| return; |
| |
| Page *p = getPage(v); |
| |
| *v = Encode(p->header.freeList); |
| p->header.freeList = v - p->values; |
| if (!--p->header.refCount) |
| freePage(p); |
| } |
| |
| void PersistentValueStorage::mark(MarkStack *markStack) |
| { |
| Page *p = static_cast<Page *>(firstPage); |
| while (p) { |
| for (int i = 0; i < kEntriesPerPage; ++i) { |
| if (Managed *m = p->values[i].as<Managed>()) |
| m->mark(markStack); |
| } |
| |
| p = p->header.next; |
| } |
| } |
| |
| ExecutionEngine *PersistentValueStorage::getEngine(Value *v) |
| { |
| return getPage(v)->header.engine; |
| } |
| |
| void PersistentValueStorage::freePage(void *page) |
| { |
| Page *p = static_cast<Page *>(page); |
| unlink(p); |
| p->header.alloc.deallocate(); |
| } |
| |
| |
| PersistentValue::PersistentValue(const PersistentValue &other) |
| : val(nullptr) |
| { |
| if (other.val) { |
| val = other.engine()->memoryManager->m_persistentValues->allocate(); |
| *val = *other.val; |
| } |
| } |
| |
| PersistentValue::PersistentValue(ExecutionEngine *engine, const Value &value) |
| { |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = value; |
| } |
| |
| PersistentValue::PersistentValue(ExecutionEngine *engine, ReturnedValue value) |
| { |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = value; |
| } |
| |
| PersistentValue::PersistentValue(ExecutionEngine *engine, Object *object) |
| : val(nullptr) |
| { |
| if (!object) |
| return; |
| |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = object; |
| } |
| |
| PersistentValue::~PersistentValue() |
| { |
| PersistentValueStorage::free(val); |
| } |
| |
| PersistentValue &PersistentValue::operator=(const PersistentValue &other) |
| { |
| if (!val) { |
| if (!other.val) |
| return *this; |
| val = other.engine()->memoryManager->m_persistentValues->allocate(); |
| } |
| if (!other.val) { |
| *val = Encode::undefined(); |
| return *this; |
| } |
| |
| Q_ASSERT(engine() == other.engine()); |
| |
| *val = *other.val; |
| return *this; |
| } |
| |
| PersistentValue &PersistentValue::operator=(const WeakValue &other) |
| { |
| if (!val) { |
| if (!other.valueRef()) |
| return *this; |
| val = other.engine()->memoryManager->m_persistentValues->allocate(); |
| } |
| if (!other.valueRef()) { |
| *val = Encode::undefined(); |
| return *this; |
| } |
| |
| Q_ASSERT(engine() == other.engine()); |
| |
| *val = *other.valueRef(); |
| return *this; |
| } |
| |
| PersistentValue &PersistentValue::operator=(Object *object) |
| { |
| if (!object) { |
| PersistentValueStorage::free(val); |
| return *this; |
| } |
| if (!val) |
| val = object->engine()->memoryManager->m_persistentValues->allocate(); |
| |
| *val = object; |
| return *this; |
| } |
| |
| void PersistentValue::set(ExecutionEngine *engine, const Value &value) |
| { |
| if (!val) |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = value; |
| } |
| |
| void PersistentValue::set(ExecutionEngine *engine, ReturnedValue value) |
| { |
| if (!val) |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = value; |
| } |
| |
| void PersistentValue::set(ExecutionEngine *engine, Heap::Base *obj) |
| { |
| if (!val) |
| val = engine->memoryManager->m_persistentValues->allocate(); |
| *val = obj; |
| } |
| |
| WeakValue::WeakValue(const WeakValue &other) |
| : val(nullptr) |
| { |
| if (other.val) { |
| allocVal(other.engine()); |
| *val = *other.val; |
| } |
| } |
| |
| WeakValue::WeakValue(ExecutionEngine *engine, const Value &value) |
| { |
| allocVal(engine); |
| *val = value; |
| } |
| |
| WeakValue &WeakValue::operator=(const WeakValue &other) |
| { |
| if (!val) { |
| if (!other.val) |
| return *this; |
| allocVal(other.engine()); |
| } |
| if (!other.val) { |
| *val = Encode::undefined(); |
| return *this; |
| } |
| |
| Q_ASSERT(engine() == other.engine()); |
| |
| *val = *other.val; |
| return *this; |
| } |
| |
| WeakValue::~WeakValue() |
| { |
| free(); |
| } |
| |
| void WeakValue::allocVal(ExecutionEngine *engine) |
| { |
| val = engine->memoryManager->m_weakValues->allocate(); |
| } |
| |
| void WeakValue::markOnce(MarkStack *markStack) |
| { |
| if (!val) |
| return; |
| val->mark(markStack); |
| } |
| |
| void WeakValue::free() |
| { |
| if (!val) |
| return; |
| |
| ExecutionEngine *e = engine(); |
| if (e && val->as<QObjectWrapper>()) { |
| // Some QV4::QObjectWrapper Value will be freed in WeakValue::~WeakValue() before MemoryManager::sweep() is being called, |
| // in this case we will never have a chance to call detroyObject() on those QV4::QObjectWrapper objects. |
| // Here we don't free these Value immediately, instead we keep track of them to free them later in MemoryManager::sweep() |
| e->memoryManager->m_pendingFreedObjectWrapperValue.push_back(val); |
| } else { |
| PersistentValueStorage::free(val); |
| } |
| |
| val = nullptr; |
| } |
| |