diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index b1539e10a..426eaceba 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -399,6 +399,19 @@ bool CompilerStack::analyze() if (source->ast && !typeChecker.checkTypeRequirements(*source->ast)) noErrors = false; + if (noErrors) + { + for (Source const* source: m_sourceOrder) + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + 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)); + } + } + if (noErrors) { // 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); }); } +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) @@ -1275,6 +1306,8 @@ 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) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index db087a663..e8ab7c8c9 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -24,6 +24,7 @@ #pragma once +#include #include #include #include @@ -342,6 +343,12 @@ 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. @@ -383,6 +390,8 @@ 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