Merge pull request #836 from crytic/dev-scope

Add scope information to SlithIR nodes
pull/839/head
Feist Josselin 4 years ago committed by GitHub
commit bd91f39003
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      slither/core/cfg/node.py
  2. 14
      slither/core/cfg/scope.py
  3. 23
      slither/core/declarations/contract.py
  4. 24
      slither/core/declarations/function.py
  5. 2
      slither/slithir/operations/binary.py
  6. 183
      slither/solc_parsing/declarations/function.py
  7. 12
      slither/solc_parsing/declarations/modifier.py
  8. 117
      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
# pylint: disable=too-few-public-methods
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,16 @@ 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 +1147,16 @@ 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 +1165,9 @@ 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 +1176,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,12 @@ 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):
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)

@ -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,19 @@ 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 +349,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 +370,45 @@ 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 +420,29 @@ 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 +578,27 @@ 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)
last_scope = cond_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 +607,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 +629,31 @@ 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 +673,10 @@ 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 +690,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 +705,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 +717,9 @@ 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 +802,9 @@ 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 +884,9 @@ 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 +903,14 @@ 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 +935,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 +944,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 +956,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 +979,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 +988,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 +1002,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 +1027,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 +1196,9 @@ 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 +1215,9 @@ 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 +1345,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 +1361,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 +1370,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,12 @@ 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,11 @@ 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 +215,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 +244,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 +261,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 +277,21 @@ 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 +303,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 +312,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,15 +342,19 @@ 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
def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
def convert_yul_function_definition(
root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]
) -> YulNode:
func = FunctionContract(root.compilation_unit)
yul_function = YulFunction(func, root, ast)
yul_function = YulFunction(func, root, ast, node_scope)
root.contract.add_function(func)
root.compilation_unit.add_function(func)
@ -347,9 +366,11 @@ 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 +379,18 @@ 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 +401,9 @@ 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 +416,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 +430,9 @@ 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 +513,12 @@ 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 +529,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 +537,42 @@ 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 +585,18 @@ 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