/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ #include using namespace dev; using namespace solidity; using namespace std; ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow): m_nodeContainer(_nodeContainer), m_currentFunctionFlow(_functionFlow), m_currentNode(_functionFlow.entry) { } unique_ptr ControlFlowBuilder::createFunctionFlow( CFG::NodeContainer& _nodeContainer, FunctionDefinition const& _function ) { auto functionFlow = unique_ptr(new FunctionFlow()); functionFlow->entry = _nodeContainer.newNode(); functionFlow->exit = _nodeContainer.newNode(); functionFlow->revert = _nodeContainer.newNode(); ControlFlowBuilder builder(_nodeContainer, *functionFlow); builder.appendControlFlow(_function); connect(builder.m_currentNode, functionFlow->exit); return functionFlow; } unique_ptr ControlFlowBuilder::createModifierFlow( CFG::NodeContainer& _nodeContainer, ModifierDefinition const& _modifier ) { auto modifierFlow = unique_ptr(new ModifierFlow()); modifierFlow->entry = _nodeContainer.newNode(); modifierFlow->exit = _nodeContainer.newNode(); modifierFlow->revert = _nodeContainer.newNode(); modifierFlow->placeholderEntry = _nodeContainer.newNode(); modifierFlow->placeholderExit = _nodeContainer.newNode(); ControlFlowBuilder builder(_nodeContainer, *modifierFlow); builder.appendControlFlow(_modifier); connect(builder.m_currentNode, modifierFlow->exit); return modifierFlow; } bool ControlFlowBuilder::visit(BinaryOperation const& _operation) { solAssert(!!m_currentNode, ""); switch(_operation.getOperator()) { case Token::Or: case Token::And: { appendControlFlow(_operation.leftExpression()); auto nodes = splitFlow<2>(); nodes[0] = createFlow(nodes[0], _operation.rightExpression()); mergeFlow(nodes, nodes[1]); return false; } default: break; } return ASTConstVisitor::visit(_operation); } bool ControlFlowBuilder::visit(Conditional const& _conditional) { solAssert(!!m_currentNode, ""); _conditional.condition().accept(*this); auto nodes = splitFlow<2>(); nodes[0] = createFlow(nodes[0], _conditional.trueExpression()); nodes[1] = createFlow(nodes[1], _conditional.falseExpression()); mergeFlow(nodes); return false; } bool ControlFlowBuilder::visit(IfStatement const& _ifStatement) { solAssert(!!m_currentNode, ""); _ifStatement.condition().accept(*this); auto nodes = splitFlow<2>(); nodes[0] = createFlow(nodes[0], _ifStatement.trueStatement()); if (_ifStatement.falseStatement()) { nodes[1] = createFlow(nodes[1], *_ifStatement.falseStatement()); mergeFlow(nodes); } else mergeFlow(nodes, nodes[1]); return false; } bool ControlFlowBuilder::visit(ForStatement const& _forStatement) { solAssert(!!m_currentNode, ""); if (_forStatement.initializationExpression()) _forStatement.initializationExpression()->accept(*this); auto condition = createLabelHere(); if (_forStatement.condition()) appendControlFlow(*_forStatement.condition()); auto loopExpression = newLabel(); auto nodes = splitFlow<2>(); auto afterFor = nodes[1]; m_currentNode = nodes[0]; { BreakContinueScope scope(*this, afterFor, loopExpression); appendControlFlow(_forStatement.body()); } placeAndConnectLabel(loopExpression); if (auto expression = _forStatement.loopExpression()) appendControlFlow(*expression); connect(m_currentNode, condition); m_currentNode = afterFor; return false; } bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) { solAssert(!!m_currentNode, ""); if (_whileStatement.isDoWhile()) { auto afterWhile = newLabel(); auto whileBody = createLabelHere(); auto condition = newLabel(); { BreakContinueScope scope(*this, afterWhile, condition); appendControlFlow(_whileStatement.body()); } placeAndConnectLabel(condition); appendControlFlow(_whileStatement.condition()); connect(m_currentNode, whileBody); placeAndConnectLabel(afterWhile); } else { auto whileCondition = createLabelHere(); appendControlFlow(_whileStatement.condition()); auto nodes = splitFlow<2>(); auto whileBody = nodes[0]; auto afterWhile = nodes[1]; m_currentNode = whileBody; { BreakContinueScope scope(*this, afterWhile, whileCondition); appendControlFlow(_whileStatement.body()); } connect(m_currentNode, whileCondition); m_currentNode = afterWhile; } return false; } bool ControlFlowBuilder::visit(Break const&) { solAssert(!!m_currentNode, ""); solAssert(!!m_breakJump, ""); connect(m_currentNode, m_breakJump); m_currentNode = newLabel(); return false; } bool ControlFlowBuilder::visit(Continue const&) { solAssert(!!m_currentNode, ""); solAssert(!!m_continueJump, ""); connect(m_currentNode, m_continueJump); m_currentNode = newLabel(); return false; } bool ControlFlowBuilder::visit(Throw const&) { solAssert(!!m_currentNode, ""); solAssert(!!m_currentFunctionFlow.revert, ""); connect(m_currentNode, m_currentFunctionFlow.revert); m_currentNode = newLabel(); return false; } bool ControlFlowBuilder::visit(Block const&) { solAssert(!!m_currentNode, ""); createLabelHere(); return true; } void ControlFlowBuilder::endVisit(Block const&) { solAssert(!!m_currentNode, ""); createLabelHere(); } bool ControlFlowBuilder::visit(Return const& _return) { solAssert(!!m_currentNode, ""); solAssert(!!m_currentFunctionFlow.exit, ""); solAssert(!m_currentNode->block.returnStatement, ""); m_currentNode->block.returnStatement = &_return; connect(m_currentNode, m_currentFunctionFlow.exit); m_currentNode = newLabel(); return true; } bool ControlFlowBuilder::visit(PlaceholderStatement const&) { solAssert(!!m_currentNode, ""); auto modifierFlow = dynamic_cast(&m_currentFunctionFlow); solAssert(!!modifierFlow, ""); connect(m_currentNode, modifierFlow->placeholderEntry); m_currentNode = newLabel(); connect(modifierFlow->placeholderExit, m_currentNode); return false; } bool ControlFlowBuilder::visitNode(ASTNode const& node) { solAssert(!!m_currentNode, ""); if (auto const* expression = dynamic_cast(&node)) m_currentNode->block.expressions.emplace_back(expression); else if (auto const* variableDeclaration = dynamic_cast(&node)) m_currentNode->block.variableDeclarations.emplace_back(variableDeclaration); else if (auto const* assembly = dynamic_cast(&node)) m_currentNode->block.inlineAssemblyStatements.emplace_back(assembly); return true; } bool ControlFlowBuilder::visit(FunctionCall const& _functionCall) { solAssert(!!m_currentNode, ""); solAssert(!!_functionCall.expression().annotation().type, ""); if (auto functionType = dynamic_pointer_cast(_functionCall.expression().annotation().type)) switch (functionType->kind()) { case FunctionType::Kind::Revert: solAssert(!!m_currentFunctionFlow.revert, ""); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_currentFunctionFlow.revert); m_currentNode = newLabel(); return false; case FunctionType::Kind::Require: case FunctionType::Kind::Assert: { solAssert(!!m_currentFunctionFlow.revert, ""); _functionCall.expression().accept(*this); ASTNode::listAccept(_functionCall.arguments(), *this); connect(m_currentNode, m_currentFunctionFlow.revert); auto nextNode = newLabel(); connect(m_currentNode, nextNode); m_currentNode = nextNode; return false; } default: break; } return ASTConstVisitor::visit(_functionCall); } void ControlFlowBuilder::appendControlFlow(ASTNode const& _node) { _node.accept(*this); } CFGNode* ControlFlowBuilder::createFlow(CFGNode* _entry, ASTNode const& _node) { auto oldCurrentNode = m_currentNode; m_currentNode = _entry; appendControlFlow(_node); auto endNode = m_currentNode; m_currentNode = oldCurrentNode; return endNode; } void ControlFlowBuilder::connect(CFGNode* _from, CFGNode* _to) { solAssert(_from, ""); solAssert(_to, ""); _from->exits.push_back(_to); _to->entries.push_back(_from); } CFGNode* ControlFlowBuilder::newLabel() { return m_nodeContainer.newNode(); } CFGNode* ControlFlowBuilder::createLabelHere() { auto label = m_nodeContainer.newNode(); connect(m_currentNode, label); m_currentNode = label; return label; } void ControlFlowBuilder::placeAndConnectLabel(CFGNode* _node) { connect(m_currentNode, _node); m_currentNode = _node; } ControlFlowBuilder::BreakContinueScope::BreakContinueScope( ControlFlowBuilder& _parser, CFGNode* _breakJump, CFGNode* _continueJump ): m_parser(_parser), m_origBreakJump(_parser.m_breakJump), m_origContinueJump(_parser.m_continueJump) { m_parser.m_breakJump = _breakJump; m_parser.m_continueJump = _continueJump; } ControlFlowBuilder::BreakContinueScope::~BreakContinueScope() { m_parser.m_breakJump = m_origBreakJump; m_parser.m_continueJump = m_origContinueJump; }