mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
fixup! Syntactic call graph
This commit is contained in:
parent
9656cf105b
commit
ccae691606
libsolidity
analysis
experimental
@ -99,14 +99,14 @@ private:
|
||||
|
||||
/// Starts at @a _entry and parses the control flow of @a _node.
|
||||
/// @returns The node at which the parsed control flow ends.
|
||||
/// m_currentNode is not affected (it is saved and restored).
|
||||
/// m_currentFunction is not affected (it is saved and restored).
|
||||
CFGNode* createFlow(CFGNode* _entry, ASTNode const& _node);
|
||||
|
||||
/// Creates an arc from @a _from to @a _to.
|
||||
static void connect(CFGNode* _from, CFGNode* _to);
|
||||
|
||||
/// Splits the control flow starting at the current node into n paths.
|
||||
/// m_currentNode is set to nullptr and has to be set manually or
|
||||
/// m_currentFunction is set to nullptr and has to be set manually or
|
||||
/// using mergeFlow later.
|
||||
template<size_t n>
|
||||
std::array<CFGNode*, n> splitFlow()
|
||||
@ -122,7 +122,7 @@ private:
|
||||
}
|
||||
|
||||
/// Splits the control flow starting at the current node into @a _n paths.
|
||||
/// m_currentNode is set to nullptr and has to be set manually or
|
||||
/// m_currentFunction is set to nullptr and has to be set manually or
|
||||
/// using mergeFlow later.
|
||||
std::vector<CFGNode*> splitFlow(size_t n)
|
||||
{
|
||||
|
@ -23,10 +23,9 @@ using namespace solidity::frontend::experimental;
|
||||
using namespace solidity::util;
|
||||
|
||||
FunctionCallGraph::FunctionCallGraph(solidity::frontend::experimental::Analysis& _analysis):
|
||||
m_analysis(_analysis),
|
||||
m_errorReporter(_analysis.errorReporter()),
|
||||
m_currentNode(nullptr),
|
||||
m_inFunctionDefinition(false)
|
||||
m_analysis(_analysis),
|
||||
m_errorReporter(_analysis.errorReporter()),
|
||||
m_currentFunction(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@ -40,8 +39,9 @@ bool FunctionCallGraph::analyze(SourceUnit const& _sourceUnit)
|
||||
|
||||
bool FunctionCallGraph::visit(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
solAssert(!m_inFunctionDefinition && !m_currentFunction);
|
||||
m_inFunctionDefinition = true;
|
||||
m_currentNode = &_functionDefinition;
|
||||
m_currentFunction = &_functionDefinition;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -49,36 +49,28 @@ void FunctionCallGraph::endVisit(FunctionDefinition const&)
|
||||
{
|
||||
// If we're done visiting a function declaration without said function referencing/calling
|
||||
// another function in its body - insert it into the graph without child nodes.
|
||||
if (!annotation().functionCallGraph.edges.count(m_currentNode))
|
||||
{
|
||||
annotation().functionCallGraph.edges.insert({m_currentNode, {}});
|
||||
annotation().functionCallGraph.reverseEdges[nullptr].insert(m_currentNode);
|
||||
}
|
||||
if (!annotation().functionCallGraph.edges.count(m_currentFunction))
|
||||
annotation().functionCallGraph.edges.insert({m_currentFunction, {}});
|
||||
m_inFunctionDefinition = false;
|
||||
m_currentFunction = nullptr;
|
||||
}
|
||||
|
||||
bool FunctionCallGraph::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto callee = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration);
|
||||
// Check that the identifier is within a function body and is a function, and add it to the graph
|
||||
// as an ``m_currentNode`` -> ``callee`` edge.
|
||||
if (m_inFunctionDefinition && _identifier.annotation().referencedDeclaration && callee)
|
||||
// as an ``m_currentFunction`` -> ``callee`` edge.
|
||||
if (m_inFunctionDefinition && callee)
|
||||
{
|
||||
solAssert(m_currentNode, "Child node must have a parent");
|
||||
add(m_currentNode, callee);
|
||||
solAssert(m_currentFunction, "Child node must have a parent");
|
||||
add(m_currentFunction, callee);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FunctionCallGraph::add(FunctionDefinition const* _caller, FunctionDefinition const* _callee)
|
||||
{
|
||||
// Add caller and callee as and edge, as well as the reverse edge, i.e. callee -> caller.
|
||||
// If the caller is already in the reverse edges as a childless node, remove it, since it now
|
||||
// has a child.
|
||||
annotation().functionCallGraph.edges[_caller].insert(_callee);
|
||||
annotation().functionCallGraph.reverseEdges[_callee].insert(_caller);
|
||||
if (annotation().functionCallGraph.reverseEdges[nullptr].count(_caller) > 0)
|
||||
annotation().functionCallGraph.reverseEdges[nullptr].erase(_caller);
|
||||
}
|
||||
|
||||
FunctionCallGraph::GlobalAnnotation& FunctionCallGraph::annotation()
|
||||
|
@ -51,8 +51,8 @@ private:
|
||||
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
FunctionDefinition const* m_currentNode;
|
||||
bool m_inFunctionDefinition;
|
||||
FunctionDefinition const* m_currentFunction;
|
||||
bool m_inFunctionDefinition = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -35,20 +35,5 @@ std::ostream& solidity::frontend::experimental::operator<<(std::ostream& _out, C
|
||||
}
|
||||
_out << "}" << std::endl;
|
||||
}
|
||||
|
||||
_out << "\nREVERSE EDGES" << "\n";
|
||||
_out << "===================" << std::endl;
|
||||
for (auto [top, subs]: _callGraph.reverseEdges)
|
||||
{
|
||||
std::string topName = top ? top->name() : "nullptr";
|
||||
_out << "(" << topName <<") --> {";
|
||||
for (auto sub: subs)
|
||||
{
|
||||
std::string subName = sub->name().empty() ? "fallback" : sub->name();
|
||||
_out << subName << ",";
|
||||
}
|
||||
_out << "}" << std::endl;
|
||||
}
|
||||
|
||||
return _out;
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ struct CallGraph
|
||||
/// The map contains a key for every possible caller, even if does not actually perform
|
||||
/// any calls.
|
||||
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*>> edges;
|
||||
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*>> reverseEdges;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& _out, CallGraph const& _callGraph);
|
||||
|
Loading…
Reference in New Issue
Block a user