Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/src/gc/GC-inl.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef gc_GC_inl_h
8
#define gc_GC_inl_h
9
10
#include "gc/GC.h"
11
12
#include "mozilla/DebugOnly.h"
13
#include "mozilla/Maybe.h"
14
15
#include "gc/Zone.h"
16
17
#include "gc/ArenaList-inl.h"
18
19
namespace js {
20
namespace gc {
21
22
class AutoAssertEmptyNursery;
23
24
class ArenaIter
25
{
26
    Arena* arena;
27
    Arena* unsweptArena;
28
    Arena* sweptArena;
29
    mozilla::DebugOnly<bool> initialized;
30
31
  public:
32
    ArenaIter()
33
8
      : arena(nullptr), unsweptArena(nullptr), sweptArena(nullptr), initialized(false) {}
34
35
0
    ArenaIter(JS::Zone* zone, AllocKind kind) : initialized(false) { init(zone, kind); }
36
37
8
    void init(JS::Zone* zone, AllocKind kind) {
38
8
        MOZ_ASSERT(!initialized);
39
8
        MOZ_ASSERT(zone);
40
8
        initialized = true;
41
8
        arena = zone->arenas.getFirstArena(kind);
42
8
        unsweptArena = zone->arenas.getFirstArenaToSweep(kind);
43
8
        sweptArena = zone->arenas.getFirstSweptArena(kind);
44
8
        if (!unsweptArena) {
45
8
            unsweptArena = sweptArena;
46
8
            sweptArena = nullptr;
47
8
        }
48
8
        if (!arena) {
49
0
            arena = unsweptArena;
50
0
            unsweptArena = sweptArena;
51
0
            sweptArena = nullptr;
52
0
        }
53
8
    }
54
55
696
    bool done() const {
56
696
        MOZ_ASSERT(initialized);
57
696
        return !arena;
58
696
    }
59
60
24
    Arena* get() const {
61
24
        MOZ_ASSERT(!done());
62
24
        return arena;
63
24
    }
64
65
24
    void next() {
66
24
        MOZ_ASSERT(!done());
67
24
        arena = arena->next;
68
24
        if (!arena) {
69
8
            arena = unsweptArena;
70
8
            unsweptArena = sweptArena;
71
8
            sweptArena = nullptr;
72
8
        }
73
24
    }
74
};
75
76
enum CellIterNeedsBarrier : uint8_t
77
{
78
    CellIterDoesntNeedBarrier = 0,
79
    CellIterMayNeedBarrier = 1
80
};
81
82
class ArenaCellIterImpl
83
{
84
    size_t firstThingOffset;
85
    size_t thingSize;
86
    Arena* arenaAddr;
87
    FreeSpan span;
88
    uint_fast16_t thing;
89
    JS::TraceKind traceKind;
90
    bool needsBarrier;
91
    mozilla::DebugOnly<bool> initialized;
92
93
    // Upon entry, |thing| points to any thing (free or used) and finds the
94
    // first used thing, which may be |thing|.
95
21.2M
    void moveForwardIfFree() {
96
21.2M
        MOZ_ASSERT(!done());
97
21.2M
        MOZ_ASSERT(thing);
98
21.2M
        // Note: if |span| is empty, this test will fail, which is what we want
99
21.2M
        // -- |span| being empty means that we're past the end of the last free
100
21.2M
        // thing, all the remaining things in the arena are used, and we'll
101
21.2M
        // never need to move forward.
102
21.2M
        if (thing == span.first) {
103
1.23k
            thing = span.last + thingSize;
104
1.23k
            span = *span.nextSpan(arenaAddr);
105
1.23k
        }
106
21.2M
    }
107
108
  public:
109
    ArenaCellIterImpl()
110
      : firstThingOffset(0),
111
        thingSize(0),
112
        arenaAddr(nullptr),
113
        thing(0),
114
        traceKind(JS::TraceKind::Null),
115
        needsBarrier(false),
116
        initialized(false)
117
8
    {}
118
119
    explicit ArenaCellIterImpl(Arena* arena, CellIterNeedsBarrier mayNeedBarrier)
120
      : initialized(false)
121
253k
    {
122
253k
        init(arena, mayNeedBarrier);
123
253k
    }
124
125
253k
    void init(Arena* arena, CellIterNeedsBarrier mayNeedBarrier) {
126
253k
        MOZ_ASSERT(!initialized);
127
253k
        MOZ_ASSERT(arena);
128
253k
        initialized = true;
129
253k
        AllocKind kind = arena->getAllocKind();
130
253k
        firstThingOffset = Arena::firstThingOffset(kind);
131
253k
        thingSize = Arena::thingSize(kind);
132
253k
        traceKind = MapAllocToTraceKind(kind);
133
253k
        needsBarrier = mayNeedBarrier && !JS::RuntimeHeapIsCollecting();
134
253k
        reset(arena);
135
253k
    }
136
137
    // Use this to move from an Arena of a particular kind to another Arena of
138
    // the same kind.
139
253k
    void reset(Arena* arena) {
140
253k
        MOZ_ASSERT(initialized);
141
253k
        MOZ_ASSERT(arena);
142
253k
        arenaAddr = arena;
143
253k
        span = *arena->getFirstFreeSpan();
144
253k
        thing = firstThingOffset;
145
253k
        moveForwardIfFree();
146
253k
    }
147
148
21.5M
    bool done() const {
149
21.5M
        MOZ_ASSERT(initialized);
150
21.5M
        MOZ_ASSERT(thing <= ArenaSize);
151
21.5M
        return thing == ArenaSize;
152
21.5M
    }
153
154
21.2M
    TenuredCell* getCell() const {
155
21.2M
        MOZ_ASSERT(!done());
156
21.2M
        TenuredCell* cell = reinterpret_cast<TenuredCell*>(uintptr_t(arenaAddr) + thing);
157
21.2M
158
21.2M
        // This can result in a a new reference being created to an object that
159
21.2M
        // an ongoing incremental GC may find to be unreachable, so we may need
160
21.2M
        // a barrier here.
161
21.2M
        if (needsBarrier) {
162
0
            ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind));
163
0
        }
164
21.2M
165
21.2M
        return cell;
166
21.2M
    }
167
168
13.0M
    template<typename T> T* get() const {
169
13.0M
        MOZ_ASSERT(!done());
170
13.0M
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
13.0M
        return reinterpret_cast<T*>(getCell());
172
13.0M
    }
JSScript* js::gc::ArenaCellIterImpl::get<JSScript>() const
Line
Count
Source
168
5.30k
    template<typename T> T* get() const {
169
5.30k
        MOZ_ASSERT(!done());
170
5.30k
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
5.30k
        return reinterpret_cast<T*>(getCell());
172
5.30k
    }
js::jit::JitCode* js::gc::ArenaCellIterImpl::get<js::jit::JitCode>() const
Line
Count
Source
168
405
    template<typename T> T* get() const {
169
405
        MOZ_ASSERT(!done());
170
405
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
405
        return reinterpret_cast<T*>(getCell());
172
405
    }
js::ObjectGroup* js::gc::ArenaCellIterImpl::get<js::ObjectGroup>() const
Line
Count
Source
168
6.53M
    template<typename T> T* get() const {
169
6.53M
        MOZ_ASSERT(!done());
170
6.53M
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
6.53M
        return reinterpret_cast<T*>(getCell());
172
6.53M
    }
Unexecuted instantiation: js::LazyScript* js::gc::ArenaCellIterImpl::get<js::LazyScript>() const
js::Shape* js::gc::ArenaCellIterImpl::get<js::Shape>() const
Line
Count
Source
168
3.28M
    template<typename T> T* get() const {
169
3.28M
        MOZ_ASSERT(!done());
170
3.28M
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
3.28M
        return reinterpret_cast<T*>(getCell());
172
3.28M
    }
js::AccessorShape* js::gc::ArenaCellIterImpl::get<js::AccessorShape>() const
Line
Count
Source
168
2.34k
    template<typename T> T* get() const {
169
2.34k
        MOZ_ASSERT(!done());
170
2.34k
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
2.34k
        return reinterpret_cast<T*>(getCell());
172
2.34k
    }
js::BaseShape* js::gc::ArenaCellIterImpl::get<js::BaseShape>() const
Line
Count
Source
168
878
    template<typename T> T* get() const {
169
878
        MOZ_ASSERT(!done());
170
878
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
878
        return reinterpret_cast<T*>(getCell());
172
878
    }
JSExternalString* js::gc::ArenaCellIterImpl::get<JSExternalString>() const
Line
Count
Source
168
36
    template<typename T> T* get() const {
169
36
        MOZ_ASSERT(!done());
170
36
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
36
        return reinterpret_cast<T*>(getCell());
172
36
    }
Unexecuted instantiation: js::FatInlineAtom* js::gc::ArenaCellIterImpl::get<js::FatInlineAtom>() const
Unexecuted instantiation: js::NormalAtom* js::gc::ArenaCellIterImpl::get<js::NormalAtom>() const
Unexecuted instantiation: JS::Symbol* js::gc::ArenaCellIterImpl::get<JS::Symbol>() const
js::Scope* js::gc::ArenaCellIterImpl::get<js::Scope>() const
Line
Count
Source
168
2.61k
    template<typename T> T* get() const {
169
2.61k
        MOZ_ASSERT(!done());
170
2.61k
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
2.61k
        return reinterpret_cast<T*>(getCell());
172
2.61k
    }
Unexecuted instantiation: js::RegExpShared* js::gc::ArenaCellIterImpl::get<js::RegExpShared>() const
JSFatInlineString* js::gc::ArenaCellIterImpl::get<JSFatInlineString>() const
Line
Count
Source
168
1.50k
    template<typename T> T* get() const {
169
1.50k
        MOZ_ASSERT(!done());
170
1.50k
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
1.50k
        return reinterpret_cast<T*>(getCell());
172
1.50k
    }
JSString* js::gc::ArenaCellIterImpl::get<JSString>() const
Line
Count
Source
168
3.26M
    template<typename T> T* get() const {
169
3.26M
        MOZ_ASSERT(!done());
170
3.26M
        MOZ_ASSERT(JS::MapTypeToTraceKind<T>::kind == traceKind);
171
3.26M
        return reinterpret_cast<T*>(getCell());
172
3.26M
    }
173
174
21.2M
    void next() {
175
21.2M
        MOZ_ASSERT(!done());
176
21.2M
        thing += thingSize;
177
21.2M
        if (thing < ArenaSize) {
178
21.0M
            moveForwardIfFree();
179
21.0M
        }
180
21.2M
    }
181
};
182
183
template<>
184
JSObject*
185
ArenaCellIterImpl::get<JSObject>() const;
186
187
class ArenaCellIter : public ArenaCellIterImpl
188
{
189
  public:
190
    explicit ArenaCellIter(Arena* arena)
191
      : ArenaCellIterImpl(arena, CellIterMayNeedBarrier)
192
0
    {
193
0
        MOZ_ASSERT(JS::RuntimeHeapIsTracing());
194
0
    }
195
};
196
197
template <typename T>
198
class ZoneCellIter;
199
200
template <>
201
class ZoneCellIter<TenuredCell> {
202
    ArenaIter arenaIter;
203
    ArenaCellIterImpl cellIter;
204
    mozilla::Maybe<JS::AutoAssertNoGC> nogc;
205
206
  protected:
207
    // For use when a subclass wants to insert some setup before init().
208
8
    ZoneCellIter() {}
209
210
8
    void init(JS::Zone* zone, AllocKind kind) {
211
8
        MOZ_ASSERT_IF(IsNurseryAllocable(kind),
212
8
                      (zone->isAtomsZone() ||
213
8
                       zone->runtimeFromMainThread()->gc.nursery().isEmpty()));
214
8
        initForTenuredIteration(zone, kind);
215
8
    }
216
217
8
    void initForTenuredIteration(JS::Zone* zone, AllocKind kind) {
218
8
        JSRuntime* rt = zone->runtimeFromAnyThread();
219
8
220
8
        // If called from outside a GC, ensure that the heap is in a state
221
8
        // that allows us to iterate.
222
8
        if (!JS::RuntimeHeapIsBusy()) {
223
0
            // Assert that no GCs can occur while a ZoneCellIter is live.
224
0
            nogc.emplace();
225
0
        }
226
8
227
8
        // We have a single-threaded runtime, so there's no need to protect
228
8
        // against other threads iterating or allocating. However, we do have
229
8
        // background finalization; we may have to wait for this to finish if
230
8
        // it's currently active.
231
8
        if (IsBackgroundFinalized(kind) && zone->arenas.needBackgroundFinalizeWait(kind)) {
232
0
            rt->gc.waitBackgroundSweepEnd();
233
0
        }
234
8
        arenaIter.init(zone, kind);
235
8
        if (!arenaIter.done()) {
236
8
            cellIter.init(arenaIter.get(), CellIterMayNeedBarrier);
237
8
            settle();
238
8
        }
239
8
    }
240
241
  public:
242
0
    ZoneCellIter(JS::Zone* zone, AllocKind kind) {
243
0
        // If we are iterating a nursery-allocated kind then we need to
244
0
        // evict first so that we can see all things.
245
0
        if (IsNurseryAllocable(kind)) {
246
0
            zone->runtimeFromMainThread()->gc.evictNursery();
247
0
        }
248
0
249
0
        init(zone, kind);
250
0
    }
251
252
0
    ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery&) {
253
0
        // No need to evict the nursery. (This constructor is known statically
254
0
        // to not GC.)
255
0
        init(zone, kind);
256
0
    }
