fixup! Syntactic call graph

This commit is contained in:
Nikola Matic 2023-10-02 15:50:36 +02:00
parent 9656cf105b
commit ccae691606
5 changed files with 17 additions and 41 deletions

View File

@ -99,14 +99,14 @@ private:
/// Starts at @a _entry and parses the control flow of @a _node. /// Starts at @a _entry and parses the control flow of @a _node.
/// @returns The node at which the parsed control flow ends. /// @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); CFGNode* createFlow(CFGNode* _entry, ASTNode const& _node);
/// Creates an arc from @a _from to @a _to. /// Creates an arc from @a _from to @a _to.
static void connect(CFGNode* _from, CFGNode* _to); static void connect(CFGNode* _from, CFGNode* _to);
/// Splits the control flow starting at the current node into n paths. /// 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. /// using mergeFlow later.
template<size_t n> template<size_t n>
std::array<CFGNode*, n> splitFlow() std::array<CFGNode*, n> splitFlow()
@ -122,7 +122,7 @@ private:
} }
/// Splits the control flow starting at the current node into @a _n paths. /// 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. /// using mergeFlow later.
std::vector<CFGNode*> splitFlow(size_t n) std::vector<CFGNode*> splitFlow(size_t n)
{ {

View File

@ -23,10 +23,9 @@ using namespace solidity::frontend::experimental;
using namespace solidity::util; using namespace solidity::util;
FunctionCallGraph::FunctionCallGraph(solidity::frontend::experimental::Analysis& _analysis): FunctionCallGraph::FunctionCallGraph(solidity::frontend::experimental::Analysis& _analysis):
m_analysis(_analysis), m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter()), m_errorReporter(_analysis.errorReporter()),
m_currentNode(nullptr), m_currentFunction(nullptr)
m_inFunctionDefinition(false)
{ {
} }
@ -40,8 +39,9 @@ bool FunctionCallGraph::analyze(SourceUnit const& _sourceUnit)
bool FunctionCallGraph::visit(FunctionDefinition const& _functionDefinition) bool FunctionCallGraph::visit(FunctionDefinition const& _functionDefinition)
{ {
solAssert(!m_inFunctionDefinition && !m_currentFunction);
m_inFunctionDefinition = true; m_inFunctionDefinition = true;
m_currentNode = &_functionDefinition; m_currentFunction = &_functionDefinition;
return true; return true;
} }
@ -49,36 +49,28 @@ void FunctionCallGraph::endVisit(FunctionDefinition const&)
{ {
// If we're done visiting a function declaration without said function referencing/calling // 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. // another function in its body - insert it into the graph without child nodes.
if (!annotation().functionCallGraph.edges.count(m_currentNode)) if (!annotation().functionCallGraph.edges.count(m_currentFunction))
{ annotation().functionCallGraph.edges.insert({m_currentFunction, {}});
annotation().functionCallGraph.edges.insert({m_currentNode, {}});
annotation().functionCallGraph.reverseEdges[nullptr].insert(m_currentNode);
}
m_inFunctionDefinition = false; m_inFunctionDefinition = false;
m_currentFunction = nullptr;
} }
bool FunctionCallGraph::visit(Identifier const& _identifier) bool FunctionCallGraph::visit(Identifier const& _identifier)
{ {
auto callee = dynamic_cast<FunctionDefinition const*>(_identifier.annotation().referencedDeclaration); 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 // 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. // as an ``m_currentFunction`` -> ``callee`` edge.
if (m_inFunctionDefinition && _identifier.annotation().referencedDeclaration && callee) if (m_inFunctionDefinition && callee)
{ {
solAssert(m_currentNode, "Child node must have a parent"); solAssert(m_currentFunction, "Child node must have a parent");
add(m_currentNode, callee); add(m_currentFunction, callee);
} }
return true; return true;
} }
void FunctionCallGraph::add(FunctionDefinition const* _caller, FunctionDefinition const* _callee) 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.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() FunctionCallGraph::GlobalAnnotation& FunctionCallGraph::annotation()

View File

@ -51,8 +51,8 @@ private:
Analysis& m_analysis; Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter; langutil::ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentNode; FunctionDefinition const* m_currentFunction;
bool m_inFunctionDefinition; bool m_inFunctionDefinition = false;
}; };
} }

View File

@ -35,20 +35,5 @@ std::ostream& solidity::frontend::experimental::operator<<(std::ostream& _out, C
} }
_out << "}" << std::endl; _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; return _out;
} }

View File

@ -35,7 +35,6 @@ struct CallGraph
/// The map contains a key for every possible caller, even if does not actually perform /// The map contains a key for every possible caller, even if does not actually perform
/// any calls. /// any calls.
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*>> edges; 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); std::ostream& operator<<(std::ostream& _out, CallGraph const& _callGraph);