#pragma once
#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libyul/optimiser/ASTWalker.h>
#include <array>
#include <memory>
namespace solidity::frontend
{
class ControlFlowBuilder: private ASTConstVisitor, private yul::ASTWalker
{
public:
static std::unique_ptr<FunctionFlow> createFunctionFlow(
CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function,
ContractDefinition const* _contract = nullptr
);
private:
explicit ControlFlowBuilder(
CFG::NodeContainer& _nodeContainer,
FunctionFlow const& _functionFlow,
ContractDefinition const* _contract = nullptr
);
bool visit(BinaryOperation const& _operation) override;
bool visit(Conditional const& _conditional) override;
bool visit(TryStatement const& _tryStatement) override;
bool visit(IfStatement const& _ifStatement) override;
bool visit(ForStatement const& _forStatement) override;
bool visit(WhileStatement const& _whileStatement) override;
bool visit(Break const&) override;
bool visit(Continue const&) override;
bool visit(Throw const&) override;
bool visit(RevertStatement const&) override;
bool visit(PlaceholderStatement const&) override;
bool visit(FunctionCall const& _functionCall) override;
bool visit(ModifierInvocation const& _modifierInvocation) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
bool visit(Return const& _return) override;
bool visit(FunctionTypeName const& _functionTypeName) override;
bool visit(InlineAssembly const& _inlineAssembly) override;
void visit(yul::Statement const& _statement) override;
void operator()(yul::If const& _if) override;
void operator()(yul::Switch const& _switch) override;
void operator()(yul::ForLoop const& _for) override;
void operator()(yul::Break const&) override;
void operator()(yul::Continue const&) override;
void operator()(yul::Identifier const& _identifier) override;
void operator()(yul::Assignment const& _assignment) override;
void operator()(yul::FunctionCall const& _functionCall) override;
void operator()(yul::FunctionDefinition const& _functionDefinition) override;
void operator()(yul::Leave const& _leaveStatement) override;
bool visit(VariableDeclaration const& _variableDeclaration) override;
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
bool visit(Identifier const& _identifier) override;
protected:
bool visitNode(ASTNode const&) override;
private:
using ASTConstVisitor::visit;
using yul::ASTWalker::visit;
using yul::ASTWalker::operator();
void appendControlFlow(ASTNode const& _node);
CFGNode* createFlow(CFGNode* _entry, ASTNode const& _node);
static void connect(CFGNode* _from, CFGNode* _to);
template<size_t n>
std::array<CFGNode*, n> splitFlow()
{
std::array<CFGNode*, n> result;
for (auto& node: result)
{
node = m_nodeContainer.newNode();
connect(m_currentNode, node);
}
m_currentNode = nullptr;
return result;
}
std::vector<CFGNode*> splitFlow(size_t n)
{
std::vector<CFGNode*> result(n);
for (auto& node: result)
{
node = m_nodeContainer.newNode();
connect(m_currentNode, node);
}
m_currentNode = nullptr;
return result;
}
template<typename C>
void mergeFlow(C const& _nodes, CFGNode* _endNode = nullptr)
{
CFGNode* mergeDestination = (_endNode == nullptr) ? m_nodeContainer.newNode() : _endNode;
for (auto& node: _nodes)
if (node != mergeDestination)
connect(node, mergeDestination);
m_currentNode = mergeDestination;
}
CFGNode* newLabel();
CFGNode* createLabelHere();
void placeAndConnectLabel(CFGNode *_node);
CFG::NodeContainer& m_nodeContainer;
CFGNode* m_currentNode = nullptr;
CFGNode* m_returnNode = nullptr;
CFGNode* m_revertNode = nullptr;
CFGNode* m_transactionReturnNode = nullptr;
ContractDefinition const* m_contract = nullptr;
CFGNode* m_breakJump = nullptr;
CFGNode* m_continueJump = nullptr;
CFGNode* m_placeholderEntry = nullptr;
CFGNode* m_placeholderExit = nullptr;
InlineAssembly const* m_inlineAssembly = nullptr;
class BreakContinueScope
{
public:
BreakContinueScope(ControlFlowBuilder& _parser, CFGNode* _breakJump, CFGNode* _continueJump);
~BreakContinueScope();
private:
ControlFlowBuilder& m_parser;
CFGNode* m_origBreakJump;
CFGNode* m_origContinueJump;
};
};
}