Add scope information in slithIR CFG

The main usage in the short term is to allow unchecked block
In the long term it will allow better handling of variables usage
pull/836/head
Josselin 4 years ago
parent 9533947d84
commit 571a849757
  1. 5
      slither/core/cfg/node.py
  2. 14
      slither/core/cfg/scope.py
  3. 13
      slither/core/declarations/contract.py
  4. 22
      slither/core/declarations/function.py
  5. 3
      slither/slithir/operations/binary.py
  6. 129
      slither/solc_parsing/declarations/function.py
  7. 10
      slither/solc_parsing/declarations/modifier.py
  8. 76
      slither/solc_parsing/yul/parse_yul.py
  9. BIN
      tests/ast-parsing/compile/scope-0.4.0-legacy.zip
  10. BIN
      tests/ast-parsing/compile/scope-0.4.1-legacy.zip
  11. BIN
      tests/ast-parsing/compile/scope-0.4.10-legacy.zip
  12. BIN
      tests/ast-parsing/compile/scope-0.4.11-legacy.zip
  13. BIN
      tests/ast-parsing/compile/scope-0.4.12-compact.zip
  14. BIN
      tests/ast-parsing/compile/scope-0.4.12-legacy.zip
  15. BIN
      tests/ast-parsing/compile/scope-0.4.13-compact.zip
  16. BIN
      tests/ast-parsing/compile/scope-0.4.13-legacy.zip
  17. BIN
      tests/ast-parsing/compile/scope-0.4.14-compact.zip
  18. BIN
      tests/ast-parsing/compile/scope-0.4.14-legacy.zip
  19. BIN
      tests/ast-parsing/compile/scope-0.4.15-compact.zip
  20. BIN
      tests/ast-parsing/compile/scope-0.4.15-legacy.zip
  21. BIN
      tests/ast-parsing/compile/scope-0.4.16-compact.zip
  22. BIN
      tests/ast-parsing/compile/scope-0.4.16-legacy.zip
  23. BIN
      tests/ast-parsing/compile/scope-0.4.17-compact.zip
  24. BIN
      tests/ast-parsing/compile/scope-0.4.17-legacy.zip
  25. BIN
      tests/ast-parsing/compile/scope-0.4.18-compact.zip
  26. BIN
      tests/ast-parsing/compile/scope-0.4.18-legacy.zip
  27. BIN
      tests/ast-parsing/compile/scope-0.4.19-compact.zip
  28. BIN
      tests/ast-parsing/compile/scope-0.4.19-legacy.zip
  29. BIN
      tests/ast-parsing/compile/scope-0.4.2-legacy.zip
  30. BIN
      tests/ast-parsing/compile/scope-0.4.20-compact.zip
  31. BIN
      tests/ast-parsing/compile/scope-0.4.20-legacy.zip
  32. BIN
      tests/ast-parsing/compile/scope-0.4.21-compact.zip
  33. BIN
      tests/ast-parsing/compile/scope-0.4.21-legacy.zip
  34. BIN
      tests/ast-parsing/compile/scope-0.4.22-compact.zip
  35. BIN
      tests/ast-parsing/compile/scope-0.4.22-legacy.zip
  36. BIN
      tests/ast-parsing/compile/scope-0.4.23-compact.zip
  37. BIN
      tests/ast-parsing/compile/scope-0.4.23-legacy.zip
  38. BIN
      tests/ast-parsing/compile/scope-0.4.24-compact.zip
  39. BIN
      tests/ast-parsing/compile/scope-0.4.24-legacy.zip
  40. BIN
      tests/ast-parsing/compile/scope-0.4.25-compact.zip
  41. BIN
      tests/ast-parsing/compile/scope-0.4.25-legacy.zip
  42. BIN
      tests/ast-parsing/compile/scope-0.4.26-compact.zip
  43. BIN
      tests/ast-parsing/compile/scope-0.4.26-legacy.zip
  44. BIN
      tests/ast-parsing/compile/scope-0.4.3-legacy.zip
  45. BIN
      tests/ast-parsing/compile/scope-0.4.4-legacy.zip
  46. BIN
      tests/ast-parsing/compile/scope-0.4.5-legacy.zip
  47. BIN
      tests/ast-parsing/compile/scope-0.4.6-legacy.zip
  48. BIN
      tests/ast-parsing/compile/scope-0.4.7-legacy.zip
  49. BIN
      tests/ast-parsing/compile/scope-0.4.8-legacy.zip
  50. BIN
      tests/ast-parsing/compile/scope-0.4.9-legacy.zip
  51. BIN
      tests/ast-parsing/compile/scope-0.5.0-compact.zip
  52. BIN
      tests/ast-parsing/compile/scope-0.5.0-legacy.zip
  53. BIN
      tests/ast-parsing/compile/scope-0.5.1-compact.zip
  54. BIN
      tests/ast-parsing/compile/scope-0.5.1-legacy.zip
  55. BIN
      tests/ast-parsing/compile/scope-0.5.10-compact.zip
  56. BIN
      tests/ast-parsing/compile/scope-0.5.10-legacy.zip
  57. BIN
      tests/ast-parsing/compile/scope-0.5.11-compact.zip
  58. BIN
      tests/ast-parsing/compile/scope-0.5.11-legacy.zip
  59. BIN
      tests/ast-parsing/compile/scope-0.5.12-compact.zip
  60. BIN
      tests/ast-parsing/compile/scope-0.5.12-legacy.zip
  61. BIN
      tests/ast-parsing/compile/scope-0.5.13-compact.zip
  62. BIN
      tests/ast-parsing/compile/scope-0.5.13-legacy.zip
  63. BIN
      tests/ast-parsing/compile/scope-0.5.14-compact.zip
  64. BIN
      tests/ast-parsing/compile/scope-0.5.14-legacy.zip
  65. BIN
      tests/ast-parsing/compile/scope-0.5.15-compact.zip
  66. BIN
      tests/ast-parsing/compile/scope-0.5.15-legacy.zip
  67. BIN
      tests/ast-parsing/compile/scope-0.5.16-compact.zip
  68. BIN
      tests/ast-parsing/compile/scope-0.5.16-legacy.zip
  69. BIN
      tests/ast-parsing/compile/scope-0.5.17-compact.zip
  70. BIN
      tests/ast-parsing/compile/scope-0.5.17-legacy.zip
  71. BIN
      tests/ast-parsing/compile/scope-0.5.2-compact.zip
  72. BIN
      tests/ast-parsing/compile/scope-0.5.2-legacy.zip
  73. BIN
      tests/ast-parsing/compile/scope-0.5.3-compact.zip
  74. BIN
      tests/ast-parsing/compile/scope-0.5.3-legacy.zip
  75. BIN
      tests/ast-parsing/compile/scope-0.5.4-compact.zip
  76. BIN
      tests/ast-parsing/compile/scope-0.5.4-legacy.zip
  77. BIN
      tests/ast-parsing/compile/scope-0.5.5-compact.zip
  78. BIN
      tests/ast-parsing/compile/scope-0.5.5-legacy.zip
  79. BIN
      tests/ast-parsing/compile/scope-0.5.6-compact.zip
  80. BIN
      tests/ast-parsing/compile/scope-0.5.6-legacy.zip
  81. BIN
      tests/ast-parsing/compile/scope-0.5.7-compact.zip
  82. BIN
      tests/ast-parsing/compile/scope-0.5.7-legacy.zip
  83. BIN
      tests/ast-parsing/compile/scope-0.5.8-compact.zip
  84. BIN
      tests/ast-parsing/compile/scope-0.5.8-legacy.zip
  85. BIN
      tests/ast-parsing/compile/scope-0.5.9-compact.zip
  86. BIN
      tests/ast-parsing/compile/scope-0.5.9-legacy.zip
  87. BIN
      tests/ast-parsing/compile/scope-0.6.0-compact.zip
  88. BIN
      tests/ast-parsing/compile/scope-0.6.0-legacy.zip
  89. BIN
      tests/ast-parsing/compile/scope-0.6.1-compact.zip
  90. BIN
      tests/ast-parsing/compile/scope-0.6.1-legacy.zip
  91. BIN
      tests/ast-parsing/compile/scope-0.6.10-compact.zip
  92. BIN
      tests/ast-parsing/compile/scope-0.6.10-legacy.zip
  93. BIN
      tests/ast-parsing/compile/scope-0.6.11-compact.zip
  94. BIN
      tests/ast-parsing/compile/scope-0.6.11-legacy.zip
  95. BIN
      tests/ast-parsing/compile/scope-0.6.12-compact.zip
  96. BIN
      tests/ast-parsing/compile/scope-0.6.12-legacy.zip
  97. BIN
      tests/ast-parsing/compile/scope-0.6.2-compact.zip
  98. BIN
      tests/ast-parsing/compile/scope-0.6.2-legacy.zip
  99. BIN
      tests/ast-parsing/compile/scope-0.6.3-compact.zip
  100. BIN
      tests/ast-parsing/compile/scope-0.6.3-legacy.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -54,6 +54,7 @@ if TYPE_CHECKING:
