diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 6ad451ed1..41ba8b2af 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -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 diff --git a/libsolidity/analysis/ControlFlowBuilder.h b/libsolidity/analysis/ControlFlowBuilder.h index 93bc52bd6..13f67410c 100644 --- a/libsolidity/analysis/ControlFlowBuilder.h +++ b/libsolidity/analysis/ControlFlowBuilder.h @@ -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 std::array 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 splitFlow(size_t n) { diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 8d3f40ca0..9c2034473 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0 #include #include +#include #include #include #include @@ -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::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::get() const +{ + return analysis.annotationContainer().functionCallGraphAnnotation; +} + +template<> +FunctionCallGraph::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() +{ + return analysis.annotationContainer().functionCallGraphAnnotation; +} + template<> TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher::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::get() { @@ -108,7 +121,6 @@ TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail: return analysis.annotationContainer().typeInferenceAnnotation; } - template<> TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher::get() { @@ -151,6 +163,7 @@ bool Analysis::check(std::vector> const& _sour { using AnalysisSteps = std::tuple< SyntaxRestrictor, + FunctionCallGraph, TypeClassRegistration, TypeRegistration, TypeInference, diff --git a/libsolidity/experimental/analysis/FunctionCallGraph.cpp b/libsolidity/experimental/analysis/FunctionCallGraph.cpp new file mode 100644 index 000000000..2e712fc5c --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionCallGraph.cpp @@ -0,0 +1,73 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include +#include + +using namespace solidity::frontend::experimental; +using namespace solidity::util; + +FunctionCallGraph::FunctionCallGraph(solidity::frontend::experimental::Analysis& _analysis): + m_analysis(_analysis), + m_errorReporter(_analysis.errorReporter()) +{ +} + +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) +{ + solAssert(!m_currentFunction); + m_currentFunction = &_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_currentFunction)) + annotation().functionCallGraph.edges.insert({m_currentFunction, {}}); + m_currentFunction = nullptr; +} + +bool FunctionCallGraph::visit(Identifier const& _identifier) +{ + auto callee = dynamic_cast(_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_currentFunction`` -> ``callee`` edge. + if (m_currentFunction && callee) + add(m_currentFunction, callee); + return true; +} + +void FunctionCallGraph::add(FunctionDefinition const* _caller, FunctionDefinition const* _callee) +{ + annotation().functionCallGraph.edges[_caller].insert(_callee); +} + +FunctionCallGraph::GlobalAnnotation& FunctionCallGraph::annotation() +{ + return m_analysis.annotation(); +} diff --git a/libsolidity/experimental/analysis/FunctionCallGraph.h b/libsolidity/experimental/analysis/FunctionCallGraph.h new file mode 100644 index 000000000..794b2f18d --- /dev/null +++ b/libsolidity/experimental/analysis/FunctionCallGraph.h @@ -0,0 +1,57 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include +#include +#include +#include + +#include + +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_currentFunction = nullptr; +}; + +} diff --git a/libsolidity/experimental/ast/CallGraph.cpp b/libsolidity/experimental/ast/CallGraph.cpp new file mode 100644 index 000000000..72bfd7dc9 --- /dev/null +++ b/libsolidity/experimental/ast/CallGraph.cpp @@ -0,0 +1,39 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +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; + } + return _out; +} diff --git a/libsolidity/experimental/ast/CallGraph.h b/libsolidity/experimental/ast/CallGraph.h new file mode 100644 index 000000000..8fc523742 --- /dev/null +++ b/libsolidity/experimental/ast/CallGraph.h @@ -0,0 +1,42 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +/// Data structure representing a function call graph. + +#pragma once + +#include + +#include +#include +#include + +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> edges; +}; + +std::ostream& operator<<(std::ostream& _out, CallGraph const& _callGraph); + +} diff --git a/test/libsolidity/syntaxTests/experimental/inference/callgraph.sol b/test/libsolidity/syntaxTests/experimental/inference/callgraph.sol new file mode 100644 index 000000000..5bc75f740 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/callgraph.sol @@ -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 diff --git a/test/libsolidity/syntaxTests/experimental/inference/callgraph_no_leaves.sol b/test/libsolidity/syntaxTests/experimental/inference/callgraph_no_leaves.sol new file mode 100644 index 000000000..c92ad56a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/experimental/inference/callgraph_no_leaves.sol @@ -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: () -> ()