|
|
|
@ -24,6 +24,7 @@ from slither.core.expressions import ( |
|
|
|
|
UnaryOperation, |
|
|
|
|
) |
|
|
|
|
from slither.core.expressions.expression import Expression |
|
|
|
|
from slither.core.scope.scope import FileScope |
|
|
|
|
from slither.core.solidity_types import ElementaryType |
|
|
|
|
from slither.core.source_mapping.source_mapping import SourceMapping |
|
|
|
|
from slither.core.variables.local_variable import LocalVariable |
|
|
|
@ -51,25 +52,30 @@ class YulNode: |
|
|
|
|
def underlying_node(self) -> Node: |
|
|
|
|
return self._node |
|
|
|
|
|
|
|
|
|
def add_unparsed_expression(self, expression: Dict): |
|
|
|
|
def add_unparsed_expression(self, expression: Dict) -> None: |
|
|
|
|
assert self._unparsed_expression is None |
|
|
|
|
self._unparsed_expression = expression |
|
|
|
|
|
|
|
|
|
def analyze_expressions(self): |
|
|
|
|
def analyze_expressions(self) -> None: |
|
|
|
|
if self._node.type == NodeType.VARIABLE and not self._node.expression: |
|
|
|
|
self._node.add_expression(self._node.variable_declaration.expression) |
|
|
|
|
expression = self._node.variable_declaration.expression |
|
|
|
|
if expression: |
|
|
|
|
self._node.add_expression(expression) |
|
|
|
|
if self._unparsed_expression: |
|
|
|
|
expression = parse_yul(self._scope, self, self._unparsed_expression) |
|
|
|
|
if expression: |
|
|
|
|
self._node.add_expression(expression) |
|
|
|
|
|
|
|
|
|
if self._node.expression: |
|
|
|
|
if self._node.type == NodeType.VARIABLE: |
|
|
|
|
# Update the expression to be an assignement to the variable |
|
|
|
|
variable_declaration = self._node.variable_declaration |
|
|
|
|
if variable_declaration: |
|
|
|
|
_expression = AssignmentOperation( |
|
|
|
|
Identifier(self._node.variable_declaration), |
|
|
|
|
self._node.expression, |
|
|
|
|
AssignmentOperationType.ASSIGN, |
|
|
|
|
self._node.variable_declaration.type, |
|
|
|
|
variable_declaration.type, |
|
|
|
|
) |
|
|
|
|
_expression.set_offset( |
|
|
|
|
self._node.expression.source_mapping, self._node.compilation_unit |
|
|
|
@ -122,13 +128,13 @@ class YulScope(metaclass=abc.ABCMeta): |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
def __init__( |
|
|
|
|
self, contract: Optional[Contract], yul_id: List[str], parent_func: Function = None |
|
|
|
|
): |
|
|
|
|
self, contract: Optional[Contract], yul_id: List[str], parent_func: Function |
|
|
|
|
) -> None: |
|
|
|
|
self._contract = contract |
|
|
|
|
self._id: List[str] = yul_id |
|
|
|
|
self._yul_local_variables: List[YulLocalVariable] = [] |
|
|
|
|
self._yul_local_functions: List[YulFunction] = [] |
|
|
|
|
self._parent_func = parent_func |
|
|
|
|
self._parent_func: Function = parent_func |
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
def id(self) -> List[str]: |
|
|
|
@ -155,10 +161,14 @@ class YulScope(metaclass=abc.ABCMeta): |
|
|
|
|
def new_node(self, node_type: NodeType, src: Union[str, Dict]) -> YulNode: |
|
|
|
|
pass |
|
|
|
|
|
|
|
|
|
def add_yul_local_variable(self, var): |
|
|
|
|
@property |
|
|
|
|
def file_scope(self) -> FileScope: |
|
|
|
|
return self._parent_func.file_scope |
|
|
|
|
|
|
|
|
|
def add_yul_local_variable(self, var: "YulLocalVariable") -> None: |
|
|
|
|
self._yul_local_variables.append(var) |
|
|
|
|
|
|
|
|
|
def get_yul_local_variable_from_name(self, variable_name): |
|
|
|
|
def get_yul_local_variable_from_name(self, variable_name: str) -> Optional["YulLocalVariable"]: |
|
|
|
|
return next( |
|
|
|
|
( |
|
|
|
|
v |
|
|
|
@ -168,10 +178,10 @@ class YulScope(metaclass=abc.ABCMeta): |
|
|
|
|
None, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
def add_yul_local_function(self, func): |
|
|
|
|
def add_yul_local_function(self, func: "YulFunction") -> None: |
|
|
|
|
self._yul_local_functions.append(func) |
|
|
|
|
|
|
|
|
|
def get_yul_local_function_from_name(self, func_name): |
|
|
|
|
def get_yul_local_function_from_name(self, func_name: str) -> Optional["YulLocalVariable"]: |
|
|
|
|
return next( |
|
|
|
|
(v for v in self._yul_local_functions if v.underlying.name == func_name), |
|
|
|
|
None, |
|
|
|
@ -242,7 +252,7 @@ class YulFunction(YulScope): |
|
|
|
|
def function(self) -> Function: |
|
|
|
|
return self._function |
|
|
|
|
|
|
|
|
|
def convert_body(self): |
|
|
|
|
def convert_body(self) -> None: |
|
|
|
|
node = self.new_node(NodeType.ENTRYPOINT, self._ast["src"]) |
|
|
|
|
link_underlying_nodes(self._entrypoint, node) |
|
|
|
|
|
|
|
|
@ -258,7 +268,7 @@ class YulFunction(YulScope): |
|
|
|
|
|
|
|
|
|
convert_yul(self, node, self._ast["body"], self.node_scope) |
|
|
|
|
|
|
|
|
|
def parse_body(self): |
|
|
|
|
def parse_body(self) -> None: |
|
|
|
|
for node in self._nodes: |
|
|
|
|
node.analyze_expressions() |
|
|
|
|
|
|
|
|
@ -289,9 +299,8 @@ class YulBlock(YulScope): |
|
|
|
|
entrypoint: Node, |
|
|
|
|
yul_id: List[str], |
|
|
|
|
node_scope: Union[Scope, Function], |
|
|
|
|
**kwargs, |
|
|
|
|
): |
|
|
|
|
super().__init__(contract, yul_id, **kwargs) |
|
|
|
|
super().__init__(contract, yul_id, entrypoint.function) |
|
|
|
|
|
|
|
|
|
self._entrypoint: YulNode = YulNode(entrypoint, self) |
|
|
|
|
self._nodes: List[YulNode] = [] |
|
|
|
@ -318,7 +327,7 @@ class YulBlock(YulScope): |
|
|
|
|
def convert(self, ast: Dict) -> YulNode: |
|
|
|
|
return convert_yul(self, self._entrypoint, ast, self.node_scope) |
|
|
|
|
|
|
|
|
|
def analyze_expressions(self): |
|
|
|
|
def analyze_expressions(self) -> None: |
|
|
|
|
for node in self._nodes: |
|
|
|
|
node.analyze_expressions() |
|
|
|
|
|
|
|
|
@ -361,18 +370,22 @@ def convert_yul_function_definition( |
|
|
|
|
while not isinstance(top_node_scope, Function): |
|
|
|
|
top_node_scope = top_node_scope.father |
|
|
|
|
|
|
|
|
|
func: Union[FunctionTopLevel, FunctionContract] |
|
|
|
|
if isinstance(top_node_scope, FunctionTopLevel): |
|
|
|
|
scope = root.contract.file_scope |
|
|
|
|
scope = root.file_scope |
|
|
|
|
func = FunctionTopLevel(root.compilation_unit, scope) |
|
|
|
|
# Note: we do not add the function in the scope |
|
|
|
|
# While its a top level function, it is not accessible outside of the function definition |
|
|
|
|
# In practice we should probably have a specific function type for function defined within a function |
|
|
|
|
else: |
|
|
|
|
func = FunctionContract(root.compilation_unit) |
|
|
|
|
|
|
|
|
|
func.function_language = FunctionLanguage.Yul |
|
|
|
|
yul_function = YulFunction(func, root, ast, node_scope) |
|
|
|
|
|
|
|
|
|
if root.contract: |
|
|
|
|
root.contract.add_function(func) |
|
|
|
|
|
|
|
|
|
root.compilation_unit.add_function(func) |
|
|
|
|
root.add_yul_local_function(yul_function) |
|
|
|
|
|
|
|
|
@ -774,14 +787,15 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ |
|
|
|
|
# check function-scoped variables |
|
|
|
|
parent_func = root.parent_func |
|
|
|
|
if parent_func: |
|
|
|
|
variable = parent_func.get_local_variable_from_name(name) |
|
|
|
|
if variable: |
|
|
|
|
return Identifier(variable) |
|
|
|
|
local_variable = parent_func.get_local_variable_from_name(name) |
|
|
|
|
if local_variable: |
|
|
|
|
return Identifier(local_variable) |
|
|
|
|
|
|
|
|
|
if isinstance(parent_func, FunctionContract): |
|
|
|
|
variable = parent_func.contract.get_state_variable_from_name(name) |
|
|
|
|
if variable: |
|
|
|
|
return Identifier(variable) |
|
|
|
|
assert parent_func.contract |
|
|
|
|
state_variable = parent_func.contract.get_state_variable_from_name(name) |
|
|
|
|
if state_variable: |
|
|
|
|
return Identifier(state_variable) |
|
|
|
|
|
|
|
|
|
# check yul-scoped variable |
|
|
|
|
variable = root.get_yul_local_variable_from_name(name) |
|
|
|
@ -798,7 +812,7 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[ |
|
|
|
|
if magic_suffix: |
|
|
|
|
return magic_suffix |
|
|
|
|
|
|
|
|
|
ret, _ = find_top_level(name, root.contract.file_scope) |
|
|
|
|
ret, _ = find_top_level(name, root.file_scope) |
|
|
|
|
if ret: |
|
|
|
|
return Identifier(ret) |
|
|
|
|
|
|
|
|
@ -840,7 +854,7 @@ def parse_yul_unsupported(_root: YulScope, _node: YulNode, ast: Dict) -> Optiona |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_yul(root: YulScope, node: YulNode, ast: Dict) -> Optional[Expression]: |
|
|
|
|
op = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast) |
|
|
|
|
op: Expression = parsers.get(ast["nodeType"], parse_yul_unsupported)(root, node, ast) |
|
|
|
|
if op: |
|
|
|
|
op.set_offset(ast["src"], root.compilation_unit) |
|
|
|
|
return op |
|
|
|
|