Merge pull request #11124 from ethereum/extend-callgraph

Add creationCode/runtimeCode contract creation detection to call graph
This commit is contained in:
chriseth 2021-03-22 15:45:55 +01:00 committed by GitHub
commit 54cea090bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 7 deletions

View File

@ -165,6 +165,18 @@ bool FunctionCallGraphBuilder::visit(Identifier const& _identifier)
bool FunctionCallGraphBuilder::visit(MemberAccess const& _memberAccess)
{
TypePointer exprType = _memberAccess.expression().annotation().type;
ASTString const& memberName = _memberAccess.memberName();
if (auto magicType = dynamic_cast<MagicType const*>(exprType))
if (magicType->kind() == MagicType::Kind::MetaType && (
memberName == "creationCode" || memberName == "runtimeCode"
))
{
ContractType const& accessedContractType = dynamic_cast<ContractType const&>(*magicType->typeArgument());
m_graph.bytecodeDependency.emplace(&accessedContractType.contractDefinition(), &_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)
@ -173,7 +185,7 @@ bool FunctionCallGraphBuilder::visit(MemberAccess const& _memberAccess)
// Super functions
if (*_memberAccess.annotation().requiredLookup == VirtualLookup::Super)
{
if (auto const* typeType = dynamic_cast<TypeType const*>(_memberAccess.expression().annotation().type))
if (auto const* typeType = dynamic_cast<TypeType const*>(exprType))
if (auto const contractType = dynamic_cast<ContractType const*>(typeType->actualType()))
{
solAssert(contractType->isSuper(), "");
@ -187,7 +199,6 @@ bool FunctionCallGraphBuilder::visit(MemberAccess const& _memberAccess)
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
functionReferenced(*functionDef, _memberAccess.annotation().calledDirectly);
return true;
}
@ -212,7 +223,7 @@ bool FunctionCallGraphBuilder::visit(ModifierInvocation const& _modifierInvocati
bool FunctionCallGraphBuilder::visit(NewExpression const& _newExpression)
{
if (ContractType const* contractType = dynamic_cast<ContractType const*>(_newExpression.typeName().annotation().type))
m_graph.createdContracts.emplace(&contractType->contractDefinition());
m_graph.bytecodeDependency.emplace(&contractType->contractDefinition(), &_newExpression);
return true;
}

View File

@ -61,8 +61,9 @@ struct CallGraph
/// any calls.
std::map<Node, std::set<Node, CompareByID>, CompareByID> edges;
/// Contracts that may get created with `new` by functions present in the graph.
std::set<ContractDefinition const*, ASTNode::CompareByID> createdContracts;
/// Contracts that need to be compiled before this one can be compiled.
/// The value is the ast node that created the dependency.
std::map<ContractDefinition const*, ASTNode const*, ASTNode::CompareByID> bytecodeDependency;
/// Events that may get emitted by functions present in the graph.
std::set<EventDefinition const*, ASTNode::CompareByID> emittedEvents;

View File

@ -159,8 +159,8 @@ void checkCallGraphExpectations(
CallGraph const& callGraph = *_callGraphs.at(contractName);
edges[contractName] = edgeNames(callGraph.edges);
if (!callGraph.createdContracts.empty())
createdContractSets[contractName] = callGraph.createdContracts | views::transform(getContractName) | to<set<string>>();
if (!callGraph.bytecodeDependency.empty())
createdContractSets[contractName] = callGraph.bytecodeDependency | views::keys | views::transform(getContractName) | to<set<string>>();
if (!callGraph.emittedEvents.empty())
emittedEventSets[contractName] = callGraph.emittedEvents | views::transform(eventToString) | to<set<string>>();
}