summaryrefslogtreecommitdiffstats
path: root/js/public/GCPolicyAPI.h
blob: eef7ee76236faf1f4cdd5d7cde0c84c19f17b7de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// GC Policy Mechanism

// A GCPolicy controls how the GC interacts with both direct pointers to GC
// things (e.g. JSObject* or JSString*), tagged and/or optional pointers to GC
// things (e.g.  Value or jsid), and C++ container types (e.g.
// JSPropertyDescriptor or GCHashMap).
//
// The GCPolicy provides at a minimum:
//
//   static void trace(JSTracer, T* tp, const char* name)
//       - Trace the edge |*tp|, calling the edge |name|. Containers like
//         GCHashMap and GCHashSet use this method to trace their children.
//
//   static bool traceWeak(T* tp)
//       - Return false if |*tp| has been set to nullptr. Otherwise, update the
//         edge for moving GC, and return true. Containers like GCHashMap and
//         GCHashSet use this method to decide when to remove an entry: if this
//         function returns false on a key/value/member/etc, its entry is
//         dropped from the container. Specializing this method is the standard
//         way to get custom weak behavior from a container type.
//
//   static bool isValid(const T& t)
//       - Return false only if |t| is corrupt in some way. The built-in GC
//         types do some memory layout checks. For debugging only; it is ok
//         to always return true or even to omit this member entirely.
//
// The default GCPolicy<T> assumes that T has a default constructor and |trace|
// and |traceWeak| methods, and forwards to them. GCPolicy has appropriate
// specializations for pointers to GC things and pointer-like types like
// JS::Heap<T> and mozilla::UniquePtr<T>.
//
// There are some stock structs your specializations can inherit from.
// IgnoreGCPolicy<T> does nothing. StructGCPolicy<T> forwards the methods to the
// referent type T.

#ifndef GCPolicyAPI_h
#define GCPolicyAPI_h

#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"

#include <type_traits>

#include "js/GCTypeMacros.h"  // JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE
#include "js/TraceKind.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"

namespace JS {

// Defines a policy for container types with non-GC, i.e. C storage. This
// policy dispatches to the underlying struct for GC interactions. Note that
// currently a type can define only the subset of the methods (trace and/or
// traceWeak) if it is never used in a context that requires the other.
template <typename T>
struct StructGCPolicy {
  static_assert(!std::is_pointer_v<T>,
                "Pointer type not allowed for StructGCPolicy");

  static void trace(JSTracer* trc, T* tp, const char* name) { tp->trace(trc); }

  static bool traceWeak(JSTracer* trc, T* tp) { return tp->traceWeak(trc); }

  static bool isValid(const T& tp) { return true; }
};

// The default GC policy attempts to defer to methods on the underlying type.
// Most C++ structures that contain a default constructor, a trace function and
// a sweep function will work out of the box with Rooted, Handle, GCVector,
// and GCHash{Set,Map}.
template <typename T>
struct GCPolicy : public StructGCPolicy<T> {};

// This policy ignores any GC interaction, e.g. for non-GC types.
template <typename T>
struct IgnoreGCPolicy {
  static void trace(JSTracer* trc, T* t, const char* name) {}
  static bool traceWeak(JSTracer*, T* v) { return true; }
  static bool isValid(const T& v) { return true; }
};
template <>
struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
template <>
struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};
template <>
struct GCPolicy<bool> : public IgnoreGCPolicy<bool> {};

template <typename T>
struct GCPointerPolicy {
  static_assert(std::is_pointer_v<T>,
                "Non-pointer type not allowed for GCPointerPolicy");

  static void trace(JSTracer* trc, T* vp, const char* name) {
    // This should only be called as part of root marking since that's the only
    // time we should trace unbarriered GC thing pointers. This will assert if
    // called at other times.
    TraceRoot(trc, vp, name);
  }
  static bool isTenured(T v) { return !v || !js::gc::IsInsideNursery(v); }
  static bool isValid(T v) { return js::gc::IsCellPointerValidOrNull(v); }
};
#define EXPAND_SPECIALIZE_GCPOLICY(Type)                   \
  template <>                                              \
  struct GCPolicy<Type> : public GCPointerPolicy<Type> {}; \
  template <>                                              \
  struct GCPolicy<Type const> : public GCPointerPolicy<Type const> {};
