Skip to content

Commit

Permalink
Merge pull request #14575 from ethereum/syntactic-call-graph
Browse files Browse the repository at this point in the history
Function dependency graph
  • Loading branch information
nikola-matic authored Dec 4, 2023
2 parents f25618c + 460fee0 commit 326ef77
Show file tree
Hide file tree
Showing 15 changed files with 470 additions and 23 deletions.
3 changes: 3 additions & 0 deletions libsolidity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ set(sources
experimental/analysis/Analysis.h
experimental/analysis/DebugWarner.cpp
experimental/analysis/DebugWarner.h
experimental/analysis/FunctionDependencyAnalysis.cpp
experimental/analysis/FunctionDependencyAnalysis.h
experimental/analysis/TypeClassRegistration.cpp
experimental/analysis/TypeClassRegistration.h
experimental/analysis/TypeInference.cpp
Expand All @@ -196,6 +198,7 @@ set(sources
experimental/analysis/TypeRegistration.h
experimental/analysis/SyntaxRestrictor.cpp
experimental/analysis/SyntaxRestrictor.h
experimental/ast/FunctionCallGraph.h
experimental/ast/Type.cpp
experimental/ast/Type.h
experimental/ast/TypeSystem.cpp
Expand Down
19 changes: 16 additions & 3 deletions libsolidity/experimental/analysis/Analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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/FunctionDependencyAnalysis.h>
#include <libsolidity/experimental/analysis/SyntaxRestrictor.h>
#include <libsolidity/experimental/analysis/TypeClassRegistration.h>
#include <libsolidity/experimental/analysis/TypeInference.h>
Expand All @@ -35,6 +36,7 @@ struct Analysis::AnnotationContainer

struct Analysis::GlobalAnnotationContainer
{
FunctionDependencyAnalysis::GlobalAnnotation functionDependencyGraphAnnotation;
TypeClassRegistration::GlobalAnnotation typeClassRegistrationAnnotation;
TypeRegistration::GlobalAnnotation typeRegistrationAnnotation;
TypeInference::GlobalAnnotation typeInferenceAnnotation;
Expand All @@ -52,7 +54,6 @@ TypeClassRegistration::GlobalAnnotation const& solidity::frontend::experimental:
return analysis.annotationContainer().typeClassRegistrationAnnotation;
}


template<>
TypeClassRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeClassRegistration>::get()
{
Expand All @@ -65,6 +66,18 @@ TypeClassRegistration::Annotation const& solidity::frontend::experimental::detai
return analysis.annotationContainer(_node).typeClassRegistrationAnnotation;
}

template<>
FunctionDependencyAnalysis::GlobalAnnotation const& solidity::frontend::experimental::detail::ConstAnnotationFetcher<FunctionDependencyAnalysis>::get() const
{
return analysis.annotationContainer().functionDependencyGraphAnnotation;
}

template<>
FunctionDependencyAnalysis::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<FunctionDependencyAnalysis>::get()
{
return analysis.annotationContainer().functionDependencyGraphAnnotation;
}

template<>
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
{
Expand All @@ -77,7 +90,6 @@ TypeRegistration::GlobalAnnotation const& solidity::frontend::experimental::deta
return analysis.annotationContainer().typeRegistrationAnnotation;
}


template<>
TypeRegistration::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get()
{
Expand Down Expand Up @@ -108,7 +120,6 @@ TypeInference::GlobalAnnotation const& solidity::frontend::experimental::detail:
return analysis.annotationContainer().typeInferenceAnnotation;
}


template<>
TypeInference::GlobalAnnotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get()
{
Expand Down Expand Up @@ -153,6 +164,8 @@ bool Analysis::check(std::vector<std::shared_ptr<SourceUnit const>> const& _sour
SyntaxRestrictor,
TypeClassRegistration,
TypeRegistration,
// TODO move after step introduced in https://github.com/ethereum/solidity/pull/14578, but before TypeInference
FunctionDependencyAnalysis,
TypeInference,
DebugWarner
>;
Expand Down
72 changes: 72 additions & 0 deletions libsolidity/experimental/analysis/FunctionDependencyAnalysis.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
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/FunctionDependencyAnalysis.h>

using namespace solidity::frontend::experimental;
using namespace solidity::util;

FunctionDependencyAnalysis::FunctionDependencyAnalysis(Analysis& _analysis):
m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter())
{
}

bool FunctionDependencyAnalysis::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
return !m_errorReporter.hasErrors();
}

bool FunctionDependencyAnalysis::visit(FunctionDefinition const& _functionDefinition)
{
solAssert(!m_currentFunction);
m_currentFunction = &_functionDefinition;
// Insert a function definition pointer that maps to an empty set; the pointed to set will later be
// populated in ``endVisit(Identifier const& _identifier)`` if ``m_currentFunction`` references another.
auto [_, inserted] = annotation().functionCallGraph.edges.try_emplace(
m_currentFunction, std::set<FunctionDefinition const*, ASTCompareByID<FunctionDefinition>>{}
);
solAssert(inserted);
return true;
}

void FunctionDependencyAnalysis::endVisit(FunctionDefinition const&)
{
m_currentFunction = nullptr;
}

void FunctionDependencyAnalysis::endVisit(Identifier const& _identifier)
{
auto const* 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_currentFunction`` -> ``callee`` edge.
if (m_currentFunction && callee)
addEdge(m_currentFunction, callee);
}

void FunctionDependencyAnalysis::addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee)
{
annotation().functionCallGraph.edges[_caller].insert(_callee);
}

FunctionDependencyAnalysis::GlobalAnnotation& FunctionDependencyAnalysis::annotation()
{
return m_analysis.annotation<FunctionDependencyAnalysis>();
}
57 changes: 57 additions & 0 deletions libsolidity/experimental/analysis/FunctionDependencyAnalysis.h
Original file line number Diff line number Diff line change
@@ -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 <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/FunctionCallGraph.h>

#include <memory>

namespace solidity::frontend::experimental
{

class Analysis;

class FunctionDependencyAnalysis: private ASTConstVisitor
{
public:
FunctionDependencyAnalysis(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);

struct Annotation {};
struct GlobalAnnotation
{
FunctionDependencyGraph functionCallGraph;
};

private:
bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const&) override;
void endVisit(Identifier const& _identifier) override;
void addEdge(FunctionDefinition const* _caller, FunctionDefinition const* _callee);
GlobalAnnotation& annotation();

Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentFunction = nullptr;
};

}
40 changes: 40 additions & 0 deletions libsolidity/experimental/ast/FunctionCallGraph.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
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 dependency graph.

