Syntactic call graph

This commit is contained in:
Nikola Matic 2023-09-25 13:47:06 +02:00
parent bdd2f02c83
commit 9656cf105b
8 changed files with 467 additions and 3 deletions

View File

@ -188,6 +188,8 @@ set(sources
experimental/analysis/Analysis.h
experimental/analysis/DebugWarner.cpp
experimental/analysis/DebugWarner.h
experimental/analysis/FunctionCallGraph.cpp
experimental/analysis/FunctionCallGraph.h
experimental/analysis/TypeClassRegistration.cpp
experimental/analysis/TypeClassRegistration.h
experimental/analysis/TypeInference.cpp
@ -196,6 +198,8 @@ set(sources
experimental/analysis/TypeRegistration.h
experimental/analysis/SyntaxRestrictor.cpp
experimental/analysis/SyntaxRestrictor.h
experimental/ast/CallGraph.cpp
experimental/ast/CallGraph.h
experimental/ast/Type.cpp
experimental/ast/Type.h
experimental/ast/TypeSystem.cpp

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/analysis/DebugWarner.h>
#include <libsolidity/experimental/analysis/FunctionCallGraph.h>
#include <libsolidity/experimental/analysis/SyntaxRestrictor.h>
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/TypeInference.h>
@ -35,9 +36,11 @@ struct Analysis::AnnotationContainer
struct Analysis::GlobalAnnotationContainer
{
FunctionCallGraph::GlobalAnnotation functionCallGraphAnnotation;
TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation;
TypeRegistration::GlobalAnnotation typeRegistrationAnnotation;
TypeInference::GlobalAnnotation typeInferenceAnnotation;
};
template<>
@ -52,7 +55,6 @@ TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental:
return analysis.annotationContainer().typeClassRegistrationAnnotation;
}
template<>
TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeClassRegistration>::get()
{
@ -65,6 +67,18 @@ TypeClassRegistration::Annotation const& solidity::frontend::experimental::detai
return analysis.annotationContainer(_node).typeClassRegistrationAnnotation;
}
template<>
FunctionCallGraph::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<FunctionCallGraph>::get() const
{
return analysis.annotationContainer().functionCallGraphAnnotation;
}
template<>
FunctionCallGraph::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<FunctionCallGraph>::get()
{
return analysis.annotationContainer().functionCallGraphAnnotation;
}
template<>
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
{
@ -77,7 +91,6 @@ TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::deta
return analysis.annotationContainer().typeRegistrationAnnotation;
}
template<>
TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get()
{
@ -108,7 +121,6 @@ TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail:
return analysis.annotationContainer().typeInferenceAnnotation;
}
template<>
TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get()
{
@ -151,6 +163,7 @@ bool Analysis::check(std::vector<std::shared_ptr<SourceUnit const>> const& _sour
{
using AnalysisSteps = std::tuple<
SyntaxRestrictor,
FunctionCallGraph,
TypeClassRegistration,
TypeRegistration,
TypeInference,

View File

@ -0,0 +1,87 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/analysis/FunctionCallGraph.h>
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)
{
}
bool FunctionCallGraph::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
// TODO remove debug output before merge
//std::cout << annotation().functionCallGraph << std::endl;
return !m_errorReporter.hasErrors();
}
bool FunctionCallGraph::visit(FunctionDefinition const& _functionDefinition)
{
m_inFunctionDefinition = true;
m_currentNode = &_functionDefinition;
return true;
}
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);
}
m_inFunctionDefinition = false;
}
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)
{
solAssert(m_currentNode, "Child node must have a parent");
add(m_currentNode, 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()
{
return m_analysis.annotation<FunctionCallGraph>();
}

View File

@ -0,0 +1,58 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <liblangutil/ErrorReporter.h>
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/experimental/ast/CallGraph.h>
#include <memory>
namespace solidity::frontend::experimental
{
class Analysis;
class FunctionCallGraph: private ASTConstVisitor
{
public:
FunctionCallGraph(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
struct Annotation {};
struct GlobalAnnotation
{
CallGraph functionCallGraph;
};
private:
bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const&) override;
bool visit(Identifier const& _identifier) override;
void add(FunctionDefinition const* _caller, FunctionDefinition const* _callee);
GlobalAnnotation& annotation();
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentNode;
bool m_inFunctionDefinition;
};
}

View File

@ -0,0 +1,54 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolidity/experimental/ast/CallGraph.h>
using namespace solidity::frontend::experimental;
// TODO remove before merge (delete entire file)
std::ostream& solidity::frontend::experimental::operator<<(std::ostream& _out, CallGraph const& _callGraph)
{
_out << "EDGES" << "\n";
_out << "===================" << std::endl;
for (auto [top, subs]: _callGraph.edges)
{
std::string topName = top->name().empty() ? "fallback" : top->name();
_out << "(" << topName <<") --> {";
for (auto sub: subs)
{
_out << sub->name() << ",";
}
_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;
}

View File

@ -0,0 +1,43 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/// Data structure representing a function call graph.
#pragma once
#include <libsolidity/ast/AST.h>
#include <map>
#include <set>
#include <ostream>
namespace solidity::frontend::experimental
{
struct CallGraph
{
/// Graph edges. Edges are directed and lead from the caller to the callee.
/// 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);
}

View File

@ -0,0 +1,113 @@
// fallback---------->f----------+
// | |
// | |
// | |
// | |
// v v
// g h
//
//
// add
//
// unreferenced
pragma experimental solidity;
type uint256 = __builtin("word");
instantiation uint256: + {
function add(x, y) -> uint256 {
let a = uint256.rep(x);
let b = uint256.rep(y);
assembly {
a := add(a,b)
}
return uint256.abs(a);
}
}
function unreferenced(x:uint256) -> uint256
{
return x;
}
function f(x:uint256) -> uint256
{
return g(h(x));
}
function g(x:uint256) -> uint256
{
return x;
}
function h(x:uint256) -> uint256
{
return x;
}
contract C {
fallback() external {
let a: uint256->uint256 = f;
}
}
// ====
// EVMVersion: >=constantinople
// ----
// Warning 2264: (278-307): Experimental features are turned on. Do not use experimental features on live deployments.
// Info 4164: (309-342): Inferred type: word:(type, +)
// Info 4164: (344-564): Inferred type: void
// Info 4164: (375-562): Inferred type: (word, word) -> word
// Info 4164: (387-393): Inferred type: (word, word)
// Info 4164: (388-389): Inferred type: word
// Info 4164: (391-392): Inferred type: word
// Info 4164: (397-404): Inferred type: word
// Info 4164: (419-420): Inferred type: word
// Info 4164: (423-437): Inferred type: word
// Info 4164: (423-434): Inferred type: word -> word
// Info 4164: (423-430): Inferred type: word
// Info 4164: (435-436): Inferred type: word
// Info 4164: (451-452): Inferred type: word
// Info 4164: (455-469): Inferred type: word
// Info 4164: (455-466): Inferred type: word -> word
// Info 4164: (455-462): Inferred type: word
// Info 4164: (467-468): Inferred type: word
// Info 4164: (541-555): Inferred type: word
// Info 4164: (541-552): Inferred type: word -> word
// Info 4164: (541-548): Inferred type: word
// Info 4164: (553-554): Inferred type: word
// Info 4164: (566-627): Inferred type: word -> word
// Info 4164: (587-598): Inferred type: word
// Info 4164: (588-597): Inferred type: word
// Info 4164: (590-597): Inferred type: word
// Info 4164: (602-609): Inferred type: word
// Info 4164: (623-624): Inferred type: word
// Info 4164: (629-685): Inferred type: word -> word
// Info 4164: (639-650): Inferred type: word
// Info 4164: (640-649): Inferred type: word
// Info 4164: (642-649): Inferred type: word
// Info 4164: (654-661): Inferred type: word
// Info 4164: (675-682): Inferred type: word
// Info 4164: (675-676): Inferred type: word -> word
// Info 4164: (677-681): Inferred type: word
// Info 4164: (677-678): Inferred type: word -> word
// Info 4164: (679-680): Inferred type: word
// Info 4164: (687-737): Inferred type: word -> word
// Info 4164: (697-708): Inferred type: word
// Info 4164: (698-707): Inferred type: word
// Info 4164: (700-707): Inferred type: word
// Info 4164: (712-719): Inferred type: word
// Info 4164: (733-734): Inferred type: word
// Info 4164: (739-789): Inferred type: word -> word
// Info 4164: (749-760): Inferred type: word
// Info 4164: (750-759): Inferred type: word
// Info 4164: (752-759): Inferred type: word
// Info 4164: (764-771): Inferred type: word
// Info 4164: (785-786): Inferred type: word
// Info 4164: (808-872): Inferred type: () -> ()
// Info 4164: (816-818): Inferred type: ()
// Info 4164: (842-861): Inferred type: word -> word
// Info 4164: (845-861): Inferred type: word -> word
// Info 4164: (845-852): Inferred type: word
// Info 4164: (854-861): Inferred type: word
// Info 4164: (864-865): Inferred type: word -> word

View File

@ -0,0 +1,92 @@
// a<------b
// | ^
// | |
// | |
// | |
// | |
// +------>c------->d-------->e------->f
// ^ |
// | |
// | |
// | |
// | v
// h<-------g
pragma experimental solidity;
function a()
{
c();
}
function b()
{
a();
}
function c()
{
b();
d();
}
function d()
{
e();
}
function e()
{
f();
}
function f()
{
g();
}
function g()
{
h();
}
function h()
{
e();
}
// ----
// Warning 2264: (366-395): Experimental features are turned on. Do not use experimental features on live deployments.
// Info 4164: (397-422): Inferred type: () -> ()
// Info 4164: (407-409): Inferred type: ()
// Info 4164: (416-419): Inferred type: ()
// Info 4164: (416-417): Inferred type: () -> ()
// Info 4164: (424-449): Inferred type: () -> ()
// Info 4164: (434-436): Inferred type: ()
// Info 4164: (443-446): Inferred type: ()
// Info 4164: (443-444): Inferred type: () -> ()
// Info 4164: (451-485): Inferred type: () -> ()
// Info 4164: (461-463): Inferred type: ()
// Info 4164: (470-473): Inferred type: ()
// Info 4164: (470-471): Inferred type: () -> ()
// Info 4164: (479-482): Inferred type: ()
// Info 4164: (479-480): Inferred type: () -> ()
// Info 4164: (487-512): Inferred type: () -> ()
// Info 4164: (497-499): Inferred type: ()
// Info 4164: (506-509): Inferred type: ()
// Info 4164: (506-507): Inferred type: () -> ()
// Info 4164: (514-539): Inferred type: () -> ()
// Info 4164: (524-526): Inferred type: ()
// Info 4164: (533-536): Inferred type: ()
// Info 4164: (533-534): Inferred type: () -> ()
// Info 4164: (541-566): Inferred type: () -> ()
// Info 4164: (551-553): Inferred type: ()
// Info 4164: (560-563): Inferred type: ()
// Info 4164: (560-561): Inferred type: () -> ()
// Info 4164: (568-593): Inferred type: () -> ()
// Info 4164: (578-580): Inferred type: ()
// Info 4164: (587-590): Inferred type: ()
// Info 4164: (587-588): Inferred type: () -> ()
// Info 4164: (595-620): Inferred type: () -> ()
// Info 4164: (605-607): Inferred type: ()
// Info 4164: (614-617): Inferred type: ()
// Info 4164: (614-615): Inferred type: () -> ()