1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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/. */
7 #ifndef js_UbiNodePostOrder_h
8 #define js_UbiNodePostOrder_h
10 #include "mozilla/Attributes.h"
14 #include "js/UbiNode.h"
15 #include "js/Vector.h"
18 class SystemAllocPolicy
;
22 class AutoCheckCannotGC
;
27 * A post-order depth-first traversal of `ubi::Node` graphs.
29 * No GC may occur while an instance of `PostOrder` is live.
31 * The `NodeVisitor` type provided to `PostOrder::traverse` must have the
34 * bool operator()(Node& node)
36 * The node visitor method. This method is called once for each `node`
37 * reachable from the start set in post-order.
39 * This visitor function should return true on success, or false if an error
40 * occurs. A false return value terminates the traversal immediately, and
41 * causes `PostOrder::traverse` to return false.
43 * The `EdgeVisitor` type provided to `PostOrder::traverse` must have the
46 * bool operator()(Node& origin, Edge& edge)
48 * The edge visitor method. This method is called once for each outgoing
49 * `edge` from `origin` that is reachable from the start set.
51 * NB: UNLIKE NODES, THERE IS NO GUARANTEED ORDER IN WHICH EDGES AND THEIR
52 * ORIGINS ARE VISITED!
54 * This visitor function should return true on success, or false if an error
55 * occurs. A false return value terminates the traversal immediately, and
56 * causes `PostOrder::traverse` to return false.
60 struct OriginAndEdges
{
64 OriginAndEdges(const Node
& node
, EdgeVector
&& edges
)
65 : origin(node
), edges(std::move(edges
)) {}
67 OriginAndEdges(const OriginAndEdges
& rhs
) = delete;
68 OriginAndEdges
& operator=(const OriginAndEdges
& rhs
) = delete;
70 OriginAndEdges(OriginAndEdges
&& rhs
)
71 : origin(rhs
.origin
), edges(std::move(rhs
.edges
)) {
72 MOZ_ASSERT(&rhs
!= this, "self-move disallowed");
75 OriginAndEdges
& operator=(OriginAndEdges
&& rhs
) {
76 this->~OriginAndEdges();
77 new (this) OriginAndEdges(std::move(rhs
));
82 using Stack
= js::Vector
<OriginAndEdges
, 256, js::SystemAllocPolicy
>;
83 using Set
= js::HashSet
<Node
, js::DefaultHasher
<Node
>, js::SystemAllocPolicy
>;
93 [[nodiscard
]] bool fillEdgesFromRange(EdgeVector
& edges
,
94 js::UniquePtr
<EdgeRange
>& range
) {
96 for (; !range
->empty(); range
->popFront()) {
97 if (!edges
.append(std::move(range
->front()))) {
104 [[nodiscard
]] bool pushForTraversing(const Node
& node
) {
106 auto range
= node
.edges(cx
, /* wantNames */ false);
107 return range
&& fillEdgesFromRange(edges
, range
) &&
108 stack
.append(OriginAndEdges(node
, std::move(edges
)));
112 // Construct a post-order traversal object.
114 // The traversal asserts that no GC happens in its runtime during its
115 // lifetime via the `AutoCheckCannotGC&` parameter. We do nothing with it,
116 // other than require it to exist with a lifetime that encloses our own.
117 PostOrder(JSContext
* cx
, AutoCheckCannotGC
&)
128 // Add `node` as a starting point for the traversal. You may add
129 // as many starting points as you like. Returns false on OOM.
130 [[nodiscard
]] bool addStart(const Node
& node
) {
131 if (!seen
.put(node
)) {
134 return pushForTraversing(node
);
137 // Traverse the graph in post-order, starting with the set of nodes passed
138 // to `addStart` and applying `onNode::operator()` for each node in the
139 // graph and `onEdge::operator()` for each edge in the graph, as described
142 // This should be called only once per instance of this class.
144 // Return false on OOM or error return from `onNode::operator()` or
145 // `onEdge::operator()`.
146 template <typename NodeVisitor
, typename EdgeVisitor
>
147 [[nodiscard
]] bool traverse(NodeVisitor onNode
, EdgeVisitor onEdge
) {
149 MOZ_ASSERT(!traversed
, "Can only traverse() once!");
153 while (!stack
.empty()) {
154 auto& origin
= stack
.back().origin
;
155 auto& edges
= stack
.back().edges
;
158 if (!onNode(origin
)) {
165 Edge edge
= std::move(edges
.back());
168 if (!onEdge(origin
, edge
)) {
172 auto ptr
= seen
.lookupForAdd(edge
.referent
);
173 // We've already seen this node, don't follow its edges.
178 // Mark the referent as seen and follow its edges.
179 if (!seen
.add(ptr
, edge
.referent
) || !pushForTraversing(edge
.referent
)) {
191 #endif // js_UbiNodePostOrder_h