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