Allocator-inl.h (7732B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * Inline definitions of the CellAllocator methods. 9 * 10 * This is included from JSContext-inl.h for the definiton of JSContext::newCell 11 * and shouldn't need to be included elsewhere. 12 */ 13 14 #ifndef gc_Allocator_inl_h 15 #define gc_Allocator_inl_h 16 17 #include "gc/Allocator.h" 18 19 #include "gc/Cell.h" 20 #include "gc/Zone.h" 21 #include "js/Class.h" 22 #include "js/RootingAPI.h" 23 24 #include "gc/Nursery-inl.h" 25 26 namespace js { 27 namespace gc { 28 29 template <typename T, AllowGC allowGC, typename... Args> 30 T* CellAllocator::NewCell(JSContext* cx, Args&&... args) { 31 static_assert(std::is_base_of_v<gc::Cell, T>); 32 33 // Objects. See the valid parameter list in NewObject, above. 34 if constexpr (std::is_base_of_v<JSObject, T>) { 35 return NewObject<T, allowGC>(cx, std::forward<Args>(args)...); 36 } 37 38 // BigInt 39 else if constexpr (std::is_base_of_v<JS::BigInt, T>) { 40 return NewBigInt<T, allowGC>(cx, std::forward<Args>(args)...); 41 } 42 43 // GetterSetter 44 else if constexpr (std::is_base_of_v<js::GetterSetter, T>) { 45 return NewGetterSetter<T, allowGC>(cx, std::forward<Args>(args)...); 46 } 47 48 // "Normal" strings (all of which can be nursery allocated). Atoms and 49 // external strings will fall through to the generic code below. All other 50 // strings go through NewString, which will forward the arguments to the 51 // appropriate string class's constructor. 52 else if constexpr (std::is_base_of_v<JSString, T> && 53 !std::is_base_of_v<JSAtom, T> && 54 !std::is_base_of_v<JSExternalString, T>) { 55 return NewString<T, allowGC>(cx, std::forward<Args>(args)...); 56 } 57 58 else { 59 // Allocate a new tenured GC thing that's not nursery-allocatable. Use 60 // cx->newCell<T>(...), where the parameters are forwarded to the type's 61 // constructor. 62 return NewTenuredCell<T, allowGC>(cx, std::forward<Args>(args)...); 63 } 64 } 65 66 template <typename T, AllowGC allowGC, typename... Args> 67 /* static */ 68 T* CellAllocator::NewString(JSContext* cx, gc::Heap heap, Args&&... args) { 69 static_assert(std::is_base_of_v<JSString, T>); 70 gc::AllocKind kind = gc::MapTypeToAllocKind<T>::kind; 71 void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::String, allowGC>( 72 cx, kind, sizeof(T), heap, nullptr); 73 if (MOZ_UNLIKELY(!ptr)) { 74 return nullptr; 75 } 76 return new (mozilla::KnownNotNull, ptr) T(std::forward<Args>(args)...); 77 } 78 79 template <typename T, AllowGC allowGC> 80 /* static */ 81 T* CellAllocator::NewBigInt(JSContext* cx, Heap heap) { 82 void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::BigInt, allowGC>( 83 cx, gc::AllocKind::BIGINT, sizeof(T), heap, nullptr); 84 if (MOZ_UNLIKELY(!ptr)) { 85 return nullptr; 86 } 87 return new (mozilla::KnownNotNull, ptr) T(); 88 } 89 90 template <typename T, AllowGC allowGC, typename... Args> 91 /* static */ 92 T* CellAllocator::NewGetterSetter(JSContext* cx, gc::Heap heap, 93 Args&&... args) { 94 static_assert(std::is_base_of_v<js::GetterSetter, T>); 95 void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::GetterSetter, allowGC>( 96 cx, gc::AllocKind::GETTER_SETTER, sizeof(T), heap, nullptr); 97 if (MOZ_UNLIKELY(!ptr)) { 98 return nullptr; 99 } 100 return new (mozilla::KnownNotNull, ptr) T(std::forward<Args>(args)...); 101 } 102 103 template <typename T, AllowGC allowGC> 104 /* static */ 105 T* CellAllocator::NewObject(JSContext* cx, gc::AllocKind kind, gc::Heap heap, 106 const JSClass* clasp, gc::AllocSite* site) { 107 MOZ_ASSERT(IsObjectAllocKind(kind)); 108 MOZ_ASSERT_IF(heap != gc::Heap::Tenured && clasp->hasFinalize() && 109 !clasp->isProxyObject(), 110 CanNurseryAllocateFinalizedClass(clasp)); 111 size_t thingSize = JSObject::thingSize(kind); 112 void* cell = AllocNurseryOrTenuredCell<JS::TraceKind::Object, allowGC>( 113 cx, kind, thingSize, heap, site); 114 if (MOZ_UNLIKELY(!cell)) { 115 return nullptr; 116 } 117 return new (mozilla::KnownNotNull, cell) T(); 118 } 119 120 template <typename T, AllowGC allowGC, typename... Args> 121 /* static */ 122 T* CellAllocator::NewTenuredCell(JSContext* cx, Args&&... args) { 123 gc::AllocKind kind = gc::MapTypeToAllocKind<T>::kind; 124 MOZ_ASSERT(Arena::thingSize(kind) == sizeof(T)); 125 void* cell = AllocTenuredCell<allowGC>(cx, kind); 126 if (MOZ_UNLIKELY(!cell)) { 127 return nullptr; 128 } 129 return new (mozilla::KnownNotNull, cell) T(std::forward<Args>(args)...); 130 } 131 132 #if defined(DEBUG) || defined(JS_GC_ZEAL) || defined(JS_OOM_BREAKPOINT) 133 134 // This serves as a single point to perform some unrelated checks that happens 135 // before every allocation. Performs the following: 136 // 137 // - checks we can't GC inside a JS::AutoAssertNoGC region 138 // - runs a zeal GC if needed 139 // 140 // This is a no-op in release builds. 141 // 142 // This is only called on paths where GC is allowed. 143 inline void PreAllocGCChecks(JSContext* cx) { 144 // Crash if we could perform a GC action when it is not safe. 145 if (!cx->suppressGC) { 146 cx->verifyIsSafeToGC(); 147 } 148 149 # ifdef JS_GC_ZEAL 150 GCRuntime* gc = &cx->runtime()->gc; 151 if (gc->needZealousGC()) { 152 gc->runDebugGC(); 153 } 154 # endif 155 } 156 157 inline bool CheckForSimulatedFailure(JSContext* cx, AllowGC allowGC) { 158 // For testing out of memory conditions. 159 if (js::oom::ShouldFailWithOOM()) { 160 // If we are doing a fallible allocation, percolate up the OOM instead of 161 // reporting it. 162 if (allowGC) { 163 ReportOutOfMemory(cx); 164 } 165 return false; 166 } 167 168 return true; 169 } 170 #else 171 172 inline void PreAllocGCChecks(JSContext* cx) {} 173 inline bool CheckForSimulatedFailure(JSContext* cx, AllowGC allowGC) { 174 return true; 175 } 176 177 #endif // DEBUG || JS_GC_ZEAL || JS_OOM_BREAKPOINT 178 179 template <JS::TraceKind traceKind, AllowGC allowGC> 180 /* static */ 181 void* CellAllocator::AllocNurseryOrTenuredCell(JSContext* cx, 182 gc::AllocKind allocKind, 183 size_t thingSize, gc::Heap heap, 184 AllocSite* site) { 185 MOZ_ASSERT(IsNurseryAllocable(allocKind)); 186 MOZ_ASSERT(MapAllocToTraceKind(allocKind) == traceKind); 187 MOZ_ASSERT(thingSize == Arena::thingSize(allocKind)); 188 MOZ_ASSERT_IF(site && site->initialHeap() == Heap::Tenured, 189 heap == Heap::Tenured); 190 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 191 MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime())); 192 193 if constexpr (allowGC) { 194 PreAllocGCChecks(cx); 195 } 196 197 if (!CheckForSimulatedFailure(cx, allowGC)) { 198 return nullptr; 199 } 200 201 JS::Zone* zone = cx->zone(); 202 gc::Heap minHeapToTenure = CheckedHeap(zone->minHeapToTenure(traceKind)); 203 if (CheckedHeap(heap) < minHeapToTenure) { 204 if (!site) { 205 site = zone->unknownAllocSite(traceKind); 206 } 207 208 #ifdef JS_GC_ZEAL 209 site = MaybeGenerateMissingAllocSite(cx, traceKind, site); 210 #endif 211 212 void* ptr = cx->nursery().tryAllocateCell(site, thingSize, traceKind); 213 if (MOZ_LIKELY(ptr)) { 214 return ptr; 215 } 216 217 return RetryNurseryAlloc<allowGC>(cx, traceKind, allocKind, thingSize, 218 site); 219 } 220 221 return AllocTenuredCellForNurseryAlloc<allowGC>(cx, allocKind); 222 } 223 224 /* static */ 225 MOZ_ALWAYS_INLINE gc::Heap CellAllocator::CheckedHeap(gc::Heap heap) { 226 if (heap > Heap::Tenured) { 227 // This helps the compiler to see that nursery allocation is never 228 // possible if Heap::Tenured is specified. 229 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad gc::Heap value"); 230 } 231 232 return heap; 233 } 234 235 } // namespace gc 236 } // namespace js 237 238 #endif // gc_Allocator_inl_h