mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add IRGenerator::verifyCallGraphs()
This commit is contained in:
parent
74ef7dd790
commit
4c283f00c1
libsolidity/codegen/ir
@ -36,18 +36,61 @@
|
||||
#include <libsolutil/CommonData.h>
|
||||
#include <libsolutil/Whiskers.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
|
||||
#include <liblangutil/SourceReferenceFormatter.h>
|
||||
|
||||
#include <range/v3/view/map.hpp>
|
||||
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <variant>
|
||||
|
||||
using namespace std;
|
||||
using namespace ranges;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void verifyCallGraph(
|
||||
set<CallableDeclaration const*, ASTNode::CompareByID> const& _expectedCallables,
|
||||
set<FunctionDefinition const*> _generatedFunctions
|
||||
)
|
||||
{
|
||||
for (auto const& expectedCallable: _expectedCallables)
|
||||
if (auto const* expectedFunction = dynamic_cast<FunctionDefinition const*>(expectedCallable))
|
||||
{
|
||||
solAssert(
|
||||
_generatedFunctions.count(expectedFunction) == 1 || expectedFunction->isConstructor(),
|
||||
"No code generated for function " + expectedFunction->name() + "even though it is not a constructor."
|
||||
);
|
||||
_generatedFunctions.erase(expectedFunction);
|
||||
}
|
||||
|
||||
solAssert(
|
||||
_generatedFunctions.size() == 0,
|
||||
"Of the generated functions " + toString(_generatedFunctions.size()) + " are not in the call graph."
|
||||
);
|
||||
}
|
||||
|
||||
set<CallableDeclaration const*, ASTNode::CompareByID> collectReachableCallables(
|
||||
FunctionCallGraphBuilder::ContractCallGraph const& _graph
|
||||
)
|
||||
{
|
||||
set<CallableDeclaration const*, ASTNode::CompareByID> reachableCallables;
|
||||
for (FunctionCallGraphBuilder::Node const& reachableNode: _graph.edges | views::keys)
|
||||
if (holds_alternative<CallableDeclaration const*>(reachableNode))
|
||||
reachableCallables.emplace(get<CallableDeclaration const*>(reachableNode));
|
||||
|
||||
return reachableCallables;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pair<string, string> IRGenerator::run(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||
@ -76,11 +119,31 @@ 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;
|
||||
@ -142,8 +205,9 @@ string IRGenerator::generate(
|
||||
|
||||
t("deploy", deployCode(_contract));
|
||||
generateImplicitConstructors(_contract);
|
||||
generateQueuedFunctions();
|
||||
m_creationFunctionList = generateQueuedFunctions();
|
||||
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
||||
|
||||
t("functions", m_context.functionCollector().requestedFunctions());
|
||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
|
||||
@ -162,7 +226,7 @@ string IRGenerator::generate(
|
||||
t("DeployedObject", IRNames::deployedObject(_contract));
|
||||
t("library_address", IRNames::libraryAddressImmutable());
|
||||
t("dispatch", dispatchRoutine(_contract));
|
||||
generateQueuedFunctions();
|
||||
m_deployedFunctionList = generateQueuedFunctions();
|
||||
generateInternalDispatchFunctions();
|
||||
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
|
||||
t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
@ -170,6 +234,7 @@ string IRGenerator::generate(
|
||||
// This has to be called only after all other code generation for the deployed object is complete.
|
||||
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
|
||||
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
|
||||
|
||||
return t.render();
|
||||
}
|
||||
|
||||
@ -180,11 +245,20 @@ string IRGenerator::generate(Block const& _block)
|
||||
return generator.code();
|
||||
}
|
||||
|
||||
void IRGenerator::generateQueuedFunctions()
|
||||
set<FunctionDefinition const*> IRGenerator::generateQueuedFunctions()
|
||||
{
|
||||
set<FunctionDefinition const*> functions;
|
||||
|
||||
while (!m_context.functionGenerationQueueEmpty())
|
||||
{
|
||||
FunctionDefinition const& functionDefinition = *m_context.dequeueFunctionForCodeGeneration();
|
||||
|
||||
functions.emplace(&functionDefinition);
|
||||
// NOTE: generateFunction() may modify function generation queue
|
||||
generateFunction(*m_context.dequeueFunctionForCodeGeneration());
|
||||
generateFunction(functionDefinition);
|
||||
}
|
||||
|
||||
return functions;
|
||||
}
|
||||
|
||||
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <libsolidity/analysis/FunctionCallGraph.h>
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
@ -56,6 +57,11 @@ 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,
|
||||
@ -65,7 +71,8 @@ private:
|
||||
|
||||
/// Generates code for all the functions from the function generation queue.
|
||||
/// The resulting code is stored in the function collector in IRGenerationContext.
|
||||
void generateQueuedFunctions();
|
||||
/// @returns A set of ast nodes of the generated functions.
|
||||
std::set<FunctionDefinition const*> generateQueuedFunctions();
|
||||
/// Generates all the internal dispatch functions necessary to handle any function that could
|
||||
/// possibly be called via a pointer.
|
||||
/// @return The content of the dispatch for reuse in runtime code. Reuse is necessary because
|
||||
@ -115,6 +122,9 @@ 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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user