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
594k
  {
105
3.30M
    while (!verticesToTraverse.empty())
106
2.70M
    {
107
2.70M
      V v = std::move(verticesToTraverse.front());
108
2.70M
      verticesToTraverse.pop_front();
109
110
2.70M
      if (!visited.insert(v).second)
111
387k
        continue;
112
113
2.31M
      _forEachChild(v, [this](V _vertex) {
114
2.09M
        verticesToTraverse.emplace_back(std::move(_vertex));
115
2.09M
      });
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
132k
      _forEachChild(v, [this](V _vertex) {
114
132k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
132k
      });
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
197k
      _forEachChild(v, [this](V _vertex) {
114
197k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
197k
      });
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
170k
      _forEachChild(v, [this](V _vertex) {
114
170k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
170k
      });
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
170k
      _forEachChild(v, [this](V _vertex) {
114
170k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
170k
      });
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
197k
      _forEachChild(v, [this](V _vertex) {
114
197k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
197k
      });
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
25.1k
      _forEachChild(v, [this](V _vertex) {
114
25.1k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
25.1k
      });
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
759k
      _forEachChild(v, [this](V _vertex) {
114
759k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
759k
      });
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
197k
      _forEachChild(v, [this](V _vertex) {
114
197k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
197k
      });
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
117k
      _forEachChild(v, [this](V _vertex) {
114
117k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
117k
      });
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
2.90k
      _forEachChild(v, [this](V _vertex) {
114
2.90k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
2.90k
      });
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
120k
      _forEachChild(v, [this](V _vertex) {
114
120k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
120k
      });
116
2.31M
    }
117
594k
    return *this;
118
594k
  }
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
32.3k
  {
105
196k
    while (!verticesToTraverse.empty())
106
164k
    {
107
164k
      V v = std::move(verticesToTraverse.front());
108
164k
      verticesToTraverse.pop_front();
109
110
164k
      if (!visited.insert(v).second)
111
14.9k
        continue;
112
113
149k
      _forEachChild(v, [this](V _vertex) {
114
149k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
149k
      });
116
149k
    }
117
32.3k
    return *this;
118
32.3k
  }
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
41.2k
  {
105
280k
    while (!verticesToTraverse.empty())
106
238k
    {
107
238k
      V v = std::move(verticesToTraverse.front());
108
238k
      verticesToTraverse.pop_front();
109
110
238k
      if (!visited.insert(v).second)
111
40.0k
        continue;
112
113
198k
      _forEachChild(v, [this](V _vertex) {
114
198k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
198k
      });
116
198k
    }
117
41.2k
    return *this;
118
41.2k
  }
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
41.2k
  {
105
252k
    while (!verticesToTraverse.empty())
106
211k
    {
107
211k
      V v = std::move(verticesToTraverse.front());
108
211k
      verticesToTraverse.pop_front();
109
110
211k
      if (!visited.insert(v).second)
111
12.4k
        continue;
112
113
198k
      _forEachChild(v, [this](V _vertex) {
114
198k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
198k
      });
116
198k
    }
117
41.2k
    return *this;
118
41.2k
  }
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
41.2k
  {
105
252k
    while (!verticesToTraverse.empty())
106
211k
    {
107
211k
      V v = std::move(verticesToTraverse.front());
108
211k
      verticesToTraverse.pop_front();
109
110
211k
      if (!visited.insert(v).second)
111
12.4k
        continue;
112
113
198k
      _forEachChild(v, [this](V _vertex) {
114
198k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
198k
      });
116
198k
    }
117
41.2k
    return *this;
118
41.2k
  }
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
41.2k
  {
105
280k
    while (!verticesToTraverse.empty())
106
238k
    {
107
238k
      V v = std::move(verticesToTraverse.front());
108
238k
      verticesToTraverse.pop_front();
109
110
238k
      if (!visited.insert(v).second)
111
40.0k
        continue;
112
113
198k
      _forEachChild(v, [this](V _vertex) {
114
198k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
198k
      });
116
198k
    }
117
41.2k
    return *this;
118
41.2k
  }
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
8.33k
  {
105
41.8k
    while (!verticesToTraverse.empty())
106
33.4k
    {
107
33.4k
      V v = std::move(verticesToTraverse.front());
108
33.4k
      verticesToTraverse.pop_front();
109
110
33.4k
      if (!visited.insert(v).second)
111
4.60k
        continue;
112
113
28.8k
      _forEachChild(v, [this](V _vertex) {
114
28.8k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
28.8k
      });
116
28.8k
    }
117
8.33k
    return *this;
118
8.33k
  }
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
310k
  {
105
1.38M
    while (!verticesToTraverse.empty())
106
1.07M
    {
107
1.07M
      V v = std::move(verticesToTraverse.front());
108
1.07M
      verticesToTraverse.pop_front();
109
110
1.07M
      if (!visited.insert(v).second)
111
174k
        continue;
112
113
895k
      _forEachChild(v, [this](V _vertex) {
114
895k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
895k
      });
116
895k
    }
117
310k
    return *this;
118
310k
  }
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
20.7k
  {
105
259k
    while (!verticesToTraverse.empty())
106
238k
    {
107
238k
      V v = std::move(verticesToTraverse.front());
108
238k
      verticesToTraverse.pop_front();
109
110
238k
      if (!visited.insert(v).second)
111
40.0k
        continue;
112
113
198k
      _forEachChild(v, [this](V _vertex) {
114
198k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
198k
      });
116
198k
    }
117
20.7k
    return *this;
118
20.7k
  }
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
20.5k
  {
105
158k
    while (!verticesToTraverse.empty())
106
138k
    {
107
138k
      V v = std::move(verticesToTraverse.front());
108
138k
      verticesToTraverse.pop_front();
109
110
138k
      if (!visited.insert(v).second)
111
23.4k
        continue;
112
113
114k
      _forEachChild(v, [this](V _vertex) {
114
114k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
114k
      });
116
114k
    }
117
20.5k
    return *this;
118
20.5k
  }
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
13.2k
  {
105
28.9k
    while (!verticesToTraverse.empty())
106
15.7k
    {
107
15.7k
      V v = std::move(verticesToTraverse.front());
108
15.7k
      verticesToTraverse.pop_front();
109
110
15.7k
      if (!visited.insert(v).second)
111
1.40k
        continue;
112
113
14.3k
      _forEachChild(v, [this](V _vertex) {
114
14.3k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
14.3k
      });
116
14.3k
    }
117
13.2k
    return *this;
118
13.2k
  }
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
23.7k
  {
105
168k
    while (!verticesToTraverse.empty())
106
144k
    {
107
144k
      V v = std::move(verticesToTraverse.front());
108
144k
      verticesToTraverse.pop_front();
109
110
144k
      if (!visited.insert(v).second)
111
23.6k
        continue;
112
113
120k
      _forEachChild(v, [this](V _vertex) {
114
120k
        verticesToTraverse.emplace_back(std::move(_vertex));
115
120k
      });
116
120k
    }
117
23.7k
    return *this;
118
23.7k
  }
119
  void abort()
120
908
  {
121
908
    verticesToTraverse.clear();
122
908
  }
123
124
  std::list<V> verticesToTraverse;
125
  std::set<V> visited{};
126
};
127
128
}