mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Implement a Function Call Graph
This commit is contained in:
parent
ef147a36b3
commit
c977c0cfd0
@ -18,6 +18,8 @@ set(sources
|
||||
analysis/DocStringAnalyser.h
|
||||
analysis/DocStringTagParser.cpp
|
||||
analysis/DocStringTagParser.h
|
||||
analysis/FunctionCallGraph.cpp
|
||||
analysis/FunctionCallGraph.h
|
||||
analysis/ImmutableValidator.cpp
|
||||
analysis/ImmutableValidator.h
|
||||
analysis/GlobalContext.cpp
|
||||
|
207
libsolidity/analysis/FunctionCallGraph.cpp
Normal file
207
libsolidity/analysis/FunctionCallGraph.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
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/analysis/FunctionCallGraph.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
bool FunctionCallGraphBuilder::CompareByID::operator()(Node const& _lhs, Node const& _rhs) const
|
||||
{
|
||||
if (_lhs.index() != _rhs.index())
|
||||
return _lhs.index() < _rhs.index();
|
||||
|
||||
if (std::holds_alternative<SpecialNode>(_lhs))
|
||||
return std::get<SpecialNode>(_lhs) < std::get<SpecialNode>(_rhs);
|
||||
return std::get<ASTNode const*>(_lhs)->id() < std::get<ASTNode const*>(_rhs)->id();
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::CompareByID::operator()(Node const& _lhs, int64_t _rhs) const
|
||||
{
|
||||
solAssert(!std::holds_alternative<SpecialNode>(_lhs), "");
|
||||
|
||||
return std::get<ASTNode const*>(_lhs)->id() < _rhs;
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::CompareByID::operator()(int64_t _lhs, Node const& _rhs) const
|
||||
{
|
||||
solAssert(!std::holds_alternative<SpecialNode>(_rhs), "");
|
||||
|
||||
return _lhs < std::get<ASTNode const*>(_rhs)->id();
|
||||
}
|
||||
|
||||
shared_ptr<FunctionCallGraphBuilder::ContractCallGraph> FunctionCallGraphBuilder::create(ContractDefinition const& _contract)
|
||||
{
|
||||
m_contract = &_contract;
|
||||
|
||||
m_graph = make_shared<ContractCallGraph>(_contract);
|
||||
|
||||
// Create graph for constructor, state vars, etc
|
||||
m_currentNode = SpecialNode::CreationRoot;
|
||||
m_currentDispatch = SpecialNode::CreationDispatch;
|
||||
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
||||
visitConstructor(*contract);
|
||||
|
||||
m_currentNode.reset();
|
||||
m_currentDispatch = SpecialNode::InternalDispatch;
|
||||
|
||||
// Create graph for all publicly reachable functions
|
||||
for (auto& [hash, functionType]: _contract.interfaceFunctionList())
|
||||
{
|
||||
(void)hash;
|
||||
if (auto const* funcDef = dynamic_cast<FunctionDefinition const*>(&functionType->declaration()))
|
||||
if (!m_graph->edges.count(funcDef))
|
||||
visitCallable(funcDef);
|
||||
}
|
||||
|
||||
// Add all CreationDispatch calls to the RuntimeDispatch as well
|
||||
for (auto node: m_graph->edges[SpecialNode::CreationDispatch])
|
||||
add(SpecialNode::InternalDispatch, node);
|
||||
|
||||
// Add all external functions to the RuntimeDispatch
|
||||
for (auto& [hash, functionType]: _contract.interfaceFunctionList())
|
||||
{
|
||||
(void)hash;
|
||||
add(SpecialNode::ExternalDispatch, &functionType->declaration());
|
||||
}
|
||||
|
||||
if (_contract.fallbackFunction())
|
||||
add(SpecialNode::ExternalDispatch, _contract.fallbackFunction());
|
||||
|
||||
if (_contract.receiveFunction())
|
||||
add(SpecialNode::ExternalDispatch, _contract.receiveFunction());
|
||||
|
||||
m_contract = nullptr;
|
||||
solAssert(!m_currentNode.has_value(), "Current node not properly reset.");
|
||||
|
||||
return m_graph;
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
|
||||
{
|
||||
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
||||
{
|
||||
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");
|
||||
|
||||
auto funType = dynamic_cast<FunctionType const*>(_identifier.annotation().type);
|
||||
|
||||
// For events kind() == Event, so we have an extra check here
|
||||
if (funType && funType->kind() == FunctionType::Kind::Internal)
|
||||
{
|
||||
processFunction(callable->resolveVirtual(*m_contract), _identifier.annotation());
|
||||
|
||||
solAssert(m_currentNode.has_value(), "");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
|
||||
{
|
||||
if (ContractType const* contractType = dynamic_cast<ContractType const*>(_newExpression.typeName().annotation().type))
|
||||
m_graph->createdContracts.emplace(&contractType->contractDefinition());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
auto functionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||
auto functionDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
||||
if (!functionType || !functionDef || functionType->kind() != FunctionType::Kind::Internal)
|
||||
return;
|
||||
|
||||
// Super functions
|
||||
if (*_memberAccess.annotation().requiredLookup == VirtualLookup::Super)
|
||||
if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type))
|
||||
{
|
||||
solAssert(type->isSuper(), "");
|
||||
functionDef = &functionDef->resolveVirtual(*m_contract, type->contractDefinition().superContract(*m_contract));
|
||||
}
|
||||
|
||||
processFunction(*functionDef, _memberAccess.annotation());
|
||||
return;
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
auto* functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
|
||||
|
||||
if (
|
||||
functionType &&
|
||||
functionType->kind() == FunctionType::Kind::Internal &&
|
||||
!functionType->hasDeclaration()
|
||||
)
|
||||
add(m_currentDispatch, &_functionCall);
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::visitCallable(CallableDeclaration const* _callable)
|
||||
{
|
||||
solAssert(!m_graph->edges.count(_callable), "");
|
||||
|
||||
auto previousNode = m_currentNode;
|
||||
m_currentNode = _callable;
|
||||
|
||||
if (previousNode.has_value())
|
||||
add(*previousNode, _callable);
|
||||
|
||||
_callable->accept(*this);
|
||||
|
||||
m_currentNode = previousNode;
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::visitConstructor(ContractDefinition const& _contract)
|
||||
{
|
||||
for (auto const* stateVar: _contract.stateVariables())
|
||||
stateVar->accept(*this);
|
||||
|
||||
for (auto arg: _contract.baseContracts())
|
||||
arg->accept(*this);
|
||||
|
||||
if (_contract.constructor())
|
||||
{
|
||||
add(*m_currentNode, _contract.constructor());
|
||||
_contract.constructor()->accept(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool FunctionCallGraphBuilder::add(Node _caller, ASTNode const* _callee)
|
||||
{
|
||||
solAssert(_callee != nullptr, "");
|
||||
auto result = m_graph->edges.find(_caller);
|
||||
|
||||
if (result == m_graph->edges.end())
|
||||
{
|
||||
m_graph->edges.emplace(_caller, std::set<ASTNode const*, ASTNode::CompareByID>{_callee});
|
||||
return true;
|
||||
}
|
||||
|
||||
return result->second.emplace(_callee).second;
|
||||
}
|
||||
|
||||
void FunctionCallGraphBuilder::processFunction(CallableDeclaration const& _callable, ExpressionAnnotation const& _annotation)
|
||||
{
|
||||
if (m_graph->edges.count(&_callable))
|
||||
return;
|
||||
|
||||
// Create edge to creation dispatch
|
||||
if (!_annotation.calledDirectly)
|
||||
add(m_currentDispatch, &_callable);
|
||||
visitCallable(&_callable);
|
||||
}
|
98
libsolidity/analysis/FunctionCallGraph.h
Normal file
98
libsolidity/analysis/FunctionCallGraph.h
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a Function call graph for a contract at the granularity of Solidity
|
||||
* functions and modifiers
|
||||
*
|
||||
* Includes the following special nodes:
|
||||
* - CreationRoot: All calls made at contract creation originate from this node
|
||||
* - CreationDispatch: Represents the internal dispatch function at creation time
|
||||
* - ExternalDispatch: Represents the runtime dispatch for all external functions
|
||||
* - InternalDispatch: Represents the runtime dispatch for internal function pointers and complex expressions
|
||||
*
|
||||
* Nodes are a variant of either the enum SpecialNode or an ASTNode pointer.
|
||||
* ASTNodes are usually inherited from CallableDeclarations
|
||||
* (FunctionDefinition, ModifierDefinition, EventDefinition) but for functions
|
||||
* without declaration it is directly the FunctionCall AST node.
|
||||
*
|
||||
* Functions that are not called right away as well as functions without
|
||||
* declarations have an edge to the internal dispatch node.
|
||||
*
|
||||
* Auto-generated getter functions for public state variables are ignored.
|
||||
*/
|
||||
class FunctionCallGraphBuilder: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
enum class SpecialNode { CreationRoot, CreationDispatch, InternalDispatch, ExternalDispatch };
|
||||
|
||||
using Node = std::variant<ASTNode 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
|
||||
{
|
||||
ContractCallGraph(ContractDefinition const& _contract): contract(_contract) {}
|
||||
|
||||
/// Contract for which this is the graph
|
||||
ContractDefinition const& contract;
|
||||
|
||||
std::map<Node, std::set<ASTNode const*, ASTNode::CompareByID>, CompareByID> edges;
|
||||
|
||||
/// Set of contracts created
|
||||
std::set<ContractDefinition const*, ASTNode::CompareByID> createdContracts;
|
||||
};
|
||||
|
||||
std::shared_ptr<ContractCallGraph> create(ContractDefinition const& _contract);
|
||||
|
||||
private:
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
bool visit(NewExpression const& _newExpression) override;
|
||||
void endVisit(MemberAccess const& _memberAccess) override;
|
||||
void endVisit(FunctionCall const& _functionCall) override;
|
||||
|
||||
void visitCallable(CallableDeclaration const* _callable);
|
||||
void visitConstructor(ContractDefinition const& _contract);
|
||||
|
||||
bool add(Node _caller, ASTNode const* _callee);
|
||||
void processFunction(CallableDeclaration const& _callable, ExpressionAnnotation const& _annotation);
|
||||
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
std::optional<Node> m_currentNode;
|
||||
std::shared_ptr<ContractCallGraph> m_graph = nullptr;
|
||||
Node m_currentDispatch = SpecialNode::CreationDispatch;
|
||||
};
|
||||
|
||||
}
|
@ -899,7 +899,7 @@ public:
|
||||
(annotation().contract && annotation().contract->isInterface());
|
||||
}
|
||||
|
||||
FunctionDefinition const& resolveVirtual(
|
||||
[[nodiscard]] FunctionDefinition const& resolveVirtual(
|
||||
ContractDefinition const& _mostDerivedContract,
|
||||
ContractDefinition const* _searchStart = nullptr
|
||||
) const override;
|
||||
|
@ -32,16 +32,29 @@ using namespace solidity::frontend;
|
||||
string MultiUseYulFunctionCollector::requestedFunctions()
|
||||
{
|
||||
string result;
|
||||
for (auto const& f: m_requestedFunctions)
|
||||
for (auto const& [name, code]: m_requestedFunctions)
|
||||
{
|
||||
solAssert(f.second != "<<STUB<<", "");
|
||||
(void) name;
|
||||
solAssert(code != "<<STUB<<", "");
|
||||
// std::map guarantees ascending order when iterating through its keys.
|
||||
result += f.second;
|
||||
result += code;
|
||||
}
|
||||
m_requestedFunctions.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
set<string> MultiUseYulFunctionCollector::requestedFunctionsNames()
|
||||
{
|
||||
set<string> names;
|
||||
for (auto const& [name, code]: m_requestedFunctions)
|
||||
{
|
||||
(void) code;
|
||||
names.emplace(name);
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
string MultiUseYulFunctionCollector::createFunction(string const& _name, function<string ()> const& _creator)
|
||||
{
|
||||
if (!m_requestedFunctions.count(_name))
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace solidity::frontend
|
||||
@ -48,9 +49,11 @@ public:
|
||||
/// empty return value.
|
||||
std::string requestedFunctions();
|
||||
|
||||
/// Helper function to get the names of all requested functions
|
||||
std::set<std::string> requestedFunctionsNames();
|
||||
|
||||
/// @returns true IFF a function with the specified name has already been collected.
|
||||
bool contains(std::string const& _name) const { return m_requestedFunctions.count(_name) > 0; }
|
||||
|
||||
private:
|
||||
/// Map from function name to code for a multi-use function.
|
||||
std::map<std::string, std::string> m_requestedFunctions;
|
||||
|
@ -35,6 +35,11 @@ YulArity YulArity::fromType(FunctionType const& _functionType)
|
||||
|
||||
string IRNames::function(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.isConstructor())
|
||||
return implicitConstructor(*_function.annotation().contract);
|
||||
|
||||
// @TODO previously, we had to distinguish creation context and runtime context,
|
||||
// but since we do not work with jump positions anymore, this should not be a problem, right?
|
||||
return "fun_" + _function.name() + "_" + to_string(_function.id());
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,47 @@ using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
namespace {
|
||||
|
||||
void verifyCallGraph(set<ASTNode const*, ASTNode::CompareByID> const& _nodes, set<string>& _functionList)
|
||||
{
|
||||
for (auto const& node: _nodes)
|
||||
if (auto const* functionDef = dynamic_cast<FunctionDefinition const*>(node))
|
||||
solAssert(_functionList.erase(IRNames::function(*functionDef)) == 1, "Function not found in generated code");
|
||||
|
||||
static string const funPrefix = "fun_";
|
||||
|
||||
for (string const& name: _functionList)
|
||||
solAssert(name.substr(0, funPrefix.size()) != funPrefix, "Functions found in code gen that were not in the call graph");
|
||||
}
|
||||
|
||||
|
||||
void collectCalls(FunctionCallGraphBuilder::ContractCallGraph const& _graph, ASTNode const* _root, set<ASTNode const*, ASTNode::CompareByID>& _functions)
|
||||
{
|
||||
if (_functions.count(_root) > 0)
|
||||
return;
|
||||
|
||||
set<ASTNode const*, ASTNode::CompareByID> toVisit{_root};
|
||||
|
||||
_functions.emplace(_root);
|
||||
|
||||
while (!toVisit.empty())
|
||||
{
|
||||
ASTNode const* function = *toVisit.begin();
|
||||
toVisit.erase(toVisit.begin());
|
||||
|
||||
auto callees = _graph.edges.find(function);
|
||||
if (callees == _graph.edges.end())
|
||||
continue;
|
||||
|
||||
for (auto& callee: callees->second)
|
||||
if (_functions.emplace(callee).second)
|
||||
toVisit.emplace(callee);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pair<string, string> IRGenerator::run(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||
@ -76,6 +117,29 @@ pair<string, string> IRGenerator::run(
|
||||
return {warning + ir, warning + asmStack.print()};
|
||||
}
|
||||
|
||||
void IRGenerator::verifyCallGraph(FunctionCallGraphBuilder::ContractCallGraph const& _graph)
|
||||
{
|
||||
set<ASTNode const*, ASTNode::CompareByID> functions;
|
||||
|
||||
auto collectFromNode = [&](FunctionCallGraphBuilder::SpecialNode _node)
|
||||
{
|
||||
auto callees = _graph.edges.find(_node);
|
||||
|
||||
if (callees != _graph.edges.end())
|
||||
for (auto callee: callees->second)
|
||||
collectCalls(_graph, callee, functions);
|
||||
};
|
||||
|
||||
collectFromNode(FunctionCallGraphBuilder::SpecialNode::CreationRoot);
|
||||
collectFromNode(FunctionCallGraphBuilder::SpecialNode::CreationDispatch);
|
||||
::verifyCallGraph(functions, m_creationFunctionList);
|
||||
|
||||
functions.clear();
|
||||
collectFromNode(FunctionCallGraphBuilder::SpecialNode::ExternalDispatch);
|
||||
collectFromNode(FunctionCallGraphBuilder::SpecialNode::InternalDispatch);
|
||||
::verifyCallGraph(functions, m_deployedFunctionList);
|
||||
}
|
||||
|
||||
string IRGenerator::generate(
|
||||
ContractDefinition const& _contract,
|
||||
map<ContractDefinition const*, string_view const> const& _otherYulSources
|
||||
@ -144,6 +208,8 @@ string IRGenerator::generate(
|
||||
generateImplicitConstructors(_contract);
|
||||
generateQueuedFunctions();
|
||||
InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions();
|
||||
|
||||
m_creationFunctionList = m_context.functionCollector().requestedFunctionsNames();
|
||||
t("functions", m_context.functionCollector().requestedFunctions());
|
||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
|
||||
@ -164,12 +230,14 @@ string IRGenerator::generate(
|
||||
t("dispatch", dispatchRoutine(_contract));
|
||||
generateQueuedFunctions();
|
||||
generateInternalDispatchFunctions();
|
||||
m_deployedFunctionList = m_context.functionCollector().requestedFunctionsNames();
|
||||
t("deployedFunctions", m_context.functionCollector().requestedFunctions());
|
||||
t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
@ -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,8 @@ public:
|
||||
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||
);
|
||||
|
||||
void verifyCallGraph(FunctionCallGraphBuilder::ContractCallGraph const& _graph);
|
||||
|
||||
private:
|
||||
std::string generate(
|
||||
ContractDefinition const& _contract,
|
||||
@ -115,6 +118,9 @@ private:
|
||||
langutil::EVMVersion const m_evmVersion;
|
||||
OptimiserSettings const m_optimiserSettings;
|
||||
|
||||
std::set<std::string> m_creationFunctionList;
|
||||
std::set<std::string> m_deployedFunctionList;
|
||||
|
||||
IRGenerationContext m_context;
|
||||
YulUtilFunctions m_utils;
|
||||
};
|
||||
|
@ -217,6 +217,7 @@ void CompilerStack::reset(bool _keepSettings)
|
||||
m_sources.clear();
|
||||
m_smtlib2Responses.clear();
|
||||
m_unhandledSMTLib2Queries.clear();
|
||||
m_contractCallGraphs.clear();
|
||||
if (!_keepSettings)
|
||||
{
|
||||
m_remappings.clear();
|
||||
@ -399,6 +400,16 @@ bool CompilerStack::analyze()
|
||||
if (source->ast && !typeChecker.checkTypeRequirements(*source->ast))
|
||||
noErrors = false;
|
||||
|
||||
if (noErrors)
|
||||
{
|
||||
FunctionCallGraphBuilder builder;
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
m_contractCallGraphs.emplace(contract, builder.create(*contract));
|
||||
}
|
||||
|
||||
if (noErrors)
|
||||
{
|
||||
// Checks that can only be done when all types of all AST nodes are known.
|
||||
@ -1275,6 +1286,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.verifyCallGraph(*m_contractCallGraphs[&_contract]);
|
||||
}
|
||||
|
||||
void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/analysis/FunctionCallGraph.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <libsolidity/interface/Version.h>
|
||||
@ -475,6 +476,7 @@ private:
|
||||
bool m_generateIR = false;
|
||||
bool m_generateEwasm = false;
|
||||
std::map<std::string, util::h160> m_libraries;
|
||||
std::map<ContractDefinition const*, std::shared_ptr<FunctionCallGraphBuilder::ContractCallGraph> const> m_contractCallGraphs;
|
||||
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
|
||||
/// "context:prefix=target"
|
||||
std::vector<Remapping> m_remappings;
|
||||
|
Loading…
Reference in New Issue
Block a user