/src/solidity/libsolidity/analysis/ControlFlowBuilder.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 | | |
19 | | #pragma once |
20 | | |
21 | | #include <libsolidity/analysis/ControlFlowGraph.h> |
22 | | #include <libsolidity/ast/AST.h> |
23 | | #include <libsolidity/ast/ASTVisitor.h> |
24 | | #include <libyul/optimiser/ASTWalker.h> |
25 | | |
26 | | #include <array> |
27 | | #include <memory> |
28 | | |
29 | | namespace solidity::frontend |
30 | | { |
31 | | |
32 | | /** |
33 | | * Helper class that builds the control flow of a function or modifier. |
34 | | */ |
35 | | class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker |
36 | | { |
37 | | public: |
38 | | static std::unique_ptr<FunctionFlow> createFunctionFlow( |
39 | | CFG::NodeContainer& _nodeContainer, |
40 | | FunctionDefinition const& _function, |
41 | | ContractDefinition const* _contract |
42 | | ); |
43 | | |
44 | | private: |
45 | | explicit ControlFlowBuilder( |
46 | | CFG::NodeContainer& _nodeContainer, |
47 | | FunctionFlow const& _functionFlow, |
48 | | ContractDefinition const* _contract |
49 | | ); |
50 | | |
51 | | // Visits for constructing the control flow. |
52 | | bool visit(BinaryOperation const& _operation) override; |
53 | | bool visit(UnaryOperation const& _operation) override; |
54 | | bool visit(Conditional const& _conditional) override; |
55 | | bool visit(TryStatement const& _tryStatement) override; |
56 | | bool visit(IfStatement const& _ifStatement) override; |
57 | | bool visit(ForStatement const& _forStatement) override; |
58 | | bool visit(WhileStatement const& _whileStatement) override; |
59 | | bool visit(Break const&) override; |
60 | | bool visit(Continue const&) override; |
61 | | bool visit(Throw const&) override; |
62 | | bool visit(RevertStatement const&) override; |
63 | | bool visit(PlaceholderStatement const&) override; |
64 | | bool visit(FunctionCall const& _functionCall) override; |
65 | | bool visit(ModifierInvocation const& _modifierInvocation) override; |
66 | | |
67 | | // Visits for constructing the control flow as well as filling variable occurrences. |
68 | | bool visit(FunctionDefinition const& _functionDefinition) override; |
69 | | bool visit(Return const& _return) override; |
70 | | |
71 | | // Visits for filling variable occurrences. |
72 | | bool visit(FunctionTypeName const& _functionTypeName) override; |
73 | | bool visit(InlineAssembly const& _inlineAssembly) override; |
74 | | void visit(yul::Statement const& _statement) override; |
75 | | void operator()(yul::If const& _if) override; |
76 | | void operator()(yul::Switch const& _switch) override; |
77 | | void operator()(yul::ForLoop const& _for) override; |
78 | | void operator()(yul::Break const&) override; |
79 | | void operator()(yul::Continue const&) override; |
80 | | void operator()(yul::Identifier const& _identifier) override; |
81 | | void operator()(yul::Assignment const& _assignment) override; |
82 | | void operator()(yul::FunctionCall const& _functionCall) override; |
83 | | void operator()(yul::FunctionDefinition const& _functionDefinition) override; |
84 | | void operator()(yul::Leave const& _leaveStatement) override; |
85 | | bool visit(VariableDeclaration const& _variableDeclaration) override; |
86 | | bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; |
87 | | bool visit(Identifier const& _identifier) override; |
88 | | |
89 | | protected: |
90 | | bool visitNode(ASTNode const&) override; |
91 | | |
92 | | private: |
93 | | using ASTConstVisitor::visit; |
94 | | using yul::ASTWalker::visit; |
95 | | using yul::ASTWalker::operator(); |
96 | | |
97 | | /// Appends the control flow of @a _node to the current control flow. |
98 | | void appendControlFlow(ASTNode const& _node); |
99 | | |
100 | | /// Starts at @a _entry and parses the control flow of @a _node. |
101 | | /// @returns The node at which the parsed control flow ends. |
102 | | /// m_currentNode is not affected (it is saved and restored). |
103 | | CFGNode* createFlow(CFGNode* _entry, ASTNode const& _node); |
104 | | |
105 | | /// Creates an arc from @a _from to @a _to. |
106 | | static void connect(CFGNode* _from, CFGNode* _to); |
107 | | |
108 | | /// Splits the control flow starting at the current node into n paths. |
109 | | /// m_currentNode is set to nullptr and has to be set manually or |
110 | | /// using mergeFlow later. |
111 | | template<size_t n> |
112 | | std::array<CFGNode*, n> splitFlow() |
113 | 53.2k | { |
114 | 53.2k | std::array<CFGNode*, n> result; |
115 | 53.2k | for (auto& node: result) |
116 | 106k | { |
117 | 106k | node = m_nodeContainer.newNode(); |
118 | 106k | connect(m_currentNode, node); |
119 | 106k | } |
120 | 53.2k | m_currentNode = nullptr; |
121 | 53.2k | return result; |
122 | 53.2k | } |
123 | | |
124 | | /// Splits the control flow starting at the current node into @a _n paths. |
125 | | /// m_currentNode is set to nullptr and has to be set manually or |
126 | | /// using mergeFlow later. |
127 | | std::vector<CFGNode*> splitFlow(size_t n) |
128 | 170 | { |
129 | 170 | std::vector<CFGNode*> result(n); |
130 | 170 | for (auto& node: result) |
131 | 334 | { |
132 | 334 | node = m_nodeContainer.newNode(); |
133 | 334 | connect(m_currentNode, node); |
134 | 334 | } |
135 | 170 | m_currentNode = nullptr; |
136 | 170 | return result; |
137 | 170 | } |
138 | | |
139 | | /// Merges the control flow of @a _nodes to @a _endNode. |
140 | | /// If @a _endNode is nullptr, a new node is created and used as end node. |
141 | | /// Sets the merge destination as current node. |
142 | | /// Note: @a _endNode may be one of the nodes in @a _nodes. |
143 | | template<typename C> |
144 | | void mergeFlow(C const& _nodes, CFGNode* _endNode = nullptr) |
145 | 49.4k | { |
146 | 49.4k | CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode; |
147 | 49.4k | for (auto& node: _nodes) |
148 | 98.9k | if (node != mergeDestination) |
149 | 50.5k | connect(node, mergeDestination); |
150 | 49.4k | m_currentNode = mergeDestination; |
151 | 49.4k | } void solidity::frontend::ControlFlowBuilder::mergeFlow<std::__1::array<solidity::frontend::CFGNode*, 2ul> >(std::__1::array<solidity::frontend::CFGNode*, 2ul> const&, solidity::frontend::CFGNode*) Line | Count | Source | 145 | 49.2k | { | 146 | 49.2k | CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode; | 147 | 49.2k | for (auto& node: _nodes) | 148 | 98.5k | if (node != mergeDestination) | 149 | 50.1k | connect(node, mergeDestination); | 150 | 49.2k | m_currentNode = mergeDestination; | 151 | 49.2k | } |
void solidity::frontend::ControlFlowBuilder::mergeFlow<std::__1::vector<solidity::frontend::CFGNode*, std::__1::allocator<solidity::frontend::CFGNode*> > >(std::__1::vector<solidity::frontend::CFGNode*, std::__1::allocator<solidity::frontend::CFGNode*> > const&, solidity::frontend::CFGNode*) Line | Count | Source | 145 | 170 | { | 146 | 170 | CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode; | 147 | 170 | for (auto& node: _nodes) | 148 | 334 | if (node != mergeDestination) | 149 | 334 | connect(node, mergeDestination); | 150 | 170 | m_currentNode = mergeDestination; | 151 | 170 | } |
|
152 | | |
153 | | CFGNode* newLabel(); |
154 | | CFGNode* createLabelHere(); |
155 | | void placeAndConnectLabel(CFGNode *_node); |
156 | | |
157 | | CFG::NodeContainer& m_nodeContainer; |
158 | | |
159 | | CFGNode* m_currentNode = nullptr; |
160 | | CFGNode* m_returnNode = nullptr; |
161 | | CFGNode* m_revertNode = nullptr; |
162 | | CFGNode* m_transactionReturnNode = nullptr; |
163 | | |
164 | | ContractDefinition const* m_contract = nullptr; |
165 | | |
166 | | /// The current jump destination of break Statements. |
167 | | CFGNode* m_breakJump = nullptr; |
168 | | /// The current jump destination of continue Statements. |
169 | | CFGNode* m_continueJump = nullptr; |
170 | | |
171 | | CFGNode* m_placeholderEntry = nullptr; |
172 | | CFGNode* m_placeholderExit = nullptr; |
173 | | |
174 | | InlineAssembly const* m_inlineAssembly = nullptr; |
175 | | |
176 | | /// Helper class that replaces the break and continue jump destinations for the |
177 | | /// current scope and restores the originals at the end of the scope. |
178 | | class BreakContinueScope |
179 | | { |
180 | | public: |
181 | | BreakContinueScope(ControlFlowBuilder& _parser, CFGNode* _breakJump, CFGNode* _continueJump); |
182 | | ~BreakContinueScope(); |
183 | | private: |
184 | | ControlFlowBuilder& m_parser; |
185 | | CFGNode* m_origBreakJump; |
186 | | CFGNode* m_origContinueJump; |
187 | | }; |
188 | | }; |
189 | | |
190 | | } |