diff options
Diffstat (limited to 'js/src/gc/Nursery-inl.h')
-rw-r--r-- | js/src/gc/Nursery-inl.h | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/js/src/gc/Nursery-inl.h b/js/src/gc/Nursery-inl.h new file mode 100644 index 0000000000..510c40553d --- /dev/null +++ b/js/src/gc/Nursery-inl.h @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=4 sw=2 et tw=80 ft=cpp: + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef gc_Nursery_inl_h +#define gc_Nursery_inl_h + +#include "gc/Nursery.h" + +#include "gc/Heap.h" +#include "gc/RelocationOverlay.h" +#include "gc/Zone.h" +#include "js/TracingAPI.h" +#include "vm/JSContext.h" +#include "vm/Runtime.h" +#include "vm/SharedMem.h" + +inline JSRuntime* js::Nursery::runtime() const { return gc->rt; } + +template <typename T> +bool js::Nursery::isInside(const SharedMem<T>& p) const { + return isInside(p.unwrap(/*safe - used for value in comparison above*/)); +} + +MOZ_ALWAYS_INLINE /* static */ bool js::Nursery::getForwardedPointer( + js::gc::Cell** ref) { + js::gc::Cell* cell = (*ref); + MOZ_ASSERT(IsInsideNursery(cell)); + if (!cell->isForwarded()) { + return false; + } + const gc::RelocationOverlay* overlay = gc::RelocationOverlay::fromCell(cell); + *ref = overlay->forwardingAddress(); + return true; +} + +inline void js::Nursery::maybeSetForwardingPointer(JSTracer* trc, void* oldData, + void* newData, bool direct) { + if (trc->isTenuringTracer()) { + setForwardingPointerWhileTenuring(oldData, newData, direct); + } +} + +inline void js::Nursery::setForwardingPointerWhileTenuring(void* oldData, + void* newData, + bool direct) { + if (isInside(oldData)) { + setForwardingPointer(oldData, newData, direct); + } +} + +inline void js::Nursery::setSlotsForwardingPointer(HeapSlot* oldSlots, + HeapSlot* newSlots, + uint32_t nslots) { + // Slot arrays always have enough space for a forwarding pointer, since the + // number of slots is never zero. + MOZ_ASSERT(nslots > 0); + setDirectForwardingPointer(oldSlots, newSlots); +} + +inline void js::Nursery::setElementsForwardingPointer(ObjectElements* oldHeader, + ObjectElements* newHeader, + uint32_t capacity) { + // Only use a direct forwarding pointer if there is enough space for one. + setForwardingPointer(oldHeader->elements(), newHeader->elements(), + capacity > 0); +} + +inline void js::Nursery::setForwardingPointer(void* oldData, void* newData, + bool direct) { + if (direct) { + setDirectForwardingPointer(oldData, newData); + return; + } + + setIndirectForwardingPointer(oldData, newData); +} + +inline void js::Nursery::setDirectForwardingPointer(void* oldData, + void* newData) { + MOZ_ASSERT(isInside(oldData)); + MOZ_ASSERT(!isInside(newData)); + + new (oldData) BufferRelocationOverlay{newData}; +} + +namespace js { + +// The allocation methods below will not run the garbage collector. If the +// nursery cannot accomodate the allocation, the malloc heap will be used +// instead. + +template <typename T> +static inline T* AllocateObjectBuffer(JSContext* cx, uint32_t count) { + size_t nbytes = RoundUp(count * sizeof(T), sizeof(Value)); + auto* buffer = + static_cast<T*>(cx->nursery().allocateBuffer(cx->zone(), nbytes)); + if (!buffer) { + ReportOutOfMemory(cx); + } + return buffer; +} + +template <typename T> +static inline T* AllocateObjectBuffer(JSContext* cx, JSObject* obj, + uint32_t count) { + if (cx->isHelperThreadContext()) { + return cx->pod_malloc<T>(count); + } + size_t nbytes = RoundUp(count * sizeof(T), sizeof(Value)); + auto* buffer = static_cast<T*>(cx->nursery().allocateBuffer(obj, nbytes)); + if (!buffer) { + ReportOutOfMemory(cx); + } + return buffer; +} + +// If this returns null then the old buffer will be left alone. +template <typename T> +static inline T* ReallocateObjectBuffer(JSContext* cx, JSObject* obj, + T* oldBuffer, uint32_t oldCount, + uint32_t newCount) { + T* buffer; + if (cx->isHelperThreadContext()) { + buffer = obj->zone()->pod_realloc<T>(oldBuffer, oldCount, newCount); + } else { + buffer = static_cast<T*>(cx->nursery().reallocateBuffer( + obj->zone(), obj, oldBuffer, oldCount * sizeof(T), + newCount * sizeof(T))); + } + + if (!buffer) { + ReportOutOfMemory(cx); + } + + return buffer; +} + +static inline JS::BigInt::Digit* AllocateBigIntDigits(JSContext* cx, + JS::BigInt* bi, + uint32_t length) { + JS::BigInt::Digit* digits; + if (cx->isHelperThreadContext()) { + digits = cx->pod_malloc<JS::BigInt::Digit>(length); + } else { + size_t nbytes = RoundUp(length * sizeof(JS::BigInt::Digit), sizeof(Value)); + digits = static_cast<JS::BigInt::Digit*>( + cx->nursery().allocateBuffer(bi, nbytes)); + } + + if (!digits) { + ReportOutOfMemory(cx); + } + + return digits; +} + +static inline JS::BigInt::Digit* ReallocateBigIntDigits( + JSContext* cx, JS::BigInt* bi, JS::BigInt::Digit* oldDigits, + uint32_t oldLength, uint32_t newLength) { + JS::BigInt::Digit* digits; + if (cx->isHelperThreadContext()) { + MOZ_ASSERT(!cx->nursery().isInside(oldDigits)); + digits = bi->zone()->pod_realloc<JS::BigInt::Digit>(oldDigits, oldLength, + newLength); + } else { + size_t oldBytes = + RoundUp(oldLength * sizeof(JS::BigInt::Digit), sizeof(Value)); + size_t newBytes = + RoundUp(newLength * sizeof(JS::BigInt::Digit), sizeof(Value)); + + digits = static_cast<JS::BigInt::Digit*>(cx->nursery().reallocateBuffer( + bi->zone(), bi, oldDigits, oldBytes, newBytes)); + } + + if (!digits) { + ReportOutOfMemory(cx); + } + + return digits; +} + +} // namespace js + +#endif /* gc_Nursery_inl_h */ |