1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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_mallocMallocProvider52   T* maybe_pod_malloc(size_t numElems) {
53     T* p = js_pod_malloc<T>(numElems);
54     if (MOZ_LIKELY(p)) client()->updateMallocCounter(numElems * sizeof(T));
55     return p;
56   }
57 
58   template <class T>
maybe_pod_callocMallocProvider59   T* maybe_pod_calloc(size_t numElems) {
60     T* p = js_pod_calloc<T>(numElems);
61     if (MOZ_LIKELY(p)) client()->updateMallocCounter(numElems * sizeof(T));
62     return p;
63   }
64 
65   template <class T>
maybe_pod_reallocMallocProvider66   T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
67     T* p = js_pod_realloc(prior, oldSize, newSize);
68     if (MOZ_LIKELY(p)) {
69       // For compatibility we do not account for realloc that decreases
70       // previously allocated memory.
71       if (newSize > oldSize)
72         client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
73     }
74     return p;
75   }
76 
77   template <class T>
pod_mallocMallocProvider78   T* pod_malloc() {
79     return pod_malloc<T>(1);
80   }
81 
82   template <class T>
pod_mallocMallocProvider83   T* pod_malloc(size_t numElems) {
84     T* p = maybe_pod_malloc<T>(numElems);
85     if (MOZ_LIKELY(p)) return p;
86     size_t bytes;
87     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
88       client()->reportAllocationOverflow();
89       return nullptr;
90     }
91     p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
92     if (p) client()->updateMallocCounter(bytes);
93     return p;
94   }
95 
96   template <class T, class U>
pod_malloc_with_extraMallocProvider97   T* pod_malloc_with_extra(size_t numExtra) {
98     size_t bytes;
99     if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
100       client()->reportAllocationOverflow();
101       return nullptr;
102     }
103     T* p = static_cast<T*>(js_malloc(bytes));
104     if (MOZ_LIKELY(p)) {
105       client()->updateMallocCounter(bytes);
106       return p;
107     }
108     p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
109     if (p) client()->updateMallocCounter(bytes);
110     return p;
111   }
112 
113   template <class T>
make_pod_arrayMallocProvider114   UniquePtr<T[], JS::FreePolicy> make_pod_array(size_t numElems) {
115     return UniquePtr<T[], JS::FreePolicy>(pod_malloc<T>(numElems));
116   }
117 
118   template <class T>
pod_callocMallocProvider119   T* pod_calloc() {
120     return pod_calloc<T>(1);
121   }
122 
123   template <class T>
pod_callocMallocProvider124   T* pod_calloc(size_t numElems) {
125     T* p = maybe_pod_calloc<T>(numElems);
126     if (MOZ_LIKELY(p)) return p;
127     size_t bytes;
128     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
129       client()->reportAllocationOverflow();
130       return nullptr;
131     }
132     p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
133     if (p) client()->updateMallocCounter(bytes);
134     return p;
135   }
136 
137   template <class T, class U>
pod_calloc_with_extraMallocProvider138   T* pod_calloc_with_extra(size_t numExtra) {
139     size_t bytes;
140     if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
141       client()->reportAllocationOverflow();
142       return nullptr;
143     }
144     T* p = static_cast<T*>(js_calloc(bytes));
145     if (p) {
146       client()->updateMallocCounter(bytes);
147       return p;
148     }
149     p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
150     if (p) client()->updateMallocCounter(bytes);
151     return p;
152   }
153 
154   template <class T>
make_zeroed_pod_arrayMallocProvider155   UniquePtr<T[], JS::FreePolicy> make_zeroed_pod_array(size_t numElems) {
156     return UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems));
157   }
158 
159   template <class T>
pod_reallocMallocProvider160   T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
161     T* p = maybe_pod_realloc(prior, oldSize, newSize);
162     if (MOZ_LIKELY(p)) return p;
163     size_t bytes;
164     if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newSize, &bytes))) {
165       client()->reportAllocationOverflow();
166       return nullptr;
167     }
168     p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, bytes, prior);
169     if (p && newSize > oldSize)
170       client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
171     return p;
172   }
173 
JS_DECLARE_NEW_METHODSMallocProvider174   JS_DECLARE_NEW_METHODS(new_, pod_malloc<uint8_t>, MOZ_ALWAYS_INLINE)
175   JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
176 
177  private:
178   Client* client() { return static_cast<Client*>(this); }
179 };
180 
181 } /* namespace js */
182 
183 #endif /* vm_MallocProvider_h */
184