LibraryCallType,
LowLevelCallType,
)
from slither.core.cfg.scope import Scope
# pylint: disable=too-many-lines,too-many-branches,too-many-instance-attributes
@ -152,7 +153,7 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
"""
def __init__(self, node_type: NodeType, node_id: int):
def __init__(self, node_type: NodeType, node_id: int, scope: Union["Scope", "Function"]):
super().__init__()
self._node_type = node_type
@ -220,6 +221,8 @@ class Node(SourceMapping, ChildFunction): # pylint: disable=too-many-public-met
self._asm_source_code: Optional[Union[str, Dict]] = None
self.scope = scope
###################################################################################
###################################################################################
# region General's properties

@ -0,0 +1,14 @@
from typing import List, TYPE_CHECKING, Union
if TYPE_CHECKING:
from slither.core.cfg.node import Node
from slither.core.declarations.function import Function
class Scope:
def __init__(self, is_checked:bool, is_yul: bool, scope: Union["Scope", "Function"]):
self.nodes: List[Node] = []
self.is_checked = is_checked
self.is_yul = is_yul
self.father = scope

@ -7,6 +7,7 @@ from typing import Optional, List, Dict, Callable, Tuple, TYPE_CHECKING, Union
from crytic_compile.platform import Type as PlatformType
from slither.core.cfg.scope import Scope
from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping
@ -1114,12 +1115,12 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
prev_node = self._create_node(constructor_variable, 0, variable_candidate, constructor_variable)
variable_candidate.node_initialization = prev_node
counter = 1
for v in self.state_variables[idx + 1 :]:
if v.expression and not v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
next_node = self._create_node(constructor_variable, counter, v, prev_node.scope)
v.node_initialization = next_node
prev_node.add_son(next_node)
next_node.add_father(prev_node)
@ -1142,12 +1143,12 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
constructor_variable.set_offset(self.source_mapping, self.compilation_unit)
self._functions[constructor_variable.canonical_name] = constructor_variable
prev_node = self._create_node(constructor_variable, 0, variable_candidate)
prev_node = self._create_node(constructor_variable, 0, variable_candidate, constructor_variable)
variable_candidate.node_initialization = prev_node
counter = 1
for v in self.state_variables[idx + 1 :]:
if v.expression and v.is_constant:
next_node = self._create_node(constructor_variable, counter, v)
next_node = self._create_node(constructor_variable, counter, v, prev_node.scope)
v.node_initialization = next_node
prev_node.add_son(next_node)
next_node.add_father(prev_node)
@ -1156,7 +1157,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
break
def _create_node(self, func: Function, counter: int, variable: "Variable"):
def _create_node(self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function]):
from slither.core.cfg.node import Node, NodeType
from slither.core.expressions import (
AssignmentOperationType,
@ -1165,7 +1166,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
)
# Function uses to create node for state variable declaration statements
node = Node(NodeType.OTHER_ENTRYPOINT, counter)
node = Node(NodeType.OTHER_ENTRYPOINT, counter, scope)
node.set_offset(variable.source_mapping, self.compilation_unit)
node.set_function(func)
func.add_node(node)

@ -8,6 +8,7 @@ from enum import Enum
from itertools import groupby
from typing import Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
from slither.core.cfg.scope import Scope
from slither.core.declarations.solidity_variables import (
SolidityFunction,
SolidityVariable,
@ -313,13 +314,16 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
return True
return self._can_send_eth
# @property
# def slither(self) -> "SlitherCore":
# return self._slither
#
# @slither.setter
# def slither(self, sl: "SlitherCore"):
# self._slither = sl
@property
def is_checked(self) -> bool:
"""
Return true if the overflow are enabled by default
:return:
"""
return self.compilation_unit.solc_version >= "0.8.0"
# endregion
###################################################################################
@ -1527,10 +1531,10 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def new_node(self, node_type: "NodeType", src: Union[str, Dict]) -> "Node":
def new_node(self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]) -> "Node":
from slither.core.cfg.node import Node
node = Node(node_type, self._counter_nodes)
node = Node(node_type, self._counter_nodes, scope)
node.set_offset(src, self.compilation_unit)
self._counter_nodes += 1
node.set_function(self)

@ -131,7 +131,7 @@ class BinaryType(Enum):
class Binary(OperationWithLValue):
def __init__(self, result, left_variable, right_variable, operation_type):
def __init__(self, result, left_variable, right_variable, operation_type: BinaryType, is_checked: bool=False):
assert is_valid_rvalue(left_variable) or isinstance(left_variable, Function)
assert is_valid_rvalue(right_variable) or isinstance(right_variable, Function)
assert is_valid_lvalue(result)
@ -144,6 +144,7 @@ class Binary(OperationWithLValue):
result.set_type(ElementaryType("bool"))
else:
result.set_type(left_variable.type)
self.is_checked = is_checked
@property
def read(self):

@ -2,6 +2,7 @@ import logging
from typing import Dict, Optional, Union, List, TYPE_CHECKING
from slither.core.cfg.node import NodeType, link_nodes, insert_node, Node
from slither.core.cfg.scope import Scope
from slither.core.declarations.contract import Contract
from slither.core.declarations.function import (
Function,
@ -328,14 +329,15 @@ class FunctionSolc:
###################################################################################
###################################################################################
def _new_node(self, node_type: NodeType, src: Union[str, Dict]) -> NodeSolc:
node = self._function.new_node(node_type, src)
def _new_node(self, node_type: NodeType, src: Union[str, Dict], scope: Union[Scope, "Function"]) -> NodeSolc:
node = self._function.new_node(node_type, src, scope)
node_parser = NodeSolc(node)
self._node_to_nodesolc[node] = node_parser
return node_parser
def _new_yul_block(self, src: Union[str, Dict]) -> YulBlock:
node = self._function.new_node(NodeType.ASSEMBLY, src)
def _new_yul_block(self, src: Union[str, Dict], father_scope: Union[Scope, Function]) -> YulBlock:
scope = Scope(False, True, father_scope)
node = self._function.new_node(NodeType.ASSEMBLY, src, scope)
contract = None
if isinstance(self._function, FunctionContract):
contract = self._function.contract
@ -343,6 +345,7 @@ class FunctionSolc:
contract,
node,
[self._function.name, f"asm_{len(self._node_to_yulobject)}"],
scope,
parent_func=self._function,
)
self._node_to_yulobject[node] = yul_object
@ -363,25 +366,29 @@ class FunctionSolc:
condition = if_statement["condition"]
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF, condition["src"])
condition_node = self._new_node(NodeType.IF, condition["src"], node.underlying_node.scope)
condition_node.add_unparsed_expression(condition)
link_underlying_nodes(node, condition_node)
trueStatement = self._parse_statement(if_statement["trueBody"], condition_node)
true_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
trueStatement = self._parse_statement(if_statement["trueBody"], condition_node, true_scope)
if "falseBody" in if_statement and if_statement["falseBody"]:
falseStatement = self._parse_statement(if_statement["falseBody"], condition_node)
false_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
falseStatement = self._parse_statement(if_statement["falseBody"], condition_node, false_scope)
else:
children = if_statement[self.get_children("children")]
condition = children[0]
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF, condition["src"])
condition_node = self._new_node(NodeType.IF, condition["src"], node.underlying_node.scope)
condition_node.add_unparsed_expression(condition)
link_underlying_nodes(node, condition_node)
trueStatement = self._parse_statement(children[1], condition_node)
true_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
trueStatement = self._parse_statement(children[1], condition_node, true_scope)
if len(children) == 3:
falseStatement = self._parse_statement(children[2], condition_node)
false_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
falseStatement = self._parse_statement(children[2], condition_node, false_scope)
endIf_node = self._new_node(NodeType.ENDIF, if_statement["src"])
endIf_node = self._new_node(NodeType.ENDIF, if_statement["src"], node.underlying_node.scope)
link_underlying_nodes(trueStatement, endIf_node)
if falseStatement:
@ -393,20 +400,21 @@ class FunctionSolc:
def _parse_while(self, whilte_statement: Dict, node: NodeSolc) -> NodeSolc:
# WhileStatement = 'while' '(' Expression ')' Statement
node_startWhile = self._new_node(NodeType.STARTLOOP, whilte_statement["src"])
node_startWhile = self._new_node(NodeType.STARTLOOP, whilte_statement["src"], node.underlying_node.scope)
body_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
if self.is_compact_ast:
node_condition = self._new_node(NodeType.IFLOOP, whilte_statement["condition"]["src"])
node_condition = self._new_node(NodeType.IFLOOP, whilte_statement["condition"]["src"], node.underlying_node.scope)
node_condition.add_unparsed_expression(whilte_statement["condition"])
statement = self._parse_statement(whilte_statement["body"], node_condition)
statement = self._parse_statement(whilte_statement["body"], node_condition, body_scope)
else:
children = whilte_statement[self.get_children("children")]
expression = children[0]
node_condition = self._new_node(NodeType.IFLOOP, expression["src"])
node_condition = self._new_node(NodeType.IFLOOP, expression["src"], node.underlying_node.scope)
node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition)
statement = self._parse_statement(children[1], node_condition, body_scope)
node_endWhile = self._new_node(NodeType.ENDLOOP, whilte_statement["src"])
node_endWhile = self._new_node(NodeType.ENDLOOP, whilte_statement["src"], node.underlying_node.scope)
link_underlying_nodes(node, node_startWhile)
link_underlying_nodes(node_startWhile, node_condition)
@ -542,17 +550,23 @@ class FunctionSolc:
else:
pre, cond, post, body = self._parse_for_legacy_ast(statement)
node_startLoop = self._new_node(NodeType.STARTLOOP, statement["src"])
node_endLoop = self._new_node(NodeType.ENDLOOP, statement["src"])
node_startLoop = self._new_node(NodeType.STARTLOOP, statement["src"], node.underlying_node.scope)
node_endLoop = self._new_node(NodeType.ENDLOOP, statement["src"], node.underlying_node.scope)
last_scope = node.underlying_node.scope
if pre:
node_init_expression = self._parse_statement(pre, node)
pre_scope = Scope(node.underlying_node.scope.is_checked, False, last_scope)
last_scope = pre_scope
node_init_expression = self._parse_statement(pre, node, pre_scope)
link_underlying_nodes(node_init_expression, node_startLoop)
else:
link_underlying_nodes(node, node_startLoop)
if cond:
node_condition = self._new_node(NodeType.IFLOOP, cond["src"])
cond_scope = Scope(node.underlying_node.scope.is_checked, False, last_scope)
cond_scope = last_scope
node_condition = self._new_node(NodeType.IFLOOP, cond["src"], cond_scope)
node_condition.add_unparsed_expression(cond)
link_underlying_nodes(node_startLoop, node_condition)
@ -561,10 +575,12 @@ class FunctionSolc:
node_condition = None
node_beforeBody = node_startLoop
node_body = self._parse_statement(body, node_beforeBody)
body_scope = Scope(node.underlying_node.scope.is_checked, False, last_scope)
last_scope = body_scope
node_body = self._parse_statement(body, node_beforeBody, body_scope)
if post:
node_loopexpression = self._parse_statement(post, node_body)
node_loopexpression = self._parse_statement(post, node_body, last_scope)
link_underlying_nodes(node_loopexpression, node_beforeBody)
else:
# node_loopexpression = None
@ -581,21 +597,23 @@ class FunctionSolc:
def _parse_dowhile(self, do_while_statement: Dict, node: NodeSolc) -> NodeSolc:
node_startDoWhile = self._new_node(NodeType.STARTLOOP, do_while_statement["src"])
node_startDoWhile = self._new_node(NodeType.STARTLOOP, do_while_statement["src"], node.underlying_node.scope)
condition_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
if self.is_compact_ast:
node_condition = self._new_node(NodeType.IFLOOP, do_while_statement["condition"]["src"])
node_condition = self._new_node(NodeType.IFLOOP, do_while_statement["condition"]["src"], condition_scope)
node_condition.add_unparsed_expression(do_while_statement["condition"])
statement = self._parse_statement(do_while_statement["body"], node_condition)
statement = self._parse_statement(do_while_statement["body"], node_condition, condition_scope)
else:
children = do_while_statement[self.get_children("children")]
# same order in the AST as while
expression = children[0]
node_condition = self._new_node(NodeType.IFLOOP, expression["src"])
node_condition = self._new_node(NodeType.IFLOOP, expression["src"], condition_scope)
node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition)
statement = self._parse_statement(children[1], node_condition, condition_scope)
node_endDoWhile = self._new_node(NodeType.ENDLOOP, do_while_statement["src"])
body_scope = Scope(node.underlying_node.scope.is_checked, False, condition_scope)
node_endDoWhile = self._new_node(NodeType.ENDLOOP, do_while_statement["src"], body_scope)
link_underlying_nodes(node, node_startDoWhile)
# empty block, loop from the start to the condition
@ -615,8 +633,8 @@ class FunctionSolc:
if externalCall is None:
raise ParsingError("Try/Catch not correctly parsed by Slither %s" % statement)
new_node = self._new_node(NodeType.TRY, statement["src"])
catch_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
new_node = self._new_node(NodeType.TRY, statement["src"], catch_scope)
new_node.add_unparsed_expression(externalCall)
link_underlying_nodes(node, new_node)
node = new_node
@ -630,7 +648,9 @@ class FunctionSolc:
if block is None:
raise ParsingError("Catch not correctly parsed by Slither %s" % statement)
try_node = self._new_node(NodeType.CATCH, statement["src"])
try_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
try_node = self._new_node(NodeType.CATCH, statement["src"], try_scope)
link_underlying_nodes(node, try_node)
if self.is_compact_ast:
@ -643,7 +663,7 @@ class FunctionSolc:
assert param[self.get_key()] == "VariableDeclaration"
self._add_param(param)
return self._parse_statement(block, try_node)
return self._parse_statement(block, try_node, try_scope)
def _parse_variable_definition(self, statement: Dict, node: NodeSolc) -> NodeSolc:
try:
@ -655,7 +675,7 @@ class FunctionSolc:
self._add_local_variable(local_var_parser)
# local_var.analyze(self)
new_node = self._new_node(NodeType.VARIABLE, statement["src"])
new_node = self._new_node(NodeType.VARIABLE, statement["src"], node.underlying_node.scope)
new_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(node, new_node)
return new_node
@ -738,7 +758,7 @@ class FunctionSolc:
"typeDescriptions": {"typeString": "tuple()"},
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION, statement["src"])
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], node.underlying_node.scope)
new_node.add_unparsed_expression(expression)
link_underlying_nodes(node, new_node)
@ -818,7 +838,7 @@ class FunctionSolc:
],
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION, statement["src"])
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], node.underlying_node.scope)
new_node.add_unparsed_expression(expression)
link_underlying_nodes(node, new_node)
@ -835,12 +855,12 @@ class FunctionSolc:
self._add_local_variable(local_var_parser)
new_node = self._new_node(NodeType.VARIABLE, statement["src"])
new_node = self._new_node(NodeType.VARIABLE, statement["src"], node.underlying_node.scope)
new_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(node, new_node)
return new_node
def _parse_statement(self, statement: Dict, node: NodeSolc) -> NodeSolc:
def _parse_statement(self, statement: Dict, node: NodeSolc, scope: Union[Scope, Function]) -> NodeSolc:
"""
Return:
@ -865,7 +885,7 @@ class FunctionSolc:
# Added with solc 0.6 - the yul code is an AST
if "AST" in statement and not self.compilation_unit.core.skip_assembly:
self._function.contains_assembly = True
yul_object = self._new_yul_block(statement["src"])
yul_object = self._new_yul_block(statement["src"], scope)
entrypoint = yul_object.entrypoint
exitpoint = yul_object.convert(statement["AST"])
@ -874,7 +894,7 @@ class FunctionSolc:
link_underlying_nodes(node, entrypoint)
node = exitpoint
else:
asm_node = self._new_node(NodeType.ASSEMBLY, statement["src"])
asm_node = self._new_node(NodeType.ASSEMBLY, statement["src"], scope)
self._function.contains_assembly = True
# Added with solc 0.4.12
if "operations" in statement:
@ -886,15 +906,15 @@ class FunctionSolc:
# For Continue / Break / Return / Throw
# The is fixed later
elif name == "Continue":
continue_node = self._new_node(NodeType.CONTINUE, statement["src"])
continue_node = self._new_node(NodeType.CONTINUE, statement["src"], scope)
link_underlying_nodes(node, continue_node)
node = continue_node
elif name == "Break":
break_node = self._new_node(NodeType.BREAK, statement["src"])
break_node = self._new_node(NodeType.BREAK, statement["src"], scope)
link_underlying_nodes(node, break_node)
node = break_node
elif name == "Return":
return_node = self._new_node(NodeType.RETURN, statement["src"])
return_node = self._new_node(NodeType.RETURN, statement["src"], scope)
link_underlying_nodes(node, return_node)
if self.is_compact_ast:
if statement.get("expression", None):
@ -909,7 +929,7 @@ class FunctionSolc:
return_node.add_unparsed_expression(expression)
node = return_node
elif name == "Throw":
throw_node = self._new_node(NodeType.THROW, statement["src"])
throw_node = self._new_node(NodeType.THROW, statement["src"], scope)
link_underlying_nodes(node, throw_node)
node = throw_node
elif name == "EmitStatement":
@ -918,7 +938,7 @@ class FunctionSolc:
expression = statement["eventCall"]
else:
expression = statement[self.get_children("children")][0]
new_node = self._new_node(NodeType.EXPRESSION, statement["src"])
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], scope)
new_node.add_unparsed_expression(expression)
link_underlying_nodes(node, new_node)
node = new_node
@ -932,7 +952,7 @@ class FunctionSolc:
expression = statement[self.get_children("expression")]
else:
expression = statement[self.get_children("expression")][0]
new_node = self._new_node(NodeType.EXPRESSION, statement["src"])
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], scope)
new_node.add_unparsed_expression(expression)
link_underlying_nodes(node, new_node)
node = new_node
@ -957,15 +977,16 @@ class FunctionSolc:
else:
statements = block[self.get_children("children")]
new_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
for statement in statements:
node = self._parse_statement(statement, node)
node = self._parse_statement(statement, node, new_scope)
return node
def _parse_cfg(self, cfg: Dict):
assert cfg[self.get_key()] == "Block"
node = self._new_node(NodeType.ENTRYPOINT, cfg["src"])
node = self._new_node(NodeType.ENTRYPOINT, cfg["src"], self.underlying_function)
self._function.entry_point = node.underlying_node
if self.is_compact_ast:
@ -1125,7 +1146,7 @@ class FunctionSolc:
for m in ExportValues(m).result():
if isinstance(m, Function):
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"])
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"], self.underlying_function)
node_parser.add_unparsed_expression(modifier)
# The latest entry point is the entry point, or the latest modifier call
if self._function.modifiers:
@ -1142,7 +1163,7 @@ class FunctionSolc:
)
elif isinstance(m, Contract):
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"])
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"], self.underlying_function)
node_parser.add_unparsed_expression(modifier)
# The latest entry point is the entry point, or the latest constructor call
if self._function.explicit_base_constructor_calls_statements:
@ -1270,14 +1291,14 @@ class FunctionSolc:
true_expr: "Expression",
false_expr: "Expression",
):
condition_node = self._new_node(NodeType.IF, node.source_mapping)
condition_node = self._new_node(NodeType.IF, node.source_mapping, node.scope)
condition_node.underlying_node.add_expression(condition)
condition_node.analyze_expressions(self)
if node.type == NodeType.VARIABLE:
condition_node.underlying_node.add_variable_declaration(node.variable_declaration)
true_node_parser = self._new_node(NodeType.EXPRESSION, node.source_mapping)
true_node_parser = self._new_node(NodeType.EXPRESSION, node.source_mapping, node.scope)
if node.type == NodeType.VARIABLE:
assert isinstance(true_expr, AssignmentOperation)
# true_expr = true_expr.expression_right
@ -1286,7 +1307,7 @@ class FunctionSolc:
true_node_parser.underlying_node.add_expression(true_expr)
true_node_parser.analyze_expressions(self)
false_node_parser = self._new_node(NodeType.EXPRESSION, node.source_mapping)
false_node_parser = self._new_node(NodeType.EXPRESSION, node.source_mapping, node.scope)
if node.type == NodeType.VARIABLE:
assert isinstance(false_expr, AssignmentOperation)
elif node.type == NodeType.RETURN:
@ -1295,7 +1316,7 @@ class FunctionSolc:
false_node_parser.underlying_node.add_expression(false_expr)
false_node_parser.analyze_expressions(self)
endif_node = self._new_node(NodeType.ENDIF, node.source_mapping)
endif_node = self._new_node(NodeType.ENDIF, node.source_mapping, node.scope)
for father in node.fathers:
father.remove_son(node)

