tor-browser

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

GCPolicyAPI.h (11635B)


      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 // GC Policy Mechanism
      8 
      9 // GCPolicy controls how the GC interacts with a given type for functionality
     10 // that is used by generic code. It is implemented for:
     11 //
     12 //  - direct pointers to GC things, e.g. JSObject* or JSString*
     13 //
     14 //  - tagged and/or optional pointers to GC things, e.g. JS::Value or jsid
     15 //
     16 //  - structures containing GC pointers, e.g. JS::PropertyDescriptor
     17 //
     18 //  - C++ container types, e.g. GCHashMap
     19 //
     20 // The GCPolicy for type |T| provides at a minimum:
     21 //
     22 //   static void trace(JSTracer, T* tp, const char* name)
     23 //
     24 //     Trace the edge |*tp|, calling the edge |name|. Generic containers
     25 //     like GCHashMap and GCHashSet use this method to trace their children.
     26 //
     27 //   static bool traceWeak(T* tp)
     28 //
     29 //     Update any GC edges if their target has been moved. Remove or clear any
     30 //     edges to GC things that are going to be collected by an incremental
     31 //     GC. Return false if this edge itself should be removed.
     32 //
     33 //     For GC thing pointers, this will clear the edge and return false if the
     34 //     target is going to be collected. In general, for structures this should
     35 //     call |traceWeak| on internal GC edges and return whether the result was
     36 //     true for all of them.
     37 //
     38 //     Containers can use this to remove entries containing GC things that are
     39 //     going to be collected (e.g. GCVector).
     40 //
     41 //   static bool isValid(const T& t)
     42 //
     43 //     Check that |t| is valid and is not corrupt in some way. The built-in GC
     44 //     types do some memory layout checks. This is for assertions only; it is ok
     45 //     to always return true.
     46 //
     47 // The GCPolicy may also provide:
     48 //
     49 //   static bool needsSweep(const T* tp)
     50 //
     51 //     Return whether this edge should be removed, like a version of |traceWeak|
     52 //     with the sense of the return value reversed.
     53 //
     54 //     The argument is const and this does not update any moved GC pointers, so
     55 //     should not be called when this is a possibility.
     56 //
     57 //     This is used internally for incremental barriers on WeakCache hash
     58 //     tables.
     59 //
     60 // The default GCPolicy<T> assumes that T has a default constructor and |trace|
     61 // and |traceWeak| methods, and forwards to them. GCPolicy has appropriate
     62 // specializations for pointers to GC things and pointer-like types like
     63 // JS::Heap<T> and mozilla::UniquePtr<T>.
     64 //
     65 // There are some stock structs your specializations can inherit from.
     66 // IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
     67 // referent type T.
     68 
     69 #ifndef GCPolicyAPI_h
     70 #define GCPolicyAPI_h
     71 
     72 #include "mozilla/Maybe.h"
     73 #include "mozilla/UniquePtr.h"
     74 
     75 #include <type_traits>
     76 
     77 #include "js/GCTypeMacros.h"  // JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE
     78 #include "js/TraceKind.h"
     79 #include "js/TracingAPI.h"
     80 #include "js/TypeDecls.h"
     81 
     82 namespace JS {
     83 
     84 // Base class for GCPolicy<T> that provides some defaults.
     85 template <typename T>
     86 struct GCPolicyBase {
     87  static bool isValid(const T& tp) { return true; /* Unchecked. */ }
     88  static constexpr bool mightBeInNursery() { return true; /* Unknown. */ }
     89 };
     90 
     91 // Defines a policy for container types with non-GC, i.e. C storage. This
     92 // policy dispatches to the underlying struct for GC interactions. Note that
     93 // currently a type can define only the subset of the methods (trace and/or
     94 // traceWeak) if it is never used in a context that requires the other.
     95 template <typename T>
     96 struct StructGCPolicy : public GCPolicyBase<T> {
     97  static_assert(
     98      !std::is_pointer_v<T>,
     99      "Pointer types must have a GCPolicy<> specialization.\n"
    100      "  - Public GC pointer types (eg JSObject*, JSString*) use "
    101      "GCPointerPolicy.\n"
    102      "  - Internal GC pointer types are handled by InternalGCPointerPolicy "
    103      "from gc/Policy.h; make sure you are including that file.\n"
    104      "  - Other pointer types (eg Realm*) must define their own "
    105      "specialization.\n"
    106      "The most likely cause of this error is attempting to root a non-GC "
    107      "pointer that should not be rooted. Only pointers on the stack that "
    108      "point to GC pointers should be or need to be rooted.");
    109 
    110  static void trace(JSTracer* trc, T* tp, const char* name) { tp->trace(trc); }
    111 
    112  static bool traceWeak(JSTracer* trc, T* tp) { return tp->traceWeak(trc); }
    113  static bool needsSweep(JSTracer* trc, const T* tp) {
    114    return tp->needsSweep(trc);
    115  }
    116 };
    117 
    118 // The default GC policy attempts to defer to methods on the underlying type.
    119 // Most C++ structures that contain a default constructor, a trace function and
    120 // a sweep function will work out of the box with Rooted, Handle, GCVector,
    121 // and GCHash{Set,Map}.
    122 template <typename T>
    123 struct GCPolicy : public StructGCPolicy<T> {};
    124 
    125 // This policy ignores any GC interaction, e.g. for non-GC types.
    126 template <typename T>
    127 struct IgnoreGCPolicy : public GCPolicyBase<T> {
    128  static void trace(JSTracer* trc, T* t, const char* name) {}
    129  static bool traceWeak(JSTracer*, T* v) { return true; }
    130  static bool needsSweep(JSTracer* trc, const T* v) { return false; }
    131 };
    132 template <>
    133 struct GCPolicy<uint8_t> : public IgnoreGCPolicy<uint8_t> {};
    134 template <>
    135 struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
    136 template <>
    137 struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
    138 template <>
    139 struct GCPolicy<bool> : public IgnoreGCPolicy<bool> {};
    140 
    141 template <typename T>
    142 struct GCPointerPolicy : public GCPolicyBase<T> {
    143  static_assert(std::is_pointer_v<T>,
    144                "Non-pointer type not allowed for GCPointerPolicy");
    145 
    146  static void trace(JSTracer* trc, T* vp, const char* name) {
    147    // This should only be called as part of root marking since that's the only
    148    // time we should trace unbarriered GC thing pointers. This will assert if
    149    // called at other times.
    150    TraceRoot(trc, vp, name);
    151  }
    152  static bool isTenured(T v) { return !v || !js::gc::IsInsideNursery(v); }
    153  static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); }
    154 };
    155 #define EXPAND_SPECIALIZE_GCPOLICY(Type)                   \
    156  template <>                                              \
    157  struct GCPolicy<Type> : public GCPointerPolicy<Type> {}; \
    158  template <>                                              \
    159  struct GCPolicy<Type const> : public GCPointerPolicy<Type const> {};
    160 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(EXPAND_SPECIALIZE_GCPOLICY)
    161 #undef EXPAND_SPECIALIZE_GCPOLICY
    162 
    163 template <typename T>
    164 struct NonGCPointerPolicy : public GCPolicyBase<T> {
    165  static void trace(JSTracer* trc, T* vp, const char* name) {
    166    if (*vp) {
    167      (*vp)->trace(trc);
    168    }
    169  }
    170  static bool traceWeak(JSTracer* trc, T* vp) {
    171    return !*vp || (*vp)->traceWeak(trc);
    172  }
    173 };
    174 
    175 template <typename T>
    176 struct GCPolicy<JS::Heap<T>> : public GCPolicyBase<JS::Heap<T>> {
    177  static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
    178    TraceEdge(trc, thingp, name);
    179  }
    180  static bool traceWeak(JSTracer* trc, JS::Heap<T>* thingp) {
    181    return !*thingp || js::gc::TraceWeakEdge(trc, thingp);
    182  }
    183  static bool needsSweep(JSTracer* trc, const JS::Heap<T>* thingp) {
    184    T* thing = const_cast<T*>(thingp->unsafeAddress());
    185    return thing && js::gc::EdgeNeedsSweepUnbarrieredSlow(thing);
    186  }
    187 };
    188 
    189 // GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
    190 template <typename T, typename D>
    191 struct GCPolicy<mozilla::UniquePtr<T, D>>
    192    : public GCPolicyBase<mozilla::UniquePtr<T, D>> {
    193  static void trace(JSTracer* trc, mozilla::UniquePtr<T, D>* tp,
    194                    const char* name) {
    195    if (tp->get()) {
    196      GCPolicy<T>::trace(trc, tp->get(), name);
    197    }
    198  }
    199  static bool traceWeak(JSTracer* trc, mozilla::UniquePtr<T, D>* tp) {
    200    return !tp->get() || GCPolicy<T>::traceWeak(trc, tp->get());
    201  }
    202  static bool needsSweep(JSTracer* trc, const mozilla::UniquePtr<T, D>* tp) {
    203    return tp->get() && GCPolicy<T>::needsSweep(trc, tp->get());
    204  }
    205  static bool isValid(const mozilla::UniquePtr<T, D>& t) {
    206    return !t.get() || GCPolicy<T>::isValid(*t.get());
    207  }
    208 };
    209 
    210 template <>
    211 struct GCPolicy<mozilla::Nothing> : public IgnoreGCPolicy<mozilla::Nothing> {};
    212 
    213 // GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
    214 // the Maybe<T> is filled and T* can be traced via GCPolicy<T*>.
    215 template <typename T>
    216 struct GCPolicy<mozilla::Maybe<T>> : public GCPolicyBase<mozilla::Maybe<T>> {
    217  static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
    218    if (tp->isSome()) {
    219      GCPolicy<T>::trace(trc, tp->ptr(), name);
    220    }
    221  }
    222  static bool traceWeak(JSTracer* trc, mozilla::Maybe<T>* tp) {
    223    return tp->isNothing() || GCPolicy<T>::traceWeak(trc, tp->ptr());
    224  }
    225  static bool needsSweep(JSTracer* trc, const mozilla::Maybe<T>* tp) {
    226    return tp->isSome() && GCPolicy<T>::needsSweep(trc, tp->ptr());
    227  }
    228  static bool isValid(const mozilla::Maybe<T>& t) {
    229    return t.isNothing() || GCPolicy<T>::isValid(t.ref());
    230  }
    231 };
    232 
    233 template <typename T1, typename T2>
    234 struct GCPolicy<std::pair<T1, T2>> : public GCPolicyBase<std::pair<T1, T2>> {
    235  static void trace(JSTracer* trc, std::pair<T1, T2>* tp, const char* name) {
    236    GCPolicy<T1>::trace(trc, &tp->first, name);
    237    GCPolicy<T2>::trace(trc, &tp->second, name);
    238  }
    239  static bool traceWeak(JSTracer* trc, std::pair<T1, T2>* tp) {
    240    return GCPolicy<T1>::traceWeak(trc, &tp->first) &&
    241           GCPolicy<T2>::traceWeak(trc, &tp->second);
    242  }
    243  static bool needsSweep(JSTracer* trc, const std::pair<T1, T2>* tp) {
    244    return GCPolicy<T1>::needsSweep(trc, &tp->first) ||
    245           GCPolicy<T2>::needsSweep(trc, &tp->second);
    246  }
    247  static bool isValid(const std::pair<T1, T2>& t) {
    248    return GCPolicy<T1>::isValid(t.first) && GCPolicy<T2>::isValid(t.second);
    249  }
    250 };
    251 
    252 template <>
    253 struct GCPolicy<JS::Realm*>;  // see Realm.h
    254 
    255 template <>
    256 struct GCPolicy<mozilla::Ok> : public IgnoreGCPolicy<mozilla::Ok> {};
    257 
    258 template <typename V, typename E>
    259 struct GCPolicy<mozilla::Result<V, E>>
    260    : public GCPolicyBase<mozilla::Result<V, E>> {
    261  static void trace(JSTracer* trc, mozilla::Result<V, E>* tp,
    262                    const char* name) {
    263    if (tp->isOk()) {
    264      V tmp = tp->unwrap();
    265      JS::GCPolicy<V>::trace(trc, &tmp, "Result value");
    266      tp->updateAfterTracing(std::move(tmp));
    267    }
    268 
    269    if (tp->isErr()) {
    270      E tmp = tp->unwrapErr();
    271      JS::GCPolicy<E>::trace(trc, &tmp, "Result error");
    272      tp->updateErrorAfterTracing(std::move(tmp));
    273    }
    274  }
    275 
    276  static bool isValid(const mozilla::Result<V, E>& t) { return true; }
    277 };
    278 
    279 template <typename... Fs>
    280 struct GCPolicy<std::tuple<Fs...>> : public GCPolicyBase<std::tuple<Fs...>> {
    281  using T = std::tuple<Fs...>;
    282  static void trace(JSTracer* trc, T* tp, const char* name) {
    283    traceFieldsFrom<0>(trc, *tp, name);
    284  }
    285  static bool isValid(const T& t) { return areFieldsValidFrom<0>(t); }
    286 
    287 private:
    288  template <size_t N>
    289  static void traceFieldsFrom(JSTracer* trc, T& tuple, const char* name) {
    290    if constexpr (N != std::tuple_size_v<T>) {
    291      using F = std::tuple_element_t<N, T>;
    292      GCPolicy<F>::trace(trc, &std::get<N>(tuple), name);
    293      traceFieldsFrom<N + 1>(trc, tuple, name);
    294    }
    295  }
    296 
    297  template <size_t N>
    298  static bool areFieldsValidFrom(const T& tuple) {
    299    if constexpr (N != std::tuple_size_v<T>) {
    300      using F = std::tuple_element_t<N, T>;
    301      return GCPolicy<F>::isValid(std::get<N>(tuple)) &&
    302             areFieldsValidFrom<N + 1>(tuple);
    303    }
    304 
    305    return true;
    306  }
    307 };
    308 
    309 }  // namespace JS
    310 
    311 #endif  // GCPolicyAPI_h