/src/solidity/libsolidity/analysis/ControlFlowBuilder.cpp
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 | | #include <libsolidity/analysis/ControlFlowBuilder.h> |
20 | | #include <libsolidity/ast/ASTUtils.h> |
21 | | #include <libyul/AST.h> |
22 | | #include <libyul/Utilities.h> |
23 | | #include <libyul/backends/evm/EVMDialect.h> |
24 | | |
25 | | using namespace solidity::langutil; |
26 | | using namespace solidity::frontend; |
27 | | |
28 | | ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow, ContractDefinition const* _contract): |
29 | | m_nodeContainer(_nodeContainer), |
30 | | m_currentNode(_functionFlow.entry), |
31 | | m_returnNode(_functionFlow.exit), |
32 | | m_revertNode(_functionFlow.revert), |
33 | | m_transactionReturnNode(_functionFlow.transactionReturn), |
34 | | m_contract(_contract) |
35 | 21.2k | { |
36 | 21.2k | } |
37 | | |
38 | | |
39 | | std::unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow( |
40 | | CFG::NodeContainer& _nodeContainer, |
41 | | FunctionDefinition const& _function, |
42 | | ContractDefinition const* _contract |
43 | | ) |
44 | 21.2k | { |
45 | 21.2k | auto functionFlow = std::make_unique<FunctionFlow>(); |
46 | 21.2k | functionFlow->entry = _nodeContainer.newNode(); |
47 | 21.2k | functionFlow->exit = _nodeContainer.newNode(); |
48 | 21.2k | functionFlow->revert = _nodeContainer.newNode(); |
49 | 21.2k | functionFlow->transactionReturn = _nodeContainer.newNode(); |
50 | 21.2k | ControlFlowBuilder builder(_nodeContainer, *functionFlow, _contract); |
51 | 21.2k | builder.appendControlFlow(_function); |
52 | | |
53 | 21.2k | return functionFlow; |
54 | 21.2k | } |
55 | | |
56 | | bool ControlFlowBuilder::visit(BinaryOperation const& _operation) |
57 | 68.8k | { |
58 | 68.8k | solAssert(!!m_currentNode, ""); |
59 | | |
60 | 68.8k | switch (_operation.getOperator()) |
61 | 68.8k | { |
62 | 511 | case Token::Or: |
63 | 1.51k | case Token::And: |
64 | 1.51k | { |
65 | 1.51k | visitNode(_operation); |
66 | 1.51k | solAssert(*_operation.annotation().userDefinedFunction == nullptr); |
67 | 1.51k | appendControlFlow(_operation.leftExpression()); |
68 | | |
69 | 1.51k | auto nodes = splitFlow<2>(); |
70 | 1.51k | nodes[0] = createFlow(nodes[0], _operation.rightExpression()); |
71 | 1.51k | mergeFlow(nodes, nodes[1]); |
72 | 1.51k | return false; |
73 | 1.51k | } |
74 | 67.3k | default: |
75 | 67.3k | { |
76 | 67.3k | if (*_operation.annotation().userDefinedFunction != nullptr) |
77 | 49 | { |
78 | 49 | visitNode(_operation); |
79 | 49 | _operation.leftExpression().accept(*this); |
80 | 49 | _operation.rightExpression().accept(*this); |
81 | | |
82 | 49 | m_currentNode->functionDefinition = *_operation.annotation().userDefinedFunction; |
83 | | |
84 | 49 | auto nextNode = newLabel(); |
85 | | |
86 | 49 | connect(m_currentNode, nextNode); |
87 | 49 | m_currentNode = nextNode; |
88 | 49 | return false; |
89 | 49 | } |
90 | 67.3k | } |
91 | 68.8k | } |
92 | 67.2k | return ASTConstVisitor::visit(_operation); |
93 | 68.8k | } |
94 | | |
95 | | bool ControlFlowBuilder::visit(UnaryOperation const& _operation) |
96 | 20.5k | { |
97 | 20.5k | solAssert(!!m_currentNode); |
98 | | |
99 | 20.5k | if (*_operation.annotation().userDefinedFunction != nullptr) |
100 | 15 | { |
101 | 15 | visitNode(_operation); |
102 | 15 | _operation.subExpression().accept(*this); |
103 | 15 | m_currentNode->functionDefinition = *_operation.annotation().userDefinedFunction; |
104 | | |
105 | 15 | auto nextNode = newLabel(); |
106 | | |
107 | 15 | connect(m_currentNode, nextNode); |
108 | 15 | m_currentNode = nextNode; |
109 | 15 | return false; |
110 | 15 | } |
111 | | |
112 | 20.5k | return ASTConstVisitor::visit(_operation); |
113 | 20.5k | } |
114 | | |
115 | | bool ControlFlowBuilder::visit(Conditional const& _conditional) |
116 | 542 | { |
117 | 542 | solAssert(!!m_currentNode, ""); |
118 | 542 | visitNode(_conditional); |
119 | | |
120 | 542 | _conditional.condition().accept(*this); |
121 | | |
122 | 542 | auto nodes = splitFlow<2>(); |
123 | | |
124 | 542 | nodes[0] = createFlow(nodes[0], _conditional.trueExpression()); |
125 | 542 | nodes[1] = createFlow(nodes[1], _conditional.falseExpression()); |
126 | | |
127 | 542 | mergeFlow(nodes); |
128 | | |
129 | 542 | return false; |
130 | 542 | } |
131 | | |
132 | | bool ControlFlowBuilder::visit(TryStatement const& _tryStatement) |
133 | 135 | { |
134 | 135 | appendControlFlow(_tryStatement.externalCall()); |
135 | | |
136 | 135 | auto nodes = splitFlow(_tryStatement.clauses().size()); |
137 | 411 | for (size_t i = 0; i < _tryStatement.clauses().size(); ++i) |
138 | 276 | nodes[i] = createFlow(nodes[i], _tryStatement.clauses()[i]->block()); |
139 | 135 | mergeFlow(nodes); |
140 | | |
141 | 135 | return false; |
142 | 135 | } |
143 | | |
144 | | bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) |
145 | 47.1k | { |
146 | 47.1k | solAssert(!!m_currentNode, ""); |
147 | 47.1k | visitNode(_ifStatement); |
148 | | |
149 | 47.1k | _ifStatement.condition().accept(*this); |
150 | | |
151 | 47.1k | auto nodes = splitFlow<2>(); |
152 | 47.1k | nodes[0] = createFlow(nodes[0], _ifStatement.trueStatement()); |
153 | | |
154 | 47.1k | if (_ifStatement.falseStatement()) |
155 | 370 | { |
156 | 370 | nodes[1] = createFlow(nodes[1], *_ifStatement.falseStatement()); |
157 | 370 | mergeFlow(nodes); |
158 | 370 | } |
159 | 46.8k | else |
160 | 46.8k | mergeFlow(nodes, nodes[1]); |
161 | | |
162 | 47.1k | return false; |
163 | 47.1k | } |
164 | | |
165 | | bool ControlFlowBuilder::visit(ForStatement const& _forStatement) |
166 | 3.78k | { |
167 | 3.78k | solAssert(!!m_currentNode, ""); |
168 | 3.78k | visitNode(_forStatement); |
169 | | |
170 | 3.78k | if (_forStatement.initializationExpression()) |
171 | 3.72k | _forStatement.initializationExpression()->accept(*this); |
172 | | |
173 | 3.78k | auto condition = createLabelHere(); |
174 | | |
175 | 3.78k | if (_forStatement.condition()) |
176 | 3.74k | appendControlFlow(*_forStatement.condition()); |
177 | | |
178 | 3.78k | auto postPart = newLabel(); |
179 | 3.78k | auto nodes = splitFlow<2>(); |
180 | 3.78k | auto afterFor = nodes[1]; |
181 | 3.78k | m_currentNode = nodes[0]; |
182 | | |
183 | 3.78k | { |
184 | 3.78k | BreakContinueScope scope(*this, afterFor, postPart); |
185 | 3.78k | appendControlFlow(_forStatement.body()); |
186 | 3.78k | } |
187 | | |
188 | 3.78k | placeAndConnectLabel(postPart); |
189 | | |
190 | 3.78k | if (auto expression = _forStatement.loopExpression()) |
191 | 3.71k | appendControlFlow(*expression); |
192 | | |
193 | 3.78k | connect(m_currentNode, condition); |
194 | 3.78k | m_currentNode = afterFor; |
195 | | |
196 | 3.78k | return false; |
197 | 3.78k | } |
198 | | |
199 | | bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) |
200 | 145 | { |
201 | 145 | solAssert(!!m_currentNode, ""); |
202 | 145 | visitNode(_whileStatement); |
203 | | |
204 | 145 | if (_whileStatement.isDoWhile()) |
205 | 31 | { |
206 | 31 | auto afterWhile = newLabel(); |
207 | 31 | auto whileBody = createLabelHere(); |
208 | 31 | auto condition = newLabel(); |
209 | | |
210 | 31 | { |
211 | 31 | BreakContinueScope scope(*this, afterWhile, condition); |
212 | 31 | appendControlFlow(_whileStatement.body()); |
213 | 31 | } |
214 | | |
215 | 31 | placeAndConnectLabel(condition); |
216 | 31 | appendControlFlow(_whileStatement.condition()); |
217 | | |
218 | 31 | connect(m_currentNode, whileBody); |
219 | 31 | placeAndConnectLabel(afterWhile); |
220 | 31 | } |
221 | 114 | else |
222 | 114 | { |
223 | 114 | auto whileCondition = createLabelHere(); |
224 | | |
225 | 114 | appendControlFlow(_whileStatement.condition()); |
226 | | |
227 | 114 | auto nodes = splitFlow<2>(); |
228 | | |
229 | 114 | auto whileBody = nodes[0]; |
230 | 114 | auto afterWhile = nodes[1]; |
231 | | |
232 | 114 | m_currentNode = whileBody; |
233 | 114 | { |
234 | 114 | BreakContinueScope scope(*this, afterWhile, whileCondition); |
235 | 114 | appendControlFlow(_whileStatement.body()); |
236 | 114 | } |
237 | | |
238 | 114 | connect(m_currentNode, whileCondition); |
239 | | |
240 | 114 | m_currentNode = afterWhile; |
241 | 114 | } |
242 | | |
243 | | |
244 | 145 | return false; |
245 | 145 | } |
246 | | |
247 | | bool ControlFlowBuilder::visit(Break const& _break) |
248 | 92 | { |
249 | 92 | solAssert(!!m_currentNode, ""); |
250 | 92 | solAssert(!!m_breakJump, ""); |
251 | 92 | visitNode(_break); |
252 | 92 | connect(m_currentNode, m_breakJump); |
253 | 92 | m_currentNode = newLabel(); |
254 | 92 | return false; |
255 | 92 | } |
256 | | |
257 | | bool ControlFlowBuilder::visit(Continue const& _continue) |
258 | 62 | { |
259 | 62 | solAssert(!!m_currentNode, ""); |
260 | 62 | solAssert(!!m_continueJump, ""); |
261 | 62 | visitNode(_continue); |
262 | 62 | connect(m_currentNode, m_continueJump); |
263 | 62 | m_currentNode = newLabel(); |
264 | 62 | return false; |
265 | 62 | } |
266 | | |
267 | | bool ControlFlowBuilder::visit(Throw const& _throw) |
268 | 0 | { |
269 | 0 | solAssert(!!m_currentNode, ""); |
270 | 0 | solAssert(!!m_revertNode, ""); |
271 | 0 | visitNode(_throw); |
272 | 0 | connect(m_currentNode, m_revertNode); |
273 | 0 | m_currentNode = newLabel(); |
274 | 0 | return false; |
275 | 0 | } |
276 | | |
277 | | bool ControlFlowBuilder::visit(RevertStatement const& _revert) |
278 | 61 | { |
279 | 61 | solAssert(!!m_currentNode, ""); |
280 | 61 | solAssert(!!m_revertNode, ""); |
281 | 61 | visitNode(_revert); |
282 | 61 | connect(m_currentNode, m_revertNode); |
283 | 61 | m_currentNode = newLabel(); |
284 | 61 | return false; |
285 | 61 | } |
286 | | |
287 | | bool ControlFlowBuilder::visit(PlaceholderStatement const&) |
288 | 510 | { |
289 | 510 | solAssert(!!m_currentNode, ""); |
290 | 510 | solAssert(!!m_placeholderEntry, ""); |
291 | 510 | solAssert(!!m_placeholderExit, ""); |
292 | | |
293 | 510 | connect(m_currentNode, m_placeholderEntry); |
294 | 510 | m_currentNode = newLabel(); |
295 | 510 | connect(m_placeholderExit, m_currentNode); |
296 | 510 | return false; |
297 | 510 | } |
298 | | |
299 | | bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) |
300 | 38.6k | { |
301 | 38.6k | solAssert(!!m_revertNode, ""); |
302 | 38.6k | solAssert(!!m_currentNode, ""); |
303 | 38.6k | solAssert(!!_functionCall.expression().annotation().type, ""); |
304 | | |
305 | 38.6k | if (auto functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type)) |
306 | 28.1k | switch (functionType->kind()) |
307 | 28.1k | { |
308 | 131 | case FunctionType::Kind::Revert: |
309 | 131 | visitNode(_functionCall); |
310 | 131 | _functionCall.expression().accept(*this); |
311 | 131 | ASTNode::listAccept(_functionCall.arguments(), *this); |
312 | | |
313 | 131 | connect(m_currentNode, m_revertNode); |
314 | | |
315 | 131 | m_currentNode = newLabel(); |
316 | 131 | return false; |
317 | 420 | case FunctionType::Kind::Require: |
318 | 2.57k | case FunctionType::Kind::Assert: |
319 | 2.57k | { |
320 | 2.57k | visitNode(_functionCall); |
321 | 2.57k | _functionCall.expression().accept(*this); |
322 | 2.57k | ASTNode::listAccept(_functionCall.arguments(), *this); |
323 | | |
324 | 2.57k | connect(m_currentNode, m_revertNode); |
325 | | |
326 | 2.57k | auto nextNode = newLabel(); |
327 | | |
328 | 2.57k | connect(m_currentNode, nextNode); |
329 | 2.57k | m_currentNode = nextNode; |
330 | 2.57k | return false; |
331 | 420 | } |
332 | 11.0k | case FunctionType::Kind::Internal: |
333 | 11.0k | { |
334 | 11.0k | visitNode(_functionCall); |
335 | 11.0k | _functionCall.expression().accept(*this); |
336 | 11.0k | ASTNode::listAccept(_functionCall.arguments(), *this); |
337 | | |
338 | 11.0k | m_currentNode->functionDefinition = ASTNode::resolveFunctionCall(_functionCall, m_contract); |
339 | | |
340 | 11.0k | auto nextNode = newLabel(); |
341 | | |
342 | 11.0k | connect(m_currentNode, nextNode); |
343 | 11.0k | m_currentNode = nextNode; |
344 | | |
345 | 11.0k | return false; |
346 | 420 | } |
347 | 14.4k | default: |
348 | 14.4k | break; |
349 | 28.1k | } |
350 | 24.8k | return ASTConstVisitor::visit(_functionCall); |
351 | 38.6k | } |
352 | | |
353 | | bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation) |
354 | 808 | { |
355 | 808 | solAssert(m_contract, "Free functions cannot have modifiers"); |
356 | | |
357 | 808 | if (auto arguments = _modifierInvocation.arguments()) |
358 | 546 | for (auto& argument: *arguments) |
359 | 539 | appendControlFlow(*argument); |
360 | | |
361 | 808 | auto modifierDefinition = dynamic_cast<ModifierDefinition const*>( |
362 | 808 | _modifierInvocation.name().annotation().referencedDeclaration |
363 | 808 | ); |
364 | | |
365 | 808 | if (!modifierDefinition) |
366 | 302 | return false; |
367 | | |
368 | 506 | VirtualLookup const& requiredLookup = *_modifierInvocation.name().annotation().requiredLookup; |
369 | | |
370 | 506 | if (requiredLookup == VirtualLookup::Virtual) |
371 | 500 | modifierDefinition = &modifierDefinition->resolveVirtual(*m_contract); |
372 | 6 | else |
373 | 506 | solAssert(requiredLookup == VirtualLookup::Static); |
374 | | |
375 | 506 | if (!modifierDefinition->isImplemented()) |
376 | 23 | return false; |
377 | | |
378 | 483 | solAssert(!!m_returnNode, ""); |
379 | | |
380 | 483 | m_placeholderEntry = newLabel(); |
381 | 483 | m_placeholderExit = newLabel(); |
382 | | |
383 | 483 | appendControlFlow(*modifierDefinition); |
384 | 483 | connect(m_currentNode, m_returnNode); |
385 | | |
386 | 483 | m_currentNode = m_placeholderEntry; |
387 | 483 | m_returnNode = m_placeholderExit; |
388 | | |
389 | 483 | m_placeholderEntry = nullptr; |
390 | 483 | m_placeholderExit = nullptr; |
391 | | |
392 | 483 | return false; |
393 | 483 | } |
394 | | |
395 | | bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition) |
396 | 21.2k | { |
397 | 21.2k | for (auto const& parameter: _functionDefinition.parameters()) |
398 | 14.2k | appendControlFlow(*parameter); |
399 | | |
400 | 21.2k | for (auto const& returnParameter: _functionDefinition.returnParameters()) |
401 | 13.5k | { |
402 | 13.5k | appendControlFlow(*returnParameter); |
403 | 13.5k | m_returnNode->variableOccurrences.emplace_back( |
404 | 13.5k | *returnParameter, |
405 | 13.5k | VariableOccurrence::Kind::Return |
406 | 13.5k | ); |
407 | | |
408 | 13.5k | } |
409 | | |
410 | 21.2k | for (auto const& modifierInvocation: _functionDefinition.modifiers()) |
411 | 808 | appendControlFlow(*modifierInvocation); |
412 | | |
413 | 21.2k | appendControlFlow(_functionDefinition.body()); |
414 | | |
415 | 21.2k | connect(m_currentNode, m_returnNode); |
416 | 21.2k | m_currentNode = nullptr; |
417 | | |
418 | 21.2k | return false; |
419 | 21.2k | } |
420 | | |
421 | | bool ControlFlowBuilder::visit(Return const& _return) |
422 | 54.5k | { |
423 | 54.5k | solAssert(!!m_currentNode, ""); |
424 | 54.5k | solAssert(!!m_returnNode, ""); |
425 | 54.5k | visitNode(_return); |
426 | 54.5k | if (_return.expression()) |
427 | 54.2k | { |
428 | 54.2k | appendControlFlow(*_return.expression()); |
429 | | // Returns with return expression are considered to be assignments to the return parameters. |
430 | 54.2k | for (auto returnParameter: _return.annotation().functionReturnParameters->parameters()) |
431 | 55.5k | m_currentNode->variableOccurrences.emplace_back( |
432 | 55.5k | *returnParameter, |
433 | 55.5k | VariableOccurrence::Kind::Assignment, |
434 | 55.5k | _return.location() |
435 | 55.5k | ); |
436 | 54.2k | } |
437 | 54.5k | connect(m_currentNode, m_returnNode); |
438 | 54.5k | m_currentNode = newLabel(); |
439 | 54.5k | return false; |
440 | 54.5k | } |
441 | | |
442 | | bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName) |
443 | 495 | { |
444 | 495 | visitNode(_functionTypeName); |
445 | | // Do not visit the parameters and return values of a function type name. |
446 | | // We do not want to consider them as variable declarations for the control flow graph. |
447 | 495 | return false; |
448 | 495 | } |
449 | | |
450 | | bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly) |
451 | 2.21k | { |
452 | 2.21k | solAssert(!!m_currentNode && !m_inlineAssembly, ""); |
453 | | |
454 | 2.21k | m_inlineAssembly = &_inlineAssembly; |
455 | 2.21k | (*this)(_inlineAssembly.operations().root()); |
456 | 2.21k | m_inlineAssembly = nullptr; |
457 | | |
458 | 2.21k | return false; |
459 | 2.21k | } |
460 | | |
461 | | void ControlFlowBuilder::visit(yul::Statement const& _statement) |
462 | 4.92k | { |
463 | 4.92k | solAssert(m_currentNode && m_inlineAssembly, ""); |
464 | 4.92k | solAssert(nativeLocationOf(_statement) == originLocationOf(_statement), ""); |
465 | 4.92k | m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, nativeLocationOf(_statement)); |
466 | 4.92k | ASTWalker::visit(_statement); |
467 | 4.92k | } |
468 | | |
469 | | void ControlFlowBuilder::operator()(yul::If const& _if) |
470 | 40 | { |
471 | 40 | solAssert(m_currentNode && m_inlineAssembly, ""); |
472 | 40 | visit(*_if.condition); |
473 | | |
474 | 40 | auto nodes = splitFlow<2>(); |
475 | 40 | m_currentNode = nodes[0]; |
476 | 40 | (*this)(_if.body); |
477 | 40 | nodes[0] = m_currentNode; |
478 | 40 | mergeFlow(nodes, nodes[1]); |
479 | 40 | } |
480 | | |
481 | | void ControlFlowBuilder::operator()(yul::Switch const& _switch) |
482 | 35 | { |
483 | 35 | solAssert(m_currentNode && m_inlineAssembly, ""); |
484 | 35 | visit(*_switch.expression); |
485 | | |
486 | 35 | auto beforeSwitch = m_currentNode; |
487 | | |
488 | 35 | auto nodes = splitFlow(_switch.cases.size()); |
489 | 93 | for (size_t i = 0u; i < _switch.cases.size(); ++i) |
490 | 58 | { |
491 | 58 | m_currentNode = nodes[i]; |
492 | 58 | (*this)(_switch.cases[i].body); |
493 | 58 | nodes[i] = m_currentNode; |
494 | 58 | } |
495 | 35 | mergeFlow(nodes); |
496 | | |
497 | 35 | if (!hasDefaultCase(_switch)) |
498 | 17 | connect(beforeSwitch, m_currentNode); |
499 | 35 | } |
500 | | |
501 | | void ControlFlowBuilder::operator()(yul::ForLoop const& _forLoop) |
502 | 47 | { |
503 | 47 | solAssert(m_currentNode && m_inlineAssembly, ""); |
504 | | |
505 | 47 | (*this)(_forLoop.pre); |
506 | | |
507 | 47 | auto condition = createLabelHere(); |
508 | | |
509 | 47 | if (_forLoop.condition) |
510 | 47 | visit(*_forLoop.condition); |
511 | | |
512 | 47 | auto loopExpression = newLabel(); |
513 | 47 | auto nodes = splitFlow<2>(); |
514 | 47 | auto afterFor = nodes[1]; |
515 | 47 | m_currentNode = nodes[0]; |
516 | | |
517 | 47 | { |
518 | 47 | BreakContinueScope scope(*this, afterFor, loopExpression); |
519 | 47 | (*this)(_forLoop.body); |
520 | 47 | } |
521 | | |
522 | 47 | placeAndConnectLabel(loopExpression); |
523 | | |
524 | 47 | (*this)(_forLoop.post); |
525 | | |
526 | 47 | connect(m_currentNode, condition); |
527 | 47 | m_currentNode = afterFor; |
528 | 47 | } |
529 | | |
530 | | void ControlFlowBuilder::operator()(yul::Break const&) |
531 | 16 | { |
532 | 16 | solAssert(m_currentNode && m_inlineAssembly, ""); |
533 | 16 | solAssert(m_breakJump, ""); |
534 | 16 | connect(m_currentNode, m_breakJump); |
535 | 16 | m_currentNode = newLabel(); |
536 | 16 | } |
537 | | |
538 | | void ControlFlowBuilder::operator()(yul::Continue const&) |
539 | 10 | { |
540 | 10 | solAssert(m_currentNode && m_inlineAssembly, ""); |
541 | 10 | solAssert(m_continueJump, ""); |
542 | 10 | connect(m_currentNode, m_continueJump); |
543 | 10 | m_currentNode = newLabel(); |
544 | 10 | } |
545 | | |
546 | | void ControlFlowBuilder::operator()(yul::Identifier const& _identifier) |
547 | 508 | { |
548 | 508 | solAssert(m_currentNode && m_inlineAssembly, ""); |
549 | 508 | auto const& externalReferences = m_inlineAssembly->annotation().externalReferences; |
550 | 508 | if (externalReferences.count(&_identifier)) |
551 | 423 | if (auto const* declaration = dynamic_cast<VariableDeclaration const*>(externalReferences.at(&_identifier).declaration)) |
552 | 419 | { |
553 | 419 | solAssert(nativeLocationOf(_identifier) == originLocationOf(_identifier), ""); |
554 | 419 | m_currentNode->variableOccurrences.emplace_back( |
555 | 419 | *declaration, |
556 | 419 | VariableOccurrence::Kind::Access, |
557 | 419 | nativeLocationOf(_identifier) |
558 | 419 | ); |
559 | 419 | } |
560 | 508 | } |
561 | | |
562 | | void ControlFlowBuilder::operator()(yul::Assignment const& _assignment) |
563 | 610 | { |
564 | 610 | solAssert(m_currentNode && m_inlineAssembly, ""); |
565 | 610 | visit(*_assignment.value); |
566 | 610 | auto const& externalReferences = m_inlineAssembly->annotation().externalReferences; |
567 | 610 | for (auto const& variable: _assignment.variableNames) |
568 | 616 | if (externalReferences.count(&variable)) |
569 | 588 | if (auto const* declaration = dynamic_cast<VariableDeclaration const*>(externalReferences.at(&variable).declaration)) |
570 | 588 | { |
571 | 588 | solAssert(nativeLocationOf(variable) == originLocationOf(variable), ""); |
572 | 588 | m_currentNode->variableOccurrences.emplace_back( |
573 | 588 | *declaration, |
574 | 588 | VariableOccurrence::Kind::Assignment, |
575 | 588 | nativeLocationOf(variable) |
576 | 588 | ); |
577 | 588 | } |
578 | 610 | } |
579 | | |
580 | | void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall) |
581 | 6.15k | { |
582 | 6.15k | using namespace yul; |
583 | 6.15k | solAssert(m_currentNode && m_inlineAssembly, ""); |
584 | 6.15k | yul::ASTWalker::operator()(_functionCall); |
585 | | |
586 | 6.15k | if (auto const* builtinFunction = resolveBuiltinFunction(_functionCall.functionName, m_inlineAssembly->dialect())) |
587 | 5.99k | { |
588 | 5.99k | if (builtinFunction->controlFlowSideEffects.canTerminate) |
589 | 79 | connect(m_currentNode, m_transactionReturnNode); |
590 | 5.99k | if (builtinFunction->controlFlowSideEffects.canRevert) |
591 | 38 | connect(m_currentNode, m_revertNode); |
592 | 5.99k | if (!builtinFunction->controlFlowSideEffects.canContinue) |
593 | 117 | m_currentNode = newLabel(); |
594 | 5.99k | } |
595 | 6.15k | } |
596 | | |
597 | | void ControlFlowBuilder::operator()(yul::FunctionDefinition const&) |
598 | 197 | { |
599 | 197 | solAssert(m_currentNode && m_inlineAssembly, ""); |
600 | | // External references cannot be accessed from within functions, so we can ignore their control flow. |
601 | | // TODO: we might still want to track if they always revert or return, though. |
602 | 197 | } |
603 | | |
604 | | void ControlFlowBuilder::operator()(yul::Leave const&) |
605 | 0 | { |
606 | | // This has to be implemented, if we ever decide to visit functions. |
607 | 0 | solUnimplemented(""); |
608 | 0 | } |
609 | | |
610 | | bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration) |
611 | 39.1k | { |
612 | 39.1k | solAssert(!!m_currentNode, ""); |
613 | 39.1k | visitNode(_variableDeclaration); |
614 | | |
615 | 39.1k | m_currentNode->variableOccurrences.emplace_back( |
616 | 39.1k | _variableDeclaration, |
617 | 39.1k | VariableOccurrence::Kind::Declaration |
618 | 39.1k | ); |
619 | | |
620 | | // Handle declaration with immediate assignment. |
621 | 39.1k | if (_variableDeclaration.value()) |
622 | 0 | m_currentNode->variableOccurrences.emplace_back( |
623 | 0 | _variableDeclaration, |
624 | 0 | VariableOccurrence::Kind::Assignment, |
625 | 0 | _variableDeclaration.value()->location() |
626 | 0 | ); |
627 | | // Function arguments are considered to be immediately assigned as well (they are "externally assigned"). |
628 | 39.1k | else if (_variableDeclaration.isCallableOrCatchParameter() && !_variableDeclaration.isReturnParameter()) |
629 | 14.5k | m_currentNode->variableOccurrences.emplace_back( |
630 | 14.5k | _variableDeclaration, |
631 | 14.5k | VariableOccurrence::Kind::Assignment |
632 | 14.5k | ); |
633 | 39.1k | return true; |
634 | 39.1k | } |
635 | | |
636 | | bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement) |
637 | 10.3k | { |
638 | 10.3k | solAssert(!!m_currentNode, ""); |
639 | 10.3k | visitNode(_variableDeclarationStatement); |
640 | | |
641 | 10.3k | for (auto const& var: _variableDeclarationStatement.declarations()) |
642 | 11.1k | if (var) |
643 | 11.0k | var->accept(*this); |
644 | 10.3k | if (_variableDeclarationStatement.initialValue()) |
645 | 8.04k | { |
646 | 8.04k | _variableDeclarationStatement.initialValue()->accept(*this); |
647 | 16.8k | for (size_t i = 0; i < _variableDeclarationStatement.declarations().size(); i++) |
648 | 8.81k | if (auto const& var = _variableDeclarationStatement.declarations()[i]) |
649 | 8.73k | { |
650 | 8.73k | auto expression = _variableDeclarationStatement.initialValue(); |
651 | 8.73k | if (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression)) |
652 | 118 | if (tupleExpression->components().size() > 1) |
653 | 74 | { |
654 | 74 | solAssert(tupleExpression->components().size() > i, ""); |
655 | 74 | expression = tupleExpression->components()[i].get(); |
656 | 74 | } |
657 | 8.73k | expression = resolveOuterUnaryTuples(expression); |
658 | 8.73k | m_currentNode->variableOccurrences.emplace_back( |
659 | 8.73k | *var, |
660 | 8.73k | VariableOccurrence::Kind::Assignment, |
661 | 8.73k | expression ? std::make_optional(expression->location()) : std::optional<langutil::SourceLocation>{} |
662 | 8.73k | ); |
663 | 8.73k | } |
664 | 8.04k | } |
665 | 10.3k | return false; |
666 | 10.3k | } |
667 | | |
668 | | bool ControlFlowBuilder::visit(Identifier const& _identifier) |
669 | 158k | { |
670 | 158k | solAssert(!!m_currentNode, ""); |
671 | 158k | visitNode(_identifier); |
672 | | |
673 | 158k | if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) |
674 | 135k | m_currentNode->variableOccurrences.emplace_back( |
675 | 135k | *variableDeclaration, |
676 | 135k | static_cast<Expression const&>(_identifier).annotation().willBeWrittenTo ? |
677 | 13.0k | VariableOccurrence::Kind::Assignment : |
678 | 135k | VariableOccurrence::Kind::Access, |
679 | 135k | _identifier.location() |
680 | 135k | ); |
681 | | |
682 | 158k | return true; |
683 | 158k | } |
684 | | |
685 | | bool ControlFlowBuilder::visitNode(ASTNode const& _node) |
686 | 1.30M | { |
687 | 1.30M | solAssert(!!m_currentNode, ""); |
688 | 1.30M | m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, _node.location()); |
689 | 1.30M | return true; |
690 | 1.30M | } |
691 | | |
692 | | void ControlFlowBuilder::appendControlFlow(ASTNode const& _node) |
693 | 189k | { |
694 | 189k | _node.accept(*this); |
695 | 189k | } |
696 | | |
697 | | CFGNode* ControlFlowBuilder::createFlow(CFGNode* _entry, ASTNode const& _node) |
698 | 50.4k | { |
699 | 50.4k | auto oldCurrentNode = m_currentNode; |
700 | 50.4k | m_currentNode = _entry; |
701 | 50.4k | appendControlFlow(_node); |
702 | 50.4k | auto endNode = m_currentNode; |
703 | 50.4k | m_currentNode = oldCurrentNode; |
704 | 50.4k | return endNode; |
705 | 50.4k | } |
706 | | |
707 | | void ControlFlowBuilder::connect(CFGNode* _from, CFGNode* _to) |
708 | 263k | { |
709 | 263k | solAssert(_from, ""); |
710 | 263k | solAssert(_to, ""); |
711 | 263k | _from->exits.push_back(_to); |
712 | 263k | _to->entries.push_back(_from); |
713 | 263k | } |
714 | | |
715 | | CFGNode* ControlFlowBuilder::newLabel() |
716 | 74.1k | { |
717 | 74.1k | return m_nodeContainer.newNode(); |
718 | 74.1k | } |
719 | | |
720 | | CFGNode* ControlFlowBuilder::createLabelHere() |
721 | 3.98k | { |
722 | 3.98k | auto label = m_nodeContainer.newNode(); |
723 | 3.98k | connect(m_currentNode, label); |
724 | 3.98k | m_currentNode = label; |
725 | 3.98k | return label; |
726 | 3.98k | } |
727 | | |
728 | | void ControlFlowBuilder::placeAndConnectLabel(CFGNode* _node) |
729 | 3.89k | { |
730 | 3.89k | connect(m_currentNode, _node); |
731 | 3.89k | m_currentNode = _node; |
732 | 3.89k | } |
733 | | |
734 | | ControlFlowBuilder::BreakContinueScope::BreakContinueScope( |
735 | | ControlFlowBuilder& _parser, |
736 | | CFGNode* _breakJump, |
737 | | CFGNode* _continueJump |
738 | | ): m_parser(_parser), m_origBreakJump(_parser.m_breakJump), m_origContinueJump(_parser.m_continueJump) |
739 | 3.98k | { |
740 | 3.98k | m_parser.m_breakJump = _breakJump; |
741 | 3.98k | m_parser.m_continueJump = _continueJump; |
742 | 3.98k | } |
743 | | |
744 | | ControlFlowBuilder::BreakContinueScope::~BreakContinueScope() |
745 | 3.98k | { |
746 | 3.98k | m_parser.m_breakJump = m_origBreakJump; |
747 | 3.98k | m_parser.m_continueJump = m_origContinueJump; |
748 | 3.98k | } |