@ -1,10 +1,11 @@
"""
Event module
"""
from typing import Dict, TYPE_CHECKING
from typing import Dict, TYPE_CHECKING, Union
from slither.core.cfg.node import NodeType
from slither.core.cfg.node import link_nodes
from slither.core.cfg.scope import Scope
from slither.core.declarations.modifier import Modifier
from slither.solc_parsing.cfg.node import NodeSolc
from slither.solc_parsing.declarations.function import FunctionSolc
@ -12,6 +13,7 @@ from slither.solc_parsing.declarations.function import FunctionSolc
if TYPE_CHECKING:
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.declarations import Function
class ModifierSolc(FunctionSolc):
@ -91,10 +93,10 @@ class ModifierSolc(FunctionSolc):
# self._analyze_read_write()
# self._analyze_calls()
def _parse_statement(self, statement: Dict, node: NodeSolc) -> NodeSolc:
def _parse_statement(self, statement: Dict, node: NodeSolc, scope: Union[Scope, "Function"]) -> NodeSolc:
name = statement[self.get_key()]
if name == "PlaceholderStatement":
placeholder_node = self._new_node(NodeType.PLACEHOLDER, statement["src"])
placeholder_node = self._new_node(NodeType.PLACEHOLDER, statement["src"], scope)
link_nodes(node.underlying_node, placeholder_node.underlying_node)
return placeholder_node
return super()._parse_statement(statement, node)
return super()._parse_statement(statement, node, scope)

