From 769a7687c6b2bbeabcc813f7c8ff81ef48358b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Feb 2021 10:01:24 +0100 Subject: [PATCH 1/4] Make CompilerStack::contractDefinition() public --- libsolidity/interface/CompilerStack.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index e8ab7c8c9..49014427e 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -251,6 +251,10 @@ public: /// @returns the parsed source unit with the supplied name. SourceUnit const& ast(std::string const& _sourceName) const; + /// @returns the parsed contract with the supplied name. Throws an exception if the contract + /// does not exist. + ContractDefinition const& contractDefinition(std::string const& _contractName) const; + /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: /// start line, start column, end line, end column @@ -442,10 +446,6 @@ private: /// Can only be called after state is SourcesSet. Source const& source(std::string const& _sourceName) const; - /// @returns the parsed contract with the supplied name. Throws an exception if the contract - /// does not exist. - ContractDefinition const& contractDefinition(std::string const& _contractName) const; - /// @returns the metadata JSON as a compact string for the given contract. std::string createMetadata(Contract const& _contract) const; From 6c28120f19a86afd14792d607ba05013a6e12e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Feb 2021 09:31:09 +0100 Subject: [PATCH 2/4] Move CallGraph structure to a separate module --- libsolidity/CMakeLists.txt | 2 + libsolidity/analysis/FunctionCallGraph.cpp | 72 ++++++------------ libsolidity/analysis/FunctionCallGraph.h | 53 +++---------- libsolidity/ast/CallGraph.cpp | 46 ++++++++++++ libsolidity/ast/CallGraph.h | 74 +++++++++++++++++++ libsolidity/codegen/ir/IRGenerator.cpp | 9 +-- libsolidity/codegen/ir/IRGenerator.h | 7 +- libsolidity/interface/CompilerStack.cpp | 4 +- libsolidity/interface/CompilerStack.h | 8 +- .../analysis/FunctionCallGraph.cpp | 12 +-- 10 files changed, 171 insertions(+), 116 deletions(-) create mode 100644 libsolidity/ast/CallGraph.cpp create mode 100644 libsolidity/ast/CallGraph.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 981617a84..6d2c095a5 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -56,6 +56,8 @@ set(sources ast/ASTJsonImporter.cpp ast/ASTJsonImporter.h ast/ASTVisitor.h + ast/CallGraph.cpp + ast/CallGraph.h ast/ExperimentalFeatures.h ast/Types.cpp ast/Types.h diff --git a/libsolidity/analysis/FunctionCallGraph.cpp b/libsolidity/analysis/FunctionCallGraph.cpp index aa182cd3a..9a03ca938 100644 --- a/libsolidity/analysis/FunctionCallGraph.cpp +++ b/libsolidity/analysis/FunctionCallGraph.cpp @@ -29,41 +29,17 @@ using namespace ranges; using namespace solidity::frontend; using namespace solidity::util; -bool FunctionCallGraphBuilder::CompareByID::operator()(Node const& _lhs, Node const& _rhs) const -{ - if (_lhs.index() != _rhs.index()) - return _lhs.index() < _rhs.index(); - - if (holds_alternative(_lhs)) - return get(_lhs) < get(_rhs); - return get(_lhs)->id() < get(_rhs)->id(); -} - -bool FunctionCallGraphBuilder::CompareByID::operator()(Node const& _lhs, int64_t _rhs) const -{ - solAssert(!holds_alternative(_lhs), ""); - - return get(_lhs)->id() < _rhs; -} - -bool FunctionCallGraphBuilder::CompareByID::operator()(int64_t _lhs, Node const& _rhs) const -{ - solAssert(!holds_alternative(_rhs), ""); - - return _lhs < get(_rhs)->id(); -} - -FunctionCallGraphBuilder::ContractCallGraph FunctionCallGraphBuilder::buildCreationGraph(ContractDefinition const& _contract) +CallGraph FunctionCallGraphBuilder::buildCreationGraph(ContractDefinition const& _contract) { FunctionCallGraphBuilder builder(_contract); - solAssert(builder.m_currentNode == Node(SpecialNode::Entry), ""); + solAssert(builder.m_currentNode == CallGraph::Node(CallGraph::SpecialNode::Entry), ""); // Create graph for constructor, state vars, etc for (ContractDefinition const* base: _contract.annotation().linearizedBaseContracts | views::reverse) { // The constructor and functions called in state variable initial assignments should have // an edge from Entry - builder.m_currentNode = SpecialNode::Entry; + builder.m_currentNode = CallGraph::SpecialNode::Entry; for (auto const* stateVar: base->stateVariables()) stateVar->accept(builder); @@ -82,21 +58,21 @@ FunctionCallGraphBuilder::ContractCallGraph FunctionCallGraphBuilder::buildCreat inheritanceSpecifier->accept(builder); } - builder.m_currentNode = SpecialNode::Entry; + builder.m_currentNode = CallGraph::SpecialNode::Entry; builder.processQueue(); return move(builder.m_graph); } -FunctionCallGraphBuilder::ContractCallGraph FunctionCallGraphBuilder::buildDeployedGraph( +CallGraph FunctionCallGraphBuilder::buildDeployedGraph( ContractDefinition const& _contract, - FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph + CallGraph const& _creationGraph ) { solAssert(&_creationGraph.contract == &_contract, ""); FunctionCallGraphBuilder builder(_contract); - solAssert(builder.m_currentNode == Node(SpecialNode::Entry), ""); + solAssert(builder.m_currentNode == CallGraph::Node(CallGraph::SpecialNode::Entry), ""); auto getSecondElement = [](auto const& _tuple){ return get<1>(_tuple); }; @@ -121,17 +97,17 @@ FunctionCallGraphBuilder::ContractCallGraph FunctionCallGraphBuilder::buildDeplo // All functions present in internal dispatch at creation time could potentially be pointers // assigned to state variables and as such may be reachable after deployment as well. - builder.m_currentNode = SpecialNode::InternalDispatch; - for (Node const& dispatchTarget: valueOrDefault(_creationGraph.edges, SpecialNode::InternalDispatch, {})) + builder.m_currentNode = CallGraph::SpecialNode::InternalDispatch; + for (CallGraph::Node const& dispatchTarget: valueOrDefault(_creationGraph.edges, CallGraph::SpecialNode::InternalDispatch, {})) { - solAssert(!holds_alternative(dispatchTarget), ""); + solAssert(!holds_alternative(dispatchTarget), ""); solAssert(get(dispatchTarget) != nullptr, ""); // Visit the callable to add not only it but also everything it calls too builder.functionReferenced(*get(dispatchTarget), false); } - builder.m_currentNode = SpecialNode::Entry; + builder.m_currentNode = CallGraph::SpecialNode::Entry; builder.processQueue(); return move(builder.m_graph); @@ -149,7 +125,7 @@ bool FunctionCallGraphBuilder::visit(FunctionCall const& _functionCall) // If it's not a direct call, we don't really know which function will be called (it may even // change at runtime). All we can do is to add an edge to the dispatch which in turn has // edges to all functions could possibly be called. - add(m_currentNode, SpecialNode::InternalDispatch); + add(m_currentNode, CallGraph::SpecialNode::InternalDispatch); return true; } @@ -241,13 +217,13 @@ void FunctionCallGraphBuilder::enqueueCallable(CallableDeclaration const& _calla m_visitQueue.push_back(&_callable); // Insert the callable to the graph (with no edges coming out of it) to mark it as visited. - m_graph.edges.insert({Node(&_callable), {}}); + m_graph.edges.insert({CallGraph::Node(&_callable), {}}); } } void FunctionCallGraphBuilder::processQueue() { - solAssert(m_currentNode == Node(SpecialNode::Entry), "Visit queue is already being processed."); + solAssert(m_currentNode == CallGraph::Node(CallGraph::SpecialNode::Entry), "Visit queue is already being processed."); while (!m_visitQueue.empty()) { @@ -258,10 +234,10 @@ void FunctionCallGraphBuilder::processQueue() get(m_currentNode)->accept(*this); } - m_currentNode = SpecialNode::Entry; + m_currentNode = CallGraph::SpecialNode::Entry; } -void FunctionCallGraphBuilder::add(Node _caller, Node _callee) +void FunctionCallGraphBuilder::add(CallGraph::Node _caller, CallGraph::Node _callee) { m_graph.edges[_caller].insert(_callee); } @@ -271,29 +247,27 @@ void FunctionCallGraphBuilder::functionReferenced(CallableDeclaration const& _ca if (_calledDirectly) { solAssert( - holds_alternative(m_currentNode) || m_graph.edges.count(m_currentNode) > 0, + holds_alternative(m_currentNode) || m_graph.edges.count(m_currentNode) > 0, "Adding an edge from a node that has not been visited yet." ); add(m_currentNode, &_callable); } else - add(SpecialNode::InternalDispatch, &_callable); + add(CallGraph::SpecialNode::InternalDispatch, &_callable); enqueueCallable(_callable); } -ostream& solidity::frontend::operator<<(ostream& _out, FunctionCallGraphBuilder::Node const& _node) +ostream& solidity::frontend::operator<<(ostream& _out, CallGraph::Node const& _node) { - using SpecialNode = FunctionCallGraphBuilder::SpecialNode; - - if (holds_alternative(_node)) - switch (get(_node)) + if (holds_alternative(_node)) + switch (get(_node)) { - case SpecialNode::InternalDispatch: + case CallGraph::SpecialNode::InternalDispatch: _out << "InternalDispatch"; break; - case SpecialNode::Entry: + case CallGraph::SpecialNode::Entry: _out << "Entry"; break; default: solAssert(false, "Invalid SpecialNode type"); diff --git a/libsolidity/analysis/FunctionCallGraph.h b/libsolidity/analysis/FunctionCallGraph.h index 53d1dceb2..cbfe95c6a 100644 --- a/libsolidity/analysis/FunctionCallGraph.h +++ b/libsolidity/analysis/FunctionCallGraph.h @@ -20,18 +20,16 @@ #include #include +#include #include #include -#include -#include namespace solidity::frontend { /** - * Creates a Function call graph for a contract at the granularity of Solidity - * functions and modifiers. The graph can represent the situation either at contract creation + * Creates a function call graph for a contract at the granularity of Solidity functions and modifiers. * or after deployment. The graph does not preserve temporal relations between calls - edges * coming out of the same node show which calls were performed but not in what order. * @@ -59,43 +57,10 @@ namespace solidity::frontend class FunctionCallGraphBuilder: private ASTConstVisitor { public: - enum class SpecialNode - { - InternalDispatch, - Entry, - }; - - using Node = std::variant; - - struct CompareByID - { - using is_transparent = void; - bool operator()(Node const& _lhs, Node const& _rhs) const; - bool operator()(Node const& _lhs, int64_t _rhs) const; - bool operator()(int64_t _lhs, Node const& _rhs) const; - }; - - struct ContractCallGraph - { - /// Contract for which this is the graph - ContractDefinition const& contract; - - /// 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, CompareByID> edges; - - /// Contracts that may get created with `new` by functions present in the graph. - std::set createdContracts; - - /// Events that may get emitted by functions present in the graph. - std::set emittedEvents; - }; - - static ContractCallGraph buildCreationGraph(ContractDefinition const& _contract); - static ContractCallGraph buildDeployedGraph( + static CallGraph buildCreationGraph(ContractDefinition const& _contract); + static CallGraph buildDeployedGraph( ContractDefinition const& _contract, - FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph + CallGraph const& _creationGraph ); private: @@ -112,14 +77,14 @@ private: void enqueueCallable(CallableDeclaration const& _callable); void processQueue(); - void add(Node _caller, Node _callee); + void add(CallGraph::Node _caller, CallGraph::Node _callee); void functionReferenced(CallableDeclaration const& _callable, bool _calledDirectly = true); - Node m_currentNode = SpecialNode::Entry; - ContractCallGraph m_graph; + CallGraph::Node m_currentNode = CallGraph::SpecialNode::Entry; + CallGraph m_graph; std::deque m_visitQueue; }; -std::ostream& operator<<(std::ostream& _out, FunctionCallGraphBuilder::Node const& _node); +std::ostream& operator<<(std::ostream& _out, CallGraph::Node const& _node); } diff --git a/libsolidity/ast/CallGraph.cpp b/libsolidity/ast/CallGraph.cpp new file mode 100644 index 000000000..bf37b2264 --- /dev/null +++ b/libsolidity/ast/CallGraph.cpp @@ -0,0 +1,46 @@ +/* + 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 std; +using namespace solidity::frontend; + +bool CallGraph::CompareByID::operator()(Node const& _lhs, Node const& _rhs) const +{ + if (_lhs.index() != _rhs.index()) + return _lhs.index() < _rhs.index(); + + if (holds_alternative(_lhs)) + return get(_lhs) < get(_rhs); + return get(_lhs)->id() < get(_rhs)->id(); +} + +bool CallGraph::CompareByID::operator()(Node const& _lhs, int64_t _rhs) const +{ + solAssert(!holds_alternative(_lhs), ""); + + return get(_lhs)->id() < _rhs; +} + +bool CallGraph::CompareByID::operator()(int64_t _lhs, Node const& _rhs) const +{ + solAssert(!holds_alternative(_rhs), ""); + + return _lhs < get(_rhs)->id(); +} diff --git a/libsolidity/ast/CallGraph.h b/libsolidity/ast/CallGraph.h new file mode 100644 index 000000000..eabf7ef10 --- /dev/null +++ b/libsolidity/ast/CallGraph.h @@ -0,0 +1,74 @@ +/* + 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 +{ + +/** + * Function call graph for a contract at the granularity of Solidity functions and modifiers. + * The graph can represent the situation either at contract creation or after deployment. + * The graph does not preserve temporal relations between calls - edges coming out of the same node + * show which calls were performed but not in what order. + * + * Stores also extra information about contracts that can be created and events that can be emitted + * from any of the functions in it. + */ +struct CallGraph +{ + enum class SpecialNode + { + InternalDispatch, + Entry, + }; + + using Node = std::variant; + + struct CompareByID + { + using is_transparent = void; + bool operator()(Node const& _lhs, Node const& _rhs) const; + bool operator()(Node const& _lhs, int64_t _rhs) const; + bool operator()(int64_t _lhs, Node const& _rhs) const; + }; + + /// Contract for which this is the graph + ContractDefinition const& contract; + + /// 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, CompareByID> edges; + + /// Contracts that may get created with `new` by functions present in the graph. + std::set createdContracts; + + /// Events that may get emitted by functions present in the graph. + std::set emittedEvents; +}; + +} diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 92406aa7b..4cea06b91 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -78,11 +78,11 @@ void verifyCallGraph( } set collectReachableCallables( - FunctionCallGraphBuilder::ContractCallGraph const& _graph + CallGraph const& _graph ) { set reachableCallables; - for (FunctionCallGraphBuilder::Node const& reachableNode: _graph.edges | views::keys) + for (CallGraph::Node const& reachableNode: _graph.edges | views::keys) if (holds_alternative(reachableNode)) reachableCallables.emplace(get(reachableNode)); @@ -119,10 +119,7 @@ pair IRGenerator::run( return {warning + ir, warning + asmStack.print()}; } -void IRGenerator::verifyCallGraphs( - FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, - FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph -) +void IRGenerator::verifyCallGraphs(CallGraph const& _creationGraph, CallGraph const& _deployedGraph) { // m_creationFunctionList and m_deployedFunctionList are not used for any other purpose so // we can just destroy them without bothering to make a copy. diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index f467b5d3f..cbf5354cd 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include #include @@ -57,10 +57,7 @@ public: std::map const& _otherYulSources ); - void verifyCallGraphs( - FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, - FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph - ); + void verifyCallGraphs(CallGraph const& _creationGraph, CallGraph const& _deployedGraph); private: std::string generate( diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 426eaceba..f897b01d2 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -950,7 +950,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const return _contract.metadata.init([&]{ return createMetadata(_contract); }); } -FunctionCallGraphBuilder::ContractCallGraph const& CompilerStack::creationCallGraph(string const& _contractName) const +CallGraph const& CompilerStack::creationCallGraph(string const& _contractName) const { if (m_stackState < AnalysisPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); @@ -959,7 +959,7 @@ FunctionCallGraphBuilder::ContractCallGraph const& CompilerStack::creationCallGr return contract(_contractName).creationCallGraph.value(); } -FunctionCallGraphBuilder::ContractCallGraph const& CompilerStack::deployedCallGraph(string const& _contractName) const +CallGraph const& CompilerStack::deployedCallGraph(string const& _contractName) const { if (m_stackState < AnalysisPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 49014427e..604a20960 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -348,10 +348,10 @@ public: Json::Value gasEstimates(std::string const& _contractName) const; /// @returns a graph with edges representing calls between functions that may happen during contract construction. - FunctionCallGraphBuilder::ContractCallGraph const& creationCallGraph(std::string const& _contractName) const; + CallGraph const& creationCallGraph(std::string const& _contractName) const; /// @returns a graph with edges representing calls between functions that may happen in a deployed contract. - FunctionCallGraphBuilder::ContractCallGraph const& deployedCallGraph(std::string const& _contractName) const; + CallGraph const& deployedCallGraph(std::string const& _contractName) const; /// Changes the format of the metadata appended at the end of the bytecode. /// This is mostly a workaround to avoid bytecode and gas differences between compiler builds @@ -394,8 +394,8 @@ private: util::LazyInit runtimeGeneratedSources; mutable std::optional sourceMapping; mutable std::optional runtimeSourceMapping; - std::optional creationCallGraph; - std::optional deployedCallGraph; + std::optional creationCallGraph; + std::optional deployedCallGraph; }; /// Loads the missing sources from @a _ast (named @a _path) using the callback diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index 506509ea9..17a6c0c76 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -53,12 +53,12 @@ using namespace solidity::langutil; using namespace solidity::frontend; using EdgeMap = map< - FunctionCallGraphBuilder::Node, - set, - FunctionCallGraphBuilder::CompareByID + CallGraph::Node, + set, + CallGraph::CompareByID >; using EdgeNames = set>; -using CallGraphMap = map; +using CallGraphMap = map; namespace { @@ -131,7 +131,7 @@ void checkCallGraphExpectations( ) { auto getContractName = [](ContractDefinition const* _contract){ return _contract->name(); }; - auto eventToString = [](EventDefinition const* _event){ return toString(FunctionCallGraphBuilder::Node(_event)); }; + auto eventToString = [](EventDefinition const* _event){ return toString(CallGraph::Node(_event)); }; auto notEmpty = [](set const& _set){ return !_set.empty(); }; soltestAssert( @@ -157,7 +157,7 @@ void checkCallGraphExpectations( for (string const& contractName: _expectedEdges | views::keys) { soltestAssert(_callGraphs.at(contractName) != nullptr, ""); - FunctionCallGraphBuilder::ContractCallGraph const& callGraph = *_callGraphs.at(contractName); + CallGraph const& callGraph = *_callGraphs.at(contractName); edges[contractName] = edgeNames(callGraph.edges); if (!callGraph.createdContracts.empty()) From 54eb34d6fd4c6d47b23dd7084a7470f1f405ac37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Feb 2021 10:21:13 +0100 Subject: [PATCH 3/4] Move call graphs from CompilerStack to ContractDefinitionAnnotation --- libsolidity/ast/ASTAnnotations.h | 6 +++ libsolidity/interface/CompilerStack.cpp | 37 ++++++++----------- libsolidity/interface/CompilerStack.h | 8 ---- .../analysis/FunctionCallGraph.cpp | 4 +- 4 files changed, 24 insertions(+), 31 deletions(-) diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index e6ef18c3a..f2c2d5812 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -49,6 +49,8 @@ class Type; using TypePointer = Type const*; using namespace util; +struct CallGraph; + struct ASTAnnotation { ASTAnnotation() = default; @@ -162,6 +164,10 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocu /// Mapping containing the nodes that define the arguments for base constructors. /// These can either be inheritance specifiers or modifier invocations. std::map baseConstructorArguments; + /// A graph with edges representing calls between functions that may happen during contract construction. + SetOnce> creationCallGraph; + /// A graph with edges representing calls between functions that may happen in a deployed contract. + SetOnce> deployedCallGraph; }; struct CallableDeclarationAnnotation: DeclarationAnnotation diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f897b01d2..297c07411 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -407,8 +407,18 @@ bool CompilerStack::analyze() if (auto const* contractDefinition = dynamic_cast(node.get())) { Contract& contractState = m_contracts.at(contractDefinition->fullyQualifiedName()); - contractState.creationCallGraph.emplace(FunctionCallGraphBuilder::buildCreationGraph(*contractDefinition)); - contractState.deployedCallGraph.emplace(FunctionCallGraphBuilder::buildDeployedGraph(*contractDefinition, *contractState.creationCallGraph)); + + contractState.contract->annotation().creationCallGraph = make_unique( + FunctionCallGraphBuilder::buildCreationGraph( + *contractDefinition + ) + ); + contractState.contract->annotation().deployedCallGraph = make_unique( + FunctionCallGraphBuilder::buildDeployedGraph( + *contractDefinition, + **contractState.contract->annotation().creationCallGraph + ) + ); } } @@ -950,24 +960,6 @@ string const& CompilerStack::metadata(Contract const& _contract) const return _contract.metadata.init([&]{ return createMetadata(_contract); }); } -CallGraph const& CompilerStack::creationCallGraph(string const& _contractName) const -{ - if (m_stackState < AnalysisPerformed) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - - solAssert(contract(_contractName).creationCallGraph.has_value(), ""); - return contract(_contractName).creationCallGraph.value(); -} - -CallGraph const& CompilerStack::deployedCallGraph(string const& _contractName) const -{ - if (m_stackState < AnalysisPerformed) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - - solAssert(contract(_contractName).deployedCallGraph.has_value(), ""); - return contract(_contractName).deployedCallGraph.value(); -} - Scanner const& CompilerStack::scanner(string const& _sourceName) const { if (m_stackState < SourcesSet) @@ -1307,7 +1299,10 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources); - generator.verifyCallGraphs(compiledContract.creationCallGraph.value(), compiledContract.deployedCallGraph.value()); + generator.verifyCallGraphs( + **compiledContract.contract->annotation().creationCallGraph, + **compiledContract.contract->annotation().deployedCallGraph + ); } void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 604a20960..a7861d221 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -347,12 +347,6 @@ public: /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions Json::Value gasEstimates(std::string const& _contractName) const; - /// @returns a graph with edges representing calls between functions that may happen during contract construction. - CallGraph const& creationCallGraph(std::string const& _contractName) const; - - /// @returns a graph with edges representing calls between functions that may happen in a deployed contract. - CallGraph const& deployedCallGraph(std::string const& _contractName) const; - /// Changes the format of the metadata appended at the end of the bytecode. /// This is mostly a workaround to avoid bytecode and gas differences between compiler builds /// caused by differences in metadata. Should only be used for testing. @@ -394,8 +388,6 @@ private: util::LazyInit runtimeGeneratedSources; mutable std::optional sourceMapping; mutable std::optional runtimeSourceMapping; - std::optional creationCallGraph; - std::optional deployedCallGraph; }; /// Loads the missing sources from @a _ast (named @a _path) using the callback diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index 17a6c0c76..057e30653 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -116,8 +116,8 @@ tuple collectGraphs(CompilerStack const& _compilerSt soltestAssert(fullyQualifiedContractName.size() > 0 && fullyQualifiedContractName[0] == ':', ""); string contractName = fullyQualifiedContractName.substr(1); - get<0>(graphs).emplace(contractName, &_compilerStack.creationCallGraph(fullyQualifiedContractName)); - get<1>(graphs).emplace(contractName, &_compilerStack.deployedCallGraph(fullyQualifiedContractName)); + get<0>(graphs).emplace(contractName, _compilerStack.contractDefinition(fullyQualifiedContractName).annotation().creationCallGraph->get()); + get<1>(graphs).emplace(contractName, _compilerStack.contractDefinition(fullyQualifiedContractName).annotation().deployedCallGraph->get()); } return graphs; From 781f00771c80cda4add5ce9acf1d870ed55faab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Tue, 23 Feb 2021 10:28:09 +0100 Subject: [PATCH 4/4] Remove IRGenerator::verifyCallGraphs and make generate() verify the graphs automatically --- libsolidity/codegen/ir/IRGenerator.cpp | 26 +++++++------------------ libsolidity/codegen/ir/IRGenerator.h | 5 ----- libsolidity/interface/CompilerStack.cpp | 5 ----- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 4cea06b91..7a13df22a 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -119,28 +119,11 @@ pair IRGenerator::run( return {warning + ir, warning + asmStack.print()}; } -void IRGenerator::verifyCallGraphs(CallGraph const& _creationGraph, CallGraph const& _deployedGraph) -{ - // m_creationFunctionList and m_deployedFunctionList are not used for any other purpose so - // we can just destroy them without bothering to make a copy. - - verifyCallGraph(collectReachableCallables(_creationGraph), move(m_creationFunctionList)); - m_creationFunctionList = {}; - - verifyCallGraph(collectReachableCallables(_deployedGraph), move(m_deployedFunctionList)); - m_deployedFunctionList = {}; -} - string IRGenerator::generate( ContractDefinition const& _contract, map const& _otherYulSources ) { - // Remember to call verifyCallGraphs() (which clears the list of generated functions) if you - // want to reuse the generator. - solAssert(m_creationFunctionList.empty(), ""); - solAssert(m_deployedFunctionList.empty(), ""); - auto subObjectSources = [&_otherYulSources](std::set const& subObjects) -> string { std::string subObjectsSources; @@ -202,7 +185,7 @@ string IRGenerator::generate( t("deploy", deployCode(_contract)); generateImplicitConstructors(_contract); - m_creationFunctionList = generateQueuedFunctions(); + set creationFunctionList = generateQueuedFunctions(); InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions(); t("functions", m_context.functionCollector().requestedFunctions()); @@ -223,7 +206,7 @@ string IRGenerator::generate( t("DeployedObject", IRNames::deployedObject(_contract)); t("library_address", IRNames::libraryAddressImmutable()); t("dispatch", dispatchRoutine(_contract)); - m_deployedFunctionList = generateQueuedFunctions(); + set deployedFunctionList = generateQueuedFunctions(); generateInternalDispatchFunctions(); t("deployedFunctions", m_context.functionCollector().requestedFunctions()); t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated())); @@ -232,6 +215,11 @@ string IRGenerator::generate( bool deployedInvolvesAssembly = m_context.inlineAssemblySeen(); t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly)); + solAssert(_contract.annotation().creationCallGraph->get() != nullptr, ""); + solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, ""); + verifyCallGraph(collectReachableCallables(**_contract.annotation().creationCallGraph), move(creationFunctionList)); + verifyCallGraph(collectReachableCallables(**_contract.annotation().deployedCallGraph), move(deployedFunctionList)); + return t.render(); } diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index cbf5354cd..83621f206 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -57,8 +57,6 @@ public: std::map const& _otherYulSources ); - void verifyCallGraphs(CallGraph const& _creationGraph, CallGraph const& _deployedGraph); - private: std::string generate( ContractDefinition const& _contract, @@ -119,9 +117,6 @@ private: langutil::EVMVersion const m_evmVersion; OptimiserSettings const m_optimiserSettings; - std::set m_creationFunctionList; - std::set m_deployedFunctionList; - IRGenerationContext m_context; YulUtilFunctions m_utils; }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 297c07411..bb323a72f 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1298,11 +1298,6 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources); - - generator.verifyCallGraphs( - **compiledContract.contract->annotation().creationCallGraph, - **compiledContract.contract->annotation().deployedCallGraph - ); } void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)