source/StronglyConnectedComponents.cpp (68 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include <unordered_map> #include <unordered_set> #include <mariana-trench/Methods.h> #include <mariana-trench/StronglyConnectedComponents.h> namespace marianatrench { namespace { /* * Tarjan's algorithm to compute strongly connected components. * https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm */ class StronglyConnectedComponentsBuilder final { public: explicit StronglyConnectedComponentsBuilder( const Methods& methods, const Dependencies& dependencies, std::vector<std::vector<const Method*>>& components) : methods_(methods), dependencies_(dependencies), components_(components) {} void build() { for (const auto* method : methods_) { if (index_.count(method) == 0) { process(method); } } std::reverse(components_.begin(), components_.end()); } private: void process(const Method* method) { index_[method] = current_index_; lowlink_[method] = current_index_; current_index_++; stack_.push_back(method); on_stack_.insert(method); for (const auto* caller : dependencies_.dependencies(method)) { if (index_.count(caller) == 0) { process(caller); lowlink_[method] = std::min(lowlink_[method], lowlink_[caller]); } else if (on_stack_.count(caller) > 0) { lowlink_[method] = std::min(lowlink_[method], index_[caller]); } } if (lowlink_[method] == index_[method]) { // Found a strongly connected component. std::vector<const Method*> component; const Method* other_method; do { other_method = stack_.back(); stack_.pop_back(); on_stack_.erase(other_method); component.push_back(other_method); } while (method != other_method); components_.push_back(std::move(component)); } } private: const Methods& methods_; const Dependencies& dependencies_; std::vector<std::vector<const Method*>>& components_; // State of the algorithm. std::size_t current_index_ = 0; std::vector<const Method*> stack_; std::unordered_map<const Method*, std::size_t> index_; std::unordered_map<const Method*, std::size_t> lowlink_; std::unordered_set<const Method*> on_stack_; }; } // namespace StronglyConnectedComponents::StronglyConnectedComponents( const Methods& methods, const Dependencies& dependencies) { StronglyConnectedComponentsBuilder(methods, dependencies, components_) .build(); } } // namespace marianatrench