improved support for external calls

pull/56/head
Evgeniy Filatov 6 years ago
parent 4a6d8f3495
commit d50b0f01f3
  1. 9
      examples/printers/call_graph.sol
  2. 29
      examples/printers/call_graph.sol.dot
  3. 54
      slither/printers/call/call_graph.py

@ -1,6 +1,14 @@
library Library {
function library_func() {
}
}
contract ContractA {
uint256 public val = 0;
function my_func_a() {
keccak256(0);
Library.library_func();
}
}
@ -21,5 +29,6 @@ contract ContractB {
}
function my_second_func_b(){
a.val();
}
}

@ -1,21 +1,28 @@
strict digraph {
subgraph cluster_9_ContractA {
subgraph cluster_5_Library {
label = "Library"
"5_library_func" [label="library_func"]
}
subgraph cluster_22_ContractA {
label = "ContractA"
"9_my_func_a" [label="my_func_a"]
"22_my_func_a" [label="my_func_a"]
"22_val" [label="val"]
}
subgraph cluster_45_ContractB {
subgraph cluster_63_ContractB {
label = "ContractB"
"45_my_func_a" [label="my_func_a"]
"45_my_second_func_b" [label="my_second_func_b"]
"45_my_func_b" [label="my_func_b"]
"45_constructor" [label="constructor"]
"45_my_func_b" -> "45_my_second_func_b"
"45_my_func_a" -> "45_my_second_func_b"
"63_my_second_func_b" [label="my_second_func_b"]
"63_my_func_a" [label="my_func_a"]
"63_constructor" [label="constructor"]
"63_my_func_b" [label="my_func_b"]
"63_my_func_b" -> "63_my_second_func_b"
"63_my_func_a" -> "63_my_second_func_b"
}
subgraph cluster_solidity {
label = "[Solidity]"
"keccak256()"
"9_my_func_a" -> "keccak256()"
"22_my_func_a" -> "keccak256()"
}
"45_my_func_b" -> "9_my_func_a"
"22_my_func_a" -> "5_library_func"
"63_my_func_b" -> "22_my_func_a"
"63_my_second_func_b" -> "22_val"
}

@ -27,29 +27,6 @@ def _function_node(contract, function):
def _solidity_function_node(solidity_function):
return f'{solidity_function.name}'
# return node for externally called function
def _external_function_node(member_access):
# we have external function name, to get Contract we need to
# check that expression is variable and is of Contract type
expression = member_access.expression
if not isinstance(expression, (Identifier)):
raise TypeError
value = expression.value
if not isinstance(value, (Variable)):
raise TypeError
value_type = value.type
if not isinstance(value_type, (UserDefinedType)):
raise TypeError
contract = value_type.type
if not isinstance(contract, (Contract)):
raise TypeError
# at this point we have contract instance and function name
return f'{contract.id}_{member_access.member_name}'
# return dot language string to add graph edge
def _edge(from_node, to_node):
return f'"{from_node}" -> "{to_node}"'
@ -71,6 +48,10 @@ class PrinterCallGraph(AbstractPrinter):
self.contract_functions = {} # contract -> contract functions nodes
self.contract_calls = {} # contract -> contract calls edges
for contract in slither.contracts:
self.contract_functions[contract] = set()
self.contract_calls[contract] = set()
self.solidity_functions = set() # solidity function nodes
self.solidity_calls = set() # solidity calls edges
@ -80,9 +61,6 @@ class PrinterCallGraph(AbstractPrinter):
def _process_contracts(self, contracts):
for contract in contracts:
self.contract_functions[contract] = set()
self.contract_calls[contract] = set()
for function in contract.functions:
self._process_function(contract, function)
@ -93,7 +71,7 @@ class PrinterCallGraph(AbstractPrinter):
for internal_call in function.internal_calls:
self._process_internal_call(contract, function, internal_call)
for external_call in function.external_calls:
for external_call in function.high_level_calls:
self._process_external_call(contract, function, external_call)
def _process_internal_call(self, contract, function, internal_call):
@ -112,15 +90,19 @@ class PrinterCallGraph(AbstractPrinter):
))
def _process_external_call(self, contract, function, external_call):
if isinstance(external_call.called, MemberAccess):
try:
self.external_calls.add(_edge(
_function_node(contract, function),
_external_function_node(external_call.called),
))
except TypeError:
# cannot visualize call if we cannot get external contract
pass
external_contract, external_function = external_call
# add variable as node to respective contract
if isinstance(external_function, (Variable)):
self.contract_functions[external_contract].add(_node(
_function_node(external_contract, external_function),
external_function.name
))
self.external_calls.add(_edge(
_function_node(contract, function),
_function_node(external_contract, external_function),
))
def _render_internal_calls(self):
lines = []

Loading…
Cancel
Save