Merge pull request #11014 from ethereum/fixConstantsCallGraph

Fix call graph with respect to constants.
This commit is contained in:
chriseth 2021-02-25 17:05:22 +01:00 committed by GitHub
commit 44493ad428
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 169 additions and 3 deletions

View File

@ -41,7 +41,8 @@ CallGraph FunctionCallGraphBuilder::buildCreationGraph(ContractDefinition const&
// an edge from Entry
builder.m_currentNode = CallGraph::SpecialNode::Entry;
for (auto const* stateVar: base->stateVariables())
stateVar->accept(builder);
if (!stateVar->isConstant())
stateVar->accept(builder);
if (base->constructor())
{
@ -140,7 +141,15 @@ bool FunctionCallGraphBuilder::visit(EmitStatement const& _emitStatement)
bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
{
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
{
if (variable->isConstant())
{
solAssert(variable->isStateVariable() || variable->isFileLevelVariable(), "");
variable->accept(*this);
}
}
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration))
{
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");

View File

@ -66,7 +66,7 @@ void verifyCallGraph(
{
solAssert(
_generatedFunctions.count(expectedFunction) == 1 || expectedFunction->isConstructor(),
"No code generated for function " + expectedFunction->name() + "even though it is not a constructor."
"No code generated for function " + expectedFunction->name() + " even though it is not a constructor."
);
_generatedFunctions.erase(expectedFunction);
}

View File

@ -2000,6 +2000,163 @@ BOOST_AUTO_TEST_CASE(conversions_and_struct_array_constructors)
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_CASE(immutable_initialization)
{
unique_ptr<CompilerStack> compilerStack = parseAndAnalyzeContracts(R"(
function free() pure returns (uint) { return 42; }
contract Base {
function ext() external pure returns (uint) { free(); }
function inr() internal pure returns (uint) { free(); }
}
contract C is Base {
uint immutable extImmutable = this.ext();
uint immutable inrImmutable = inr();
}
contract D is Base {
uint immutable extImmutable;
uint immutable inrImmutable;
constructor () {
extImmutable = this.ext();
inrImmutable = inr();
}
}
)"s);
tuple<CallGraphMap, CallGraphMap> graphs = collectGraphs(*compilerStack);
map<string, EdgeNames> expectedCreationEdges = {
{"Base", {}},
{"C", {
{"Entry", "function Base.inr()"},
{"function Base.inr()", "function free()"},
}},
{"D", {
{"Entry", "constructor of D"},
{"constructor of D", "function Base.inr()"},
{"function Base.inr()", "function free()"},
}},
};
map<string, EdgeNames> expectedDeployedEdges = {
{"Base", {
{"Entry", "function Base.ext()"},
{"function Base.ext()", "function free()"},
}},
{"C", {
{"Entry", "function Base.ext()"},
{"function Base.ext()", "function free()"},
}},
{"D", {
{"Entry", "function Base.ext()"},
{"function Base.ext()", "function free()"},
}},
};
checkCallGraphExpectations(get<0>(graphs), expectedCreationEdges);
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_CASE(function_selector_access)
{
unique_ptr<CompilerStack> compilerStack = parseAndAnalyzeContracts(R"(
function free() pure {}
bytes4 constant extFreeConst = Base.ext.selector;
bytes4 constant pubFreeConst = Base.pub.selector;
contract Base {
function ext() external pure { free(); extFreeConst; }
function pub() public pure { free(); pubFreeConst; }
}
contract C is Base {
bytes4 constant extConst = Base.ext.selector;
bytes4 constant pubConst = Base.pub.selector;
}
contract D is Base {
bytes4 immutable extImmutable = Base.ext.selector;
bytes4 immutable pubImmutable = Base.pub.selector;
}
contract E is Base {
bytes4 extVar = Base.ext.selector;
bytes4 pubVar = Base.pub.selector;
}
contract F is Base {
function f() public pure returns (bytes4, bytes4) {
return (Base.ext.selector, Base.pub.selector);
}
}
library L {
bytes4 constant extConst = Base.ext.selector;
bytes4 constant pubConst = Base.pub.selector;
}
)"s);
tuple<CallGraphMap, CallGraphMap> graphs = collectGraphs(*compilerStack);
map<string, EdgeNames> expectedCreationEdges = {
{"Base", {}},
{"C", {}},
{"D", {
{"InternalDispatch", "function Base.pub()"},
{"function Base.pub()", "function free()"},
}},
{"E", {
{"InternalDispatch", "function Base.pub()"},
{"function Base.pub()", "function free()"},
}},
{"F", {}},
{"L", {}},
};
map<string, EdgeNames> expectedDeployedEdges = {
{"Base", {
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}},
{"C", {
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}},
{"D", {
{"InternalDispatch", "function Base.pub()"},
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}},
{"E", {
{"InternalDispatch", "function Base.pub()"},
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}},
{"F", {
{"InternalDispatch", "function Base.pub()"},
{"Entry", "function Base.ext()"},
{"Entry", "function Base.pub()"},
{"Entry", "function F.f()"},
{"function Base.ext()", "function free()"},
{"function Base.pub()", "function free()"},
}},
{"L", {}},
};
checkCallGraphExpectations(get<0>(graphs), expectedCreationEdges);
checkCallGraphExpectations(get<1>(graphs), expectedDeployedEdges);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace solidity::frontend::test