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
@ -36,18 +36,61 @@
|
|||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
#include <libsolutil/StringUtils.h>
|
#include <libsolutil/StringUtils.h>
|
||||||
|
#include <libsolutil/Algorithms.h>
|
||||||
|
|
||||||
#include <liblangutil/SourceReferenceFormatter.h>
|
#include <liblangutil/SourceReferenceFormatter.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/map.hpp>
|
||||||
|
|
||||||
#include <boost/range/adaptor/map.hpp>
|
#include <boost/range/adaptor/map.hpp>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace ranges;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
using namespace solidity::frontend;
|
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(
|
pair<string, string> IRGenerator::run(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||||
@ -76,11 +119,31 @@ pair<string, string> IRGenerator::run(
|
|||||||
return {warning + ir, warning + asmStack.print()};
|
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(
|
string IRGenerator::generate(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
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
|
auto subObjectSources = [&_otherYulSources](std::set<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string
|
||||||
{
|
{
|
||||||
std::string subObjectsSources;
|
std::string subObjectsSources;
|
||||||
@ -142,8 +205,9 @@ string IRGenerator::generate(
|
|||||||
|
|
||||||
t("deploy", deployCode(_contract));
|
t("deploy", deployCode(_contract));
|
||||||
generateImplicitConstructors(_contract);
|
generateImplicitConstructors(_contract);
|
||||||
generateQueuedFunctions();
|
m_creationFunctionList = generateQueuedFunctions();
|
||||||
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
||||||
|
|
||||||
t("functions", m_context.functionCollector().requestedFunctions());
|
t("functions", m_context.functionCollector().requestedFunctions());
|
||||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||||
|
|
||||||
@ -162,7 +226,7 @@ string IRGenerator::generate(
|
|||||||
t("DeployedObject", IRNames::deployedObject(_contract));
|
t("DeployedObject", IRNames::deployedObject(_contract));
|
||||||
t("library_address", IRNames::libraryAddressImmutable());
|
t("library_address", IRNames::libraryAddressImmutable());
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
generateQueuedFunctions();
|
m_deployedFunctionList = generateQueuedFunctions();
|
||||||
generateInternalDispatchFunctions();
|
generateInternalDispatchFunctions();
|
||||||
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
|
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
|
||||||
t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
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.
|
// This has to be called only after all other code generation for the deployed object is complete.
|
||||||
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
|
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
|
||||||
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
|
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
|
||||||
|
|
||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,11 +245,20 @@ string IRGenerator::generate(Block const& _block)
|
|||||||
return generator.code();
|
return generator.code();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGenerator::generateQueuedFunctions()
|
set<FunctionDefinition const*> IRGenerator::generateQueuedFunctions()
|
||||||
{
|
{
|
||||||
|
set<FunctionDefinition const*> functions;
|
||||||
|
|
||||||
while (!m_context.functionGenerationQueueEmpty())
|
while (!m_context.functionGenerationQueueEmpty())
|
||||||
|
{
|
||||||
|
FunctionDefinition const& functionDefinition = *m_context.dequeueFunctionForCodeGeneration();
|
||||||
|
|
||||||
|
functions.emplace(&functionDefinition);
|
||||||
// NOTE: generateFunction() may modify function generation queue
|
// NOTE: generateFunction() may modify function generation queue
|
||||||
generateFunction(*m_context.dequeueFunctionForCodeGeneration());
|
generateFunction(functionDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
|
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions()
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/OptimiserSettings.h>
|
#include <libsolidity/interface/OptimiserSettings.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
#include <libsolidity/analysis/FunctionCallGraph.h>
|
||||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||||
#include <liblangutil/EVMVersion.h>
|
#include <liblangutil/EVMVersion.h>
|
||||||
@ -56,6 +57,11 @@ public:
|
|||||||
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void verifyCallGraphs(
|
||||||
|
FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph,
|
||||||
|
FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string generate(
|
std::string generate(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
@ -65,7 +71,8 @@ private:
|
|||||||
|
|
||||||
/// Generates code for all the functions from the function generation queue.
|
/// Generates code for all the functions from the function generation queue.
|
||||||
/// The resulting code is stored in the function collector in IRGenerationContext.
|
/// 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
|
/// Generates all the internal dispatch functions necessary to handle any function that could
|
||||||
/// possibly be called via a pointer.
|
/// possibly be called via a pointer.
|
||||||
/// @return The content of the dispatch for reuse in runtime code. Reuse is necessary because
|
/// @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;
|
langutil::EVMVersion const m_evmVersion;
|
||||||
OptimiserSettings const m_optimiserSettings;
|
OptimiserSettings const m_optimiserSettings;
|
||||||
|
|
||||||
|
std::set<FunctionDefinition const*> m_creationFunctionList;
|
||||||
|
std::set<FunctionDefinition const*> m_deployedFunctionList;
|
||||||
|
|
||||||
IRGenerationContext m_context;
|
IRGenerationContext m_context;
|
||||||
YulUtilFunctions m_utils;
|
YulUtilFunctions m_utils;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user