summaryrefslogtreecommitdiff
path: root/js/public/GCPolicyAPI.h
blob: 054e397af4a3c9d5277ff11c776be11b9fc4f27a (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
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * 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 T initial()
//       - Construct and return an empty T.
//
//   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 needsSweep(T* tp)
//       - Return true if |*tp| is about to be finalized. Otherwise, update the
//         edge for moving GC, and return false. Containers like GCHashMap and
//         GCHashSet use this method to decide when to remove an entry: if this
//         function returns true 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.
//
// The default GCPolicy<T> assumes that T has a default constructor and |trace|
// and |needsSweep| 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/UniquePtr.h"

#include "js/TraceKind.h"
#include "js/TracingAPI.h"

// Expand the given macro D for each public GC pointer.
#define FOR_EACH_PUBLIC_GC_POINTER_TYPE(D) \
    D(JS::Symbol*) \
    D(JSAtom*) \
    D(JSFunction*) \
    D(JSObject*) \
    D(JSScript*) \
    D(JSString*)

// Expand the given macro D for each public tagged GC pointer type.
#define FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(D) \
    D(JS::Value) \
    D(jsid)

#define FOR_EACH_PUBLIC_AGGREGATE_GC_POINTER_TYPE(D) \
    D(JSPropertyDescriptor)

class JSAtom;
class JSFunction;
class JSObject;
class JSScript;
class JSString;
namespace JS {
class Symbol;
}

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.
template <typename T>
struct StructGCPolicy
{
    static T initial() {
        return T();
    }

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

    static void sweep(T* tp) {
        return tp->sweep();
    }

    static bool needsSweep(T* tp) {
        return tp->needsSweep();
    }
};

// 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 T initial() { return T(); }
    static void trace(JSTracer* trc, T* t, const char* name) {}
    static bool needsSweep(T* v) { return false; }
};
template <> struct GCPolicy<uint32_t> : public IgnoreGCPolicy<uint32_t> {};
template <> struct GCPolicy<uint64_t> : public IgnoreGCPolicy<uint64_t> {};

template <typename T>
struct GCPointerPolicy
{
    static T initial() { return nullptr; }
    static void trace(JSTracer* trc, T* vp, const char* name) {
        if (*vp)
            js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
    }
    static bool needsSweep(T* vp) {
        if (*vp)
            return js::gc::IsAboutToBeFinalizedUnbarriered(vp);
        return false;
    }
};
template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};

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 needsSweep(JS::Heap<T>* thingp) {
        return js::gc::EdgeNeedsSweep(thingp);
    }
};

// GCPolicy<UniquePtr<T>> forwards the contained pointer to GCPolicy<T>.
template <typename T, typename D>
struct GCPolicy<mozilla::UniquePtr<T, D>>
{
    static mozilla::UniquePtr<T,D> initial() { return 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 needsSweep(mozilla::UniquePtr<T,D>* tp) {
        if (tp->get())
            return GCPolicy<T>::needsSweep(tp->get());
        return false;
    }
};

} // namespace JS

#endif // GCPolicyAPI_h