tor-browser

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

Allocator-inl.h (7732B)


      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 * Inline definitions of the CellAllocator methods.
      9 *
     10 * This is included from JSContext-inl.h for the definiton of JSContext::newCell
     11 * and shouldn't need to be included elsewhere.
     12 */
     13 
     14 #ifndef gc_Allocator_inl_h
     15 #define gc_Allocator_inl_h
     16 
     17 #include "gc/Allocator.h"
     18 
     19 #include "gc/Cell.h"
     20 #include "gc/Zone.h"
     21 #include "js/Class.h"
     22 #include "js/RootingAPI.h"
     23 
     24 #include "gc/Nursery-inl.h"
     25 
     26 namespace js {
     27 namespace gc {
     28 
     29 template <typename T, AllowGC allowGC, typename... Args>
     30 T* CellAllocator::NewCell(JSContext* cx, Args&&... args) {
     31  static_assert(std::is_base_of_v<gc::Cell, T>);
     32 
     33  // Objects. See the valid parameter list in NewObject, above.
     34  if constexpr (std::is_base_of_v<JSObject, T>) {
     35    return NewObject<T, allowGC>(cx, std::forward<Args>(args)...);
     36  }
     37 
     38  // BigInt
     39  else if constexpr (std::is_base_of_v<JS::BigInt, T>) {
     40    return NewBigInt<T, allowGC>(cx, std::forward<Args>(args)...);
     41  }
     42 
     43  // GetterSetter
     44  else if constexpr (std::is_base_of_v<js::GetterSetter, T>) {
     45    return NewGetterSetter<T, allowGC>(cx, std::forward<Args>(args)...);
     46  }
     47 
     48  // "Normal" strings (all of which can be nursery allocated). Atoms and
     49  // external strings will fall through to the generic code below. All other
     50  // strings go through NewString, which will forward the arguments to the
     51  // appropriate string class's constructor.
     52  else if constexpr (std::is_base_of_v<JSString, T> &&
     53                     !std::is_base_of_v<JSAtom, T> &&
     54                     !std::is_base_of_v<JSExternalString, T>) {
     55    return NewString<T, allowGC>(cx, std::forward<Args>(args)...);
     56  }
     57 
     58  else {
     59    // Allocate a new tenured GC thing that's not nursery-allocatable. Use
     60    // cx->newCell<T>(...), where the parameters are forwarded to the type's
     61    // constructor.
     62    return NewTenuredCell<T, allowGC>(cx, std::forward<Args>(args)...);
     63  }
     64 }
     65 
     66 template <typename T, AllowGC allowGC, typename... Args>
     67 /* static */
     68 T* CellAllocator::NewString(JSContext* cx, gc::Heap heap, Args&&... args) {
     69  static_assert(std::is_base_of_v<JSString, T>);
     70  gc::AllocKind kind = gc::MapTypeToAllocKind<T>::kind;
     71  void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::String, allowGC>(
     72      cx, kind, sizeof(T), heap, nullptr);
     73  if (MOZ_UNLIKELY(!ptr)) {
     74    return nullptr;
     75  }
     76  return new (mozilla::KnownNotNull, ptr) T(std::forward<Args>(args)...);
     77 }
     78 
     79 template <typename T, AllowGC allowGC>
     80 /* static */
     81 T* CellAllocator::NewBigInt(JSContext* cx, Heap heap) {
     82  void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::BigInt, allowGC>(
     83      cx, gc::AllocKind::BIGINT, sizeof(T), heap, nullptr);
     84  if (MOZ_UNLIKELY(!ptr)) {
     85    return nullptr;
     86  }
     87  return new (mozilla::KnownNotNull, ptr) T();
     88 }
     89 
     90 template <typename T, AllowGC allowGC, typename... Args>
     91 /* static */
     92 T* CellAllocator::NewGetterSetter(JSContext* cx, gc::Heap heap,
     93                                  Args&&... args) {
     94  static_assert(std::is_base_of_v<js::GetterSetter, T>);
     95  void* ptr = AllocNurseryOrTenuredCell<JS::TraceKind::GetterSetter, allowGC>(
     96      cx, gc::AllocKind::GETTER_SETTER, sizeof(T), heap, nullptr);
     97  if (MOZ_UNLIKELY(!ptr)) {
     98    return nullptr;
     99  }
    100  return new (mozilla::KnownNotNull, ptr) T(std::forward<Args>(args)...);
    101 }
    102 
    103 template <typename T, AllowGC allowGC>
    104 /* static */
    105 T* CellAllocator::NewObject(JSContext* cx, gc::AllocKind kind, gc::Heap heap,
    106                            const JSClass* clasp, gc::AllocSite* site) {
    107  MOZ_ASSERT(IsObjectAllocKind(kind));
    108  MOZ_ASSERT_IF(heap != gc::Heap::Tenured && clasp->hasFinalize() &&
    109                    !clasp->isProxyObject(),
    110                CanNurseryAllocateFinalizedClass(clasp));
    111  size_t thingSize = JSObject::thingSize(kind);
    112  void* cell = AllocNurseryOrTenuredCell<JS::TraceKind::Object, allowGC>(
    113      cx, kind, thingSize, heap, site);
    114  if (MOZ_UNLIKELY(!cell)) {
    115    return nullptr;
    116  }
    117  return new (mozilla::KnownNotNull, cell) T();
    118 }
    119 
    120 template <typename T, AllowGC allowGC, typename... Args>
    121 /* static */
    122 T* CellAllocator::NewTenuredCell(JSContext* cx, Args&&... args) {
    123  gc::AllocKind kind = gc::MapTypeToAllocKind<T>::kind;
    124  MOZ_ASSERT(Arena::thingSize(kind) == sizeof(T));
    125  void* cell = AllocTenuredCell<allowGC>(cx, kind);
    126  if (MOZ_UNLIKELY(!cell)) {
    127    return nullptr;
    128  }
    129  return new (mozilla::KnownNotNull, cell) T(std::forward<Args>(args)...);
    130 }
    131 
    132 #if defined(DEBUG) || defined(JS_GC_ZEAL) || defined(JS_OOM_BREAKPOINT)
    133 
    134 // This serves as a single point to perform some unrelated checks that happens
    135 // before every allocation. Performs the following:
    136 //
    137 //  - checks we can't GC inside a JS::AutoAssertNoGC region
    138 //  - runs a zeal GC if needed
    139 //
    140 // This is a no-op in release builds.
    141 //
    142 // This is only called on paths where GC is allowed.
    143 inline void PreAllocGCChecks(JSContext* cx) {
    144  // Crash if we could perform a GC action when it is not safe.
    145  if (!cx->suppressGC) {
    146    cx->verifyIsSafeToGC();
    147  }
    148 
    149 #  ifdef JS_GC_ZEAL
    150  GCRuntime* gc = &cx->runtime()->gc;
    151  if (gc->needZealousGC()) {
    152    gc->runDebugGC();
    153  }
    154 #  endif
    155 }
    156 
    157 inline bool CheckForSimulatedFailure(JSContext* cx, AllowGC allowGC) {
    158  // For testing out of memory conditions.
    159  if (js::oom::ShouldFailWithOOM()) {
    160    // If we are doing a fallible allocation, percolate up the OOM instead of
    161    // reporting it.
    162    if (allowGC) {
    163      ReportOutOfMemory(cx);
    164    }
    165    return false;
    166  }
    167 
    168  return true;
    169 }
    170 #else
    171 
    172 inline void PreAllocGCChecks(JSContext* cx) {}
    173 inline bool CheckForSimulatedFailure(JSContext* cx, AllowGC allowGC) {
    174  return true;
    175 }
    176 
    177 #endif  // DEBUG || JS_GC_ZEAL || JS_OOM_BREAKPOINT
    178 
    179 template <JS::TraceKind traceKind, AllowGC allowGC>
    180 /* static */
    181 void* CellAllocator::AllocNurseryOrTenuredCell(JSContext* cx,
    182                                               gc::AllocKind allocKind,
    183                                               size_t thingSize, gc::Heap heap,
    184                                               AllocSite* site) {
    185  MOZ_ASSERT(IsNurseryAllocable(allocKind));
    186  MOZ_ASSERT(MapAllocToTraceKind(allocKind) == traceKind);
    187  MOZ_ASSERT(thingSize == Arena::thingSize(allocKind));
    188  MOZ_ASSERT_IF(site && site->initialHeap() == Heap::Tenured,
    189                heap == Heap::Tenured);
    190  MOZ_ASSERT(!cx->zone()->isAtomsZone());
    191  MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
    192 
    193  if constexpr (allowGC) {
    194    PreAllocGCChecks(cx);
    195  }
    196 
    197  if (!CheckForSimulatedFailure(cx, allowGC)) {
    198    return nullptr;
    199  }
    200 
    201  JS::Zone* zone = cx->zone();
    202  gc::Heap minHeapToTenure = CheckedHeap(zone->minHeapToTenure(traceKind));
    203  if (CheckedHeap(heap) < minHeapToTenure) {
    204    if (!site) {
    205      site = zone->unknownAllocSite(traceKind);
    206    }
    207 
    208 #ifdef JS_GC_ZEAL
    209    site = MaybeGenerateMissingAllocSite(cx, traceKind, site);
    210 #endif
    211 
    212    void* ptr = cx->nursery().tryAllocateCell(site, thingSize, traceKind);
    213    if (MOZ_LIKELY(ptr)) {
    214      return ptr;
    215    }
    216 
    217    return RetryNurseryAlloc<allowGC>(cx, traceKind, allocKind, thingSize,
    218                                      site);
    219  }
    220 
    221  return AllocTenuredCellForNurseryAlloc<allowGC>(cx, allocKind);
    222 }
    223 
    224 /* static */
    225 MOZ_ALWAYS_INLINE gc::Heap CellAllocator::CheckedHeap(gc::Heap heap) {
    226  if (heap > Heap::Tenured) {
    227    // This helps the compiler to see that nursery allocation is never
    228    // possible if Heap::Tenured is specified.
    229    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad gc::Heap value");
    230  }
    231 
    232  return heap;
    233 }
    234 
    235 }  // namespace gc
    236 }  // namespace js
    237 
    238 #endif  // gc_Allocator_inl_h