tor-browser

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

AllocPolicy.h (9071B)


      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 * JS allocation policies.
      9 *
     10 * The allocators here are for system memory with lifetimes which are not
     11 * managed by the GC. See the comment at the top of vm/MallocProvider.h.
     12 */
     13 
     14 #ifndef js_AllocPolicy_h
     15 #define js_AllocPolicy_h
     16 
     17 #include "js/TypeDecls.h"
     18 #include "js/Utility.h"
     19 
     20 extern MOZ_COLD JS_PUBLIC_API void JS_ReportOutOfMemory(JSContext* cx);
     21 
     22 namespace js {
     23 
     24 class FrontendContext;
     25 
     26 enum class AllocFunction { Malloc, Calloc, Realloc };
     27 
     28 class ArenaAllocPolicyBase {
     29 public:
     30  template <typename T>
     31  T* maybe_pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
     32    return js_pod_arena_malloc<T>(arenaId, numElems);
     33  }
     34  template <typename T>
     35  T* maybe_pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
     36    return js_pod_arena_calloc<T>(arenaId, numElems);
     37  }
     38  template <typename T>
     39  T* maybe_pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
     40                             size_t newSize) {
     41    return js_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
     42  }
     43  template <typename T>
     44  T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
     45    return maybe_pod_arena_malloc<T>(arenaId, numElems);
     46  }
     47  template <typename T>
     48  T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
     49    return maybe_pod_arena_calloc<T>(arenaId, numElems);
     50  }
     51  template <typename T>
     52  T* pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
     53                       size_t newSize) {
     54    return maybe_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
     55  }
     56 };
     57 
     58 /* Base class allocation policies providing allocation methods. */
     59 class AllocPolicyBase : public ArenaAllocPolicyBase {
     60 public:
     61  template <typename T>
     62  T* maybe_pod_malloc(size_t numElems) {
     63    return maybe_pod_arena_malloc<T>(js::MallocArena, numElems);
     64  }
     65  template <typename T>
     66  T* maybe_pod_calloc(size_t numElems) {
     67    return maybe_pod_arena_calloc<T>(js::MallocArena, numElems);
     68  }
     69  template <typename T>
     70  T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
     71    return maybe_pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize);
     72  }
     73  template <typename T>
     74  T* pod_malloc(size_t numElems) {
     75    return pod_arena_malloc<T>(js::MallocArena, numElems);
     76  }
     77  template <typename T>
     78  T* pod_calloc(size_t numElems) {
     79    return pod_arena_calloc<T>(js::MallocArena, numElems);
     80  }
     81  template <typename T>
     82  T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
     83    return pod_arena_realloc<T>(js::MallocArena, p, oldSize, newSize);
     84  }
     85 
     86  template <typename T>
     87  void free_(T* p, size_t numElems = 0) {
     88    js_free(p);
     89  }
     90 };
     91 
     92 /*
     93 * Base class allocation policies providing allocation methods for allocations
     94 * off the main thread.
     95 */
     96 class BackgroundAllocPolicyBase : ArenaAllocPolicyBase {
     97 public:
     98  template <typename T>
     99  T* maybe_pod_malloc(size_t numElems) {
    100    return maybe_pod_arena_malloc<T>(js::BackgroundMallocArena, numElems);
    101  }
    102  template <typename T>
    103  T* maybe_pod_calloc(size_t numElems) {
    104    return maybe_pod_arena_calloc<T>(js::BackgroundMallocArena, numElems);
    105  }
    106  template <typename T>
    107  T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) {
    108    return maybe_pod_arena_realloc<T>(js::BackgroundMallocArena, p, oldSize,
    109                                      newSize);
    110  }
    111  template <typename T>
    112  T* pod_malloc(size_t numElems) {
    113    return pod_arena_malloc<T>(js::BackgroundMallocArena, numElems);
    114  }
    115  template <typename T>
    116  T* pod_calloc(size_t numElems) {
    117    return pod_arena_calloc<T>(js::BackgroundMallocArena, numElems);
    118  }
    119  template <typename T>
    120  T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
    121    return pod_arena_realloc<T>(js::BackgroundMallocArena, p, oldSize, newSize);
    122  }
    123 
    124  template <typename T>
    125  void free_(T* p, size_t numElems = 0) {
    126    js_free(p);
    127  }
    128 };
    129 
    130 /* Policy for using system memory functions and doing no error reporting. */
    131 class SystemAllocPolicy : public AllocPolicyBase {
    132 public:
    133  void reportAllocOverflow() const {}
    134  bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); }
    135 };
    136 
    137 class BackgroundSystemAllocPolicy : public BackgroundAllocPolicyBase {
    138 public:
    139  void reportAllocOverflow() const {}
    140  bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); }
    141 };
    142 
    143 MOZ_COLD JS_PUBLIC_API void ReportOutOfMemory(JSContext* cx);
    144 MOZ_COLD JS_PUBLIC_API void ReportOutOfMemory(FrontendContext* fc);
    145 
    146 // An out of memory condition which is easily user generatable and should
    147 // be specially handled to try and avoid a tab crash.
    148 MOZ_COLD JS_PUBLIC_API void ReportLargeOutOfMemory(JSContext* cx);
    149 
    150 /*
    151 * Allocation policy that calls the system memory functions and reports errors
    152 * to the context. Since the JSContext given on construction is stored for
    153 * the lifetime of the container, this policy may only be used for containers
    154 * whose lifetime is a shorter than the given JSContext.
    155 *
    156 * FIXME bug 647103 - rewrite this in terms of temporary allocation functions,
    157 * not the system ones.
    158 */
    159 class JS_PUBLIC_API TempAllocPolicy : public AllocPolicyBase {
    160  // Type tag for context_bits_
    161  static constexpr uintptr_t JsContextTag = 0x1;
    162 
    163  // Either a JSContext* (if JsContextTag is set), or FrontendContext*
    164  uintptr_t const context_bits_;
    165 
    166  MOZ_ALWAYS_INLINE bool hasJSContext() const {
    167    return (context_bits_ & JsContextTag) == JsContextTag;
    168  }
    169 
    170  MOZ_ALWAYS_INLINE JSContext* cx() const {
    171    MOZ_ASSERT(hasJSContext());
    172    return reinterpret_cast<JSContext*>(context_bits_ ^ JsContextTag);
    173  }
    174 
    175  MOZ_ALWAYS_INLINE FrontendContext* fc() const {
    176    MOZ_ASSERT(!hasJSContext());
    177    return reinterpret_cast<FrontendContext*>(context_bits_);
    178  }
    179 
    180  /*
    181   * Non-inline helper to call JSRuntime::onOutOfMemory with minimal
    182   * code bloat.
    183   */
    184  void* onOutOfMemory(arena_id_t arenaId, AllocFunction allocFunc,
    185                      size_t nbytes, void* reallocPtr = nullptr);
    186 
    187  template <typename T>
    188  T* onOutOfMemoryTyped(arena_id_t arenaId, AllocFunction allocFunc,
    189                        size_t numElems, void* reallocPtr = nullptr) {
    190    size_t bytes;
    191    if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) {
    192      return nullptr;
    193    }
    194    return static_cast<T*>(
    195        onOutOfMemory(arenaId, allocFunc, bytes, reallocPtr));
    196  }
    197 
    198 public:
    199  MOZ_IMPLICIT TempAllocPolicy(JSContext* cx)
    200      : context_bits_(uintptr_t(cx) | JsContextTag) {
    201    MOZ_ASSERT((uintptr_t(cx) & JsContextTag) == 0);
    202  }
    203  MOZ_IMPLICIT TempAllocPolicy(FrontendContext* fc)
    204      : context_bits_(uintptr_t(fc)) {
    205    MOZ_ASSERT((uintptr_t(fc) & JsContextTag) == 0);
    206  }
    207 
    208  template <typename T>
    209  T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
    210    T* p = this->maybe_pod_arena_malloc<T>(arenaId, numElems);
    211    if (MOZ_UNLIKELY(!p)) {
    212      p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Malloc, numElems);
    213    }
    214    return p;
    215  }
    216 
    217  template <typename T>
    218  T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
    219    T* p = this->maybe_pod_arena_calloc<T>(arenaId, numElems);
    220    if (MOZ_UNLIKELY(!p)) {
    221      p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Calloc, numElems);
    222    }
    223    return p;
    224  }
    225 
    226  template <typename T>
    227  T* pod_arena_realloc(arena_id_t arenaId, T* prior, size_t oldSize,
    228                       size_t newSize) {
    229    T* p2 = this->maybe_pod_arena_realloc<T>(arenaId, prior, oldSize, newSize);
    230    if (MOZ_UNLIKELY(!p2)) {
    231      p2 = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Realloc, newSize,
    232                                 prior);
    233    }
    234    return p2;
    235  }
    236 
    237  template <typename T>
    238  T* pod_malloc(size_t numElems) {
    239    return pod_arena_malloc<T>(js::MallocArena, numElems);
    240  }
    241 
    242  template <typename T>
    243  T* pod_calloc(size_t numElems) {
    244    return pod_arena_calloc<T>(js::MallocArena, numElems);
    245  }
    246 
    247  template <typename T>
    248  T* pod_realloc(T* prior, size_t oldSize, size_t newSize) {
    249    return pod_arena_realloc<T>(js::MallocArena, prior, oldSize, newSize);
    250  }
    251 
    252  template <typename T>
    253  void free_(T* p, size_t numElems = 0) {
    254    js_free(p);
    255  }
    256 
    257  void reportAllocOverflow() const;
    258 
    259  bool checkSimulatedOOM() const {
    260    if (js::oom::ShouldFailWithOOM()) {
    261      if (hasJSContext()) {
    262        ReportOutOfMemory(cx());
    263      } else {
    264        ReportOutOfMemory(fc());
    265      }
    266      return false;
    267    }
    268 
    269    return true;
    270  }
    271 };
    272 
    273 /*
    274 * A replacement for MallocAllocPolicy that allocates in the JS heap and adds no
    275 * extra behaviours.
    276 *
    277 * This is currently used for allocating source buffers for parsing. Since these
    278 * are temporary and will not be freed by GC, the memory is not tracked by the
    279 * usual accounting.
    280 */
    281 class MallocAllocPolicy : public AllocPolicyBase {
    282 public:
    283  void reportAllocOverflow() const {}
    284 
    285  [[nodiscard]] bool checkSimulatedOOM() const { return true; }
    286 };
    287 
    288 } /* namespace js */
    289 
    290 #endif /* js_AllocPolicy_h */