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/. */
9 // GCPolicy controls how the GC interacts with a given type for functionality
10 // that is used by generic code. It is implemented for:
12 // - direct pointers to GC things, e.g. JSObject* or JSString*
14 // - tagged and/or optional pointers to GC things, e.g. JS::Value or jsid
16 // - structures containing GC pointers, e.g. JS::PropertyDescriptor
18 // - C++ container types, e.g. GCHashMap
20 // The GCPolicy for type |T| provides at a minimum:
22 // static void trace(JSTracer, T* tp, const char* name)
24 // Trace the edge |*tp|, calling the edge |name|. Generic containers
25 // like GCHashMap and GCHashSet use this method to trace their children.
27 // static bool traceWeak(T* tp)
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.
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.
38 // Containers can use this to remove entries containing GC things that are
39 // going to be collected (e.g. GCVector).
41 // static bool isValid(const T& t)
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.
47 // The GCPolicy may also provide:
49 // static bool needsSweep(const T* tp)
51 // Return whether this edge should be removed, like a version of |traceWeak|
52 // with the sense of the return value reversed.
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.
57 // This is used internally for incremental barriers on WeakCache hash
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>.
65 // There are some stock structs your specializations can inherit from.
66 // IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
72 #include "mozilla/Maybe.h"
73 #include "mozilla/UniquePtr.h"
75 #include <type_traits>
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"
84 // Defines a policy for container types with non-GC, i.e. C storage. This
85 // policy dispatches to the underlying struct for GC interactions. Note that
86 // currently a type can define only the subset of the methods (trace and/or
87 // traceWeak) if it is never used in a context that requires the other.
89 struct StructGCPolicy
{
90 static_assert(!std::is_pointer_v
<T
>,
91 "Pointer type not allowed for StructGCPolicy");
93 static void trace(JSTracer
* trc
, T
* tp
, const char* name
) { tp
->trace(trc
); }
95 static bool traceWeak(JSTracer
* trc
, T
* tp
) { return tp
->traceWeak(trc
); }
96 static bool needsSweep(JSTracer
* trc
, const T
* tp
) {
97 return tp
->needsSweep(trc
);
100 static bool isValid(const T
& tp
) { return true; }
103 // The default GC policy attempts to defer to methods on the underlying type.
104 // Most C++ structures that contain a default constructor, a trace function and
105 // a sweep function will work out of the box with Rooted, Handle, GCVector,
106 // and GCHash{Set,Map}.
107 template <typename T
>
108 struct GCPolicy
: public StructGCPolicy
<T
> {};
110 // This policy ignores any GC interaction, e.g. for non-GC types.
111 template <typename T
>
112 struct IgnoreGCPolicy
{
113 static void trace(JSTracer
* trc
, T
* t
, const char* name
) {}
114 static bool traceWeak(JSTracer
*, T
* v
) { return true; }
115 static bool needsSweep(JSTracer
* trc
, const T
* v
) { return false; }
116 static bool isValid(const T
& v
) { return true; }
119 struct GCPolicy
<uint32_t> : public IgnoreGCPolicy
<uint32_t> {};
121 struct GCPolicy
<uint64_t> : public IgnoreGCPolicy
<uint64_t> {};
123 struct GCPolicy
<bool> : public IgnoreGCPolicy
<bool> {};
125 template <typename T
>
126 struct GCPointerPolicy
{
127 static_assert(std::is_pointer_v
<T
>,
128 "Non-pointer type not allowed for GCPointerPolicy");
130 static void trace(JSTracer
* trc
, T
* vp
, const char* name
) {
131 // This should only be called as part of root marking since that's the only
132 // time we should trace unbarriered GC thing pointers. This will assert if
133 // called at other times.
134 TraceRoot(trc
, vp
, name
);
136 static bool isTenured(T v
) { return !v
|| !js::gc::IsInsideNursery(v
); }
137 static bool isValid(T v
) { return js::gc::IsCellPointerValidOrNull(v
); }
139 #define EXPAND_SPECIALIZE_GCPOLICY(Type) \
141 struct GCPolicy<Type> : public GCPointerPolicy<Type> {}; \
143 struct GCPolicy<Type const> : public GCPointerPolicy<Type const> {};
144 JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(EXPAND_SPECIALIZE_GCPOLICY
)
145 #undef EXPAND_SPECIALIZE_GCPOLICY
147 template <typename T
>
148 struct NonGCPointerPolicy
{
149 static void trace(JSTracer
* trc
, T
* vp
, const char* name
) {
154 static bool traceWeak(JSTracer
* trc
, T
* vp
) {
155 return !*vp
|| (*vp
)->traceWeak(trc
);
157 static bool isValid(T v
) { return true; }
160 template <typename T
>
161 struct GCPolicy
<JS::Heap
<T
>> {
162 static void trace(JSTracer
* trc
, JS::Heap
<T
>* thingp
, const char* name
) {
163 TraceEdge(trc
, thingp
, name
);
165 static bool traceWeak(JSTracer
* trc
, JS::Heap
<T
>* thingp
) {
166 return !*thingp
|| js::gc::TraceWeakEdge(trc
, thingp
);
170 // GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
171 template <typename T
, typename D
>
172 struct GCPolicy
<mozilla::UniquePtr
<T
, D
>> {
173 static void trace(JSTracer
* trc
, mozilla::UniquePtr
<T
, D
>* tp
,
176 GCPolicy
<T
>::trace(trc
, tp
->get(), name
);
179 static bool traceWeak(JSTracer
* trc
, mozilla::UniquePtr
<T
, D
>* tp
) {
180 return !tp
->get() || GCPolicy
<T
>::traceWeak(trc
, tp
->get());
182 static bool needsSweep(JSTracer
* trc
, const mozilla::UniquePtr
<T
, D
>* tp
) {
183 return tp
->get() && GCPolicy
<T
>::needsSweep(trc
, tp
->get());
185 static bool isValid(const mozilla::UniquePtr
<T
, D
>& t
) {
186 return !t
.get() || GCPolicy
<T
>::isValid(*t
.get());
191 struct GCPolicy
<mozilla::Nothing
> : public IgnoreGCPolicy
<mozilla::Nothing
> {};
193 // GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
194 // the Maybe<T> is filled and T* can be traced via GCPolicy<T*>.
195 template <typename T
>
196 struct GCPolicy
<mozilla::Maybe
<T
>> {
197 static void trace(JSTracer
* trc
, mozilla::Maybe
<T
>* tp
, const char* name
) {
199 GCPolicy
<T
>::trace(trc
, tp
->ptr(), name
);
202 static bool traceWeak(JSTracer
* trc
, mozilla::Maybe
<T
>* tp
) {
203 return tp
->isNothing() || GCPolicy
<T
>::traceWeak(trc
, tp
->ptr());
205 static bool needsSweep(JSTracer
* trc
, const mozilla::Maybe
<T
>* tp
) {
206 return tp
->isSome() && GCPolicy
<T
>::needsSweep(trc
, tp
->ptr());
208 static bool isValid(const mozilla::Maybe
<T
>& t
) {
209 return t
.isNothing() || GCPolicy
<T
>::isValid(t
.ref());
213 template <typename T1
, typename T2
>
214 struct GCPolicy
<std::pair
<T1
, T2
>> {
215 static void trace(JSTracer
* trc
, std::pair
<T1
, T2
>* tp
, const char* name
) {
216 GCPolicy
<T1
>::trace(trc
, &tp
->first
, name
);
217 GCPolicy
<T2
>::trace(trc
, &tp
->second
, name
);
219 static bool traceWeak(JSTracer
* trc
, std::pair
<T1
, T2
>* tp
) {
220 return GCPolicy
<T1
>::traceWeak(trc
, &tp
->first
) &&
221 GCPolicy
<T2
>::traceWeak(trc
, &tp
->second
);
223 static bool needsSweep(JSTracer
* trc
, const std::pair
<T1
, T2
>* tp
) {
224 return GCPolicy
<T1
>::needsSweep(trc
, &tp
->first
) ||
225 GCPolicy
<T2
>::needsSweep(trc
, &tp
->second
);
227 static bool isValid(const std::pair
<T1
, T2
>& t
) {
228 return GCPolicy
<T1
>::isValid(t
.first
) && GCPolicy
<T2
>::isValid(t
.second
);
233 struct GCPolicy
<JS::Realm
*>; // see Realm.h
236 struct GCPolicy
<mozilla::Ok
> : public IgnoreGCPolicy
<mozilla::Ok
> {};
238 template <typename V
, typename E
>
239 struct GCPolicy
<mozilla::Result
<V
, E
>> {
240 static void trace(JSTracer
* trc
, mozilla::Result
<V
, E
>* tp
,
243 V tmp
= tp
->unwrap();
244 JS::GCPolicy
<V
>::trace(trc
, &tmp
, "Result value");
245 tp
->updateAfterTracing(std::move(tmp
));
249 E tmp
= tp
->unwrapErr();
250 JS::GCPolicy
<E
>::trace(trc
, &tmp
, "Result error");
251 tp
->updateErrorAfterTracing(std::move(tmp
));
255 static bool isValid(const mozilla::Result
<V
, E
>& t
) { return true; }
258 template <typename
... Fs
>
259 struct GCPolicy
<std::tuple
<Fs
...>> {
260 using T
= std::tuple
<Fs
...>;
261 static void trace(JSTracer
* trc
, T
* tp
, const char* name
) {
262 traceFieldsFrom
<0>(trc
, *tp
, name
);
264 static bool isValid(const T
& t
) { return areFieldsValidFrom
<0>(t
); }
268 static void traceFieldsFrom(JSTracer
* trc
, T
& tuple
, const char* name
) {
269 if constexpr (N
!= std::tuple_size_v
<T
>) {
270 using F
= std::tuple_element_t
<N
, T
>;
271 GCPolicy
<F
>::trace(trc
, &std::get
<N
>(tuple
), name
);
272 traceFieldsFrom
<N
+ 1>(trc
, tuple
, name
);
277 static bool areFieldsValidFrom(const T
& tuple
) {
278 if constexpr (N
!= std::tuple_size_v
<T
>) {
279 using F
= std::tuple_element_t
<N
, T
>;
280 return GCPolicy
<F
>::isValid(std::get
<N
>(tuple
)) &&
281 areFieldsValidFrom
<N
+ 1>(tuple
);
290 #endif // GCPolicyAPI_h