#pragma once

#include <libsolidity/ast/AST.h>

#include <map>
#include <set>
#include <ostream>

namespace solidity::frontend::experimental
{

struct FunctionDependencyGraph
{
/// Graph edges. Edges are directed and lead from the caller to the callee.
/// The map contains a key for every function, even if does not actually perform
/// any calls.
std::map<FunctionDefinition const*, std::set<FunctionDefinition const*, ASTCompareByID<FunctionDefinition>>, ASTCompareByID<FunctionDefinition>> edges;
};

}
7 changes: 7 additions & 0 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
#include <libsolidity/parsing/Parser.h>

#include <libsolidity/experimental/analysis/Analysis.h>
#include <libsolidity/experimental/analysis/FunctionDependencyAnalysis.h>
#include <libsolidity/experimental/codegen/IRGenerator.h>

#include <libsolidity/codegen/ir/Common.h>
Expand Down Expand Up @@ -1947,3 +1948,9 @@ Json::Value CompilerStack::gasEstimates(std::string const& _contractName) const

return output;
}

experimental::Analysis const& CompilerStack::experimentalAnalysis() const
{
solAssert(!!m_experimentalAnalysis);
return *m_experimentalAnalysis;
}
2 changes: 2 additions & 0 deletions libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
/// Changes the format of the metadata appended at the end of the bytecode.
void setMetadataFormat(MetadataFormat _metadataFormat) { m_metadataFormat = _metadataFormat; }

experimental::Analysis const& experimentalAnalysis() const;

