| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef gc_FindSCCs_inl_h |
| #define gc_FindSCCs_inl_h |
| |
| #include "jsfriendapi.h" |
| |
| #include "gc/FindSCCs.h" |
| |
| namespace js { |
| namespace gc { |
| |
| template<class Node> |
| ComponentFinder<Node>::ComponentFinder(uintptr_t sl) |
| : clock(1), |
| stack(NULL), |
| firstComponent(NULL), |
| cur(NULL), |
| stackLimit(sl), |
| stackFull(false) |
| { |
| } |
| |
| template<class Node> |
| ComponentFinder<Node>::~ComponentFinder() |
| { |
| JS_ASSERT(!stack); |
| JS_ASSERT(!firstComponent); |
| } |
| |
| template<class Node> |
| void |
| ComponentFinder<Node>::addNode(Node *v) |
| { |
| if (v->gcDiscoveryTime == Undefined) { |
| JS_ASSERT(v->gcLowLink == Undefined); |
| processNode(v); |
| } |
| } |
| |
| template<class Node> |
| void |
| ComponentFinder<Node>::processNode(Node *v) |
| { |
| v->gcDiscoveryTime = clock; |
| v->gcLowLink = clock; |
| ++clock; |
| |
| v->gcNextGraphNode = stack; |
| stack = v; |
| |
| int stackDummy; |
| if (stackFull || !JS_CHECK_STACK_SIZE(stackLimit, &stackDummy)) { |
| stackFull = true; |
| return; |
| } |
| |
| Node *old = cur; |
| cur = v; |
| cur->findOutgoingEdges(*this); |
| cur = old; |
| |
| if (stackFull) |
| return; |
| |
| if (v->gcLowLink == v->gcDiscoveryTime) { |
| Node *nextComponent = firstComponent; |
| Node *w; |
| do { |
| JS_ASSERT(stack); |
| w = stack; |
| stack = w->gcNextGraphNode; |
| |
| /* |
| * Record that the element is no longer on the stack by setting the |
| * discovery time to a special value that's not Undefined. |
| */ |
| w->gcDiscoveryTime = Finished; |
| |
| /* Figure out which group we're in. */ |
| w->gcNextGraphComponent = nextComponent; |
| |
| /* |
| * Prepend the component to the beginning of the output list to |
| * reverse the list and achieve the desired order. |
| */ |
| w->gcNextGraphNode = firstComponent; |
| firstComponent = w; |
| } while (w != v); |
| } |
| } |
| |
| template<class Node> |
| void |
| ComponentFinder<Node>::addEdgeTo(Node *w) |
| { |
| if (w->gcDiscoveryTime == Undefined) { |
| processNode(w); |
| cur->gcLowLink = Min(cur->gcLowLink, w->gcLowLink); |
| } else if (w->gcDiscoveryTime != Finished) { |
| cur->gcLowLink = Min(cur->gcLowLink, w->gcDiscoveryTime); |
| } |
| } |
| |
| template<class Node> |
| Node * |
| ComponentFinder<Node>::getResultsList() |
| { |
| if (stackFull) { |
| /* |
| * All nodes after the stack overflow are in |stack|. Put them all in |
| * one big component of their own. |
| */ |
| Node *firstGoodComponent = firstComponent; |
| for (Node *v = stack; v; v = stack) { |
| stack = v->gcNextGraphNode; |
| v->gcNextGraphComponent = firstGoodComponent; |
| v->gcNextGraphNode = firstComponent; |
| firstComponent = v; |
| } |
| stackFull = false; |
| } |
| |
| JS_ASSERT(!stack); |
| |
| Node *result = firstComponent; |
| firstComponent = NULL; |
| |
| for (Node *v = result; v; v = v->gcNextGraphNode) { |
| v->gcDiscoveryTime = Undefined; |
| v->gcLowLink = Undefined; |
| } |
| |
| return result; |
| } |
| |
| template<class Node> |
| /* static */ void |
| ComponentFinder<Node>::mergeGroups(Node *first) |
| { |
| for (Node *v = first; v; v = v->gcNextGraphNode) |
| v->gcNextGraphComponent = NULL; |
| } |
| |
| } /* namespace gc */ |
| } /* namespace js */ |
| |
| #endif /* gc_FindSCCs_inl_h */ |