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 #ifndef js_Utility_h
8 #define js_Utility_h
9 
10 #include "mozilla/Assertions.h"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/Compiler.h"
14 #include "mozilla/TemplateLib.h"
15 #include "mozilla/UniquePtr.h"
16 
17 #include <stdlib.h>
18 #include <string.h>
19 #include <type_traits>
20 #include <utility>
21 
22 #include "jstypes.h"
23 #include "mozmemory.h"
24 #include "js/TypeDecls.h"
25 
26 /* The public JS engine namespace. */
27 namespace JS {}
28 
29 /* The mozilla-shared reusable template/utility namespace. */
30 namespace mozilla {}
31 
32 /* The private JS engine namespace. */
33 namespace js {}
34 
35 extern MOZ_NORETURN MOZ_COLD JS_PUBLIC_API void JS_Assert(const char* s,
36                                                           const char* file,
37                                                           int ln);
38 
39 /*
40  * Custom allocator support for SpiderMonkey
41  */
42 #if defined JS_USE_CUSTOM_ALLOCATOR
43 #  include "jscustomallocator.h"
44 #else
45 
46 namespace js {
47 
48 /*
49  * Thread types are used to tag threads for certain kinds of testing (see
50  * below), and also used to characterize threads in the thread scheduler (see
51  * js/src/vm/HelperThreads.cpp).
52  *
53  * Please update oom::FirstThreadTypeToTest and oom::LastThreadTypeToTest when
54  * adding new thread types.
55  */
56 enum ThreadType {
57   THREAD_TYPE_NONE = 0,              // 0
58   THREAD_TYPE_MAIN,                  // 1
59   THREAD_TYPE_WASM_COMPILE_TIER1,    // 2
60   THREAD_TYPE_WASM_COMPILE_TIER2,    // 3
61   THREAD_TYPE_ION,                   // 4
62   THREAD_TYPE_PARSE,                 // 5
63   THREAD_TYPE_COMPRESS,              // 6
64   THREAD_TYPE_GCPARALLEL,            // 7
65   THREAD_TYPE_PROMISE_TASK,          // 8
66   THREAD_TYPE_ION_FREE,              // 9
67   THREAD_TYPE_WASM_GENERATOR_TIER2,  // 10
68   THREAD_TYPE_WORKER,                // 11
69   THREAD_TYPE_MAX                    // Used to check shell function arguments
70 };
71 
72 namespace oom {
73 
74 /*
75  * Theads are tagged only in certain debug contexts.  Notably, to make testing
76  * OOM in certain helper threads more effective, we allow restricting the OOM
77  * testing to a certain helper thread type. This allows us to fail e.g. in
78  * off-thread script parsing without causing an OOM in the active thread first.
79  *
80  * Getter/Setter functions to encapsulate mozilla::ThreadLocal, implementation
81  * is in util/Utility.cpp.
82  */
83 #  if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
84 
85 // Define the range of threads tested by simulated OOM testing and the
86 // like. Testing worker threads is not supported.
87 const ThreadType FirstThreadTypeToTest = THREAD_TYPE_MAIN;
88 const ThreadType LastThreadTypeToTest = THREAD_TYPE_WASM_GENERATOR_TIER2;
89 
90 extern bool InitThreadType(void);
91 extern void SetThreadType(ThreadType);
92 extern JS_PUBLIC_API uint32_t GetThreadType(void);
93 
94 #  else
95 
96 inline bool InitThreadType(void) { return true; }
97 inline void SetThreadType(ThreadType t){};
98 inline uint32_t GetThreadType(void) { return 0; }
99 inline uint32_t GetAllocationThreadType(void) { return 0; }
100 inline uint32_t GetStackCheckThreadType(void) { return 0; }
101 inline uint32_t GetInterruptCheckThreadType(void) { return 0; }
102 
103 #  endif
104 
105 } /* namespace oom */
106 } /* namespace js */
107 
108 #  if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
109 
110 #    ifdef JS_OOM_BREAKPOINT
111 #      if defined(_MSC_VER)
js_failedAllocBreakpoint()112 static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() {
113   __asm {}
114   ;
115 }
116 #      else
js_failedAllocBreakpoint()117 static MOZ_NEVER_INLINE void js_failedAllocBreakpoint() { asm(""); }
118 #      endif
119 #      define JS_OOM_CALL_BP_FUNC() js_failedAllocBreakpoint()
120 #    else
121 #      define JS_OOM_CALL_BP_FUNC() \
122         do {                        \
123         } while (0)
124 #    endif
125 
126 namespace js {
127 namespace oom {
128 
129 /*
130  * Out of memory testing support.  We provide various testing functions to
131  * simulate OOM conditions and so we can test that they are handled correctly.
132  */
133 class FailureSimulator {
134  public:
135   enum class Kind : uint8_t { Nothing, OOM, StackOOM, Interrupt };
136 
137  private:
138   Kind kind_ = Kind::Nothing;
139   uint32_t targetThread_ = 0;
140   uint64_t maxChecks_ = UINT64_MAX;
141   uint64_t counter_ = 0;
142   bool failAlways_ = true;
143   bool inUnsafeRegion_ = false;
144 
145  public:
maxChecks()146   uint64_t maxChecks() const { return maxChecks_; }
counter()147   uint64_t counter() const { return counter_; }
setInUnsafeRegion(bool b)148   void setInUnsafeRegion(bool b) {
149     MOZ_ASSERT(inUnsafeRegion_ != b);
150     inUnsafeRegion_ = b;
151   }
targetThread()152   uint32_t targetThread() const { return targetThread_; }
isThreadSimulatingAny()153   bool isThreadSimulatingAny() const {
154     return targetThread_ && targetThread_ == js::oom::GetThreadType() &&
155            !inUnsafeRegion_;
156   }
isThreadSimulating(Kind kind)157   bool isThreadSimulating(Kind kind) const {
158     return kind_ == kind && isThreadSimulatingAny();
159   }
isSimulatedFailure(Kind kind)160   bool isSimulatedFailure(Kind kind) const {
161     if (!isThreadSimulating(kind)) {
162       return false;
163     }
164     return counter_ == maxChecks_ || (counter_ > maxChecks_ && failAlways_);
165   }
hadFailure(Kind kind)166   bool hadFailure(Kind kind) const {
167     return kind_ == kind && counter_ >= maxChecks_;
168   }
shouldFail(Kind kind)169   bool shouldFail(Kind kind) {
170     if (!isThreadSimulating(kind)) {
171       return false;
172     }
173     counter_++;
174     if (isSimulatedFailure(kind)) {
175       JS_OOM_CALL_BP_FUNC();
176       return true;
177     }
178     return false;
179   }
180 
181   void simulateFailureAfter(Kind kind, uint64_t checks, uint32_t thread,
182                             bool always);
183   void reset();
184 };
185 extern JS_PUBLIC_DATA FailureSimulator simulator;
186 
IsSimulatedOOMAllocation()187 inline bool IsSimulatedOOMAllocation() {
188   return simulator.isSimulatedFailure(FailureSimulator::Kind::OOM);
189 }
190 
ShouldFailWithOOM()191 inline bool ShouldFailWithOOM() {
192   return simulator.shouldFail(FailureSimulator::Kind::OOM);
193 }
194 
HadSimulatedOOM()195 inline bool HadSimulatedOOM() {
196   return simulator.hadFailure(FailureSimulator::Kind::OOM);
197 }
198 
199 /*
200  * Out of stack space testing support, similar to OOM testing functions.
201  */
202 
IsSimulatedStackOOMCheck()203 inline bool IsSimulatedStackOOMCheck() {
204   return simulator.isSimulatedFailure(FailureSimulator::Kind::StackOOM);
205 }
206 
ShouldFailWithStackOOM()207 inline bool ShouldFailWithStackOOM() {
208   return simulator.shouldFail(FailureSimulator::Kind::StackOOM);
209 }
210 
HadSimulatedStackOOM()211 inline bool HadSimulatedStackOOM() {
212   return simulator.hadFailure(FailureSimulator::Kind::StackOOM);
213 }
214 
215 /*
216  * Interrupt testing support, similar to OOM testing functions.
217  */
218 
IsSimulatedInterruptCheck()219 inline bool IsSimulatedInterruptCheck() {
220   return simulator.isSimulatedFailure(FailureSimulator::Kind::Interrupt);
221 }
222 
ShouldFailWithInterrupt()223 inline bool ShouldFailWithInterrupt() {
224   return simulator.shouldFail(FailureSimulator::Kind::Interrupt);
225 }
226 
HadSimulatedInterrupt()227 inline bool HadSimulatedInterrupt() {
228   return simulator.hadFailure(FailureSimulator::Kind::Interrupt);
229 }
230 
231 } /* namespace oom */
232 } /* namespace js */
233 
234 #    define JS_OOM_POSSIBLY_FAIL()                        \
235       do {                                                \
236         if (js::oom::ShouldFailWithOOM()) return nullptr; \
237       } while (0)
238 
239 #    define JS_OOM_POSSIBLY_FAIL_BOOL()                 \
240       do {                                              \
241         if (js::oom::ShouldFailWithOOM()) return false; \
242       } while (0)
243 
244 #    define JS_STACK_OOM_POSSIBLY_FAIL()                     \
245       do {                                                   \
246         if (js::oom::ShouldFailWithStackOOM()) return false; \
247       } while (0)
248 
249 #    define JS_INTERRUPT_POSSIBLY_FAIL()                             \
250       do {                                                           \
251         if (MOZ_UNLIKELY(js::oom::ShouldFailWithInterrupt())) {      \
252           cx->requestInterrupt(js::InterruptReason::CallbackUrgent); \
253           return cx->handleInterrupt();                              \
254         }                                                            \
255       } while (0)
256 
257 #  else
258 
259 #    define JS_OOM_POSSIBLY_FAIL() \
260       do {                         \
261       } while (0)
262 #    define JS_OOM_POSSIBLY_FAIL_BOOL() \
263       do {                              \
264       } while (0)
265 #    define JS_STACK_OOM_POSSIBLY_FAIL() \
266       do {                               \
267       } while (0)
268 #    define JS_INTERRUPT_POSSIBLY_FAIL() \
269       do {                               \
270       } while (0)
271 namespace js {
272 namespace oom {
IsSimulatedOOMAllocation()273 static inline bool IsSimulatedOOMAllocation() { return false; }
ShouldFailWithOOM()274 static inline bool ShouldFailWithOOM() { return false; }
275 } /* namespace oom */
276 } /* namespace js */
277 
278 #  endif /* DEBUG || JS_OOM_BREAKPOINT */
279 
280 #  ifdef FUZZING
281 namespace js {
282 namespace oom {
283 extern JS_PUBLIC_DATA size_t largeAllocLimit;
284 extern void InitLargeAllocLimit();
285 } /* namespace oom */
286 } /* namespace js */
287 
288 #    define JS_CHECK_LARGE_ALLOC(x)                                     \
289       do {                                                              \
290         if (js::oom::largeAllocLimit && x > js::oom::largeAllocLimit) { \
291           if (getenv("MOZ_FUZZ_CRASH_ON_LARGE_ALLOC")) {                \
292             MOZ_CRASH("Large allocation");                              \
293           } else {                                                      \
294             return nullptr;                                             \
295           }                                                             \
296         }                                                               \
297       } while (0)
298 #  else
299 #    define JS_CHECK_LARGE_ALLOC(x) \
300       do {                          \
301       } while (0)
302 #  endif
303 
304 namespace js {
305 
306 /* Disable OOM testing in sections which are not OOM safe. */
307 struct MOZ_RAII JS_PUBLIC_DATA AutoEnterOOMUnsafeRegion {
308   MOZ_NORETURN MOZ_COLD void crash(const char* reason);
309   MOZ_NORETURN MOZ_COLD void crash(size_t size, const char* reason);
310 
311   using AnnotateOOMAllocationSizeCallback = void (*)(size_t);
312   static mozilla::Atomic<AnnotateOOMAllocationSizeCallback, mozilla::Relaxed>
313       annotateOOMSizeCallback;
setAnnotateOOMAllocationSizeCallbackAutoEnterOOMUnsafeRegion314   static void setAnnotateOOMAllocationSizeCallback(
315       AnnotateOOMAllocationSizeCallback callback) {
316     annotateOOMSizeCallback = callback;
317   }
318 
319 #  if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
AutoEnterOOMUnsafeRegionAutoEnterOOMUnsafeRegion320   AutoEnterOOMUnsafeRegion()
321       : oomEnabled_(oom::simulator.isThreadSimulatingAny()) {
322     if (oomEnabled_) {
323       MOZ_ALWAYS_TRUE(owner_.compareExchange(nullptr, this));
324       oom::simulator.setInUnsafeRegion(true);
325     }
326   }
327 
~AutoEnterOOMUnsafeRegionAutoEnterOOMUnsafeRegion328   ~AutoEnterOOMUnsafeRegion() {
329     if (oomEnabled_) {
330       oom::simulator.setInUnsafeRegion(false);
331       MOZ_ALWAYS_TRUE(owner_.compareExchange(this, nullptr));
332     }
333   }
334 
335  private:
336   // Used to catch concurrent use from other threads.
337   static mozilla::Atomic<AutoEnterOOMUnsafeRegion*> owner_;
338 
339   bool oomEnabled_;
340 #  endif
341 };
342 
343 } /* namespace js */
344 
345 // Malloc allocation.
346 
347 namespace js {
348 
349 extern JS_PUBLIC_DATA arena_id_t MallocArena;
350 extern JS_PUBLIC_DATA arena_id_t ArrayBufferContentsArena;
351 extern JS_PUBLIC_DATA arena_id_t StringBufferArena;
352 
353 extern void InitMallocAllocator();
354 extern void ShutDownMallocAllocator();
355 
356 // This is a no-op if built without MOZ_MEMORY and MOZ_DEBUG.
357 extern void AssertJSStringBufferInCorrectArena(const void* ptr);
358 
359 } /* namespace js */
360 
js_arena_malloc(arena_id_t arena,size_t bytes)361 static inline void* js_arena_malloc(arena_id_t arena, size_t bytes) {
362   JS_OOM_POSSIBLY_FAIL();
363   JS_CHECK_LARGE_ALLOC(bytes);
364   return moz_arena_malloc(arena, bytes);
365 }
366 
js_malloc(size_t bytes)367 static inline void* js_malloc(size_t bytes) {
368   return js_arena_malloc(js::MallocArena, bytes);
369 }
370 
js_arena_calloc(arena_id_t arena,size_t bytes)371 static inline void* js_arena_calloc(arena_id_t arena, size_t bytes) {
372   JS_OOM_POSSIBLY_FAIL();
373   JS_CHECK_LARGE_ALLOC(bytes);
374   return moz_arena_calloc(arena, bytes, 1);
375 }
376 
js_arena_calloc(arena_id_t arena,size_t nmemb,size_t size)377 static inline void* js_arena_calloc(arena_id_t arena, size_t nmemb,
378                                     size_t size) {
379   JS_OOM_POSSIBLY_FAIL();
380   JS_CHECK_LARGE_ALLOC(nmemb * size);
381   return moz_arena_calloc(arena, nmemb, size);
382 }
383 
js_calloc(size_t bytes)384 static inline void* js_calloc(size_t bytes) {
385   return js_arena_calloc(js::MallocArena, bytes);
386 }
387 
js_calloc(size_t nmemb,size_t size)388 static inline void* js_calloc(size_t nmemb, size_t size) {
389   return js_arena_calloc(js::MallocArena, nmemb, size);
390 }
391 
js_arena_realloc(arena_id_t arena,void * p,size_t bytes)392 static inline void* js_arena_realloc(arena_id_t arena, void* p, size_t bytes) {
393   // realloc() with zero size is not portable, as some implementations may
394   // return nullptr on success and free |p| for this.  We assume nullptr
395   // indicates failure and that |p| is still valid.
396   MOZ_ASSERT(bytes != 0);
397 
398   JS_OOM_POSSIBLY_FAIL();
399   JS_CHECK_LARGE_ALLOC(bytes);
400   return moz_arena_realloc(arena, p, bytes);
401 }
402 
js_realloc(void * p,size_t bytes)403 static inline void* js_realloc(void* p, size_t bytes) {
404   return js_arena_realloc(js::MallocArena, p, bytes);
405 }
406 
js_free(void * p)407 static inline void js_free(void* p) {
408   // TODO: This should call |moz_arena_free(js::MallocArena, p)| but we
409   // currently can't enforce that all memory freed here was allocated by
410   // js_malloc().
411   free(p);
412 }
413 #endif /* JS_USE_CUSTOM_ALLOCATOR */
414 
415 #include <new>
416 
417 /*
418  * [SMDOC] Low-level memory management in SpiderMonkey
419  *
420  *  ** Do not use the standard malloc/free/realloc: SpiderMonkey allows these
421  *     to be redefined (via JS_USE_CUSTOM_ALLOCATOR) and Gecko even #define's
422  *     these symbols.
423  *
424  *  ** Do not use the builtin C++ operator new and delete: these throw on
425  *     error and we cannot override them not to.
426  *
427  * Allocation:
428  *
429  * - If the lifetime of the allocation is tied to the lifetime of a GC-thing
430  *   (that is, finalizing the GC-thing will free the allocation), call one of
431  *   the following functions:
432  *
433  *     JSContext::{pod_malloc,pod_calloc,pod_realloc}
434  *     Zone::{pod_malloc,pod_calloc,pod_realloc}
435  *
436  *   These functions accumulate the number of bytes allocated which is used as
437  *   part of the GC-triggering heuristics.
438  *
439  *   The difference between the JSContext and Zone versions is that the
440  *   cx version report an out-of-memory error on OOM. (This follows from the
441  *   general SpiderMonkey idiom that a JSContext-taking function reports its
442  *   own errors.)
443  *
444  *   If you don't want to report an error on failure, there are maybe_ versions
445  *   of these methods available too, e.g. maybe_pod_malloc.
446  *
447  *   The methods above use templates to allow allocating memory suitable for an
448  *   array of a given type and number of elements. There are _with_extra
449  *   versions to allow allocating an area of memory which is larger by a
450  *   specified number of bytes, e.g. pod_malloc_with_extra.
451  *
452  *   These methods are available on a JSRuntime, but calling them is
453  *   discouraged. Memory attributed to a runtime can only be reclaimed by full
454  *   GCs, and we try to avoid those where possible.
455  *
456  * - Otherwise, use js_malloc/js_realloc/js_calloc/js_new
457  *
458  * Deallocation:
459  *
460  * - Ordinarily, use js_free/js_delete.
461  *
462  * - For deallocations during GC finalization, use one of the following
463  *   operations on the JSFreeOp provided to the finalizer:
464  *
465  *     JSFreeOp::{free_,delete_}
466  */
467 
468 /*
469  * Given a class which should provide a 'new' method, add
470  * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
471  *
472  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
473  * or the build will break.
474  */
475 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)              \
476   template <class T, typename... Args>                                      \
477   QUALIFIERS T* MOZ_HEAP_ALLOCATOR NEWNAME(Args&&... args) {                \
478     void* memory = ALLOCATOR(sizeof(T));                                    \
479     return MOZ_LIKELY(memory) ? new (memory) T(std::forward<Args>(args)...) \
480                               : nullptr;                                    \
481   }
482 
483 /*
484  * Given a class which should provide a 'new' method that takes an arena as
485  * its first argument, add JS_DECLARE_NEW_ARENA_METHODS
486  * (see js::MallocProvider for an example).
487  *
488  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_ARENA_METHODS,
489  * or the build will break.
490  */
491 #define JS_DECLARE_NEW_ARENA_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)           \
492   template <class T, typename... Args>                                         \
493   QUALIFIERS T* MOZ_HEAP_ALLOCATOR NEWNAME(arena_id_t arena, Args&&... args) { \
494     void* memory = ALLOCATOR(arena, sizeof(T));                                \
495     return MOZ_LIKELY(memory) ? new (memory) T(std::forward<Args>(args)...)    \
496                               : nullptr;                                       \
497   }
498 
499 /*
500  * Given a class which should provide 'make' methods, add
501  * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example).  This
502  * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
503  * methods that return mozilla::UniquePtr instances that will singly-manage
504  * ownership of the created object.
505  *
506  * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
507  * or the build will break.
508  */
509 #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)             \
510   template <class T, typename... Args>                                     \
511   QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> MOZ_HEAP_ALLOCATOR \
512   MAKENAME(Args&&... args) {                                               \
513     T* ptr = NEWNAME<T>(std::forward<Args>(args)...);                      \
514     return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr);                \
515   }
516 
JS_DECLARE_NEW_METHODS(js_new,js_malloc,static MOZ_ALWAYS_INLINE)517 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
518 JS_DECLARE_NEW_ARENA_METHODS(js_arena_new, js_arena_malloc,
519                              static MOZ_ALWAYS_INLINE)
520 
521 namespace js {
522 
523 /*
524  * Calculate the number of bytes needed to allocate |numElems| contiguous
525  * instances of type |T|.  Return false if the calculation overflowed.
526  */
527 template <typename T>
528 [[nodiscard]] inline bool CalculateAllocSize(size_t numElems,
529                                              size_t* bytesOut) {
530   *bytesOut = numElems * sizeof(T);
531   return (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) == 0;
532 }
533 
534 /*
535  * Calculate the number of bytes needed to allocate a single instance of type
536  * |T| followed by |numExtra| contiguous instances of type |Extra|.  Return
537  * false if the calculation overflowed.
538  */
539 template <typename T, typename Extra>
540 [[nodiscard]] inline bool CalculateAllocSizeWithExtra(size_t numExtra,
541                                                       size_t* bytesOut) {
542   *bytesOut = sizeof(T) + numExtra * sizeof(Extra);
543   return (numExtra & mozilla::tl::MulOverflowMask<sizeof(Extra)>::value) == 0 &&
544          *bytesOut >= sizeof(T);
545 }
546 
547 } /* namespace js */
548 
549 template <class T>
js_delete(const T * p)550 static MOZ_ALWAYS_INLINE void js_delete(const T* p) {
551   if (p) {
552     p->~T();
553     js_free(const_cast<T*>(p));
554   }
555 }
556 
557 template <class T>
js_delete_poison(const T * p)558 static MOZ_ALWAYS_INLINE void js_delete_poison(const T* p) {
559   if (p) {
560     p->~T();
561     memset(static_cast<void*>(const_cast<T*>(p)), 0x3B, sizeof(T));
562     js_free(const_cast<T*>(p));
563   }
564 }
565 
566 template <class T>
js_pod_arena_malloc(arena_id_t arena,size_t numElems)567 static MOZ_ALWAYS_INLINE T* js_pod_arena_malloc(arena_id_t arena,
568                                                 size_t numElems) {
569   size_t bytes;
570   if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
571     return nullptr;
572   }
573   return static_cast<T*>(js_arena_malloc(arena, bytes));
574 }
575 
576 template <class T>
js_pod_malloc(size_t numElems)577 static MOZ_ALWAYS_INLINE T* js_pod_malloc(size_t numElems) {
578   return js_pod_arena_malloc<T>(js::MallocArena, numElems);
579 }
580 
581 template <class T>
js_pod_arena_calloc(arena_id_t arena,size_t numElems)582 static MOZ_ALWAYS_INLINE T* js_pod_arena_calloc(arena_id_t arena,
583                                                 size_t numElems) {
584   size_t bytes;
585   if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(numElems, &bytes))) {
586     return nullptr;
587   }
588   return static_cast<T*>(js_arena_calloc(arena, bytes, 1));
589 }
590 
591 template <class T>
js_pod_calloc(size_t numElems)592 static MOZ_ALWAYS_INLINE T* js_pod_calloc(size_t numElems) {
593   return js_pod_arena_calloc<T>(js::MallocArena, numElems);
594 }
595 
596 template <class T>
js_pod_arena_realloc(arena_id_t arena,T * prior,size_t oldSize,size_t newSize)597 static MOZ_ALWAYS_INLINE T* js_pod_arena_realloc(arena_id_t arena, T* prior,
598                                                  size_t oldSize,
599                                                  size_t newSize) {
600   MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value));
601   size_t bytes;
602   if (MOZ_UNLIKELY(!js::CalculateAllocSize<T>(newSize, &bytes))) {
603     return nullptr;
604   }
605   return static_cast<T*>(js_arena_realloc(arena, prior, bytes));
606 }
607 
608 template <class T>
js_pod_realloc(T * prior,size_t oldSize,size_t newSize)609 static MOZ_ALWAYS_INLINE T* js_pod_realloc(T* prior, size_t oldSize,
610                                            size_t newSize) {
611   return js_pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
612 }
613 
614 namespace JS {
615 
616 template <typename T>
617 struct DeletePolicy {
618   constexpr DeletePolicy() = default;
619 
620   template <typename U>
621   MOZ_IMPLICIT DeletePolicy(
622       DeletePolicy<U> other,
623       std::enable_if_t<std::is_convertible_v<U*, T*>, int> dummy = 0) {}
624 
operatorDeletePolicy625   void operator()(const T* ptr) { js_delete(const_cast<T*>(ptr)); }
626 };
627 
628 struct FreePolicy {
operatorFreePolicy629   void operator()(const void* ptr) { js_free(const_cast<void*>(ptr)); }
630 };
631 
632 typedef mozilla::UniquePtr<char[], JS::FreePolicy> UniqueChars;
633 typedef mozilla::UniquePtr<char16_t[], JS::FreePolicy> UniqueTwoByteChars;
634 typedef mozilla::UniquePtr<JS::Latin1Char[], JS::FreePolicy> UniqueLatin1Chars;
635 
636 }  // namespace JS
637 
638 /* sixgill annotation defines */
639 #ifndef HAVE_STATIC_ANNOTATIONS
640 #  define HAVE_STATIC_ANNOTATIONS
641 #  ifdef XGILL_PLUGIN
642 #    define STATIC_PRECONDITION(COND) __attribute__((precondition(#    COND)))
643 #    define STATIC_PRECONDITION_ASSUME(COND) \
644       __attribute__((precondition_assume(#COND)))
645 #    define STATIC_POSTCONDITION(COND) __attribute__((postcondition(#    COND)))
646 #    define STATIC_POSTCONDITION_ASSUME(COND) \
647       __attribute__((postcondition_assume(#COND)))
648 #    define STATIC_INVARIANT(COND) __attribute__((invariant(#    COND)))
649 #    define STATIC_INVARIANT_ASSUME(COND) \
650       __attribute__((invariant_assume(#COND)))
651 #    define STATIC_ASSUME(COND)                                          \
652       JS_BEGIN_MACRO                                                     \
653         __attribute__((assume_static(#COND), unused)) int STATIC_PASTE1( \
654             assume_static_, __COUNTER__);                                \
655       JS_END_MACRO
656 #  else                                       /* XGILL_PLUGIN */
657 #    define STATIC_PRECONDITION(COND)         /* nothing */
658 #    define STATIC_PRECONDITION_ASSUME(COND)  /* nothing */
659 #    define STATIC_POSTCONDITION(COND)        /* nothing */
660 #    define STATIC_POSTCONDITION_ASSUME(COND) /* nothing */
661 #    define STATIC_INVARIANT(COND)            /* nothing */
662 #    define STATIC_INVARIANT_ASSUME(COND)     /* nothing */
663 #    define STATIC_ASSUME(COND)    \
664       JS_BEGIN_MACRO /* nothing */ \
665       JS_END_MACRO
666 #  endif /* XGILL_PLUGIN */
667 #  define STATIC_SKIP_INFERENCE STATIC_INVARIANT(skip_inference())
668 #endif /* HAVE_STATIC_ANNOTATIONS */
669 
670 #endif /* js_Utility_h */
671