mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #10990 from ethereum/call-graph-as-contract-annotation
Call graph as contract annotation
This commit is contained in:
		
						commit
						dd1ae5ba11
					
				| @ -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 | ||||
|  | ||||
| @ -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<SpecialNode>(_lhs)) | ||||
| 		return get<SpecialNode>(_lhs) < get<SpecialNode>(_rhs); | ||||
| 	return get<CallableDeclaration const*>(_lhs)->id() < get<CallableDeclaration const*>(_rhs)->id(); | ||||
| } | ||||
| 
 | ||||
| bool FunctionCallGraphBuilder::CompareByID::operator()(Node const& _lhs, int64_t _rhs) const | ||||
| { | ||||
| 	solAssert(!holds_alternative<SpecialNode>(_lhs), ""); | ||||
| 
 | ||||
| 	return get<CallableDeclaration const*>(_lhs)->id() < _rhs; | ||||
| } | ||||
| 
 | ||||
| bool FunctionCallGraphBuilder::CompareByID::operator()(int64_t _lhs, Node const& _rhs) const | ||||
| { | ||||
| 	solAssert(!holds_alternative<SpecialNode>(_rhs), ""); | ||||
| 
 | ||||
| 	return _lhs < get<CallableDeclaration const*>(_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<SpecialNode>(dispatchTarget), ""); | ||||
| 		solAssert(!holds_alternative<CallGraph::SpecialNode>(dispatchTarget), ""); | ||||
| 		solAssert(get<CallableDeclaration const*>(dispatchTarget) != nullptr, ""); | ||||
| 
 | ||||
| 		// Visit the callable to add not only it but also everything it calls too
 | ||||
| 		builder.functionReferenced(*get<CallableDeclaration const*>(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<CallableDeclaration const*>(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<SpecialNode>(m_currentNode) || m_graph.edges.count(m_currentNode) > 0, | ||||
| 			holds_alternative<CallGraph::SpecialNode>(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<SpecialNode>(_node)) | ||||
| 		switch (get<SpecialNode>(_node)) | ||||
| 	if (holds_alternative<CallGraph::SpecialNode>(_node)) | ||||
| 		switch (get<CallGraph::SpecialNode>(_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"); | ||||
|  | ||||
| @ -20,18 +20,16 @@ | ||||
| 
 | ||||
| #include <libsolidity/ast/ASTForward.h> | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| #include <libsolidity/ast/CallGraph.h> | ||||
| 
 | ||||
| #include <deque> | ||||
| #include <ostream> | ||||
| #include <variant> | ||||
| #include <vector> | ||||
| 
 | ||||
| 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<CallableDeclaration const*, SpecialNode>; | ||||
| 
 | ||||
| 	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<Node, std::set<Node, CompareByID>, CompareByID> edges; | ||||
| 
 | ||||
| 		/// Contracts that may get created with `new` by functions present in the graph.
 | ||||
| 		std::set<ContractDefinition const*, ASTNode::CompareByID> createdContracts; | ||||
| 
 | ||||
| 		/// Events that may get emitted by functions present in the graph.
 | ||||
| 		std::set<EventDefinition const*, ASTNode::CompareByID> 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<CallableDeclaration const*> m_visitQueue; | ||||
| }; | ||||
| 
 | ||||
| std::ostream& operator<<(std::ostream& _out, FunctionCallGraphBuilder::Node const& _node); | ||||
| std::ostream& operator<<(std::ostream& _out, CallGraph::Node const& _node); | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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<FunctionDefinition const*, ASTNode const*> baseConstructorArguments; | ||||
| 	/// A graph with edges representing calls between functions that may happen during contract construction.
 | ||||
| 	SetOnce<std::shared_ptr<CallGraph const>> creationCallGraph; | ||||
| 	/// A graph with edges representing calls between functions that may happen in a deployed contract.
 | ||||
| 	SetOnce<std::shared_ptr<CallGraph const>> deployedCallGraph; | ||||
| }; | ||||
| 
 | ||||
| struct CallableDeclarationAnnotation: DeclarationAnnotation | ||||
|  | ||||
							
								
								
									
										46
									
								
								libsolidity/ast/CallGraph.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								libsolidity/ast/CallGraph.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -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 <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| // SPDX-License-Identifier: GPL-3.0
 | ||||
| 
 | ||||
| #include <libsolidity/ast/CallGraph.h> | ||||
| 
 | ||||
| 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<SpecialNode>(_lhs)) | ||||
| 		return get<SpecialNode>(_lhs) < get<SpecialNode>(_rhs); | ||||
| 	return get<CallableDeclaration const*>(_lhs)->id() < get<CallableDeclaration const*>(_rhs)->id(); | ||||
| } | ||||
| 
 | ||||
| bool CallGraph::CompareByID::operator()(Node const& _lhs, int64_t _rhs) const | ||||
| { | ||||
| 	solAssert(!holds_alternative<SpecialNode>(_lhs), ""); | ||||
| 
 | ||||
| 	return get<CallableDeclaration const*>(_lhs)->id() < _rhs; | ||||
| } | ||||
| 
 | ||||
| bool CallGraph::CompareByID::operator()(int64_t _lhs, Node const& _rhs) const | ||||
| { | ||||
| 	solAssert(!holds_alternative<SpecialNode>(_rhs), ""); | ||||
| 
 | ||||
| 	return _lhs < get<CallableDeclaration const*>(_rhs)->id(); | ||||
| } | ||||
							
								
								
									
										74
									
								
								libsolidity/ast/CallGraph.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								libsolidity/ast/CallGraph.h
									
									
									
									
									
										Normal file
									
								
							| @ -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 <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 <variant> | ||||
| 
 | ||||
| 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<CallableDeclaration const*, SpecialNode>; | ||||
| 
 | ||||
| 	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<Node, std::set<Node, CompareByID>, CompareByID> edges; | ||||
| 
 | ||||
| 	/// Contracts that may get created with `new` by functions present in the graph.
 | ||||
| 	std::set<ContractDefinition const*, ASTNode::CompareByID> createdContracts; | ||||
| 
 | ||||
| 	/// Events that may get emitted by functions present in the graph.
 | ||||
| 	std::set<EventDefinition const*, ASTNode::CompareByID> emittedEvents; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| @ -78,11 +78,11 @@ void verifyCallGraph( | ||||
| } | ||||
| 
 | ||||
| set<CallableDeclaration const*, ASTNode::CompareByID> collectReachableCallables( | ||||
| 	FunctionCallGraphBuilder::ContractCallGraph const& _graph | ||||
| 	CallGraph const& _graph | ||||
| ) | ||||
| { | ||||
| 	set<CallableDeclaration const*, ASTNode::CompareByID> reachableCallables; | ||||
| 	for (FunctionCallGraphBuilder::Node const& reachableNode: _graph.edges | views::keys) | ||||
| 	for (CallGraph::Node const& reachableNode: _graph.edges | views::keys) | ||||
| 		if (holds_alternative<CallableDeclaration const*>(reachableNode)) | ||||
| 			reachableCallables.emplace(get<CallableDeclaration const*>(reachableNode)); | ||||
| 
 | ||||
| @ -119,31 +119,11 @@ pair<string, string> IRGenerator::run( | ||||
| 	return {warning + ir, warning + asmStack.print()}; | ||||
| } | ||||
| 
 | ||||
| void IRGenerator::verifyCallGraphs( | ||||
| 	FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, | ||||
| 	FunctionCallGraphBuilder::ContractCallGraph 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<ContractDefinition const*, string_view const> 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<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string | ||||
| 	{ | ||||
| 		std::string subObjectsSources; | ||||
| @ -205,7 +185,7 @@ string IRGenerator::generate( | ||||
| 
 | ||||
| 	t("deploy", deployCode(_contract)); | ||||
| 	generateImplicitConstructors(_contract); | ||||
| 	m_creationFunctionList = generateQueuedFunctions(); | ||||
| 	set<FunctionDefinition const*> creationFunctionList = generateQueuedFunctions(); | ||||
| 	InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions(); | ||||
| 
 | ||||
| 	t("functions", m_context.functionCollector().requestedFunctions()); | ||||
| @ -226,7 +206,7 @@ string IRGenerator::generate( | ||||
| 	t("DeployedObject", IRNames::deployedObject(_contract)); | ||||
| 	t("library_address", IRNames::libraryAddressImmutable()); | ||||
| 	t("dispatch", dispatchRoutine(_contract)); | ||||
| 	m_deployedFunctionList = generateQueuedFunctions(); | ||||
| 	set<FunctionDefinition const*> deployedFunctionList = generateQueuedFunctions(); | ||||
| 	generateInternalDispatchFunctions(); | ||||
| 	t("deployedFunctions", m_context.functionCollector().requestedFunctions()); | ||||
| 	t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated())); | ||||
| @ -235,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(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
| 
 | ||||
| #include <libsolidity/interface/OptimiserSettings.h> | ||||
| #include <libsolidity/ast/ASTForward.h> | ||||
| #include <libsolidity/analysis/FunctionCallGraph.h> | ||||
| #include <libsolidity/ast/CallGraph.h> | ||||
| #include <libsolidity/codegen/ir/IRGenerationContext.h> | ||||
| #include <libsolidity/codegen/YulUtilFunctions.h> | ||||
| #include <liblangutil/EVMVersion.h> | ||||
| @ -57,11 +57,6 @@ public: | ||||
| 		std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources | ||||
| 	); | ||||
| 
 | ||||
| 	void verifyCallGraphs( | ||||
| 		FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, | ||||
| 		FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph | ||||
| 	); | ||||
| 
 | ||||
| private: | ||||
| 	std::string generate( | ||||
| 		ContractDefinition const& _contract, | ||||
| @ -122,9 +117,6 @@ private: | ||||
| 	langutil::EVMVersion const m_evmVersion; | ||||
| 	OptimiserSettings const m_optimiserSettings; | ||||
| 
 | ||||
| 	std::set<FunctionDefinition const*> m_creationFunctionList; | ||||
| 	std::set<FunctionDefinition const*> m_deployedFunctionList; | ||||
| 
 | ||||
| 	IRGenerationContext m_context; | ||||
| 	YulUtilFunctions m_utils; | ||||
| }; | ||||
|  | ||||
| @ -407,8 +407,18 @@ bool CompilerStack::analyze() | ||||
| 						if (auto const* contractDefinition = dynamic_cast<ContractDefinition*>(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<CallGraph>( | ||||
| 								FunctionCallGraphBuilder::buildCreationGraph( | ||||
| 									*contractDefinition | ||||
| 								) | ||||
| 							); | ||||
| 							contractState.contract->annotation().deployedCallGraph = make_unique<CallGraph>( | ||||
| 								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); }); | ||||
| } | ||||
| 
 | ||||
| FunctionCallGraphBuilder::ContractCallGraph 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(); | ||||
| } | ||||
| 
 | ||||
| FunctionCallGraphBuilder::ContractCallGraph 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) | ||||
| @ -1306,8 +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.creationCallGraph.value(), compiledContract.deployedCallGraph.value()); | ||||
| } | ||||
| 
 | ||||
