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/. */
10 #include "mozilla/Assertions.h" // MOZ_ASSERT
12 #include <algorithm> // std::min
14 #include "js/AllocPolicy.h" // js::SystemAllocPolicy
15 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
16 #include "js/HashTable.h" // js::HashSet, js::DefaultHasher
21 template <typename Node
>
22 struct GraphNodeBase
{
24 js::HashSet
<Node
*, js::DefaultHasher
<Node
*>, js::SystemAllocPolicy
>;
27 Node
* gcNextGraphNode
= nullptr;
28 Node
* gcNextGraphComponent
= nullptr;
29 unsigned gcDiscoveryTime
= 0;
30 unsigned gcLowLink
= 0;
32 Node
* nextNodeInGroup() const {
33 if (gcNextGraphNode
&&
34 gcNextGraphNode
->gcNextGraphComponent
== gcNextGraphComponent
) {
35 return gcNextGraphNode
;
40 Node
* nextGroup() const { return gcNextGraphComponent
; }
44 * Find the strongly connected components of a graph using Tarjan's algorithm,
45 * and return them in topological order.
47 * Nodes derive from GraphNodeBase and add target edge pointers to
48 * sourceNode.gcGraphEdges to describe the graph:
50 * struct MyGraphNode : public GraphNodeBase<MyGraphNode>
55 * MyGraphNode node1, node2, node3;
56 * node1.gcGraphEdges.put(node2); // Error checking elided.
57 * node2.gcGraphEdges.put(node3);
58 * node3.gcGraphEdges.put(node2);
60 * ComponentFinder<MyGraphNode> finder;
61 * finder.addNode(node1);
62 * finder.addNode(node2);
63 * finder.addNode(node3);
64 * MyGraphNode* result = finder.getResultsList();
67 template <typename Node
>
68 class ComponentFinder
{
70 explicit ComponentFinder(JSContext
* cx
) : cx(cx
) {}
74 MOZ_ASSERT(!firstComponent
);
77 /* Forces all nodes to be added to a single component. */
78 void useOneComponent() { stackFull
= true; }
80 void addNode(Node
* v
) {
81 if (v
->gcDiscoveryTime
== Undefined
) {
82 MOZ_ASSERT(v
->gcLowLink
== Undefined
);
87 Node
* getResultsList() {
90 * All nodes after the stack overflow are in |stack|. Put them all in
91 * one big component of their own.
93 Node
* firstGoodComponent
= firstComponent
;
94 for (Node
* v
= stack
; v
; v
= stack
) {
95 stack
= v
->gcNextGraphNode
;
96 v
->gcNextGraphComponent
= firstGoodComponent
;
97 v
->gcNextGraphNode
= firstComponent
;
105 Node
* result
= firstComponent
;
106 firstComponent
= nullptr;
108 for (Node
* v
= result
; v
; v
= v
->gcNextGraphNode
) {
109 v
->gcDiscoveryTime
= Undefined
;
110 v
->gcLowLink
= Undefined
;
116 static void mergeGroups(Node
* first
) {
117 for (Node
* v
= first
; v
; v
= v
->gcNextGraphNode
) {
118 v
->gcNextGraphComponent
= nullptr;
123 // Constant used to indicate an unprocessed vertex.
124 static const unsigned Undefined
= 0;
126 // Constant used to indicate a processed vertex that is no longer on the
128 static const unsigned Finished
= (unsigned)-1;
130 void addEdgeTo(Node
* w
) {
131 if (w
->gcDiscoveryTime
== Undefined
) {
133 cur
->gcLowLink
= std::min(cur
->gcLowLink
, w
->gcLowLink
);
134 } else if (w
->gcDiscoveryTime
!= Finished
) {
135 cur
->gcLowLink
= std::min(cur
->gcLowLink
, w
->gcDiscoveryTime
);
139 void processNode(Node
* v
) {
140 v
->gcDiscoveryTime
= clock
;
141 v
->gcLowLink
= clock
;
144 v
->gcNextGraphNode
= stack
;
151 AutoCheckRecursionLimit
recursion(cx
);
152 if (!recursion
.checkSystemDontReport(cx
)) {
159 for (auto r
= cur
->gcGraphEdges
.all(); !r
.empty(); r
.popFront()) {
160 addEdgeTo(r
.front());
168 if (v
->gcLowLink
== v
->gcDiscoveryTime
) {
169 Node
* nextComponent
= firstComponent
;
174 stack
= w
->gcNextGraphNode
;
177 * Record that the element is no longer on the stack by setting the
178 * discovery time to a special value that's not Undefined.
180 w
->gcDiscoveryTime
= Finished
;
182 /* Figure out which group we're in. */
183 w
->gcNextGraphComponent
= nextComponent
;
186 * Prepend the component to the beginning of the output list to
187 * reverse the list and achieve the desired order.
189 w
->gcNextGraphNode
= firstComponent
;
197 Node
* stack
= nullptr;
198 Node
* firstComponent
= nullptr;
201 bool stackFull
= false;
207 #endif /* gc_FindSCCs_h */