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 * Hierarchy of SpiderMonkey system memory allocators: 9 * 10 * - System {m,c,re}alloc/new/free: Overridden by jemalloc in most 11 * environments. Do not use these functions directly. 12 * 13 * - js_{m,c,re}alloc/new/free: Wraps the system allocators and adds a 14 * failure injection framework for use by the fuzzers as well as templated, 15 * typesafe variants. See js/public/Utility.h. 16 * 17 * - AllocPolicy: An interface for the js allocators, for use with templates. 18 * These allocators are for system memory whose lifetime is not associated 19 * with a GC thing. See js/public/AllocPolicy.h. 20 * 21 * - SystemAllocPolicy: No extra functionality over bare allocators. 22 * 23 * - TempAllocPolicy: Adds automatic error reporting to the provided 24 * JSContext when allocations fail. 25 * 26 * - ZoneAllocPolicy: Forwards to the Zone MallocProvider. 27 * 28 * - MallocProvider. A mixin base class that handles automatically updating 29 * the GC's state in response to allocations that are tied to a GC lifetime 30 * or are for a particular GC purpose. These allocators must only be used 31 * for memory that will be freed when a GC thing is swept. 32 * 33 * - gc::Zone: Automatically triggers zone GC. 34 * - JSRuntime: Automatically triggers full GC. 35 * - JSContext: Dispatches directly to the runtime. 36 */ 37 38 #ifndef vm_MallocProvider_h 39 #define vm_MallocProvider_h 40 41 #include "mozilla/Attributes.h" // MOZ_ALWAYS_INLINE 42 #include "mozilla/Likely.h" // MOZ_LIKELY, MOZ_UNLIKELY 43 44 #include <stddef.h> // size_t 45 #include <stdint.h> // uint8_t 46 47 #include "js/AllocPolicy.h" // AllocFunction 48 #include "js/UniquePtr.h" // UniquePtr 49 #include "js/Utility.h" // js_malloc, MallocArena, CalculateAllocSize, CalculateAllocSizeWithExtra, JS::FreePolicy 50 51 namespace js { 52 53 template <class Client> 54 struct MallocProvider { 55 template <class T> maybe_pod_arena_mallocMallocProvider56 T* maybe_pod_arena_malloc(arena_id_t arena, size_t numElems) { 57 T* p = js_pod_arena_malloc<T>(arena, numElems); 58 if (MOZ_LIKELY(p)) { 59 client()->updateMallocCounter(numElems * sizeof(T)); 60 } 61 return p; 62 } 63 64 template <class T> maybe_pod_arena_callocMallocProvider65 T* maybe_pod_arena_calloc(arena_id_t arena, size_t numElems) { 66 T* p = js_pod_arena_calloc<T>(arena, numElems); 67 if (MOZ_LIKELY(p)) { 68 client()->updateMallocCounter(numElems * sizeof(T)); 69 } 70 return p; 71 } 72 73 template <class T> maybe_pod_arena_reallocMallocProvider74 T* maybe_pod_arena_realloc(arena_id_t arena, T* prior, size_t oldSize, 75 size_t newSize) { 76 T* p = js_pod_arena_realloc<T>(arena, prior, oldSize, newSize); 77 if (MOZ_LIKELY(p)) { 78 // For compatibility we do not account for realloc that decreases 79 // previously allocated memory. 80 if (newSize > oldSize) { 81 client()->updateMallocCounter((newSize - oldSize) * sizeof(T)); 82 } 83 } 84 return p; 85 } 86 87 template <class T> maybe_pod_mallocMallocProvider88 T* maybe_pod_malloc(size_t numElems) { 89 return maybe_pod_arena_malloc<T>(js::MallocArena, numElems); 90 } 91 92 template <class T> maybe_pod_callocMallocProvider93 T* maybe_pod_calloc(size_t numElems) { 94 return maybe_pod_arena_calloc<T>(js::MallocArena, numElems); 95 } 96 97 template <class T> maybe_pod_reallocMallocProvider98 T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) { 99 return maybe_pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize); 100 } 101 102 template <class T> pod_mallocMallocProvider103 T* pod_malloc() { 104 return pod_malloc<T>(1); 105 } 106 107 template <class T> pod_arena_mallocMallocProvider108 T* pod_arena_malloc(arena_id_t arena, size_t numElems) { 109 T* p = maybe_pod_arena_malloc<T>(arena, numElems); 110 if (MOZ_LIKELY(p)) { 111 return p; 112 } 113 size_t bytes; 114 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) { 115 client()->reportAllocationOverflow(); 116 return nullptr; 117 } 118 p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, arena, bytes); 119 if (p) { 120 client()->updateMallocCounter(bytes); 121 } 122 return p; 123 } 124 125 template <class T> pod_mallocMallocProvider126 T* pod_malloc(size_t numElems) { 127 return pod_arena_malloc<T>(js::MallocArena, numElems); 128 } 129 130 template <class T, class U> pod_malloc_with_extraMallocProvider131 T* pod_malloc_with_extra(size_t numExtra) { 132 size_t bytes; 133 if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) { 134 client()->reportAllocationOverflow(); 135 return nullptr; 136 } 137 T* p = static_cast<T*>(js_malloc(bytes)); 138 if (MOZ_LIKELY(p)) { 139 client()->updateMallocCounter(bytes); 140 return p; 141 } 142 p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, js::MallocArena, 143 bytes); 144 if (p) { 145 client()->updateMallocCounter(bytes); 146 } 147 return p; 148 } 149 150 template <class T> make_pod_arena_arrayMallocProvider151 UniquePtr<T[], JS::FreePolicy> make_pod_arena_array(arena_id_t arena, 152 size_t numElems) { 153 return UniquePtr<T[], JS::FreePolicy>(pod_arena_malloc<T>(arena, numElems)); 154 } 155 156 template <class T> make_pod_arrayMallocProvider157 UniquePtr<T[], JS::FreePolicy> make_pod_array(size_t numElems) { 158 return make_pod_arena_array<T>(js::MallocArena, numElems); 159 } 160 161 template <class T> 162 T* pod_arena_calloc(arena_id_t arena, size_t numElems = 1) { 163 T* p = maybe_pod_arena_calloc<T>(arena, numElems); 164 if (MOZ_LIKELY(p)) { 165 return p; 166 } 167 size_t bytes; 168 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) { 169 client()->reportAllocationOverflow(); 170 return nullptr; 171 } 172 p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, arena, bytes); 173 if (p) { 174 client()->updateMallocCounter(bytes); 175 } 176 return p; 177 } 178 179 template <class T> 180 T* pod_calloc(size_t numElems = 1) { 181 return pod_arena_calloc<T>(js::MallocArena, numElems); 182 } 183 184 template <class T, class U> pod_calloc_with_extraMallocProvider185 T* pod_calloc_with_extra(size_t numExtra) { 186 size_t bytes; 187 if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) { 188 client()->reportAllocationOverflow(); 189 return nullptr; 190 } 191 T* p = static_cast<T*>(js_calloc(bytes)); 192 if (p) { 193 client()->updateMallocCounter(bytes); 194 return p; 195 } 196 p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, js::MallocArena, 197 bytes); 198 if (p) { 199 client()->updateMallocCounter(bytes); 200 } 201 return p; 202 } 203 204 template <class T> make_zeroed_pod_arrayMallocProvider205 UniquePtr<T[], JS::FreePolicy> make_zeroed_pod_array(size_t numElems) { 206 return UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems)); 207 } 208 209 template <class T> pod_arena_reallocMallocProvider210 T* pod_arena_realloc(arena_id_t arena, T* prior, size_t oldSize, 211 size_t newSize) { 212 T* p = maybe_pod_arena_realloc(arena, prior, oldSize, newSize); 213 if (MOZ_LIKELY(p)) { 214 return p; 215 } 216 size_t bytes; 217 if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newSize, &bytes))) { 218 client()->reportAllocationOverflow(); 219 return nullptr; 220 } 221 p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, arena, bytes, 222 prior); 223 if (p && newSize > oldSize) { 224 client()->updateMallocCounter((newSize - oldSize) * sizeof(T)); 225 } 226 return p; 227 } 228 229 template <class T> pod_reallocMallocProvider230 T* pod_realloc(T* prior, size_t oldSize, size_t newSize) { 231 return pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize); 232 } 233 JS_DECLARE_NEW_METHODSMallocProvider234 JS_DECLARE_NEW_METHODS(new_, pod_malloc<uint8_t>, MOZ_ALWAYS_INLINE) 235 JS_DECLARE_NEW_ARENA_METHODS( 236 arena_new_, 237 [this](arena_id_t arena, size_t size) { 238 return pod_malloc<uint8_t>(size, arena); 239 }, 240 MOZ_ALWAYS_INLINE) 241 242 JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE) 243 JS_DECLARE_MAKE_METHODS(arena_make_unique, arena_new_, MOZ_ALWAYS_INLINE) 244 245 private: 246 Client* client() { return static_cast<Client*>(this); } 247 248 // The Default implementation is a no-op which can be overridden by the 249 // client. updateMallocCounterMallocProvider250 void updateMallocCounter(size_t nbytes) {} 251 }; 252 253 } /* namespace js */ 254 255 #endif /* vm_MallocProvider_h */ 256