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"
42 #include "mozilla/Likely.h"
43 
44 #include "js/UniquePtr.h"
45 #include "js/Utility.h"
46 
47 namespace js {
48 
49 template <class Client>
50 struct MallocProvider {
51   template <class T>
maybe_pod_arena_mallocMallocProvider52   T* maybe_pod_arena_malloc(arena_id_t arena, size_t numElems) {
53     T* p = js_pod_arena_malloc<T>(arena, numElems);
54     if (MOZ_LIKELY(p)) {
55       client()->updateMallocCounter(numElems * sizeof(T));
56     }
57     return p;
58   }
59 
60   template <class T>
maybe_pod_arena_callocMallocProvider61   T* maybe_pod_arena_calloc(arena_id_t arena, size_t numElems) {
62     T* p = js_pod_arena_calloc<T>(arena, numElems);
63     if (MOZ_LIKELY(p)) {
64       client()->updateMallocCounter(numElems * sizeof(T));
65     }
66     return p;
67   }
68 
69   template <class T>
maybe_pod_arena_reallocMallocProvider70   T* maybe_pod_arena_realloc(arena_id_t arena, T* prior, size_t oldSize,
71                              size_t newSize) {
72     T* p = js_pod_arena_realloc<T>(arena, prior, oldSize, newSize);
73     if (MOZ_LIKELY(p)) {
74       // For compatibility we do not account for realloc that decreases
75       // previously allocated memory.
76       if (newSize > oldSize) {
77         client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
78       }
79     }
80     return p;
81   }
82 
83   template <class T>
maybe_pod_mallocMallocProvider84   T* maybe_pod_malloc(size_t numElems) {
85     return maybe_pod_arena_malloc<T>(js::MallocArena, numElems);
86   }
87 
88   template <class T>
maybe_pod_callocMallocProvider89   T* maybe_pod_calloc(size_t numElems) {
90     return maybe_pod_arena_calloc<T>(js::MallocArena, numElems);
91   }
92 
93   template <class T>
maybe_pod_reallocMallocProvider94   T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
95     return maybe_pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
96   }
97 
98   template <class T>
pod_mallocMallocProvider99   T* pod_malloc() {
100     return pod_malloc<T>(1);
101   }
102 
103   template <class T>
pod_arena_mallocMallocProvider104   T* pod_arena_malloc(arena_id_t arena, size_t numElems) {
105     T* p = maybe_pod_arena_malloc<T>(arena, numElems);
106     if (MOZ_LIKELY(p)) {
107       return p;
108     }
109     size_t bytes;
110     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
111       client()->reportAllocationOverflow();
112       return nullptr;
113     }
114     p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, arena, bytes);
115     if (p) {
116       client()->updateMallocCounter(bytes);
117     }
118     return p;
119   }
120 
121   template <class T>
pod_mallocMallocProvider122   T* pod_malloc(size_t numElems) {
123     return pod_arena_malloc<T>(js::MallocArena, numElems);
124   }
125 
126   template <class T, class U>
pod_malloc_with_extraMallocProvider127   T* pod_malloc_with_extra(size_t numExtra) {
128     size_t bytes;
129     if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
130       client()->reportAllocationOverflow();
131       return nullptr;
132     }
133     T* p = static_cast<T*>(js_malloc(bytes));
134     if (MOZ_LIKELY(p)) {
135       client()->updateMallocCounter(bytes);
136       return p;
137     }
138     p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, js::MallocArena,
139                                     bytes);
140     if (p) {
141       client()->updateMallocCounter(bytes);
142     }
143     return p;
144   }
145 
146   template <class T>
make_pod_arena_arrayMallocProvider147   UniquePtr<T[], JS::FreePolicy> make_pod_arena_array(arena_id_t arena,
148                                                       size_t numElems) {
149     return UniquePtr<T[], JS::FreePolicy>(pod_arena_malloc<T>(arena, numElems));
150   }
151 
152   template <class T>
make_pod_arrayMallocProvider153   UniquePtr<T[], JS::FreePolicy> make_pod_array(size_t numElems) {
154     return make_pod_arena_array<T>(js::MallocArena, numElems);
155   }
156 
157   template <class T>
158   T* pod_arena_calloc(arena_id_t arena, size_t numElems = 1) {
159     T* p = maybe_pod_arena_calloc<T>(arena, numElems);
160     if (MOZ_LIKELY(p)) {
161       return p;
162     }
163     size_t bytes;
164     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
165       client()->reportAllocationOverflow();
166       return nullptr;
167     }
168     p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, arena, bytes);
169     if (p) {
170       client()->updateMallocCounter(bytes);
171     }
172     return p;
173   }
174 
175   template <class T>
176   T* pod_calloc(size_t numElems = 1) {
177     return pod_arena_calloc<T>(js::MallocArena, numElems);
178   }
179 
180   template <class T, class U>
pod_calloc_with_extraMallocProvider181   T* pod_calloc_with_extra(size_t numExtra) {
182     size_t bytes;
183     if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
184       client()->reportAllocationOverflow();
185       return nullptr;
186     }
187     T* p = static_cast<T*>(js_calloc(bytes));
188     if (p) {
189       client()->updateMallocCounter(bytes);
190       return p;
191     }
192     p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, js::MallocArena,
193                                     bytes);
194     if (p) {
195       client()->updateMallocCounter(bytes);
196     }
197     return p;
198   }
199 
200   template <class T>
make_zeroed_pod_arrayMallocProvider201   UniquePtr<T[], JS::FreePolicy> make_zeroed_pod_array(size_t numElems) {
202     return UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems));
203   }
204 
205   template <class T>
pod_arena_reallocMallocProvider206   T* pod_arena_realloc(arena_id_t arena, T* prior, size_t oldSize,
207                        size_t newSize) {
208     T* p = maybe_pod_arena_realloc(arena, prior, oldSize, newSize);
209     if (MOZ_LIKELY(p)) {
210       return p;
211     }
212     size_t bytes;
213     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newSize, &bytes))) {
214       client()->reportAllocationOverflow();
215       return nullptr;
216     }
217     p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, arena, bytes,
218                                     prior);
219     if (p && newSize > oldSize) {
220       client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
221     }
222     return p;
223   }
224 
225   template <class T>
pod_reallocMallocProvider226   T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
227     return pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
228   }
229 
JS_DECLARE_NEW_METHODSMallocProvider230   JS_DECLARE_NEW_METHODS(new_, pod_malloc<uint8_t>, MOZ_ALWAYS_INLINE)
231   JS_DECLARE_NEW_ARENA_METHODS(
232       arena_new_,
233       [this](arena_id_t arena, size_t size) {
234         return pod_malloc<uint8_t>(size, arena);
235       },
236       MOZ_ALWAYS_INLINE)
237 
238   JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
239   JS_DECLARE_MAKE_METHODS(arena_make_unique, arena_new_, MOZ_ALWAYS_INLINE)
240 
241  private:
242   Client* client() { return static_cast<Client*>(this); }
243 
244   // The Default implementation is a no-op which can be overridden by the
245   // client.
updateMallocCounterMallocProvider246   void updateMallocCounter(size_t nbytes) {}
247 };
248 
249 } /* namespace js */
250 
251 #endif /* vm_MallocProvider_h */
252