Merge branch 'dev-scope' into dev-unchecked-block

pull/837/head
Josselin 4 years ago
commit d6c24351f9
  1. 6
      slither/core/cfg/scope.py
  2. 20
      slither/core/declarations/contract.py
  3. 4
      slither/core/declarations/function.py
  4. 3
      slither/slithir/operations/binary.py
  5. 108
      slither/solc_parsing/declarations/function.py
  6. 4
      slither/solc_parsing/declarations/modifier.py
  7. 71
      slither/solc_parsing/yul/parse_yul.py

@ -5,10 +5,10 @@ if TYPE_CHECKING:
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] = []
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

@ -1115,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, constructor_variable)
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, prev_node.scope)
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)
@ -1143,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, constructor_variable)
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, prev_node.scope)
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)
@ -1157,7 +1165,9 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
break
def _create_node(self, func: Function, counter: int, variable: "Variable", scope: Union[Scope, Function]):
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,

@ -1531,7 +1531,9 @@ class Function(metaclass=ABCMeta): # pylint: disable=too-many-public-methods
###################################################################################
###################################################################################
def new_node(self, node_type: "NodeType", src: Union[str, Dict], scope: Union[Scope, "Function"]) -> "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, scope)

@ -134,7 +134,7 @@ class BinaryType(Enum):
class Binary(OperationWithLValue):
def __init__(self, result, left_variable, right_variable, operation_type: BinaryType, is_checked: bool=False):
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)
@ -147,7 +147,6 @@ class Binary(OperationWithLValue):
result.set_type(ElementaryType("bool"))
else:
result.set_type(left_variable.type)
self.is_checked = is_checked
@property
def read(self):

@ -329,13 +329,17 @@ class FunctionSolc:
###################################################################################
###################################################################################
def _new_node(self, node_type: NodeType, src: Union[str, Dict], scope: Union[Scope, "Function"]) -> NodeSolc:
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], father_scope: Union[Scope, Function]) -> YulBlock:
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
@ -366,26 +370,42 @@ 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"], node.underlying_node.scope)
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)
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)
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"]:
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)
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"], node.underlying_node.scope)
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)
true_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
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:
false_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
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"], node.underlying_node.scope)
@ -400,21 +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.underlying_node.scope)
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.underlying_node.scope)
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, body_scope)
else:
children = whilte_statement[self.get_children("children")]
expression = children[0]
node_condition = self._new_node(NodeType.IFLOOP, expression["src"], node.underlying_node.scope)
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, body_scope)
node_endWhile = self._new_node(NodeType.ENDLOOP, whilte_statement["src"], node.underlying_node.scope)
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)
@ -550,8 +578,12 @@ class FunctionSolc:
else:
pre, cond, post, body = self._parse_for_legacy_ast(statement)
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)
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
@ -597,13 +629,21 @@ 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.underlying_node.scope)
condition_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
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"], condition_scope)
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, condition_scope)
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
@ -633,7 +673,9 @@ class FunctionSolc:
if externalCall is None:
raise ParsingError("Try/Catch not correctly parsed by Slither %s" % statement)
catch_scope = Scope(node.underlying_node.scope.is_checked, False, node.underlying_node.scope)
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)
@ -675,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"], node.underlying_node.scope)
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
@ -758,7 +802,9 @@ class FunctionSolc:
"typeDescriptions": {"typeString": "tuple()"},
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], node.underlying_node.scope)
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)
@ -838,7 +884,9 @@ class FunctionSolc:
],
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], node.underlying_node.scope)
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)
@ -860,7 +908,9 @@ class FunctionSolc:
link_underlying_nodes(node, new_node)
return new_node
def _parse_statement(self, statement: Dict, node: NodeSolc, scope: Union[Scope, Function]) -> NodeSolc:
def _parse_statement(
self, statement: Dict, node: NodeSolc, scope: Union[Scope, Function]
) -> NodeSolc:
"""
Return:
@ -1167,7 +1217,9 @@ class FunctionSolc:
for m in ExportValues(m).result():
if isinstance(m, Function):
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"], self.underlying_function)
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:
@ -1184,7 +1236,9 @@ class FunctionSolc:
)
elif isinstance(m, Contract):
node_parser = self._new_node(NodeType.EXPRESSION, modifier["src"], self.underlying_function)
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:

@ -93,7 +93,9 @@ class ModifierSolc(FunctionSolc):
# self._analyze_read_write()
# self._analyze_calls()
def _parse_statement(self, statement: Dict, node: NodeSolc, scope: Union[Scope, "Function"]) -> 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"], scope)

@ -200,7 +200,9 @@ class YulLocalVariable: # pylint: disable=too-few-public-methods
class YulFunction(YulScope):
__slots__ = ["_function", "_root", "_ast", "_nodes", "_entrypoint", "node_scope"]
def __init__(self, func: Function, root: YulScope, ast: Dict, node_scope: Union[Function, Scope]):
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"
@ -277,7 +279,14 @@ class YulBlock(YulScope):
__slots__ = ["_entrypoint", "_parent_func", "_nodes", "node_scope"]
def __init__(self, contract: Optional[Contract], entrypoint: Node, yul_id: List[str], node_scope: Union[Scope, Function], **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)
@ -333,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, node_scope: Union[Function, Scope]) -> 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, 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)
@ -353,7 +366,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, node_scope: Union[Function, Scope]) -> 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, node_scope)
@ -364,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, _node_scope: Union[Function, Scope]) -> 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, _node_scope: Union[Function, Scope]) -> 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"]
@ -382,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, node_scope: Union[Function, Scope]) -> 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"]
@ -409,7 +430,9 @@ def convert_yul_if(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union
return end
def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> 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.
@ -493,7 +516,9 @@ def convert_yul_switch(root: YulScope, parent: YulNode, ast: Dict, node_scope: U
return convert_yul(root, parent, rewritten_switch, node_scope)
def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]) -> 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"]
@ -521,25 +546,33 @@ def convert_yul_for_loop(root: YulScope, parent: YulNode, ast: Dict, node_scope:
return end_loop
def convert_yul_break(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> 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, _node_scope: Union[Function, Scope]) -> 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, _node_scope: Union[Function, Scope]) -> 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, _node_scope: Union[Function, Scope]) -> 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)
@ -552,13 +585,17 @@ def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict, _node_sco
return node
def convert_yul_unsupported(root: YulScope, parent: YulNode, ast: Dict, _node_scope: Union[Function, Scope]) -> 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, node_scope: Union[Function, Scope]) -> YulNode:
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)

Loading…
Cancel
Save