Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/UbiNodePostOrder.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_UbiNodePostOrder_h
8
#define js_UbiNodePostOrder_h
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/Maybe.h"
12
#include "mozilla/Move.h"
13
14
#include "js/AllocPolicy.h"
15
#include "js/UbiNode.h"
16
#include "js/Utility.h"
17
#include "js/Vector.h"
18
19
namespace JS {
20
namespace ubi {
21
22
/**
23
 * A post-order depth-first traversal of `ubi::Node` graphs.
24
 *
25
 * No GC may occur while an instance of `PostOrder` is live.
26
 *
27
 * The `NodeVisitor` type provided to `PostOrder::traverse` must have the
28
 * following member:
29
 *
30
 *   bool operator()(Node& node)
31
 *
32
 *     The node visitor method. This method is called once for each `node`
33
 *     reachable from the start set in post-order.
34
 *
35
 *     This visitor function should return true on success, or false if an error
36
 *     occurs. A false return value terminates the traversal immediately, and
37
 *     causes `PostOrder::traverse` to return false.
38
 *
39
 * The `EdgeVisitor` type provided to `PostOrder::traverse` must have the
40
 * following member:
41
 *
42
 *   bool operator()(Node& origin, Edge& edge)
43
 *
44
 *     The edge visitor method. This method is called once for each outgoing
45
 *     `edge` from `origin` that is reachable from the start set.
46
 *
47
 *     NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR
48
 *     ORIGINS ARE VISITED!
49
 *
50
 *     This visitor function should return true on success, or false if an error
51
 *     occurs. A false return value terminates the traversal immediately, and
52
 *     causes `PostOrder::traverse` to return false.
53
 */
54
struct PostOrder {
55
  private:
56
    struct OriginAndEdges {
57
        Node                 origin;
58
        EdgeVector           edges;
59
60
        OriginAndEdges(const Node& node, EdgeVector&& edges)
61
          : origin(node)
62
          , edges(std::move(edges))
63
0
        { }
64
65
        OriginAndEdges(const OriginAndEdges& rhs) = delete;
66
        OriginAndEdges& operator=(const OriginAndEdges& rhs) = delete;
67
68
        OriginAndEdges(OriginAndEdges&& rhs)
69
          : origin(rhs.origin)
70
          , edges(std::move(rhs.edges))
71
0
        {
72
0
            MOZ_ASSERT(&rhs != this, "self-move disallowed");
73
0
        }
74
75
0
        OriginAndEdges& operator=(OriginAndEdges&& rhs) {
76
0
            this->~OriginAndEdges();
77
0
            new (this) OriginAndEdges(std::move(rhs));
78
0
            return *this;
79
0
        }
80
    };
81
82
    using Stack = js::Vector<OriginAndEdges, 256, js::SystemAllocPolicy>;
83
    using Set = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
84
85
    JSContext*               cx;
86
    Set                      seen;
87
    Stack                    stack;
88
#ifdef DEBUG
89
    bool                     traversed;
90
#endif
91
92
  private:
93
0
    MOZ_MUST_USE bool fillEdgesFromRange(EdgeVector& edges, js::UniquePtr<EdgeRange>& range) {
94
0
        MOZ_ASSERT(range);
95
0
        for ( ; !range->empty(); range->popFront()) {
96
0
            if (!edges.append(std::move(range->front()))) {
97
0
                return false;
98
0
            }
99
0
        }
100
0
        return true;
101
0
    }
102
103
0
    MOZ_MUST_USE bool pushForTraversing(const Node& node) {
104
0
        EdgeVector edges;
105
0
        auto range = node.edges(cx, /* wantNames */ false);
106
0
        return range &&
107
0
            fillEdgesFromRange(edges, range) &&
108
0
            stack.append(OriginAndEdges(node, std::move(edges)));
109
0
    }
110
111
112
  public:
113
    // Construct a post-order traversal object.
114
    //
115
    // The traversal asserts that no GC happens in its runtime during its
116
    // lifetime via the `AutoCheckCannotGC&` parameter. We do nothing with it,
117
    // other than require it to exist with a lifetime that encloses our own.
118
    PostOrder(JSContext* cx, AutoCheckCannotGC&)
119
      : cx(cx)
120
      , seen()
121
      , stack()
122
#ifdef DEBUG
123
      , traversed(false)
124
#endif
125
0
    { }
126
127
    // Add `node` as a starting point for the traversal. You may add
128
    // as many starting points as you like. Returns false on OOM.
129
0
    MOZ_MUST_USE bool addStart(const Node& node) {
130
0
        if (!seen.put(node)) {
131
0
            return false;
132
0
        }
133
0
        return pushForTraversing(node);
134
0
    }
135
136
    // Traverse the graph in post-order, starting with the set of nodes passed
137
    // to `addStart` and applying `onNode::operator()` for each node in the
138
    // graph and `onEdge::operator()` for each edge in the graph, as described
139
    // above.
140
    //
141
    // This should be called only once per instance of this class.
142
    //
143
    // Return false on OOM or error return from `onNode::operator()` or
144
    // `onEdge::operator()`.
145
    template<typename NodeVisitor, typename EdgeVisitor>
146
0
    MOZ_MUST_USE bool traverse(NodeVisitor onNode, EdgeVisitor onEdge) {
147
#ifdef DEBUG
148
        MOZ_ASSERT(!traversed, "Can only traverse() once!");
149
        traversed = true;
150
#endif
151
152
0
        while (!stack.empty()) {
153
0
            auto& origin = stack.back().origin;
154
0
            auto& edges = stack.back().edges;
155
0
156
0
            if (edges.empty()) {
157
0
                if (!onNode(origin)) {
158
0
                    return false;
159
0
                }
160
0
                stack.popBack();
161
0
                continue;
162
0
            }
163
0
164
0
            Edge edge = std::move(edges.back());
165
0
            edges.popBack();
166
0
167
0
            if (!onEdge(origin, edge)) {
168
0
                return false;
169
0
            }
170
0
171
0
            auto ptr = seen.lookupForAdd(edge.referent);
172
0
            // We've already seen this node, don't follow its edges.
173
0
            if (ptr) {
174
0
                continue;
175
0
            }
176
0
177
0
            // Mark the referent as seen and follow its edges.
178
0
            if (!seen.add(ptr, edge.referent) ||
179
0
                !pushForTraversing(edge.referent))
180
0
            {
181
0
                return false;
182
0
            }
183
0
        }
184
0
185
0
        return true;
186
0
    }
187
};
188
189
} // namespace ubi
190
} // namespace JS
191
192
#endif // js_UbiNodePostOrder_h