static MetadataFormat defaultMetadataFormat()
{
return VersionIsRelease ? MetadataFormat::WithReleaseVersionTag : MetadataFormat::WithPrereleaseVersionTag;
Expand Down
2 changes: 2 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ set(libsolidity_sources
libsolidity/ASTJSONTest.h
libsolidity/ErrorCheck.cpp
libsolidity/ErrorCheck.h
libsolidity/FunctionDependencyGraphTest.cpp
libsolidity/FunctionDependencyGraphTest.h
libsolidity/GasCosts.cpp
libsolidity/GasMeter.cpp
libsolidity/GasTest.cpp
Expand Down
42 changes: 22 additions & 20 deletions test/InteractiveTests.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <test/libsolidity/ABIJsonTest.h>
#include <test/libsolidity/ASTJSONTest.h>
#include <test/libsolidity/ASTPropertyTest.h>
#include <libsolidity/FunctionDependencyGraphTest.h>
#include <test/libsolidity/GasTest.h>
#include <test/libsolidity/MemoryGuardTest.h>
#include <test/libsolidity/NatspecJSONTest.h>
Expand Down Expand Up @@ -60,26 +61,27 @@ struct Testsuite
/// Array of testsuits that can be run interactively as well as automatically
Testsuite const g_interactiveTestsuites[] = {
/*
Title Path Subpath SMT NeedsVM Creator function */
{"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create},
{"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create},
{"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create},
{"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create},
{"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create},
{"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create},
{"Control Flow Side Effects","libyul", "controlFlowSideEffects",false, false, &yul::test::ControlFlowSideEffectsTest::create},
{"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create},
{"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create},
{"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}},
{"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create},
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
{"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create},
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
{"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
{"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create},
Title Path Subpath SMT NeedsVM Creator function */
{"Yul Optimizer", "libyul", "yulOptimizerTests", false, false, &yul::test::YulOptimizerTest::create},
{"Yul Interpreter", "libyul", "yulInterpreterTests", false, false, &yul::test::YulInterpreterTest::create},
{"Yul Object Compiler", "libyul", "objectCompiler", false, false, &yul::test::ObjectCompilerTest::create},
{"Yul Control Flow Graph", "libyul", "yulControlFlowGraph", false, false, &yul::test::ControlFlowGraphTest::create},
{"Yul Stack Layout", "libyul", "yulStackLayout", false, false, &yul::test::StackLayoutGeneratorTest::create},
{"Yul Stack Shuffling", "libyul", "yulStackShuffling", false, false, &yul::test::StackShufflingTest::create},
{"Control Flow Side Effects", "libyul", "controlFlowSideEffects", false, false, &yul::test::ControlFlowSideEffectsTest::create},
{"Function Side Effects", "libyul", "functionSideEffects", false, false, &yul::test::FunctionSideEffects::create},
{"Yul Syntax", "libyul", "yulSyntaxTests", false, false, &yul::test::SyntaxTest::create},
{"EVM Code Transform", "libyul", "evmCodeTransform", false, false, &yul::test::EVMCodeTransformTest::create, {"nooptions"}},
{"Syntax", "libsolidity", "syntaxTests", false, false, &SyntaxTest::create},
{"Semantic", "libsolidity", "semanticTests", false, true, &SemanticTest::create},
{"JSON AST", "libsolidity", "ASTJSON", false, false, &ASTJSONTest::create},
{"JSON ABI", "libsolidity", "ABIJson", false, false, &ABIJsonTest::create},
{"JSON Natspec", "libsolidity", "natspecJSON", false, false, &NatspecJSONTest::create},
{"SMT Checker", "libsolidity", "smtCheckerTests", true, false, &SMTCheckerTest::create},
{"Gas Estimates", "libsolidity", "gasTests", false, false, &GasTest::create},
{"Memory Guard", "libsolidity", "memoryGuardTests", false, false, &MemoryGuardTest::create},
{"AST Properties", "libsolidity", "astPropertyTests", false, false, &ASTPropertyTest::create},
{"Function Dependency Graph", "libsolidity", "functionDependencyGraphTests", false, false, &FunctionDependencyGraphTest::create},
};

}
Loading

0 comments on commit 326ef77

Please sign in to comment.