AllocPolicy.h (9071B)
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 * JS allocation policies. 9 * 10 * The allocators here are for system memory with lifetimes which are not 11 * managed by the GC. See the comment at the top of vm/MallocProvider.h. 12 */ 13 14 #ifndef js_AllocPolicy_h 15 #define js_AllocPolicy_h 16 17 #include "js/TypeDecls.h" 18 #include "js/Utility.h" 19 20 extern MOZ_COLD JS_PUBLIC_API void JS_ReportOutOfMemory(JSContext* cx); 21 22 namespace js { 23 24 class FrontendContext; 25 26 enum class AllocFunction { Malloc, Calloc, Realloc }; 27 28 class ArenaAllocPolicyBase { 29 public: 30 template <typename T> 31 T* maybe_pod_arena_malloc(arena_id_t arenaId, size_t numElems) { 32 return js_pod_arena_malloc<T>(arenaId, numElems); 33 } 34 template <typename T> 35 T* maybe_pod_arena_calloc(arena_id_t arenaId, size_t numElems) { 36 return js_pod_arena_calloc<T>(arenaId, numElems); 37 } 38 template <typename T> 39 T* maybe_pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize, 40 size_t newSize) { 41 return js_pod_arena_realloc<T>(arenaId, p, oldSize, newSize); 42 } 43 template <typename T> 44 T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) { 45 return maybe_pod_arena_malloc<T>(arenaId, numElems); 46 } 47 template <typename T> 48 T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) { 49 return maybe_pod_arena_calloc<T>(arenaId, numElems); 50 } 51 template <typename T> 52 T* pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize, 53 size_t newSize) { 54 return maybe_pod_arena_realloc<T>(arenaId, p, oldSize, newSize); 55 } 56 }; 57 58 /* Base class allocation policies providing allocation methods. */ 59 class AllocPolicyBase : public ArenaAllocPolicyBase { 60 public: 61 template <typename T> 62 T* maybe_pod_malloc(size_t numElems) { 63 return maybe_pod_arena_malloc<T>(js::MallocArena, numElems); 64 } 65 template <typename T> 66 T* maybe_pod_calloc(size_t numElems) { 67 return maybe_pod_arena_calloc<T>(js::MallocArena, numElems); 68 } 69 template <typename T> 70 T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) { 71 return maybe_pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize); 72 } 73 template <typename T> 74 T* pod_malloc(size_t numElems) { 75 return pod_arena_malloc<T>(js::MallocArena, numElems); 76 } 77 template <typename T> 78 T* pod_calloc(size_t numElems) { 79 return pod_arena_calloc<T>(js::MallocArena, numElems); 80 } 81 template <typename T> 82 T* pod_realloc(T* p, size_t oldSize, size_t newSize) { 83 return pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize); 84 } 85 86 template <typename T> 87 void free_(T* p, size_t numElems = 0) { 88 js_free(p); 89 } 90 }; 91 92 /* 93 * Base class allocation policies providing allocation methods for allocations 94 * off the main thread. 95 */ 96 class BackgroundAllocPolicyBase : ArenaAllocPolicyBase { 97 public: 98 template <typename T> 99 T* maybe_pod_malloc(size_t numElems) { 100 return maybe_pod_arena_malloc<T>(js::BackgroundMallocArena, numElems); 101 } 102 template <typename T> 103 T* maybe_pod_calloc(size_t numElems) { 104 return maybe_pod_arena_calloc<T>(js::BackgroundMallocArena, numElems); 105 } 106 template <typename T> 107 T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) { 108 return maybe_pod_arena_realloc<T>(js::BackgroundMallocArena, p, oldSize, 109 newSize); 110 } 111 template <typename T> 112 T* pod_malloc(size_t numElems) { 113 return pod_arena_malloc<T>(js::BackgroundMallocArena, numElems); 114 } 115 template <typename T> 116 T* pod_calloc(size_t numElems) { 117 return pod_arena_calloc<T>(js::BackgroundMallocArena, numElems); 118 } 119 template <typename T> 120 T* pod_realloc(T* p, size_t oldSize, size_t newSize) { 121 return pod_arena_realloc<T>(js::BackgroundMallocArena, p, oldSize, newSize); 122 } 123 124 template <typename T> 125 void free_(T* p, size_t numElems = 0) { 126 js_free(p); 127 } 128 }; 129 130 /* Policy for using system memory functions and doing no error reporting. */ 131 class SystemAllocPolicy : public AllocPolicyBase { 132 public: 133 void reportAllocOverflow() const {} 134 bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); } 135 }; 136 137 class BackgroundSystemAllocPolicy : public BackgroundAllocPolicyBase { 138 public: 139 void reportAllocOverflow() const {} 140 bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); } 141 }; 142 143 MOZ_COLD JS_PUBLIC_API void ReportOutOfMemory(JSContext* cx); 144 MOZ_COLD JS_PUBLIC_API void ReportOutOfMemory(FrontendContext* fc); 145 146 // An out of memory condition which is easily user generatable and should 147 // be specially handled to try and avoid a tab crash. 148 MOZ_COLD JS_PUBLIC_API void ReportLargeOutOfMemory(JSContext* cx); 149 150 /* 151 * Allocation policy that calls the system memory functions and reports errors 152 * to the context. Since the JSContext given on construction is stored for 153 * the lifetime of the container, this policy may only be used for containers 154 * whose lifetime is a shorter than the given JSContext. 155 * 156 * FIXME bug 647103 - rewrite this in terms of temporary allocation functions, 157 * not the system ones. 158 */ 159 class JS_PUBLIC_API TempAllocPolicy : public AllocPolicyBase { 160 // Type tag for context_bits_ 161 static constexpr uintptr_t JsContextTag = 0x1; 162 163 // Either a JSContext* (if JsContextTag is set), or FrontendContext* 164 uintptr_t const context_bits_; 165 166 MOZ_ALWAYS_INLINE bool hasJSContext() const { 167 return (context_bits_ & JsContextTag) == JsContextTag; 168 } 169 170 MOZ_ALWAYS_INLINE JSContext* cx() const { 171 MOZ_ASSERT(hasJSContext()); 172 return reinterpret_cast<JSContext*>(context_bits_ ^ JsContextTag); 173 } 174 175 MOZ_ALWAYS_INLINE FrontendContext* fc() const { 176 MOZ_ASSERT(!hasJSContext()); 177 return reinterpret_cast<FrontendContext*>(context_bits_); 178 } 179 180 /* 181 * Non-inline helper to call JSRuntime::onOutOfMemory with minimal 182 * code bloat. 183 */ 184 void* onOutOfMemory(arena_id_t arenaId, AllocFunction allocFunc, 185 size_t nbytes, void* reallocPtr = nullptr); 186 187 template <typename T> 188 T* onOutOfMemoryTyped(arena_id_t arenaId, AllocFunction allocFunc, 189 size_t numElems, void* reallocPtr = nullptr) { 190 size_t bytes; 191 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) { 192 return nullptr; 193 } 194 return static_cast<T*>( 195 onOutOfMemory(arenaId, allocFunc, bytes, reallocPtr)); 196 } 197 198 public: 199 MOZ_IMPLICIT TempAllocPolicy(JSContext* cx) 200 : context_bits_(uintptr_t(cx) | JsContextTag) { 201 MOZ_ASSERT((uintptr_t(cx) & JsContextTag) == 0); 202 } 203 MOZ_IMPLICIT TempAllocPolicy(FrontendContext* fc) 204 : context_bits_(uintptr_t(fc)) { 205 MOZ_ASSERT((uintptr_t(fc) & JsContextTag) == 0); 206 } 207 208 template <typename T> 209 T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) { 210 T* p = this->maybe_pod_arena_malloc<T>(arenaId, numElems); 211 if (MOZ_UNLIKELY(!p)) { 212 p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Malloc, numElems); 213 } 214 return p; 215 } 216 217 template <typename T> 218 T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) { 219 T* p = this->maybe_pod_arena_calloc<T>(arenaId, numElems); 220 if (MOZ_UNLIKELY(!p)) { 221 p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Calloc, numElems); 222 } 223 return p; 224 } 225 226 template <typename T> 227 T* pod_arena_realloc(arena_id_t arenaId, T* prior, size_t oldSize, 228 size_t newSize) { 229 T* p2 = this->maybe_pod_arena_realloc<T>(arenaId, prior, oldSize, newSize); 230 if (MOZ_UNLIKELY(!p2)) { 231 p2 = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Realloc, newSize, 232 prior); 233 } 234 return p2; 235 } 236 237 template <typename T> 238 T* pod_malloc(size_t numElems) { 239 return pod_arena_malloc<T>(js::MallocArena, numElems); 240 } 241 242 template <typename T> 243 T* pod_calloc(size_t numElems) { 244 return pod_arena_calloc<T>(js::MallocArena, numElems); 245 } 246 247 template <typename T> 248 T* pod_realloc(T* prior, size_t oldSize, size_t newSize) { 249 return pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize); 250 } 251 252 template <typename T> 253 void free_(T* p, size_t numElems = 0) { 254 js_free(p); 255 } 256 257 void reportAllocOverflow() const; 258 259 bool checkSimulatedOOM() const { 260 if (js::oom::ShouldFailWithOOM()) { 261 if (hasJSContext()) { 262 ReportOutOfMemory(cx()); 263 } else { 264 ReportOutOfMemory(fc()); 265 } 266 return false; 267 } 268 269 return true; 270 } 271 }; 272 273 /* 274 * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no 275 * extra behaviours. 276 * 277 * This is currently used for allocating source buffers for parsing. Since these 278 * are temporary and will not be freed by GC, the memory is not tracked by the 279 * usual accounting. 280 */ 281 class MallocAllocPolicy : public AllocPolicyBase { 282 public: 283 void reportAllocOverflow() const {} 284 285 [[nodiscard]] bool checkSimulatedOOM() const { return true; } 286 }; 287 288 } /* namespace js */ 289 290 #endif /* js_AllocPolicy_h */