| void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) | ||||
|  | ||||
| @ -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
 | ||||
| @ -343,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.
 | ||||
| 	FunctionCallGraphBuilder::ContractCallGraph 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; | ||||
| 
 | ||||
| 	/// 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.
 | ||||
| @ -390,8 +388,6 @@ private: | ||||
| 		util::LazyInit<Json::Value const> runtimeGeneratedSources; | ||||
| 		mutable std::optional<std::string const> sourceMapping; | ||||
| 		mutable std::optional<std::string const> runtimeSourceMapping; | ||||
| 		std::optional<FunctionCallGraphBuilder::ContractCallGraph const> creationCallGraph; | ||||
| 		std::optional<FunctionCallGraphBuilder::ContractCallGraph const> deployedCallGraph; | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Loads the missing sources from @a _ast (named @a _path) using the callback
 | ||||
| @ -442,10 +438,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; | ||||
| 
 | ||||
|  | ||||
| @ -53,12 +53,12 @@ using namespace solidity::langutil; | ||||
| using namespace solidity::frontend; | ||||
| 
 | ||||
| using EdgeMap = map< | ||||
| 	FunctionCallGraphBuilder::Node, | ||||
| 	set<FunctionCallGraphBuilder::Node, FunctionCallGraphBuilder::CompareByID>, | ||||
| 	FunctionCallGraphBuilder::CompareByID | ||||
| 	CallGraph::Node, | ||||
| 	set<CallGraph::Node, CallGraph::CompareByID>, | ||||
| 	CallGraph::CompareByID | ||||
| >; | ||||
| using EdgeNames = set<tuple<string, string>>; | ||||
| using CallGraphMap = map<string, FunctionCallGraphBuilder::ContractCallGraph const*>; | ||||
| using CallGraphMap = map<string, CallGraph const*>; | ||||
| 
 | ||||
| namespace | ||||
| { | ||||
| @ -116,8 +116,8 @@ tuple<CallGraphMap, CallGraphMap> 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; | ||||
| @ -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<string> 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()) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user