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