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/src/jsalloc.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  *       - RuntimeAllocPolicy: Forwards to the JSRuntime MallocProvider.
27  *
28  *       - ZoneAllocPolicy: Forwards to the Zone MallocProvider.
29  *
30  *   - MallocProvider. A mixin base class that handles automatically updating
31  *     the GC's state in response to allocations that are tied to a GC lifetime
32  *     or are for a particular GC purpose. These allocators must only be used
33  *     for memory that will be freed when a GC thing is swept.
34  *
35  *       - gc::Zone:  Automatically triggers zone GC.
36  *       - JSRuntime: Automatically triggers full GC.
37  *       - ThreadsafeContext > ExclusiveContext > JSContext:
38  *                    Dispatches directly to the runtime.
39  */
40 
41 #ifndef vm_MallocProvider_h
42 #define vm_MallocProvider_h
43 
44 #include "mozilla/Attributes.h"
45 #include "mozilla/Likely.h"
46 
47 #include "js/UniquePtr.h"
48 #include "js/Utility.h"
49 
50 namespace js {
51 
52 template<class Client>
53 struct MallocProvider
54 {
55     template <class T>
maybe_pod_mallocMallocProvider56     T* maybe_pod_malloc(size_t numElems) {
57         T* p = js_pod_malloc<T>(numElems);
58         if (MOZ_LIKELY(p))
59             client()->updateMallocCounter(numElems * sizeof(T));
60         return p;
61     }
62 
63     template <class T>
maybe_pod_callocMallocProvider64     T* maybe_pod_calloc(size_t numElems) {
65         T* p = js_pod_calloc<T>(numElems);
66         if (MOZ_LIKELY(p))
67             client()->updateMallocCounter(numElems * sizeof(T));
68         return p;
69     }
70 
71     template <class T>
maybe_pod_reallocMallocProvider72     T* maybe_pod_realloc(T* prior, size_t oldSize, size_t newSize) {
73         T* p = js_pod_realloc(prior, oldSize, newSize);
74         if (MOZ_LIKELY(p)) {
75             // For compatibility we do not account for realloc that decreases
76             // previously allocated memory.
77             if (newSize > oldSize)
78                 client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
79         }
80         return p;
81     }
82 
83     template <class T>
pod_mallocMallocProvider84     T* pod_malloc() {
85         return pod_malloc<T>(1);
86     }
87 
88     template <class T>
pod_mallocMallocProvider89     T* pod_malloc(size_t numElems) {
90         T* p = maybe_pod_malloc<T>(numElems);
91         if (MOZ_LIKELY(p))
92             return p;
93         size_t bytes;
94         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
95             client()->reportAllocationOverflow();
96             return nullptr;
97         }
98         p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
99         if (p)
100             client()->updateMallocCounter(bytes);
101         return p;
102     }
103 
104     template <class T, class U>
pod_malloc_with_extraMallocProvider105     T* pod_malloc_with_extra(size_t numExtra) {
106         size_t bytes;
107         if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
108             client()->reportAllocationOverflow();
109             return nullptr;
110         }
111         T* p = static_cast<T*>(js_malloc(bytes));
112         if (MOZ_LIKELY(p)) {
113             client()->updateMallocCounter(bytes);
114             return p;
115         }
116         p = (T*)client()->onOutOfMemory(AllocFunction::Malloc, bytes);
117         if (p)
118             client()->updateMallocCounter(bytes);
119         return p;
120     }
121 
122     template <class T>
123     UniquePtr<T[], JS::FreePolicy>
make_pod_arrayMallocProvider124     make_pod_array(size_t numElems) {
125         return UniquePtr<T[], JS::FreePolicy>(pod_malloc<T>(numElems));
126     }
127 
128     template <class T>
pod_callocMallocProvider129     T* pod_calloc() {
130         return pod_calloc<T>(1);
131     }
132 
133     template <class T>
pod_callocMallocProvider134     T* pod_calloc(size_t numElems) {
135         T* p = maybe_pod_calloc<T>(numElems);
136         if (MOZ_LIKELY(p))
137             return p;
138         size_t bytes;
139         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
140             client()->reportAllocationOverflow();
141             return nullptr;
142         }
143         p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
144         if (p)
145             client()->updateMallocCounter(bytes);
146         return p;
147     }
148 
149     template <class T, class U>
pod_calloc_with_extraMallocProvider150     T* pod_calloc_with_extra(size_t numExtra) {
151         size_t bytes;
152         if (MOZ_UNLIKELY((!CalculateAllocSizeWithExtra<T, U>(numExtra, &bytes)))) {
153             client()->reportAllocationOverflow();
154             return nullptr;
155         }
156         T* p = static_cast<T*>(js_calloc(bytes));
157         if (p) {
158             client()->updateMallocCounter(bytes);
159             return p;
160         }
161         p = (T*)client()->onOutOfMemory(AllocFunction::Calloc, bytes);
162         if (p)
163             client()->updateMallocCounter(bytes);
164         return p;
165     }
166 
167     template <class T>
168     UniquePtr<T[], JS::FreePolicy>
make_zeroed_pod_arrayMallocProvider169     make_zeroed_pod_array(size_t numElems)
170     {
171         return UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems));
172     }
173 
174     template <class T>
pod_reallocMallocProvider175     T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
176         T* p = maybe_pod_realloc(prior, oldSize, newSize);
177         if (MOZ_LIKELY(p))
178             return p;
179         size_t bytes;
180         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(newSize, &bytes))) {
181             client()->reportAllocationOverflow();
182             return nullptr;
183         }
184         p = (T*)client()->onOutOfMemory(AllocFunction::Realloc, bytes, prior);
185         if (p && newSize > oldSize)
186             client()->updateMallocCounter((newSize - oldSize) * sizeof(T));
187         return p;
188     }
189 
JS_DECLARE_NEW_METHODSMallocProvider190     JS_DECLARE_NEW_METHODS(new_, pod_malloc<uint8_t>, MOZ_ALWAYS_INLINE)
191     JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
192 
193   private:
194     Client* client() { return static_cast<Client*>(this); }
195 };
196 
197 } /* namespace js */
198 
199 #endif /* vm_MallocProvider_h */
200