1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 // A GCPolicy controls how the GC interacts with both direct pointers to GC
10 // things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
11 // things (e.g.  Value or jsid), and C++ container types (e.g.
12 // JSPropertyDescriptor or GCHashMap).
13 //
14 // The GCPolicy provides at a minimum:
15 //
16 //   static T initial()
17 //       - Construct and return an empty T.
18 //
19 //   static void trace(JSTracer, T* tp, const char* name)
20 //       - Trace the edge |*tp|, calling the edge |name|. Containers like
21 //         GCHashMap and GCHashSet use this method to trace their children.
22 //
23 //   static bool needsSweep(T* tp)
24 //       - Return true if |*tp| is about to be finalized. Otherwise, update the
25 //         edge for moving GC, and return false. Containers like GCHashMap and
26 //         GCHashSet use this method to decide when to remove an entry: if this
27 //         function returns true on a key/value/member/etc, its entry is dropped
28 //         from the container. Specializing this method is the standard way to
29 //         get custom weak behavior from a container type.
30 //
31 // The default GCPolicy<T> assumes that T has a default constructor and |trace|
32 // and |needsSweep| methods, and forwards to them. GCPolicy has appropriate
33 // specializations for pointers to GC things and pointer-like types like
34 // JS::Heap<T> and mozilla::UniquePtr<T>.
35 //
36 // There are some stock structs your specializations can inherit from.
37 // IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
38 // referent type T.
39 
40 #ifndef GCPolicyAPI_h
41 #define GCPolicyAPI_h
42 
43 #include "mozilla/Maybe.h"
44 #include "mozilla/UniquePtr.h"
45 
46 #include "js/TraceKind.h"
47 #include "js/TracingAPI.h"
48 #include "js/TypeDecls.h"
49 
50 // Expand the given macro D for each public GC pointer.
51 #define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
52   D(JS::Symbol*)                           \
53   D(JSAtom*)                               \
54   D(JSFunction*)                           \
55   D(JSObject*)                             \
56   D(JSScript*)                             \
57   D(JSString*)
58 
59 // Expand the given macro D for each public tagged GC pointer type.
60 #define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
61   D(JS::Value)                                    \
62   D(jsid)
63 
64 #define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) D(JSPropertyDescriptor)
65 
66 namespace JS {
67 
68 // Defines a policy for container types with non-GC, i.e. C storage. This
69 // policy dispatches to the underlying struct for GC interactions.
70 template <typename T>
71 struct StructGCPolicy {
initialStructGCPolicy72   static T initial() { return T(); }
73 
traceStructGCPolicy74   static void trace(JSTracer* trc, T* tp, const char* name) { tp->trace(trc); }
75 
sweepStructGCPolicy76   static void sweep(T* tp) { return tp->sweep(); }
77 
needsSweepStructGCPolicy78   static bool needsSweep(T* tp) { return tp->needsSweep(); }
79 
isValidStructGCPolicy80   static bool isValid(const T& tp) { return true; }
81 };
82 
83 // The default GC policy attempts to defer to methods on the underlying type.
84 // Most C++ structures that contain a default constructor, a trace function and
85 // a sweep function will work out of the box with Rooted, Handle, GCVector,
86 // and GCHash{Set,Map}.
87 template <typename T>
88 struct GCPolicy : public StructGCPolicy<T> {};
89 
90 // This policy ignores any GC interaction, e.g. for non-GC types.
91 template <typename T>
92 struct IgnoreGCPolicy {
initialIgnoreGCPolicy93   static T initial() { return T(); }
traceIgnoreGCPolicy94   static void trace(JSTracer* trc, T* t, const char* name) {}
needsSweepIgnoreGCPolicy95   static bool needsSweep(T* v) { return false; }
isValidIgnoreGCPolicy96   static bool isValid(const T& v) { return true; }
97 };
98 template <>
99 struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
100 template <>
101 struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
102 
103 template <typename T>
104 struct GCPointerPolicy {
105   static T initial() { return nullptr; }
106   static void trace(JSTracer* trc, T* vp, const char* name) {
107     if (*vp) js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
108   }
109   static bool needsSweep(T* vp) {
110     if (*vp) return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
111     return false;
112   }
113   static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); }
114 };
115 template <>
116 struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
117 template <>
118 struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
119 template <>
120 struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
121 template <>
122 struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
123 template <>
124 struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
125 template <>
126 struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
127 
128 template <typename T>
129 struct NonGCPointerPolicy {
130   static T initial() { return nullptr; }
131   static void trace(JSTracer* trc, T* vp, const char* name) {
132     if (*vp) (*vp)->trace(trc);
133   }
134   static bool needsSweep(T* vp) {
135     if (*vp) return (*vp)->needsSweep();
136     return false;
137   }
138   static bool isValid(T v) { return true; }
139 };
140 
141 template <typename T>
142 struct GCPolicy<JS::Heap<T>> {
143   static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
144     TraceEdge(trc, thingp, name);
145   }
146   static bool needsSweep(JS::Heap<T>* thingp) {
147     return *thingp && js::gc::EdgeNeedsSweep(thingp);
148   }
149 };
150 
151 // GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
152 template <typename T, typename D>
153 struct GCPolicy<mozilla::UniquePtr<T, D>> {
154   static mozilla::UniquePtr<T, D> initial() {
155     return mozilla::UniquePtr<T, D>();
156   }
157   static void trace(JSTracer* trc, mozilla::UniquePtr<T, D>* tp,
158                     const char* name) {
159     if (tp->get()) GCPolicy<T>::trace(trc, tp->get(), name);
160   }
161   static bool needsSweep(mozilla::UniquePtr<T, D>* tp) {
162     if (tp->get()) return GCPolicy<T>::needsSweep(tp->get());
163     return false;
164   }
165   static bool isValid(const mozilla::UniquePtr<T, D>& t) {
166     if (t.get()) return GCPolicy<T>::isValid(*t.get());
167     return true;
168   }
169 };
170 
171 // GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
172 // when the Maybe<T> is full.
173 template <typename T>
174 struct GCPolicy<mozilla::Maybe<T>> {
175   static mozilla::Maybe<T> initial() { return mozilla::Maybe<T>(); }
176   static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
177     if (tp->isSome()) GCPolicy<T>::trace(trc, tp->ptr(), name);
178   }
179   static bool needsSweep(mozilla::Maybe<T>* tp) {
180     if (tp->isSome()) return GCPolicy<T>::needsSweep(tp->ptr());
181     return false;
182   }
183   static bool isValid(const mozilla::Maybe<T>& t) {
184     if (t.isSome()) return GCPolicy<T>::isValid(t.ref());
185     return true;
186   }
187 };
188 
189 template <>
190 struct GCPolicy<JS::Realm*>;  // see Realm.h
191 
192 }  // namespace JS
193 
194 #endif  // GCPolicyAPI_h
195