@ -3,6 +3,7 @@ import json
from typing import Optional, Dict, List, Union
from slither.core.cfg.node import NodeType, Node, link_nodes
from slither.core.cfg.scope import Scope
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import (
Function,
@ -22,6 +23,7 @@ from slither.core.expressions import (
)
from slither.core.expressions.expression import Expression
from slither.core.solidity_types import ElementaryType
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
from slither.exceptions import SlitherException
from slither.solc_parsing.yul.evm_functions import (
@ -196,9 +198,9 @@ class YulLocalVariable: # pylint: disable=too-few-public-methods
class YulFunction(YulScope):
__slots__ = ["_function", "_root", "_ast", "_nodes", "_entrypoint"]
__slots__ = ["_function", "_root", "_ast", "_nodes", "_entrypoint", "node_scope"]
def __init__(self, func: Function, root: YulScope, ast: Dict):
def __init__(self, func: Function, root: YulScope, ast: Dict, node_scope: Union[Function, Scope]):
super().__init__(root.contract, root.id + [ast["name"]], parent_func=root.parent_func)
assert ast["nodeType"] == "YulFunctionDefinition"
@ -211,12 +213,15 @@ class YulFunction(YulScope):
func.name = ast["name"]
func.set_visibility("private")
func.set_offset(ast["src"], root.compilation_unit)
func.set_contract(root.contract)
if isinstance(func, SourceMapping):
func.set_offset(ast["src"], root.compilation_unit)
if isinstance(func, FunctionContract):
func.set_contract(root.contract)
func.set_contract_declarer(root.contract)
func.compilation_unit = root.compilation_unit
func.set_contract_declarer(root.contract)
func.scope = root.id
func.is_implemented = True
self.node_scope = node_scope
self._nodes: List[YulNode] = []
self._entrypoint = self.new_node(NodeType.ASSEMBLY, ast["src"])
@ -237,16 +242,16 @@ class YulFunction(YulScope):
link_underlying_nodes(self._entrypoint, node)
for param in self._ast.get("parameters", []):
node = convert_yul(self, node, param)
node = convert_yul(self, node, param, self.node_scope)
self._function.add_parameters(
self.get_yul_local_variable_from_name(param["name"]).underlying
)
for ret in self._ast.get("returnVariables", []):
node = convert_yul(self, node, ret)
node = convert_yul(self, node, ret, self.node_scope)
self._function.add_return(self.get_yul_local_variable_from_name(ret["name"]).underlying)
convert_yul(self, node, self._ast["body"])
convert_yul(self, node, self._ast["body"], self.node_scope)
def parse_body(self):
for node in self._nodes:
@ -254,7 +259,7 @@ class YulFunction(YulScope):
def new_node(self, node_type, src) -> YulNode:
if self._function:
node = self._function.new_node(node_type, src)
node = self._function.new_node(node_type, src, self.node_scope)
else:
raise SlitherException("standalone yul objects are not supported yet")
@ -270,13 +275,14 @@ class YulBlock(YulScope):
"""
__slots__ = ["_entrypoint", "_parent_func", "_nodes"]
__slots__ = ["_entrypoint", "_parent_func", "_nodes", "node_scope"]
def __init__(self, contract: Optional[Contract], entrypoint: Node, yul_id: List[str], **kwargs):
def __init__(self, contract: Optional[Contract], entrypoint: Node, yul_id: List[str], node_scope: Union[Scope, Function], **kwargs):
super().__init__(contract, yul_id, **kwargs)
self._entrypoint: YulNode = YulNode(entrypoint, self)
self._nodes: List[YulNode] = []
self.node_scope = node_scope
@property
def entrypoint(self) -> YulNode:
@ -288,7 +294,7 @@ class YulBlock(YulScope):
def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode:
if self._parent_func:
node = self._parent_func.new_node(node_type, src)
node = self._parent_func.new_node(node_type, src, self.node_scope)
else:
raise SlitherException("standalone yul objects are not supported yet")
@ -297,7 +303,7 @@ class YulBlock(YulScope):
return yul_node
def convert(self, ast: Dict) -> YulNode:
return convert_yul(self, self._entrypoint, ast)
return convert_yul(self, self._entrypoint, ast, self.node_scope)
def analyze_expressions(self):
for node in self._nodes:
@ -327,9 +333,9 @@ class YulBlock(YulScope):
# dispatches to a specialized function based on a lookup dictionary.
def convert_yul_block(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_block(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
for statement in ast["statements"]:
parent = convert_yul(root, parent, statement)
parent = convert_yul(root, parent, statement, node_scope)
return parent
@ -347,9 +353,9 @@ def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict)
return parent
def convert_yul_variable_declaration(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_variable_declaration(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
for variable_ast in ast["variables"]:
parent = convert_yul(root, parent, variable_ast)
parent = convert_yul(root, parent, variable_ast, node_scope)
node = root.new_node(NodeType.EXPRESSION, ast["src"])
node.add_unparsed_expression(ast)
@ -358,14 +364,14 @@ def convert_yul_variable_declaration(root: YulScope, parent: YulNode, ast: Dict)
return node
def convert_yul_assignment(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_assignment(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
node = root.new_node(NodeType.EXPRESSION, ast["src"])
node.add_unparsed_expression(ast)
link_underlying_nodes(parent, node)
return node
def convert_yul_expression_statement(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_expression_statement(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
src = ast["src"]
expression_ast = ast["expression"]
@ -376,7 +382,7 @@ def convert_yul_expression_statement(root: YulScope, parent: YulNode, ast: Dict)
return expression
def convert_yul_if(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_if(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
# we're cheating and pretending that yul supports if/else so we can convert switch cleaner
src = ast["src"]
@ -389,10 +395,10 @@ def convert_yul_if(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
condition.add_unparsed_expression(condition_ast)
true_body = convert_yul(root, condition, true_body_ast)
true_body = convert_yul(root, condition, true_body_ast, node_scope)
if false_body_ast:
false_body = convert_yul(root, condition, false_body_ast)
false_body = convert_yul(root, condition, false_body_ast, node_scope)
link_underlying_nodes(false_body, end)
else:
link_underlying_nodes(condition, end)
@ -403,7 +409,7 @@ def convert_yul_if(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
return end
def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
"""
This is unfortunate. We don't really want a switch in our IR so we're going to
translate it into a series of if/else statements.
@ -484,10 +490,10 @@ def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
else:
rewritten_switch["statements"].append(body_ast)
return convert_yul(root, parent, rewritten_switch)
return convert_yul(root, parent, rewritten_switch, node_scope)
def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
pre_ast = ast["pre"]
condition_ast = ast["condition"]
post_ast = ast["post"]
@ -498,7 +504,7 @@ def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
link_underlying_nodes(parent, start_loop)
pre = convert_yul(root, start_loop, pre_ast)
pre = convert_yul(root, start_loop, pre_ast, node_scope)
condition = root.new_node(NodeType.IFLOOP, condition_ast["src"])
condition.add_unparsed_expression(condition_ast)
@ -506,34 +512,34 @@ def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
link_underlying_nodes(condition, end_loop)
body = convert_yul(root, condition, body_ast)
body = convert_yul(root, condition, body_ast, node_scope)
post = convert_yul(root, body, post_ast)
post = convert_yul(root, body, post_ast, node_scope)
link_underlying_nodes(post, condition)
return end_loop
def convert_yul_break(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_break(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
break_ = root.new_node(NodeType.BREAK, ast["src"])
link_underlying_nodes(parent, break_)
return break_
def convert_yul_continue(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_continue(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
continue_ = root.new_node(NodeType.CONTINUE, ast["src"])
link_underlying_nodes(parent, continue_)
return continue_
def convert_yul_leave(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_leave(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
leave = root.new_node(NodeType.RETURN, ast["src"])
link_underlying_nodes(parent, leave)
return leave
def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
local_var = LocalVariable()
var = YulLocalVariable(local_var, root, ast)
@ -546,14 +552,14 @@ def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict) -> YulNod
return node
def convert_yul_unsupported(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_unsupported(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> YulNode:
raise SlitherException(
f"no converter available for {ast['nodeType']} {json.dumps(ast, indent=2)}"
)
def convert_yul(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
return converters.get(ast["nodeType"], convert_yul_unsupported)(root, parent, ast)
def convert_yul(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> YulNode:
return converters.get(ast["nodeType"], convert_yul_unsupported)(root, parent, ast, node_scope)
converters = {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save