Merge pull request #10990 from ethereum/call-graph-as-contract-annotation

Call graph as contract annotation
This commit is contained in:
chriseth 2021-02-23 11:43:24 +01:00 committed by GitHub
commit dd1ae5ba11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 194 additions and 168 deletions

View File

@ -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

View File

@ -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");

View File

@ -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);
}

View File

@ -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

View 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();
}

View 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;
};
}

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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)

View File

@ -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;

View File

@ -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())