JS_FOR_EACH_PUBLIC_GC_POINTER_TYPE(EXPAND_SPECIALIZE_GCPOLICY)
#undef EXPAND_SPECIALIZE_GCPOLICY

template <typename T>
struct NonGCPointerPolicy {
  static void trace(JSTracer* trc, T* vp, const char* name) {
    if (*vp) {
      (*vp)->trace(trc);
    }
  }
  static bool traceWeak(JSTracer* trc, T* vp) {
    if (*vp) {
      return (*vp)->traceWeak(trc);
    }
    return true;
  }

  static bool isValid(T v) { return true; }
};

template <typename T>
struct GCPolicy<JS::Heap<T>> {
  static void trace(JSTracer* trc, JS::Heap<T>* thingp, const char* name) {
    TraceEdge(trc, thingp, name);
  }
  static bool traceWeak(JSTracer* trc, JS::Heap<T>* thingp) {
    return !*thingp || js::gc::TraceWeakEdge(trc, thingp);
  }
};

// GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
template <typename T, typename D>
struct GCPolicy<mozilla::UniquePtr<T, D>> {
  static void trace(JSTracer* trc, mozilla::UniquePtr<T, D>* tp,
                    const char* name) {
    if (tp->get()) {
      GCPolicy<T>::trace(trc, tp->get(), name);
    }
  }
  static bool traceWeak(JSTracer* trc, mozilla::UniquePtr<T, D>* tp) {
    if (tp->get()) {
      return GCPolicy<T>::traceWeak(trc, tp->get());
    }
    return true;
  }
  static bool isValid(const mozilla::UniquePtr<T, D>& t) {
    if (t.get()) {
      return GCPolicy<T>::isValid(*t.get());
    }
    return true;
  }
};

template <>
struct GCPolicy<mozilla::Nothing> : public IgnoreGCPolicy<mozilla::Nothing> {};

// GCPolicy<Maybe<T>> forwards tracing/sweeping to GCPolicy<T*> if
// the Maybe<T> is filled and T* can be traced via GCPolicy<T*>.
template <typename T>
struct GCPolicy<mozilla::Maybe<T>> {
  static void trace(JSTracer* trc, mozilla::Maybe<T>* tp, const char* name) {
    if (tp->isSome()) {
      GCPolicy<T>::trace(trc, tp->ptr(), name);
    }
  }
  static bool traceWeak(JSTracer* trc, mozilla::Maybe<T>* tp) {
    if (tp->isSome()) {
      return GCPolicy<T>::traceWeak(trc, tp->ptr());
    }
    return true;
  }
  static bool isValid(const mozilla::Maybe<T>& t) {
    if (t.isSome()) {
      return GCPolicy<T>::isValid(t.ref());
    }
    return true;
  }
};

template <typename T1, typename T2>
struct GCPolicy<std::pair<T1, T2>> {
  static void trace(JSTracer* trc, std::pair<T1, T2>* tp, const char* name) {
    GCPolicy<T1>::trace(trc, &tp->first, name);
    GCPolicy<T2>::trace(trc, &tp->second, name);
  }
  static bool traceWeak(JSTracer* trc, std::pair<T1, T2>* tp) {
    return GCPolicy<T1>::traceWeak(trc, &tp->first) &&
           GCPolicy<T2>::traceWeak(trc, &tp->second);
  }
  static bool isValid(const std::pair<T1, T2>& t) {
    return GCPolicy<T1>::isValid(t.first) && GCPolicy<T2>::isValid(t.second);
  }
};

template <>
struct GCPolicy<JS::Realm*>;  // see Realm.h

template <>
struct GCPolicy<mozilla::Ok> : public IgnoreGCPolicy<mozilla::Ok> {};

template <typename V, typename E>
struct GCPolicy<mozilla::Result<V, E>> {
  static void trace(JSTracer* trc, mozilla::Result<V, E>* tp,
                    const char* name) {
    if (tp->isOk()) {
      V tmp = tp->unwrap();
      JS::GCPolicy<V>::trace(trc, &tmp, "Result value");
      tp->updateAfterTracing(std::move(tmp));
    }

    if (tp->isErr()) {
      E tmp = tp->unwrapErr();
      JS::GCPolicy<E>::trace(trc, &tmp, "Result error");
      tp->updateErrorAfterTracing(std::move(tmp));
    }
  }

  static bool isValid(const mozilla::Result<V, E>& t) { return true; }
};

}  // namespace JS

#endif  // GCPolicyAPI_h