Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include <stdarg.h>
9 #include <string.h>
11 #include "gc/FindSCCs.h"
12 #include "jsapi-tests/tests.h"
14 static const unsigned MaxVertices = 10;
16 using js::gc::GraphNodeBase;
17 using js::gc::ComponentFinder;
19 struct TestNode : public GraphNodeBase<TestNode>
20 {
21 unsigned index;
22 bool hasEdge[MaxVertices];
24 void findOutgoingEdges(ComponentFinder<TestNode> &finder);
25 };
27 static TestNode Vertex[MaxVertices];
29 void
30 TestNode::findOutgoingEdges(ComponentFinder<TestNode> &finder)
31 {
32 for (unsigned i = 0; i < MaxVertices; ++i) {
33 if (hasEdge[i])
34 finder.addEdgeTo(&Vertex[i]);
35 }
36 }
38 BEGIN_TEST(testFindSCCs)
39 {
40 // no vertices
42 setup(0);
43 run();
44 CHECK(end());
46 // no edges
48 setup(1);
49 run();
50 CHECK(group(0, -1));
51 CHECK(end());
53 setup(3);
54 run();
55 CHECK(group(2, -1));
56 CHECK(group(1, -1));
57 CHECK(group(0, -1));
58 CHECK(end());
60 // linear
62 setup(3);
63 edge(0, 1);
64 edge(1, 2);
65 run();
66 CHECK(group(0, -1));
67 CHECK(group(1, -1));
68 CHECK(group(2, -1));
69 CHECK(end());
71 // tree
73 setup(3);
74 edge(0, 1);
75 edge(0, 2);
76 run();
77 CHECK(group(0, -1));
78 CHECK(group(2, -1));
79 CHECK(group(1, -1));
80 CHECK(end());
82 // cycles
84 setup(3);
85 edge(0, 1);
86 edge(1, 2);
87 edge(2, 0);
88 run();
89 CHECK(group(0, 1, 2, -1));
90 CHECK(end());
92 setup(4);
93 edge(0, 1);
94 edge(1, 2);
95 edge(2, 1);
96 edge(2, 3);
97 run();
98 CHECK(group(0, -1));
99 CHECK(group(1, 2, -1));
100 CHECK(group(3, -1));
101 CHECK(end());
103 // remaining
105 setup(2);
106 edge(0, 1);
107 run();
108 CHECK(remaining(0, 1, -1));
109 CHECK(end());
111 setup(2);
112 edge(0, 1);
113 run();
114 CHECK(group(0, -1));
115 CHECK(remaining(1, -1));
116 CHECK(end());
118 setup(2);
119 edge(0, 1);
120 run();
121 CHECK(group(0, -1));
122 CHECK(group(1, -1));
123 CHECK(remaining(-1));
124 CHECK(end());
126 return true;
127 }
129 unsigned vertex_count;
130 ComponentFinder<TestNode> *finder;
131 TestNode *resultsList;
133 void setup(unsigned count)
134 {
135 vertex_count = count;
136 for (unsigned i = 0; i < MaxVertices; ++i) {
137 TestNode &v = Vertex[i];
138 v.gcNextGraphNode = nullptr;
139 v.index = i;
140 memset(&v.hasEdge, 0, sizeof(v.hasEdge));
141 }
142 }
144 void edge(unsigned src_index, unsigned dest_index)
145 {
146 Vertex[src_index].hasEdge[dest_index] = true;
147 }
149 void run()
150 {
151 finder = new ComponentFinder<TestNode>(rt->mainThread.nativeStackLimit[js::StackForSystemCode]);
152 for (unsigned i = 0; i < vertex_count; ++i)
153 finder->addNode(&Vertex[i]);
154 resultsList = finder->getResultsList();
155 }
157 bool group(int vertex, ...)
158 {
159 TestNode *v = resultsList;
161 va_list ap;
162 va_start(ap, vertex);
163 while (vertex != -1) {
164 CHECK(v != nullptr);
165 CHECK(v->index == unsigned(vertex));
166 v = v->nextNodeInGroup();
167 vertex = va_arg(ap, int);
168 }
169 va_end(ap);
171 CHECK(v == nullptr);
172 resultsList = resultsList->nextGroup();
173 return true;
174 }
176 bool remaining(int vertex, ...)
177 {
178 TestNode *v = resultsList;
180 va_list ap;
181 va_start(ap, vertex);
182 while (vertex != -1) {
183 CHECK(v != nullptr);
184 CHECK(v->index == unsigned(vertex));
185 v = (TestNode *)v->gcNextGraphNode;
186 vertex = va_arg(ap, int);
187 }
188 va_end(ap);
190 CHECK(v == nullptr);
191 resultsList = nullptr;
192 return true;
193 }
195 bool end()
196 {
197 CHECK(resultsList == nullptr);
199 delete finder;
200 finder = nullptr;
201 return true;
202 }
203 END_TEST(testFindSCCs)
205 struct TestNode2 : public GraphNodeBase<TestNode2>
206 {
207 TestNode2 *edge;
209 TestNode2() :
210 edge(nullptr)
211 {
212 }
214 void
215 findOutgoingEdges(ComponentFinder<TestNode2> &finder) {
216 if (edge)
217 finder.addEdgeTo(edge);
218 }
219 };
221 BEGIN_TEST(testFindSCCsStackLimit)
222 {
223 /*
224 * Test what happens if recusion causes the stack to become full while
225 * traversing the graph.
226 *
227 * The test case is a large number of vertices, almost all of which are
228 * arranged in a linear chain. The last few are left unlinked to exercise
229 * adding vertices after the stack full condition has already been detected.
230 *
231 * Such an arrangement with no cycles would normally result in one group for
232 * each vertex, but since the stack is exhasted in processing a single group
233 * is returned containing all the vertices.
234 */
235 const unsigned max = 1000000;
236 const unsigned initial = 10;
238 TestNode2 *vertices = new TestNode2[max]();
239 for (unsigned i = initial; i < (max - 10); ++i)
240 vertices[i].edge = &vertices[i + 1];
242 ComponentFinder<TestNode2> finder(rt->mainThread.nativeStackLimit[js::StackForSystemCode]);
243 for (unsigned i = 0; i < max; ++i)
244 finder.addNode(&vertices[i]);
246 TestNode2 *r = finder.getResultsList();
247 CHECK(r);
248 TestNode2 *v = r;
250 unsigned count = 0;
251 while (v) {
252 ++count;
253 v = v->nextNodeInGroup();
254 }
255 CHECK(count == max - initial);
257 count = 0;
258 v = r->nextGroup();
259 while (v) {
260 ++count;
261 CHECK(!v->nextNodeInGroup());
262 v = v->nextGroup();
263 }
265 delete [] vertices;
266 return true;
267 }
268 END_TEST(testFindSCCsStackLimit)