tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Utility.h (24464B)


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