Coverage Report

Created: 2022-08-24 06:38

/src/solidity/libsolutil/Algorithms.h
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
#pragma once
19
20
21
#include <functional>
22
#include <list>
23
#include <set>
24
25
namespace solidity::util
26
{
27
28
/**
29
 * Detector for cycles in directed graphs. It returns the first
30
 * vertex on the path towards a cycle or a nullptr if there is
31
 * no reachable cycle starting from a given vertex.
32
 */
33
template <typename V>
34
class CycleDetector
35
{
36
public:
37
  using Visitor = std::function<void(V const&, CycleDetector&, size_t)>;
38
39
  /// Initializes the cycle detector
40
  /// @param _visit function that is given the current vertex
41
  ///               and is supposed to call @a run on all
42
  ///               adjacent vertices.
43
  explicit CycleDetector(Visitor _visit):
44
    m_visit(std::move(_visit))
45
  {  }
46
47
  /// Recursively perform cycle detection starting
48
  /// (or continuing) with @param _vertex
49
  /// @returns the first vertex on the path towards a cycle from @a _vertex
50
  /// or nullptr if no cycle is reachable from @a _vertex.
51
  V const* run(V const& _vertex)
52
  {
53
    if (m_firstCycleVertex)
54
      return m_firstCycleVertex;
55
    if (m_processed.count(&_vertex))
56
      return nullptr;
57
    else if (m_processing.count(&_vertex))
58
      return m_firstCycleVertex = &_vertex;
59
    m_processing.insert(&_vertex);
60
61
    m_depth++;
62
    m_visit(_vertex, *this, m_depth);
63
    m_depth--;
64
    if (m_firstCycleVertex && m_depth == 1)
65
      m_firstCycleVertex = &_vertex;
66
67
    m_processing.erase(&_vertex);
68
    m_processed.insert(&_vertex);
69
    return m_firstCycleVertex;
70
  }
71
72
private:
73
  Visitor m_visit;
74
  std::set<V const*> m_processing;
75
  std::set<V const*> m_processed;
76
  size_t m_depth = 0;
77
  V const* m_firstCycleVertex = nullptr;
78
};
79
80
/**
81
 * Generic breadth first search.
82
 *
83
 * Note that V needs to be a comparable value type or a pointer.
84
 *
85
 * Example: Gather all (recursive) children in a graph starting at (and including) ``root``:
86
 *
87
 * Node const* root = ...;
88
 * std::set<Node const*> allNodes = BreadthFirstSearch<Node const*>{{root}}.run([](Node const* _node, auto&& _addChild) {
89
 *   // Potentially process ``_node``.
90
 *   for (Node const& _child: _node->children())
91
 *     // Potentially filter the children to be visited.
92
 *     _addChild(&_child);
93
 * }).visited;
94
 */
95
template<typename V>
96
struct BreadthFirstSearch
97
{
98
  /// Runs the breadth first search. The verticesToTraverse member of the struct needs to be initialized.
99
  /// @param _forEachChild is a callable of the form [...](V const& _node, auto&& _addChild) { ... }
100
  /// that is called for each visited node and is supposed to call _addChild(childNode) for every child
101
  /// node of _node.
102
  template<typename ForEachChild>
103
  BreadthFirstSearch& run(ForEachChild&& _forEachChild)
104
685k
  {
105
4.64M
    while (!verticesToTraverse.empty())
106
3.96M
    {
107
3.96M
      V v = std::move(verticesToTraverse.front());
108
3.96M
      verticesToTraverse.pop_front();
109
110
3.96M
      if (!visited.insert(v).second)
111
955k
        continue;
112
113
3.24M
      _forEachChild(v, [this](V _vertex) {
114
3.24M
        verticesToTraverse.emplace_back(std::move(_vertex));
115
3.24M
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::processEntryPoint(solidity::yul::CFG::BasicBlock const&)::$_4>(solidity::yul::StackLayoutGenerator::processEntryPoint(solidity::yul::CFG::BasicBlock const&)::$_4&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
230k
      _forEachChild(v, [this](V _vertex) {
114
230k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
230k
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::collectBackwardsJumps(solidity::yul::CFG::BasicBlock const&) const::$_10>(solidity::yul::StackLayoutGenerator::collectBackwardsJumps(solidity::yul::CFG::BasicBlock const&) const::$_10&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
246k
      _forEachChild(v, [this](V _vertex) {
114
246k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
246k
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::stitchConditionalJumps(solidity::yul::CFG::BasicBlock const&)::$_11>(solidity::yul::StackLayoutGenerator::stitchConditionalJumps(solidity::yul::CFG::BasicBlock const&)::$_11&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
212k
      _forEachChild(v, [this](V _vertex) {
114
212k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
212k
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::reportStackTooDeep(solidity::yul::CFG::BasicBlock const&) const::$_14>(solidity::yul::StackLayoutGenerator::reportStackTooDeep(solidity::yul::CFG::BasicBlock const&) const::$_14&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
212k
      _forEachChild(v, [this](V _vertex) {
114
212k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
212k
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_17>(solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_17&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
246k
      _forEachChild(v, [this](V _vertex) {
114
246k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
246k
      });
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_15::operator()(solidity::yul::CFG::BasicBlock const*, unsigned long) const::{lambda(solidity::yul::CFG::BasicBlock const*, auto:1)#1}>(solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_15::operator()(solidity::yul::CFG::BasicBlock const*, unsigned long) const::{lambda(solidity::yul::CFG::BasicBlock const*, auto:1)#1}&&)::{lambda(solidity::yul::CFG::BasicBlock const*)#1}::operator()(solidity::yul::CFG::BasicBlock const*) const
Line
Count
Source
113
28.2k
      _forEachChild(v, [this](V _vertex) {
114
28.2k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
28.2k
      });
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::cleanUnreachable(solidity::yul::CFG&)::$_16>((anonymous namespace)::cleanUnreachable(solidity::yul::CFG&)::$_16&&)::{lambda(solidity::yul::CFG::BasicBlock*)#1}::operator()(solidity::yul::CFG::BasicBlock*) const
Line
Count
Source
113
246k
      _forEachChild(v, [this](V _vertex) {
114
246k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
246k
      });
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_17::operator()(solidity::yul::CFG::BasicBlock*) const::{lambda(solidity::yul::CFG::BasicBlock*, auto:1)#1}>((anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_17::operator()(solidity::yul::CFG::BasicBlock*) const::{lambda(solidity::yul::CFG::BasicBlock*, auto:1)#1}&&)::{lambda(solidity::yul::CFG::BasicBlock*)#1}::operator()(solidity::yul::CFG::BasicBlock*) const
Line
Count
Source
113
168k
      _forEachChild(v, [this](V _vertex) {
114
168k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
168k
      });
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::FunctionCall*>::run<(anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_18>((anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_18&&)::{lambda(solidity::yul::CFG::FunctionCall*)#1}::operator()(solidity::yul::CFG::FunctionCall*) const
Line
Count
Source
113
7.79k
      _forEachChild(v, [this](V _vertex) {
114
7.79k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
7.79k
      });
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::markNeedsCleanStack(solidity::yul::CFG&)::$_20>((anonymous namespace)::markNeedsCleanStack(solidity::yul::CFG&)::$_20&&)::{lambda(solidity::yul::CFG::BasicBlock*)#1}::operator()(solidity::yul::CFG::BasicBlock*) const
Line
Count
Source
113
118k
      _forEachChild(v, [this](V _vertex) {
114
118k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
118k
      });
CircularReferencesPruner.cpp:solidity::util::BreadthFirstSearch<solidity::yul::YulString>::run<solidity::yul::CircularReferencesPruner::functionsCalledFromOutermostContext(solidity::yul::CallGraph const&)::$_0>(solidity::yul::CircularReferencesPruner::functionsCalledFromOutermostContext(solidity::yul::CallGraph const&)::$_0&&)::{lambda(solidity::yul::YulString)#1}::operator()(solidity::yul::YulString) const
Line
Count
Source
113
1.53M
      _forEachChild(v, [this](V _vertex) {
114
1.53M
        verticesToTraverse.emplace_back(std::move(_vertex));
115
1.53M
      });
116
3.00M
    }
117
685k
    return *this;
118
685k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::processEntryPoint(solidity::yul::CFG::BasicBlock const&)::$_4>(solidity::yul::StackLayoutGenerator::processEntryPoint(solidity::yul::CFG::BasicBlock const&)::$_4&&)
Line
Count
Source
104
37.8k
  {
105
306k
    while (!verticesToTraverse.empty())
106
268k
    {
107
268k
      V v = std::move(verticesToTraverse.front());
108
268k
      verticesToTraverse.pop_front();
109
110
268k
      if (!visited.insert(v).second)
111
32.7k
        continue;
112
113
235k
      _forEachChild(v, [this](V _vertex) {
114
235k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
235k
      });
116
235k
    }
117
37.8k
    return *this;
118
37.8k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::collectBackwardsJumps(solidity::yul::CFG::BasicBlock const&) const::$_10>(solidity::yul::StackLayoutGenerator::collectBackwardsJumps(solidity::yul::CFG::BasicBlock const&) const::$_10&&)
Line
Count
Source
104
56.6k
  {
105
359k
    while (!verticesToTraverse.empty())
106
302k
    {
107
302k
      V v = std::move(verticesToTraverse.front());
108
302k
      verticesToTraverse.pop_front();
109
110
302k
      if (!visited.insert(v).second)
111
51.3k
        continue;
112
113
251k
      _forEachChild(v, [this](V _vertex) {
114
251k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
251k
      });
116
251k
    }
117
56.6k
    return *this;
118
56.6k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::stitchConditionalJumps(solidity::yul::CFG::BasicBlock const&)::$_11>(solidity::yul::StackLayoutGenerator::stitchConditionalJumps(solidity::yul::CFG::BasicBlock const&)::$_11&&)
Line
Count
Source
104
56.6k
  {
105
326k
    while (!verticesToTraverse.empty())
106
269k
    {
107
269k
      V v = std::move(verticesToTraverse.front());
108
269k
      verticesToTraverse.pop_front();
109
110
269k
      if (!visited.insert(v).second)
111
18.1k
        continue;
112
113
251k
      _forEachChild(v, [this](V _vertex) {
114
251k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
251k
      });
116
251k
    }
117
56.6k
    return *this;
118
56.6k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::reportStackTooDeep(solidity::yul::CFG::BasicBlock const&) const::$_14>(solidity::yul::StackLayoutGenerator::reportStackTooDeep(solidity::yul::CFG::BasicBlock const&) const::$_14&&)
Line
Count
Source
104
56.6k
  {
105
326k
    while (!verticesToTraverse.empty())
106
269k
    {
107
269k
      V v = std::move(verticesToTraverse.front());
108
269k
      verticesToTraverse.pop_front();
109
110
269k
      if (!visited.insert(v).second)
111
18.1k
        continue;
112
113
251k
      _forEachChild(v, [this](V _vertex) {
114
251k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
251k
      });
116
251k
    }
117
56.6k
    return *this;
118
56.6k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_17>(solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_17&&)
Line
Count
Source
104
56.6k
  {
105
359k
    while (!verticesToTraverse.empty())
106
302k
    {
107
302k
      V v = std::move(verticesToTraverse.front());
108
302k
      verticesToTraverse.pop_front();
109
110
302k
      if (!visited.insert(v).second)
111
51.3k
        continue;
112
113
251k
      _forEachChild(v, [this](V _vertex) {
114
251k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
251k
      });
116
251k
    }
117
56.6k
    return *this;
118
56.6k
  }
StackLayoutGenerator.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock const*>::run<solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_15::operator()(solidity::yul::CFG::BasicBlock const*, unsigned long) const::{lambda(solidity::yul::CFG::BasicBlock const*, auto:1)#1}>(solidity::yul::StackLayoutGenerator::fillInJunk(solidity::yul::CFG::BasicBlock const&)::$_15::operator()(solidity::yul::CFG::BasicBlock const*, unsigned long) const::{lambda(solidity::yul::CFG::BasicBlock const*, auto:1)#1}&&)
Line
Count
Source
104
5.59k
  {
105
39.4k
    while (!verticesToTraverse.empty())
106
33.8k
    {
107
33.8k
      V v = std::move(verticesToTraverse.front());
108
33.8k
      verticesToTraverse.pop_front();
109
110
33.8k
      if (!visited.insert(v).second)
111
6.14k
        continue;
112
113
27.6k
      _forEachChild(v, [this](V _vertex) {
114
27.6k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
27.6k
      });
116
27.6k
    }
117
5.59k
    return *this;
118
5.59k
  }
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::cleanUnreachable(solidity::yul::CFG&)::$_16>((anonymous namespace)::cleanUnreachable(solidity::yul::CFG&)::$_16&&)
Line
Count
Source
104
25.9k
  {
105
328k
    while (!verticesToTraverse.empty())
106
302k
    {
107
302k
      V v = std::move(verticesToTraverse.front());
108
302k
      verticesToTraverse.pop_front();
109
110
302k
      if (!visited.insert(v).second)
111
51.3k
        continue;
112
113
251k
      _forEachChild(v, [this](V _vertex) {
114
251k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
251k
      });
116
251k
    }
117
25.9k
    return *this;
118
25.9k
  }
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_17::operator()(solidity::yul::CFG::BasicBlock*) const::{lambda(solidity::yul::CFG::BasicBlock*, auto:1)#1}>((anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_17::operator()(solidity::yul::CFG::BasicBlock*) const::{lambda(solidity::yul::CFG::BasicBlock*, auto:1)#1}&&)
Line
Count
Source
104
30.7k
  {
105
230k
    while (!verticesToTraverse.empty())
106
199k
    {
107
199k
      V v = std::move(verticesToTraverse.front());
108
199k
      verticesToTraverse.pop_front();
109
110
199k
      if (!visited.insert(v).second)
111
32.5k
        continue;
112
113
166k
      _forEachChild(v, [this](V _vertex) {
114
166k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
166k
      });
116
166k
    }
117
30.7k
    return *this;
118
30.7k
  }
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::FunctionCall*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::FunctionCall*>::run<(anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_18>((anonymous namespace)::markRecursiveCalls(solidity::yul::CFG&)::$_18&&)
Line
Count
Source
104
35.1k
  {
105
75.9k
    while (!verticesToTraverse.empty())
106
40.7k
    {
107
40.7k
      V v = std::move(verticesToTraverse.front());
108
40.7k
      verticesToTraverse.pop_front();
109
110
40.7k
      if (!visited.insert(v).second)
111
3.54k
        continue;
112
113
37.2k
      _forEachChild(v, [this](V _vertex) {
114
37.2k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
37.2k
      });
116
37.2k
    }
117
35.1k
    return *this;
118
35.1k
  }
ControlFlowGraphBuilder.cpp:solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>& solidity::util::BreadthFirstSearch<solidity::yul::CFG::BasicBlock*>::run<(anonymous namespace)::markNeedsCleanStack(solidity::yul::CFG&)::$_20>((anonymous namespace)::markNeedsCleanStack(solidity::yul::CFG&)::$_20&&)
Line
Count
Source
104
39.5k
  {
105
197k
    while (!verticesToTraverse.empty())
106
157k
    {
107
157k
      V v = std::move(verticesToTraverse.front());
108
157k
      verticesToTraverse.pop_front();
109
110
157k
      if (!visited.insert(v).second)
111
19.6k
        continue;
112
113
137k
      _forEachChild(v, [this](V _vertex) {
114
137k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
137k
      });
116
137k
    }
117
39.5k
    return *this;
118
39.5k
  }
CircularReferencesPruner.cpp:solidity::util::BreadthFirstSearch<solidity::yul::YulString>& solidity::util::BreadthFirstSearch<solidity::yul::YulString>::run<solidity::yul::CircularReferencesPruner::functionsCalledFromOutermostContext(solidity::yul::CallGraph const&)::$_0>(solidity::yul::CircularReferencesPruner::functionsCalledFromOutermostContext(solidity::yul::CallGraph const&)::$_0&&)
Line
Count
Source
104
284k
  {
105
2.09M
    while (!verticesToTraverse.empty())
106
1.81M
    {
107
1.81M
      V v = std::move(verticesToTraverse.front());
108
1.81M
      verticesToTraverse.pop_front();
109
110
1.81M
      if (!visited.insert(v).second)
111
670k
        continue;
112
113
1.14M
      _forEachChild(v, [this](V _vertex) {
114
1.14M
        verticesToTraverse.emplace_back(std::move(_vertex));
115
1.14M
      });
116
1.14M
    }
117
284k
    return *this;
118
284k
  }
119
  void abort()
120
1.64k
  {
121
1.64k
    verticesToTraverse.clear();
122
1.64k
  }
123
124
  std::list<V> verticesToTraverse;
125
  std::set<V> visited{};
126
};
127
128
}