/src/solidity/libsolidity/analysis/ControlFlowGraph.h
Line | Count | Source (jump to first uncovered line) |
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 | | |
19 | | #pragma once |
20 | | |
21 | | #include <libsolidity/ast/AST.h> |
22 | | #include <libsolidity/ast/ASTVisitor.h> |
23 | | #include <liblangutil/ErrorReporter.h> |
24 | | #include <liblangutil/EVMVersion.h> |
25 | | #include <liblangutil/SourceLocation.h> |
26 | | |
27 | | #include <map> |
28 | | #include <memory> |
29 | | #include <stack> |
30 | | #include <utility> |
31 | | #include <vector> |
32 | | |
33 | | namespace solidity::frontend |
34 | | { |
35 | | |
36 | | /** |
37 | | * Occurrence of a variable in a block of control flow. |
38 | | * Stores the declaration of the referenced variable, the |
39 | | * kind of the occurrence and possibly the source location |
40 | | * at which it occurred. |
41 | | */ |
42 | | class VariableOccurrence |
43 | | { |
44 | | public: |
45 | | enum class Kind |
46 | | { |
47 | | Declaration, |
48 | | Access, |
49 | | Return, |
50 | | Assignment, |
51 | | InlineAssembly |
52 | | }; |
53 | | VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> _occurrence = {}): |
54 | | m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(std::move(_occurrence)) |
55 | 1.92M | { |
56 | 1.92M | } |
57 | | |
58 | | /// Defines a deterministic order on variable occurrences. |
59 | | bool operator<(VariableOccurrence const& _rhs) const |
60 | 883k | { |
61 | 883k | if (m_occurrence && _rhs.m_occurrence) |
62 | 883k | { |
63 | 883k | if (*m_occurrence < *_rhs.m_occurrence) return true; |
64 | 330k | if (*_rhs.m_occurrence < *m_occurrence) return false; |
65 | 330k | } |
66 | 0 | else if (_rhs.m_occurrence) |
67 | 0 | return true; |
68 | 0 | else if (m_occurrence) |
69 | 0 | return false; |
70 | | |
71 | 4.68k | using KindCompareType = std::underlying_type<VariableOccurrence::Kind>::type; |
72 | 4.68k | return |
73 | 4.68k | std::make_pair(m_declaration.id(), static_cast<KindCompareType>(m_occurrenceKind)) < |
74 | 4.68k | std::make_pair(_rhs.m_declaration.id(), static_cast<KindCompareType>(_rhs.m_occurrenceKind)); |
75 | 883k | } |
76 | | |
77 | 1.97M | VariableDeclaration const& declaration() const { return m_declaration; } |
78 | 1.92M | Kind kind() const { return m_occurrenceKind; } |
79 | 46.9k | std::optional<langutil::SourceLocation> const& occurrence() const { return m_occurrence; } |
80 | | private: |
81 | | /// Declaration of the occurring variable. |
82 | | VariableDeclaration const& m_declaration; |
83 | | /// Kind of occurrence. |
84 | | Kind m_occurrenceKind = Kind::Access; |
85 | | /// Source location at which the variable occurred, if available (may be nullptr). |
86 | | std::optional<langutil::SourceLocation> m_occurrence; |
87 | | }; |
88 | | |
89 | | /** |
90 | | * Node of the Control Flow Graph. |
91 | | * The control flow is a directed graph connecting control flow blocks. |
92 | | * An arc between two nodes indicates that the control flow can possibly |
93 | | * move from its start node to its end node during execution. |
94 | | */ |
95 | | struct CFGNode |
96 | | { |
97 | | /// Entry nodes. All CFG nodes from which control flow may move into this node. |
98 | | std::vector<CFGNode*> entries; |
99 | | /// Exit nodes. All CFG nodes to which control flow may continue after this node. |
100 | | std::vector<CFGNode*> exits; |
101 | | /// Function call done by this node |
102 | | FunctionCall const* functionCall = nullptr; |
103 | | |
104 | | /// Variable occurrences in the node. |
105 | | std::vector<VariableOccurrence> variableOccurrences; |
106 | | // Source location of this control flow block. |
107 | | langutil::SourceLocation location; |
108 | | }; |
109 | | |
110 | | /** Describes the control flow of a function. */ |
111 | | struct FunctionFlow |
112 | | { |
113 | 39.6k | virtual ~FunctionFlow() = default; |
114 | | |
115 | | /// Entry node. Control flow of the function starts here. |
116 | | /// This node is empty and does not have any entries. |
117 | | CFGNode* entry = nullptr; |
118 | | /// Exit node. All non-reverting control flow of the function ends here. |
119 | | /// This node is empty and does not have any exits, but may have multiple entries |
120 | | /// (e.g. all return statements of the function). |
121 | | CFGNode* exit = nullptr; |
122 | | /// Revert node. Control flow of the function in case of revert. |
123 | | /// This node is empty and does not have any exits, but may have multiple entries |
124 | | /// (e.g. all assert, require, revert and throw statements). |
125 | | CFGNode* revert = nullptr; |
126 | | /// Transaction return node. Destination node for inline assembly "return" calls. |
127 | | /// This node is empty and does not have any exits, but may have multiple entries |
128 | | /// (e.g. all inline assembly return calls). |
129 | | CFGNode* transactionReturn = nullptr; |
130 | | }; |
131 | | |
132 | | class CFG: private ASTConstVisitor |
133 | | { |
134 | | public: |
135 | | struct FunctionContractTuple |
136 | | { |
137 | | ContractDefinition const* contract = nullptr; |
138 | | FunctionDefinition const* function = nullptr; |
139 | | |
140 | | // Use AST ids for comparison to keep a deterministic order in the |
141 | | // containers using this struct |
142 | | bool operator<(FunctionContractTuple const& _other) const |
143 | 2.39M | { |
144 | 2.39M | return |
145 | 2.39M | std::make_pair(contract ? contract->id() : -1, function->id()) < |
146 | 2.39M | std::make_pair(_other.contract ? _other.contract->id() : -1, _other.function->id()); |
147 | 2.39M | } |
148 | | }; |
149 | 3.60k | explicit CFG(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} |
150 | | |
151 | | bool constructFlow(ASTNode const& _astRoot); |
152 | | |
153 | | bool visit(FunctionDefinition const& _function) override; |
154 | | bool visit(ContractDefinition const& _contract) override; |
155 | | |
156 | | /// Get the function flow for the given function, using `_contract` as the |
157 | | /// most derived contract |
158 | | /// @param _function function to find the function flow for |
159 | | /// @param _contract most derived contract or nullptr for free functions |
160 | | FunctionFlow const& functionFlow(FunctionDefinition const& _function, ContractDefinition const* _contract = nullptr) const; |
161 | | |
162 | | std::map<FunctionContractTuple, std::unique_ptr<FunctionFlow>> const& allFunctionFlows() const |
163 | 7.21k | { |
164 | 7.21k | return m_functionControlFlow; |
165 | 7.21k | } |
166 | | |
167 | | class NodeContainer |
168 | | { |
169 | | public: |
170 | | CFGNode* newNode(); |
171 | | private: |
172 | | std::vector<std::unique_ptr<CFGNode>> m_nodes; |
173 | | }; |
174 | | private: |
175 | | langutil::ErrorReporter& m_errorReporter; |
176 | | |
177 | | /// Node container. |
178 | | /// All nodes allocated during the construction of the control flow graph |
179 | | /// are owned by the CFG class and stored in this container. |
180 | | NodeContainer m_nodeContainer; |
181 | | |
182 | | std::map<FunctionContractTuple, std::unique_ptr<FunctionFlow>> m_functionControlFlow; |
183 | | }; |
184 | | |
185 | | } |