/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * 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_Allocator_h #define gc_Allocator_h #include "mozilla/OperatorNewExtensions.h" #include #include "gc/AllocKind.h" #include "js/TypeDecls.h" namespace js { // [SMDOC] AllowGC template parameter // // AllowGC is a template parameter for functions that support both with and // without GC operation. // // The CanGC variant of the function can trigger a garbage collection, and // should set a pending exception on failure. // // The NoGC variant of the function cannot trigger a garbage collection, and // should not set any pending exception on failure. This variant can be called // in fast paths where the caller has unrooted pointers. The failure means we // need to perform GC to allocate an object. The caller can fall back to a slow // path that roots pointers before calling a CanGC variant of the function, // without having to clear a pending exception. enum AllowGC { NoGC = 0, CanGC = 1 }; namespace gc { class AllocSite; struct Cell; class TenuredCell; // `friend` js::gc::CellAllocator in a subtype T of Cell in order to allow it to // be allocated with cx->newCell(...). The friend declaration will allow // calling T's constructor. class CellAllocator { template static gc::Cell* AllocateStringCell(JSContext* cx, gc::AllocKind kind, size_t size, gc::InitialHeap heap); // Allocate a string. Use cx->newCell([heap]). // // Use for nursery-allocatable strings. Returns a value cast to the correct // type. Non-nursery-allocatable strings will go through the fallback // tenured-only allocation path. template static StringT* AllocateString(JSContext* cx, gc::InitialHeap heap, Args&&... args) { static_assert(std::is_base_of_v); gc::AllocKind kind = gc::MapTypeToAllocKind::kind; gc::Cell* cell = AllocateStringCell(cx, kind, sizeof(StringT), heap); if (!cell) { return nullptr; } return new (mozilla::KnownNotNull, cell) StringT(std::forward(args)...); } public: template static T* NewCell(JSContext* cx, Args&&... args); }; namespace detail { // Allocator implementation functions. SpiderMonkey code outside this file // should use // // cx->newCell(...) // // or optionally // // cx->newCell(...) // // The parameters will be passed to a type-specific function or constructor. For // nursery-allocatable types, see eg AllocateString, AllocateObject, and // AllocateBigInt below. For all other types, the parameters will be // forwarded to the constructor. template gc::TenuredCell* AllocateTenuredImpl(JSContext* cx, gc::AllocKind kind, size_t size); // Allocate a JSObject. Use cx->newCell(kind, ...). // // Parameters support various optimizations. If dynamic slots are requested they // will be allocated and the pointer stored directly in |NativeObject::slots_|. template JSObject* AllocateObject(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots, gc::InitialHeap heap, const JSClass* clasp, gc::AllocSite* site = nullptr); // Allocate a BigInt. Use cx->newCell(heap). // // Use for nursery-allocatable BigInt. template JS::BigInt* AllocateBigInt(JSContext* cx, gc::InitialHeap heap); } // namespace detail } // namespace gc // This is the entry point for all allocation, though callers should still not // use this directly. Use cx->newCell(...) instead. template T* gc::CellAllocator::NewCell(JSContext* cx, Args&&... args) { static_assert(std::is_base_of_v); if constexpr (std::is_base_of_v && !std::is_base_of_v && !std::is_base_of_v) { return AllocateString(cx, std::forward(args)...); } else if constexpr (std::is_base_of_v) { return gc::detail::AllocateBigInt(cx, args...); } else if constexpr (std::is_base_of_v) { return static_cast( gc::detail::AllocateObject(cx, std::forward(args)...)); } else { // Allocate a new tenured GC thing that's not nursery-allocatable. Use // cx->newCell(...), where the parameters are prefixed with a cx // parameter and forwarded to the type's constructor. // // After a successful allocation the caller must fully initialize the thing // before calling any function that can potentially trigger GC. This will // ensure that GC tracing never sees junk values stored in the partially // initialized thing. gc::AllocKind kind = gc::MapTypeToAllocKind::kind; gc::TenuredCell* cell = gc::detail::AllocateTenuredImpl(cx, kind, sizeof(T)); if (!cell) { return nullptr; } return new (mozilla::KnownNotNull, cell) T(std::forward(args)...); } } } // namespace js #endif // gc_Allocator_h