Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/UbiNodeBreadthFirst.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 js_UbiNodeBreadthFirst_h
8
#define js_UbiNodeBreadthFirst_h
9
10
#include "js/UbiNode.h"
11
#include "js/Utility.h"
12
#include "js/Vector.h"
13
14
namespace JS {
15
namespace ubi {
16
17
// A breadth-first traversal template for graphs of ubi::Nodes.
18
//
19
// No GC may occur while an instance of this template is live.
20
//
21
// The provided Handler type should have two members:
22
//
23
//   typename NodeData;
24
//
25
//      The value type of |BreadthFirst<Handler>::visited|, the HashMap of
26
//      ubi::Nodes that have been visited so far. Since the algorithm needs a
27
//      hash table like this for its own use anyway, it is simple to let
28
//      Handler store its own metadata about each node in the same table.
29
//
30
//      For example, if you want to find a shortest path to each node from any
31
//      traversal starting point, your |NodeData| type could record the first
32
//      edge to reach each node, and the node from which it originates. Then,
33
//      when the traversal is complete, you can walk backwards from any node
34
//      to some starting point, and the path recorded will be a shortest path.
35
//
36
//      This type must have a default constructor. If this type owns any other
37
//      resources, move constructors and assignment operators are probably a
38
//      good idea, too.
39
//
40
//   bool operator() (BreadthFirst& traversal,
41
//                    Node origin, const Edge& edge,
42
//                    Handler::NodeData* referentData, bool first);
43
//
44
//      The visitor function, called to report that we have traversed
45
//      |edge| from |origin|. This is called once for each edge we traverse.
46
//      As this is a breadth-first search, any prior calls to the visitor function
47
//      were for origin nodes not further from the start nodes than |origin|.
48
//
49
//      |traversal| is this traversal object, passed along for convenience.
50
//
51
//      |referentData| is a pointer to the value of the entry in
52
//      |traversal.visited| for |edge.referent|; the visitor function can
53
//      store whatever metadata it likes about |edge.referent| there.
54
//
55
//      |first| is true if this is the first time we have visited an edge
56
//      leading to |edge.referent|. This could be stored in NodeData, but
57
//      the algorithm knows whether it has just created the entry in
58
//      |traversal.visited|, so it passes it along for convenience.
59
//
60
//      The visitor function may call |traversal.abandonReferent()| if it
61
//      doesn't want to traverse the outgoing edges of |edge.referent|. You can
62
//      use this to limit the traversal to a given portion of the graph: it will
63
//      never visit nodes reachable only through nodes that you have abandoned.
64
//      Note that |abandonReferent| must be called the first time the given node
65
//      is reached; that is, |first| must be true.
66
//
67
//      The visitor function may call |traversal.stop()| if it doesn't want
68
//      to visit any more nodes at all.
69
//
70
//      The visitor function may consult |traversal.visited| for information
71
//      about other nodes, but it should not add or remove entries.
72
//
73
//      The visitor function should return true on success, or false if an
74
//      error occurs. A false return value terminates the traversal
75
//      immediately, and causes BreadthFirst<Handler>::traverse to return
76
//      false.
77
template<typename Handler>
78
struct BreadthFirst {
79
80
    // Construct a breadth-first traversal object that reports the nodes it
81
    // reaches to |handler|. The traversal asserts that no GC happens in its
82
    // runtime during its lifetime.
83
    //
84
    // We do nothing with noGC, other than require it to exist, with a lifetime
85
    // that encloses our own.
86
    BreadthFirst(JSContext* cx, Handler& handler, const JS::AutoRequireNoGC& noGC)
87
      : wantNames(true), cx(cx), visited(), handler(handler), pending(),
88
        traversalBegun(false), stopRequested(false), abandonRequested(false)
89
0
    { }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::BreadthFirst(JSContext*, JS::ubi::ShortestPaths::Handler&, JS::AutoRequireNoGC const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::BreadthFirst(JSContext*, JS::ubi::CensusHandler&, JS::AutoRequireNoGC const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::BreadthFirst(JSContext*, mozilla::devtools::HeapSnapshotHandler&, JS::AutoRequireNoGC const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::BreadthFirst(JSContext*, heaptools::FindPathHandler&, JS::AutoRequireNoGC const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::BreadthFirst(JSContext*, js::Debugger::ObjectQuery&, JS::AutoRequireNoGC const&)
90
91
    // Add |node| as a starting point for the traversal. You may add
92
    // as many starting points as you like. Return false on OOM.
93
0
    bool addStart(Node node) { return pending.append(node); }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::addStart(JS::ubi::Node)
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::addStart(JS::ubi::Node)
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::addStart(JS::ubi::Node)
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::addStart(JS::ubi::Node)
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::addStart(JS::ubi::Node)
94
95
    // Add |node| as a starting point for the traversal (see addStart) and also
96
    // add it to the |visited| set. Return false on OOM.
97
0
    bool addStartVisited(Node node) {
98
0
        typename NodeMap::AddPtr ptr = visited.lookupForAdd(node);
99
0
        if (!ptr && !visited.add(ptr, node, typename Handler::NodeData())) {
100
0
            return false;
101
0
        }
102
0
        return addStart(node);
103
0
    }
104
105
    // True if the handler wants us to compute edge names; doing so can be
106
    // expensive in time and memory. True by default.
107
    bool wantNames;
108
109
    // Traverse the graph in breadth-first order, starting at the given
110
    // start nodes, applying |handler::operator()| for each edge traversed
111
    // as described above.
112
    //
113
    // This should be called only once per instance of this class.
114
    //
115
    // Return false on OOM or error return from |handler::operator()|.
116
    bool traverse()
117
0
    {
118
0
        MOZ_ASSERT(!traversalBegun);
119
0
        traversalBegun = true;
120
0
121
0
        // While there are pending nodes, visit them.
122
0
        while (!pending.empty()) {
123
0
            Node origin = pending.front();
124
0
            pending.popFront();
125
0
126
0
            // Get a range containing all origin's outgoing edges.
127
0
            auto range = origin.edges(cx, wantNames);
128
0
            if (!range) {
129
0
                return false;
130
0
            }
131
0
132
0
            // Traverse each edge.
133
0
            for (; !range->empty(); range->popFront()) {
134
0
                MOZ_ASSERT(!stopRequested);
135
0
136
0
                Edge& edge = range->front();
137
0
                typename NodeMap::AddPtr a = visited.lookupForAdd(edge.referent);
138
0
                bool first = !a;
139
0
140
0
                if (first) {
141
0
                    // This is the first time we've reached |edge.referent|.
142
0
                    // Mark it as visited.
143
0
                    if (!visited.add(a, edge.referent, typename Handler::NodeData())) {
144
0
                        return false;
145
0
                    }
146
0
                }
147
0
148
0
                MOZ_ASSERT(a);
149
0
150
0
                // Report this edge to the visitor function.
151
0
                if (!handler(*this, origin, edge, &a->value(), first)) {
152
0
                    return false;
153
0
                }
154
0
155
0
                if (stopRequested) {
156
0
                    return true;
157
0
                }
158
0
159
0
                // Arrange to traverse this edge's referent's outgoing edges
160
0
                // later --- unless |handler| asked us not to.
161
0
                if (abandonRequested) {
162
0
                    // Skip the enqueue; reset flag for future iterations.
163
0
                    abandonRequested = false;
164
0
                } else if (first) {
165
0
                    if (!pending.append(edge.referent)) {
166
0
                        return false;
167
0
                    }
168
0
                }
169
0
            }
170
0
        }
171
0
172
0
        return true;
173
0
    }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::traverse()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::traverse()
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::traverse()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::traverse()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::traverse()
174
175
    // Stop traversal, and return true from |traverse| without visiting any
176
    // more nodes. Only |handler::operator()| should call this function; it
177
    // may do so to stop the traversal early, without returning false and
178
    // then making |traverse|'s caller disambiguate that result from a real
179
    // error.
180
0
    void stop() { stopRequested = true; }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::stop()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::stop()
181
182
    // Request that the current edge's referent's outgoing edges not be
183
    // traversed. This must be called the first time that referent is reached.
184
    // Other edges *to* that referent will still be traversed.
185
0
    void abandonReferent() { abandonRequested = true; }
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::abandonReferent()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::abandonReferent()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::abandonReferent()
186
187
    // The context with which we were constructed.
188
    JSContext* cx;
189
190
    // A map associating each node N that we have reached with a
191
    // Handler::NodeData, for |handler|'s use. This is public, so that
192
    // |handler| can access it to see the traversal thus far.
193
    using NodeMap = js::HashMap<Node, typename Handler::NodeData, js::DefaultHasher<Node>,
194
                                js::SystemAllocPolicy>;
195
    NodeMap visited;
196
197
  private:
198
    // Our handler object.
199
    Handler& handler;
200
201
    // A queue template. Appending and popping the front are constant time.
202
    // Wasted space is never more than some recent actual population plus the
203
    // current population.
204
    template <typename T>
205
    class Queue {
206
        js::Vector<T, 0, js::SystemAllocPolicy> head, tail;
207
        size_t frontIndex;
208
      public:
209
0
        Queue() : head(), tail(), frontIndex(0) { }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::Queue<JS::ubi::Node>::Queue()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::Queue<JS::ubi::Node>::Queue()
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::Queue<JS::ubi::Node>::Queue()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::Queue<JS::ubi::Node>::Queue()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::Queue<JS::ubi::Node>::Queue()
210
0
        bool empty() { return frontIndex >= head.length(); }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::Queue<JS::ubi::Node>::empty()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::Queue<JS::ubi::Node>::empty()
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::Queue<JS::ubi::Node>::empty()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::Queue<JS::ubi::Node>::empty()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::Queue<JS::ubi::Node>::empty()
211
0
        T& front() {
212
0
            MOZ_ASSERT(!empty());
213
0
            return head[frontIndex];
214
0
        }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::Queue<JS::ubi::Node>::front()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::Queue<JS::ubi::Node>::front()
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::Queue<JS::ubi::Node>::front()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::Queue<JS::ubi::Node>::front()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::Queue<JS::ubi::Node>::front()
215
0
        void popFront() {
216
0
            MOZ_ASSERT(!empty());
217
0
            frontIndex++;
218
0
            if (frontIndex >= head.length()) {
219
0
                head.clearAndFree();
220
0
                head.swap(tail);
221
0
                frontIndex = 0;
222
0
            }
223
0
        }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::Queue<JS::ubi::Node>::popFront()
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::Queue<JS::ubi::Node>::popFront()
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::Queue<JS::ubi::Node>::popFront()
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::Queue<JS::ubi::Node>::popFront()
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::Queue<JS::ubi::Node>::popFront()
224
0
        bool append(const T& elt) {
225
0
            return frontIndex == 0 ? head.append(elt) : tail.append(elt);
226
0
        }
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::ShortestPaths::Handler>::Queue<JS::ubi::Node>::append(JS::ubi::Node const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<JS::ubi::CensusHandler>::Queue<JS::ubi::Node>::append(JS::ubi::Node const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<mozilla::devtools::HeapSnapshotHandler>::Queue<JS::ubi::Node>::append(JS::ubi::Node const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<heaptools::FindPathHandler>::Queue<JS::ubi::Node>::append(JS::ubi::Node const&)
Unexecuted instantiation: JS::ubi::BreadthFirst<js::Debugger::ObjectQuery>::Queue<JS::ubi::Node>::append(JS::ubi::Node const&)
227
    };
228
229
    // A queue of nodes that we have reached, but whose outgoing edges we
230
    // have not yet traversed. Nodes reachable in fewer edges are enqueued
231
    // earlier.
232
    Queue<Node> pending;
233
234
    // True if our traverse function has been called.
235
    bool traversalBegun;
236
237
    // True if we've been asked to stop the traversal.
238
    bool stopRequested;
239
240
    // True if we've been asked to abandon the current edge's referent.
241
    bool abandonRequested;
242
};
243
244
} // namespace ubi
245
} // namespace JS
246
247
#endif // js_UbiNodeBreadthFirst_h