257
258
632
    bool done() const {
259
632
        return arenaIter.done();
260
632
    }
261
262
    template<typename T>
263
2.50k
    T* get() const {
264
2.50k
        MOZ_ASSERT(!done());
265
2.50k
        return cellIter.get<T>();
266
2.50k
    }
Unexecuted instantiation: JSObject* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<JSObject>() const
JSScript* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<JSScript>() const
Line
Count
Source
263
2.50k
    T* get() const {
264
2.50k
        MOZ_ASSERT(!done());
265
2.50k
        return cellIter.get<T>();
266
2.50k
    }
Unexecuted instantiation: js::jit::JitCode* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<js::jit::JitCode>() const
Unexecuted instantiation: js::ObjectGroup* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<js::ObjectGroup>() const
Unexecuted instantiation: js::BaseShape* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<js::BaseShape>() const
Unexecuted instantiation: js::LazyScript* js::gc::ZoneCellIter<js::gc::TenuredCell>::get<js::LazyScript>() const
267
268
0
    TenuredCell* getCell() const {
269
0
        MOZ_ASSERT(!done());
270
0
        return cellIter.getCell();
271
0
    }
272
273
632
    void settle() {
274
656
        while (cellIter.done() && !arenaIter.done()) {
275
24
            arenaIter.next();
276
24
            if (!arenaIter.done()) {
277
16
                cellIter.reset(arenaIter.get());
278
16
            }
279
24
        }
280
632
    }
