CFGNode: For function calls store a pointer to a resolved function definition rather than the FunctionCall AST node

This commit is contained in:
wechman 2022-08-30 09:46:55 +02:00 committed by Kamil Śliwak
parent 2f33105d11
commit fbe1181517
5 changed files with 46 additions and 50 deletions

View File

@ -300,8 +300,7 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
_functionCall.expression().accept(*this); _functionCall.expression().accept(*this);
ASTNode::listAccept(_functionCall.arguments(), *this); ASTNode::listAccept(_functionCall.arguments(), *this);
solAssert(!m_currentNode->functionCall); m_currentNode->functionDefinition = ASTNode::resolveFunctionCall(_functionCall, m_contract);
m_currentNode->functionCall = &_functionCall;
auto nextNode = newLabel(); auto nextNode = newLabel();
@ -318,6 +317,8 @@ bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation) bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
{ {
solAssert(m_contract, "Free functions cannot have modifiers");
if (auto arguments = _modifierInvocation.arguments()) if (auto arguments = _modifierInvocation.arguments())
for (auto& argument: *arguments) for (auto& argument: *arguments)
appendControlFlow(*argument); appendControlFlow(*argument);

View File

@ -38,14 +38,14 @@ public:
static std::unique_ptr<FunctionFlow> createFunctionFlow( static std::unique_ptr<FunctionFlow> createFunctionFlow(
CFG::NodeContainer& _nodeContainer, CFG::NodeContainer& _nodeContainer,
FunctionDefinition const& _function, FunctionDefinition const& _function,
ContractDefinition const* _contract = nullptr ContractDefinition const* _contract
); );
private: private:
explicit ControlFlowBuilder( explicit ControlFlowBuilder(
CFG::NodeContainer& _nodeContainer, CFG::NodeContainer& _nodeContainer,
FunctionFlow const& _functionFlow, FunctionFlow const& _functionFlow,
ContractDefinition const* _contract = nullptr ContractDefinition const* _contract
); );
// Visits for constructing the control flow. // Visits for constructing the control flow.

View File

@ -34,7 +34,11 @@ bool CFG::constructFlow(ASTNode const& _astRoot)
bool CFG::visit(FunctionDefinition const& _function) bool CFG::visit(FunctionDefinition const& _function)
{ {
if (_function.isImplemented() && _function.isFree()) if (_function.isImplemented() && _function.isFree())
m_functionControlFlow[{nullptr, &_function}] = ControlFlowBuilder::createFunctionFlow(m_nodeContainer, _function); m_functionControlFlow[{nullptr, &_function}] = ControlFlowBuilder::createFunctionFlow(
m_nodeContainer,
_function,
nullptr /* _contract */
);
return false; return false;
} }

View File

@ -98,9 +98,8 @@ struct CFGNode
std::vector<CFGNode*> entries; std::vector<CFGNode*> entries;
/// Exit nodes. All CFG nodes to which control flow may continue after this node. /// Exit nodes. All CFG nodes to which control flow may continue after this node.
std::vector<CFGNode*> exits; std::vector<CFGNode*> exits;
/// Function call done by this node /// Resolved definition of the function called by this node
FunctionCall const* functionCall = nullptr; FunctionDefinition const* functionDefinition = nullptr;
/// Variable occurrences in the node. /// Variable occurrences in the node.
std::vector<VariableOccurrence> variableOccurrences; std::vector<VariableOccurrence> variableOccurrences;
// Source location of this control flow block. // Source location of this control flow block.

View File

@ -81,27 +81,23 @@ void ControlFlowRevertPruner::findRevertStates()
if (_node == functionFlow.exit) if (_node == functionFlow.exit)
foundExit = true; foundExit = true;
if (auto const* functionCall = _node->functionCall) auto const* resolvedFunction = _node->functionDefinition;
if (resolvedFunction && resolvedFunction->isImplemented())
{ {
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.contract); CFG::FunctionContractTuple calledFunctionTuple{
findScopeContract(*resolvedFunction, item.contract),
if (resolvedFunction && resolvedFunction->isImplemented()) resolvedFunction
};
switch (m_functions.at(calledFunctionTuple))
{ {
CFG::FunctionContractTuple calledFunctionTuple{ case RevertState::Unknown:
findScopeContract(*resolvedFunction, item.contract), wakeUp[calledFunctionTuple].insert(item);
resolvedFunction foundUnknown = true;
}; return;
switch (m_functions.at(calledFunctionTuple)) case RevertState::AllPathsRevert:
{ return;
case RevertState::Unknown: case RevertState::HasNonRevertingPath:
wakeUp[calledFunctionTuple].insert(item); break;
foundUnknown = true;
return;
case RevertState::AllPathsRevert:
return;
case RevertState::HasNonRevertingPath:
break;
}
} }
} }
@ -135,30 +131,26 @@ void ControlFlowRevertPruner::modifyFunctionFlows()
FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract); FunctionFlow const& functionFlow = m_cfg.functionFlow(*item.first.function, item.first.contract);
solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run( solidity::util::BreadthFirstSearch<CFGNode*>{{functionFlow.entry}}.run(
[&](CFGNode* _node, auto&& _addChild) { [&](CFGNode* _node, auto&& _addChild) {
if (auto const* functionCall = _node->functionCall) auto const* resolvedFunction = _node->functionDefinition;
{ if (resolvedFunction && resolvedFunction->isImplemented())
auto const* resolvedFunction = ASTNode::resolveFunctionCall(*functionCall, item.first.contract); switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction}))
{
case RevertState::Unknown:
[[fallthrough]];
case RevertState::AllPathsRevert:
// If the revert states of the functions do not
// change anymore, we treat all "unknown" states as
// "reverting", since they can only be caused by
// recursion.
for (CFGNode * node: _node->exits)
ranges::remove(node->entries, _node);
if (resolvedFunction && resolvedFunction->isImplemented()) _node->exits = {functionFlow.revert};
switch (m_functions.at({findScopeContract(*resolvedFunction, item.first.contract), resolvedFunction})) functionFlow.revert->entries.push_back(_node);
{ return;
case RevertState::Unknown: default:
[[fallthrough]]; break;
case RevertState::AllPathsRevert: }
// If the revert states of the functions do not
// change anymore, we treat all "unknown" states as
// "reverting", since they can only be caused by
// recursion.
for (CFGNode * node: _node->exits)
ranges::remove(node->entries, _node);
_node->exits = {functionFlow.revert};
functionFlow.revert->entries.push_back(_node);
return;
default:
break;
}
}
for (CFGNode* exit: _node->exits) for (CFGNode* exit: _node->exits)
_addChild(exit); _addChild(exit);