CompilerStack: Build call graphs in the analysis phase

This commit is contained in:
Mathias Baumann 2020-11-18 18:24:33 +01:00 committed by Kamil Śliwak
parent 4c283f00c1
commit 53d70dec57
2 changed files with 42 additions and 0 deletions

View File

@ -399,6 +399,19 @@ bool CompilerStack::analyze()
if (source->ast && !typeChecker.checkTypeRequirements(*source->ast)) if (source->ast && !typeChecker.checkTypeRequirements(*source->ast))
noErrors = false; noErrors = false;
if (noErrors)
{
for (Source const* source: m_sourceOrder)
if (source->ast)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
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));
}
}
if (noErrors) if (noErrors)
{ {
// Checks that can only be done when all types of all AST nodes are known. // Checks that can only be done when all types of all AST nodes are known.
@ -937,6 +950,24 @@ string const& CompilerStack::metadata(Contract const& _contract) const
return _contract.metadata.init([&]{ return createMetadata(_contract); }); 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 Scanner const& CompilerStack::scanner(string const& _sourceName) const
{ {
if (m_stackState < SourcesSet) if (m_stackState < SourcesSet)
@ -1275,6 +1306,8 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings); IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings);
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources);
generator.verifyCallGraphs(compiledContract.creationCallGraph.value(), compiledContract.deployedCallGraph.value());
} }
void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)

View File

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <libsolidity/analysis/FunctionCallGraph.h>
#include <libsolidity/interface/ReadFile.h> #include <libsolidity/interface/ReadFile.h>
#include <libsolidity/interface/OptimiserSettings.h> #include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/Version.h> #include <libsolidity/interface/Version.h>
@ -342,6 +343,12 @@ public:
/// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
Json::Value gasEstimates(std::string const& _contractName) const; 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. /// 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 /// 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. /// caused by differences in metadata. Should only be used for testing.
@ -383,6 +390,8 @@ private:
util::LazyInit<Json::Value const> runtimeGeneratedSources; util::LazyInit<Json::Value const> runtimeGeneratedSources;
mutable std::optional<std::string const> sourceMapping; mutable std::optional<std::string const> sourceMapping;
mutable std::optional<std::string const> runtimeSourceMapping; 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 /// Loads the missing sources from @a _ast (named @a _path) using the callback