281
282
624
    void next() {
283
624
        MOZ_ASSERT(!done());
284
624
        cellIter.next();
285
624
        settle();
286
624
    }
287
};
288
289
// Iterator over the cells in a Zone, where the GC type (JSString, JSObject) is
290
// known, for a single AllocKind. Example usages:
291
//
292
//   for (auto obj = zone->cellIter<JSObject>(AllocKind::OBJECT0); !obj.done(); obj.next())
293
//       ...
294
//
295
//   for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next())
296
//       f(script->code());
297
//
298
// As this code demonstrates, you can use 'script' as if it were a JSScript*.
299
// Its actual type is ZoneCellIter<JSScript>, but for most purposes it will
300
// autoconvert to JSScript*.
301
//
302
// Note that in the JSScript case, ZoneCellIter is able to infer the AllocKind
303
// from the type 'JSScript', whereas in the JSObject case, the kind must be
304
// given (because there are multiple AllocKinds for objects).
305
//
306
// Also, the static rooting hazard analysis knows that the JSScript case will
307
// not GC during construction. The JSObject case needs to GC, or more precisely
308
// to empty the nursery and clear out the store buffer, so that it can see all
309
// objects to iterate over (the nursery is not iterable) and remove the
310
// possibility of having pointers from the store buffer to data hanging off
311
// stuff we're iterating over that we are going to delete. (The latter should
312
// not be a problem, since such instances should be using RelocatablePtr do
313
// remove themselves from the store buffer on deletion, but currently for
314
// subtle reasons that isn't good enough.)
315
//
316
// If the iterator is used within a GC, then there is no need to evict the
317
// nursery (again). You may select a variant that will skip the eviction either
318
// by specializing on a GCType that is never allocated in the nursery, or
319
// explicitly by passing in a trailing AutoAssertEmptyNursery argument.
320
//
321
template <typename GCType>
322
class ZoneCellIter : public ZoneCellIter<TenuredCell> {
323
  public:
324
    // Non-nursery allocated (equivalent to having an entry in
325
    // MapTypeToFinalizeKind). The template declaration here is to discard this
326
    // constructor overload if MapTypeToFinalizeKind<GCType>::kind does not
327
    // exist. Note that there will be no remaining overloads that will work,
328
    // which makes sense given that you haven't specified which of the
329
    // AllocKinds to use for GCType.
330
    //
331
    // If we later add a nursery allocable GCType with a single AllocKind, we
332
    // will want to add an overload of this constructor that does the right
333
    // thing (ie, it empties the nursery before iterating.)
334
8
    explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter<TenuredCell>() {
335
8
        init(zone, MapTypeToFinalizeKind<GCType>::kind);
336
8
    }
js::gc::ZoneCellIter<JSScript>::ZoneCellIter(JS::Zone*)
Line
Count
Source
334
8
    explicit ZoneCellIter(JS::Zone* zone) : ZoneCellIter<TenuredCell>() {
335
8
        init(zone, MapTypeToFinalizeKind<GCType>::kind);
336
8
    }
Unexecuted instantiation: js::gc::ZoneCellIter<js::jit::JitCode>::ZoneCellIter(JS::Zone*)
Unexecuted instantiation: js::gc::ZoneCellIter<js::ObjectGroup>::ZoneCellIter(JS::Zone*)
Unexecuted instantiation: js::gc::ZoneCellIter<js::BaseShape>::ZoneCellIter(JS::Zone*)
Unexecuted instantiation: js::gc::ZoneCellIter<js::LazyScript>::ZoneCellIter(JS::Zone*)
337
338
    // Non-nursery allocated, nursery is known to be empty: same behavior as above.
339
0
    ZoneCellIter(JS::Zone* zone, const js::gc::AutoAssertEmptyNursery&) : ZoneCellIter(zone) {
340
0
    }
Unexecuted instantiation: js::gc::ZoneCellIter<JSScript>::ZoneCellIter(JS::Zone*, js::gc::AutoAssertEmptyNursery const&)
Unexecuted instantiation: js::gc::ZoneCellIter<js::LazyScript>::ZoneCellIter(JS::Zone*, js::gc::AutoAssertEmptyNursery const&)
341
342
    // Arbitrary kind, which will be assumed to be nursery allocable (and
343
    // therefore the nursery will be emptied before iterating.)
344
0
    ZoneCellIter(JS::Zone* zone, AllocKind kind) : ZoneCellIter<TenuredCell>(zone, kind) {
345
0
    }
346
347
    // Arbitrary kind, which will be assumed to be nursery allocable, but the
348
    // nursery is known to be empty already: same behavior as non-nursery types.
349
    ZoneCellIter(JS::Zone* zone, AllocKind kind, const js::gc::AutoAssertEmptyNursery& empty)
350
      : ZoneCellIter<TenuredCell>(zone, kind, empty)
351
0
    {
352
0
    }
353
354
2.50k
    GCType* get() const { return ZoneCellIter<TenuredCell>::get<GCType>(); }
js::gc::ZoneCellIter<JSScript>::get() const
Line
Count
Source
354
2.50k
    GCType* get() const { return ZoneCellIter<TenuredCell>::get<GCType>(); }
Unexecuted instantiation: js::gc::ZoneCellIter<js::jit::JitCode>::get() const
Unexecuted instantiation: js::gc::ZoneCellIter<JSObject>::get() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::ObjectGroup>::get() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::BaseShape>::get() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::LazyScript>::get() const
355
1.24k
    operator GCType*() const { return get(); }
js::gc::ZoneCellIter<JSScript>::operator JSScript*() const
Line
Count
Source
355
1.24k
    operator GCType*() const { return get(); }
Unexecuted instantiation: js::gc::ZoneCellIter<js::jit::JitCode>::operator js::jit::JitCode*() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::ObjectGroup>::operator js::ObjectGroup*() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::LazyScript>::operator js::LazyScript*() const
356
1.25k
    GCType* operator ->() const { return get(); }
js::gc::ZoneCellIter<JSScript>::operator->() const
Line
Count
Source
356
1.25k
    GCType* operator ->() const { return get(); }
Unexecuted instantiation: js::gc::ZoneCellIter<JSObject>::operator->() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::BaseShape>::operator->() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::ObjectGroup>::operator->() const
Unexecuted instantiation: js::gc::ZoneCellIter<js::LazyScript>::operator->() const
357
};
358
359
} /* namespace gc */
360
} /* namespace js */
361
362
#endif /* gc_GC_inl_h */