pull/2099/head
alpharush 1 year ago
parent 4b07cbbc56
commit db88cd5855
  1. 57
      slither/vyper_parsing/ast/ast.py
  2. 2
      slither/vyper_parsing/ast/types.py
  3. 231
      slither/vyper_parsing/declarations/contract.py
  4. 130
      slither/vyper_parsing/declarations/function.py
  5. 174
      slither/vyper_parsing/expressions/expression_parsing.py
  6. 9
      slither/vyper_parsing/expressions/find_variable.py
  7. 10
      slither/vyper_parsing/type_parsing.py
  8. 1
      slither/vyper_parsing/variables/local_variable.py
  9. 5
      slither/vyper_parsing/variables/state_variable.py
  10. 6
      slither/vyper_parsing/vyper_compilation_unit.py

@ -197,26 +197,56 @@ def parse_raise(raw: Dict) -> Raise:
def parse_expr(raw: Dict) -> Expr:
return Expr(value=parse(raw["value"]), **_extract_base_props(raw))
# This is done for convenience so we can call `UnaryOperationType.get_type` during expression parsing.
unop_ast_type_to_op_symbol = {"Not": "!", "USub": "-"}
def parse_unary_op(raw: Dict) -> UnaryOp:
unop_str = unop_ast_type_to_op_symbol[raw["op"]["ast_type"]]
return UnaryOp(op=unop_str, operand=parse(raw["operand"]), **_extract_base_props(raw))
# This is done for convenience so we can call `BinaryOperationType.get_type` during expression parsing.
binop_ast_type_to_op_symbol = {"Add": "+", "Mult": "*", "Sub": "-", "Div": "/", "Pow": "**", "Mod": "%", "BitAnd": "&", "BitOr": "|", "Shr": "<<", "Shl": ">>", "NotEq": "!=", "Eq": "==", "LtE": "<=", "GtE": ">=", "Lt": "<", "Gt": ">", "In": "In", "NotIn": "NotIn"}
binop_ast_type_to_op_symbol = {
"Add": "+",
"Mult": "*",
"Sub": "-",
"Div": "/",
"Pow": "**",
"Mod": "%",
"BitAnd": "&",
"BitOr": "|",
"Shr": "<<",
"Shl": ">>",
"NotEq": "!=",
"Eq": "==",
"LtE": "<=",
"GtE": ">=",
"Lt": "<",
"Gt": ">",
"In": "In",
"NotIn": "NotIn",
}
def parse_bin_op(raw: Dict) -> BinOp:
arith_op_str = binop_ast_type_to_op_symbol[raw["op"]["ast_type"]]
return BinOp(
left=parse(raw["left"]), op=arith_op_str, right=parse(raw["right"]), **_extract_base_props(raw)
left=parse(raw["left"]),
op=arith_op_str,
right=parse(raw["right"]),
**_extract_base_props(raw),
)
def parse_compare(raw: Dict) -> Compare:
logical_op_str = binop_ast_type_to_op_symbol[raw["op"]["ast_type"]]
return Compare(
left=parse(raw["left"]), op=logical_op_str, right=parse(raw["right"]), **_extract_base_props(raw)
left=parse(raw["left"]),
op=logical_op_str,
right=parse(raw["right"]),
**_extract_base_props(raw),
)
@ -301,7 +331,20 @@ def parse_enum_def(raw: Dict) -> EnumDef:
return EnumDef(name=raw["name"], body=nodes_parsed, **_extract_base_props(raw))
aug_assign_ast_type_to_op_symbol = {"Add": "+=", "Mult": "*=", "Sub": "-=", "Div": "-=", "Pow": "**=", "Mod": "%=", "BitAnd": "&=", "BitOr": "|=", "Shr": "<<=", "Shl": ">>="}
aug_assign_ast_type_to_op_symbol = {
"Add": "+=",
"Mult": "*=",
"Sub": "-=",
"Div": "-=",
"Pow": "**=",
"Mod": "%=",
"BitAnd": "&=",
"BitOr": "|=",
"Shr": "<<=",
"Shl": ">>=",
}
def parse_aug_assign(raw: Dict) -> AugAssign:
op_str = aug_assign_ast_type_to_op_symbol[raw["op"]["ast_type"]]
@ -316,13 +359,13 @@ def parse_aug_assign(raw: Dict) -> AugAssign:
def parse_unsupported(raw: Dict) -> ASTNode:
raise ParsingError("unsupported Vyper node", raw["ast_type"], raw.keys(), raw)
bool_op_ast_type_to_op_symbol = {"And": "&&", "Or": "||"}
def parse_bool_op(raw: Dict) -> BoolOp:
op_str = bool_op_ast_type_to_op_symbol[raw["op"]["ast_type"]]
return BoolOp(
op=op_str, values=[parse(x) for x in raw["values"]], **_extract_base_props(raw)
)
return BoolOp(op=op_str, values=[parse(x) for x in raw["values"]], **_extract_base_props(raw))
def parse(raw: Dict) -> ASTNode:

@ -184,8 +184,6 @@ class UnaryOp(ASTNode):
operand: ASTNode
@dataclass
class BinOp(ASTNode):
left: ASTNode

@ -180,7 +180,236 @@ class ContractVyper:
),
],
),
"ERC20": InterfaceDef(src="-1:-1:-1", node_id=1, name='ERC20', body=[FunctionDef(src="-1:-1:-1", node_id=2, doc_string=None, name='totalSupply', args=Arguments(src="-1:-1:-1", node_id=3, args=[], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=7, id='uint256'), body=[Expr(src="-1:-1:-1", node_id=4, value=Name(src="-1:-1:-1", node_id=5, id='view'))], decorators=[], pos=None), FunctionDef(src="-1:-1:-1", node_id=9, doc_string=None, name='balanceOf', args=Arguments(src="-1:-1:-1", node_id=10, args=[Arg(src="-1:-1:-1", node_id=11, arg='_owner', annotation=Name(src="-1:-1:-1", node_id=12, id='address'))], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=17, id='uint256'), body=[Expr(src="-1:-1:-1", node_id=14, value=Name(src="-1:-1:-1", node_id=15, id='view'))], decorators=[], pos=None), FunctionDef(src="-1:-1:-1", node_id=19, doc_string=None, name='allowance', args=Arguments(src="-1:-1:-1", node_id=20, args=[Arg(src="-1:-1:-1", node_id=21, arg='_owner', annotation=Name(src="-1:-1:-1", node_id=22, id='address')), Arg(src="-1:-1:-1", node_id=24, arg='_spender', annotation=Name(src="-1:-1:-1", node_id=25, id='address'))], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=30, id='uint256'), body=[Expr(src="-1:-1:-1", node_id=27, value=Name(src="-1:-1:-1", node_id=28, id='view'))], decorators=[], pos=None), FunctionDef(src="-1:-1:-1", node_id=32, doc_string=None, name='transfer', args=Arguments(src="-1:-1:-1", node_id=33, args=[Arg(src="-1:-1:-1", node_id=34, arg='_to', annotation=Name(src="-1:-1:-1", node_id=35, id='address')), Arg(src="-1:-1:-1", node_id=37, arg='_value', annotation=Name(src="-1:-1:-1", node_id=38, id='uint256'))], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=43, id='bool'), body=[Expr(src="-1:-1:-1", node_id=40, value=Name(src="-1:-1:-1", node_id=41, id='nonpayable'))], decorators=[], pos=None), FunctionDef(src="-1:-1:-1", node_id=45, doc_string=None, name='transferFrom', args=Arguments(src="-1:-1:-1", node_id=46, args=[Arg(src="-1:-1:-1", node_id=47, arg='_from', annotation=Name(src="-1:-1:-1", node_id=48, id='address')), Arg(src="-1:-1:-1", node_id=50, arg='_to', annotation=Name(src="-1:-1:-1", node_id=51, id='address')), Arg(src="-1:-1:-1", node_id=53, arg='_value', annotation=Name(src="-1:-1:-1", node_id=54, id='uint256'))], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=59, id='bool'), body=[Expr(src="-1:-1:-1", node_id=56, value=Name(src="-1:-1:-1", node_id=57, id='nonpayable'))], decorators=[], pos=None), FunctionDef(src="-1:-1:-1", node_id=61, doc_string=None, name='approve', args=Arguments(src="-1:-1:-1", node_id=62, args=[Arg(src="-1:-1:-1", node_id=63, arg='_spender', annotation=Name(src="-1:-1:-1", node_id=64, id='address')), Arg(src="-1:-1:-1", node_id=66, arg='_value', annotation=Name(src="-1:-1:-1", node_id=67, id='uint256'))], default=None, defaults=[]), returns=Name(src="-1:-1:-1", node_id=72, id='bool'), body=[Expr(src="-1:-1:-1", node_id=69, value=Name(src="-1:-1:-1", node_id=70, id='nonpayable'))], decorators=[], pos=None)]),
"ERC20": InterfaceDef(
src="-1:-1:-1",
node_id=1,
name="ERC20",
body=[
FunctionDef(
src="-1:-1:-1",
node_id=2,
doc_string=None,
name="totalSupply",
args=Arguments(
src="-1:-1:-1",
node_id=3,
args=[],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=7, id="uint256"),
body=[
Expr(
src="-1:-1:-1",
node_id=4,
value=Name(src="-1:-1:-1", node_id=5, id="view"),
)
],
decorators=[],
pos=None,
),
FunctionDef(
src="-1:-1:-1",
node_id=9,
doc_string=None,
name="balanceOf",
args=Arguments(
src="-1:-1:-1",
node_id=10,
args=[
Arg(
src="-1:-1:-1",
node_id=11,
arg="_owner",
annotation=Name(
src="-1:-1:-1", node_id=12, id="address"
),
)
],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=17, id="uint256"),
body=[
Expr(
src="-1:-1:-1",
node_id=14,
value=Name(src="-1:-1:-1", node_id=15, id="view"),
)
],
decorators=[],
pos=None,
),
FunctionDef(
src="-1:-1:-1",
node_id=19,
doc_string=None,
name="allowance",
args=Arguments(
src="-1:-1:-1",
node_id=20,
args=[
Arg(
src="-1:-1:-1",
node_id=21,
arg="_owner",
annotation=Name(
src="-1:-1:-1", node_id=22, id="address"
),
),
Arg(
src="-1:-1:-1",
node_id=24,
arg="_spender",
annotation=Name(
src="-1:-1:-1", node_id=25, id="address"
),
),
],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=30, id="uint256"),
body=[
Expr(
src="-1:-1:-1",
node_id=27,
value=Name(src="-1:-1:-1", node_id=28, id="view"),
)
],
decorators=[],
pos=None,
),
FunctionDef(
src="-1:-1:-1",
node_id=32,
doc_string=None,
name="transfer",
args=Arguments(
src="-1:-1:-1",
node_id=33,
args=[
Arg(
src="-1:-1:-1",
node_id=34,
arg="_to",
annotation=Name(
src="-1:-1:-1", node_id=35, id="address"
),
),
Arg(
src="-1:-1:-1",
node_id=37,
arg="_value",
annotation=Name(
src="-1:-1:-1", node_id=38, id="uint256"
),
),
],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=43, id="bool"),
body=[
Expr(
src="-1:-1:-1",
node_id=40,
value=Name(src="-1:-1:-1", node_id=41, id="nonpayable"),
)
],
decorators=[],
pos=None,
),
FunctionDef(
src="-1:-1:-1",
node_id=45,
doc_string=None,
name="transferFrom",
args=Arguments(
src="-1:-1:-1",
node_id=46,
args=[
Arg(
src="-1:-1:-1",
node_id=47,
arg="_from",
annotation=Name(
src="-1:-1:-1", node_id=48, id="address"
),
),
Arg(
src="-1:-1:-1",
node_id=50,
arg="_to",
annotation=Name(
src="-1:-1:-1", node_id=51, id="address"
),
),
Arg(
src="-1:-1:-1",
node_id=53,
arg="_value",
annotation=Name(
src="-1:-1:-1", node_id=54, id="uint256"
),
),
],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=59, id="bool"),
body=[
Expr(
src="-1:-1:-1",
node_id=56,
value=Name(src="-1:-1:-1", node_id=57, id="nonpayable"),
)
],
decorators=[],
pos=None,
),
FunctionDef(
src="-1:-1:-1",
node_id=61,
doc_string=None,
name="approve",
args=Arguments(
src="-1:-1:-1",
node_id=62,
args=[
Arg(
src="-1:-1:-1",
node_id=63,
arg="_spender",
annotation=Name(
src="-1:-1:-1", node_id=64, id="address"
),
),
Arg(
src="-1:-1:-1",
node_id=66,
arg="_value",
annotation=Name(
src="-1:-1:-1", node_id=67, id="uint256"
),
),
],
default=None,
defaults=[],
),
returns=Name(src="-1:-1:-1", node_id=72, id="bool"),
body=[
Expr(
src="-1:-1:-1",
node_id=69,
value=Name(src="-1:-1:-1", node_id=70, id="nonpayable"),
)
],
decorators=[],
pos=None,
),
],
),
"ERC165": [],
"ERC721": [],
"ERC4626": [],

@ -180,8 +180,6 @@ class FunctionVyper:
for node_parser in self._node_to_NodeVyper.values():
node_parser.analyze_expressions(self._function)
# endregion
###################################################################################
###################################################################################
@ -206,7 +204,6 @@ class FunctionVyper:
def _parse_cfg(self, cfg: Dict) -> None:
entry_node = self._new_node(NodeType.ENTRYPOINT, "-1:-1:-1", self.underlying_function)
self._function.entry_point = entry_node.underlying_node
scope = Scope(True, False, self.underlying_function)
@ -215,6 +212,7 @@ class FunctionVyper:
self._function.is_empty = False
curr_node = entry_node
for expr in cfg:
def parse_statement(curr_node, expr):
if isinstance(expr, AnnAssign):
local_var = LocalVariable()
@ -240,7 +238,6 @@ class FunctionVyper:
curr_node = new_node
# elif isinstance(expr, Assign):
# new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
# new_node.add_unparsed_expression(expr.target)
@ -256,7 +253,13 @@ class FunctionVyper:
local_var.set_function(self._function)
local_var.set_offset(expr.src, self._function.compilation_unit)
counter_var = AnnAssign(expr.target.src, expr.target.node_id, target=Name("-1:-1:-1", -1, "counter_var"), annotation=Name("-1:-1:-1", -1, "uint256"), value=Int("-1:-1:-1", -1, 0))
counter_var = AnnAssign(
expr.target.src,
expr.target.node_id,
target=Name("-1:-1:-1", -1, "counter_var"),
annotation=Name("-1:-1:-1", -1, "uint256"),
value=Int("-1:-1:-1", -1, 0),
)
local_var_parser = LocalVariableVyper(local_var, counter_var)
self._add_local_variable(local_var_parser)
counter_node = self._new_node(NodeType.VARIABLE, expr.src, scope)
@ -267,45 +270,93 @@ class FunctionVyper:
link_underlying_nodes(node_startLoop, counter_node)
node_condition = None
if isinstance(expr.iter, (Attribute,Name)):
# HACK
if isinstance(expr.iter, (Attribute, Name)):
# HACK
# The loop variable is not annotated so we infer its type by looking at the type of the iterator
if isinstance(expr.iter, Attribute): # state variable
if isinstance(expr.iter, Attribute): # state variable
iter_expr = expr.iter
loop_iterator = list(filter(lambda x: x._variable.name == iter_expr.attr, self._contract_parser._variables_parser))[0]
else: # local variable
loop_iterator = list(
filter(
lambda x: x._variable.name == iter_expr.attr,
self._contract_parser._variables_parser,
)
)[0]
else: # local variable
iter_expr = expr.iter
loop_iterator = list(filter(lambda x: x._variable.name == iter_expr.id, self._local_variables_parser))[0]
loop_iterator = list(
filter(
lambda x: x._variable.name == iter_expr.id,
self._local_variables_parser,
)
)[0]
# TODO use expr.src instead of -1:-1:1?
cond_expr = Compare("-1:-1:-1", -1, left=Name("-1:-1:-1", -1, "counter_var"), op="<=", right=Call("-1:-1:-1", -1, func=Name("-1:-1:-1", -1, "len"), args=[iter_expr], keywords=[], keyword=None))
cond_expr = Compare(
"-1:-1:-1",
-1,
left=Name("-1:-1:-1", -1, "counter_var"),
op="<=",
right=Call(
"-1:-1:-1",
-1,
func=Name("-1:-1:-1", -1, "len"),
args=[iter_expr],
keywords=[],
keyword=None,
),
)
node_condition = self._new_node(NodeType.IFLOOP, expr.src, scope)
node_condition.add_unparsed_expression(cond_expr)
if loop_iterator._elem_to_parse.value.id == "DynArray":
loop_var_annotation = loop_iterator._elem_to_parse.slice.value.elements[0]
loop_var_annotation = (
loop_iterator._elem_to_parse.slice.value.elements[0]
)
else:
loop_var_annotation = loop_iterator._elem_to_parse.value
value = Subscript("-1:-1:-1", -1, value=Name("-1:-1:-1", -1, loop_iterator._variable.name), slice=Index("-1:-1:-1", -1, value=Name("-1:-1:-1", -1, "counter_var")))
loop_var = AnnAssign(expr.target.src, expr.target.node_id, target=expr.target, annotation=loop_var_annotation, value=value)
elif isinstance(expr.iter, Call): # range
value = Subscript(
"-1:-1:-1",
-1,
value=Name("-1:-1:-1", -1, loop_iterator._variable.name),
slice=Index(
"-1:-1:-1", -1, value=Name("-1:-1:-1", -1, "counter_var")
),
)
loop_var = AnnAssign(
expr.target.src,
expr.target.node_id,
target=expr.target,
annotation=loop_var_annotation,
value=value,
)
elif isinstance(expr.iter, Call): # range
range_val = expr.iter.args[0]
cond_expr = Compare("-1:-1:-1", -1, left=Name("-1:-1:-1", -1, "counter_var"), op="<=", right=range_val)
cond_expr = Compare(
"-1:-1:-1",
-1,
left=Name("-1:-1:-1", -1, "counter_var"),
op="<=",
right=range_val,
)
node_condition = self._new_node(NodeType.IFLOOP, expr.src, scope)
node_condition.add_unparsed_expression(cond_expr)
loop_var = AnnAssign(expr.target.src, expr.target.node_id, target=expr.target, annotation=Name("-1:-1:-1", -1, "uint256"), value=Name("-1:-1:-1", -1, "counter_var"))
loop_var = AnnAssign(
expr.target.src,
expr.target.node_id,
target=expr.target,
annotation=Name("-1:-1:-1", -1, "uint256"),
value=Name("-1:-1:-1", -1, "counter_var"),
)
else:
raise NotImplementedError
# link
# link
link_underlying_nodes(counter_node, node_condition)
# We update the index variable or range variable in the loop body
expr.body.insert(0, loop_var)
body_node = None
@ -313,17 +364,23 @@ class FunctionVyper:
for stmt in expr.body:
body_node = parse_statement(new_node, stmt)
new_node = body_node
node_endLoop = self._new_node(NodeType.ENDLOOP, expr.src, scope)
loop_increment = AugAssign("-1:-1:-1", -1, target=Name("-1:-1:-1", -1, "counter_var"), op="+=", value=Int("-1:-1:-1", -1, 1))
loop_increment = AugAssign(
"-1:-1:-1",
-1,
target=Name("-1:-1:-1", -1, "counter_var"),
op="+=",
value=Int("-1:-1:-1", -1, 1),
)
node_increment = self._new_node(NodeType.EXPRESSION, expr.src, scope)
node_increment.add_unparsed_expression(loop_increment)
link_underlying_nodes(node_increment, node_condition)
if body_node is not None:
link_underlying_nodes(body_node, node_increment)
link_underlying_nodes(node_condition, node_endLoop)
curr_node = node_endLoop
@ -357,8 +414,8 @@ class FunctionVyper:
elif isinstance(expr, If):
condition_node = self._new_node(NodeType.IF, expr.test.src, scope)
condition_node.add_unparsed_expression(expr.test)
endIf_node = self._new_node(NodeType.ENDIF, expr.src, scope)
endIf_node = self._new_node(NodeType.ENDIF, expr.src, scope)
true_node = None
new_node = condition_node
@ -367,20 +424,20 @@ class FunctionVyper:
new_node = true_node
# link_underlying_nodes(condition_node, true_node)
link_underlying_nodes(true_node, endIf_node)
false_node = None
new_node = condition_node
for stmt in expr.orelse:
false_node = parse_statement(new_node, stmt)
new_node = false_node
if false_node is not None:
# link_underlying_nodes(condition_node, false_node)
link_underlying_nodes(false_node, endIf_node)
else:
link_underlying_nodes(condition_node, endIf_node)
link_underlying_nodes(curr_node, condition_node)
curr_node = endIf_node
@ -396,6 +453,7 @@ class FunctionVyper:
print(f"isinstance(expr, {expr.__class__.__name__})")
assert False
return curr_node
curr_node = parse_statement(curr_node, expr)
# self._parse_block(cfg, node, self.underlying_function)
else:
@ -437,7 +495,7 @@ class FunctionVyper:
print(returns)
self._function.returns_src().set_offset(returns.src, self._function.compilation_unit)
# Only the type of the arg is given, not a name. We create an an `Arg` with an empty name
# so that the function has the correct return type in its signature but doesn't clash with
# so that the function has the correct return type in its signature but doesn't clash with
# other identifiers during name resolution (`find_variable`).
if isinstance(returns, (Name, Subscript)):
local_var = self._add_param(Arg(returns.src, returns.node_id, "", annotation=returns))

@ -254,7 +254,28 @@ def _user_defined_op_call(
from collections import deque
from slither.vyper_parsing.ast.types import Int, Call, Attribute, Name, Tuple, Hex, BinOp, Str, Assert, Compare, UnaryOp, Subscript, NameConstant, VyDict, Bytes, BoolOp, Assign, AugAssign, VyList
from slither.vyper_parsing.ast.types import (
Int,
Call,
Attribute,
Name,
Tuple,
Hex,
BinOp,
Str,
Assert,
Compare,
UnaryOp,
Subscript,
NameConstant,
VyDict,
Bytes,
BoolOp,
Assign,
AugAssign,
VyList,
)
def parse_expression(expression: Dict, caller_context) -> "Expression":
print("parse_expression")
@ -265,18 +286,18 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
literal = Literal(str(expression.value), ElementaryType("uint256"))
literal.set_offset(expression.src, caller_context.compilation_unit)
return literal
if isinstance(expression, Hex):
# TODO this is an implicit conversion and could potentially be bytes20 or other?
literal = Literal(str(expression.value), ElementaryType("address"))
literal = Literal(str(expression.value), ElementaryType("address"))
literal.set_offset(expression.src, caller_context.compilation_unit)
return literal
if isinstance(expression, Str):
literal = Literal(str(expression.value), ElementaryType("string"))
literal = Literal(str(expression.value), ElementaryType("string"))
literal.set_offset(expression.src, caller_context.compilation_unit)
return literal
if isinstance(expression, Bytes):
literal = Literal(str(expression.value), ElementaryType("bytes"))
literal.set_offset(expression.src, caller_context.compilation_unit)
@ -287,7 +308,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
literal = Literal(str(expression.value), ElementaryType("bool"))
literal.set_offset(expression.src, caller_context.compilation_unit)
return literal
if isinstance(expression, Call):
called = parse_expression(expression.func, caller_context)
@ -297,29 +318,44 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = CallExpression(called, [], str(type_to))
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name == "convert()":
arg = parse_expression(expression.args[0], caller_context)
arg = parse_expression(expression.args[0], caller_context)
type_to = parse_type(expression.args[1], caller_context)
parsed_expr = TypeConversion(arg, type_to)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
elif called.value.name== "min_value()":
elif called.value.name == "min_value()":
type_to = parse_type(expression.args[0], caller_context)
member_type = str(type_to)
member_type = str(type_to)
# TODO return Literal
parsed_expr = MemberAccess("min", member_type, CallExpression(Identifier(SolidityFunction("type()")), [ElementaryTypeNameExpression(type_to)], member_type))
parsed_expr = MemberAccess(
"min",
member_type,
CallExpression(
Identifier(SolidityFunction("type()")),
[ElementaryTypeNameExpression(type_to)],
member_type,
),
)
return parsed_expr
elif called.value.name== "max_value()":
elif called.value.name == "max_value()":
type_to = parse_type(expression.args[0], caller_context)
member_type = str(type_to)
member_type = str(type_to)
# TODO return Literal
parsed_expr = MemberAccess("max", member_type, CallExpression(Identifier(SolidityFunction("type()")), [ElementaryTypeNameExpression(type_to)], member_type))
parsed_expr = MemberAccess(
"max",
member_type,
CallExpression(
Identifier(SolidityFunction("type()")),
[ElementaryTypeNameExpression(type_to)],
member_type,
),
)
return parsed_expr
if expression.args and isinstance(expression.args[0], VyDict):
arguments = []
for val in expression.args[0].values:
@ -342,7 +378,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = TypeConversion(arguments[0], type_to)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
else:
rets = ["tuple()"]
@ -352,23 +388,28 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
rets = [called.type]
else:
rets = ["tuple()"]
def get_type_str(x):
if isinstance(x, str):
return x
return str(x.type)
print(rets)
type_str = get_type_str(rets[0]) if len(rets) == 1 else f"tuple({','.join(map(get_type_str, rets))})"
type_str = (
get_type_str(rets[0])
if len(rets) == 1
else f"tuple({','.join(map(get_type_str, rets))})"
)
parsed_expr = CallExpression(called, arguments, type_str)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, Attribute):
member_name = expression.attr
if isinstance(expression.value, Name):
if expression.value.id == "self" and member_name != "balance":
if expression.value.id == "self" and member_name != "balance":
var, was_created = find_variable(member_name, caller_context)
# TODO replace with self
if was_created:
@ -376,7 +417,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = SuperIdentifier(var)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
expr = parse_expression(expression.value, caller_context)
# TODO this is ambiguous because it could be a type conversion of an interface or a member access
if expression.attr == "address":
@ -385,7 +426,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
return parsed_expr
member_access = MemberAccess(member_name, None, expr)
if str(member_access) in SOLIDITY_VARIABLES_COMPOSED:
parsed_expr = Identifier(SolidityVariableComposed(str(member_access)))
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
@ -395,23 +436,28 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
expr = parse_expression(expression.value, caller_context)
member_name_ret_type = None
# (recover_type_1) This may be a call to an interface and we don't have the return types,
# so we see if there's a function identifier with `member_name` and propagate the type to
# so we see if there's a function identifier with `member_name` and propagate the type to
# its enclosing `CallExpression`
# TODO this is using the wrong caller_context and needs to be interface instead of self namespace
print(expr)
print(expr.__class__.__name__)
if isinstance(expr, TypeConversion) and isinstance(expr.type, UserDefinedType):
# try:
# try:
var, was_created = find_variable(member_name, expr.type.type)
if isinstance(var, Function):
rets = var.returns
def get_type_str(x):
if isinstance(x, str):
return x
return str(x.type)
type_str = get_type_str(rets[0]) if len(rets) == 1 else f"tuple({','.join(map(get_type_str, rets))})"
type_str = (
get_type_str(rets[0])
if len(rets) == 1
else f"tuple({','.join(map(get_type_str, rets))})"
)
member_name_ret_type = type_str
# except:
# pass
@ -428,14 +474,14 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = Identifier(var)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, Assign):
lhs = parse_expression(expression.target, caller_context)
rhs = parse_expression(expression.value, caller_context)
parsed_expr = AssignmentOperation(lhs, rhs, AssignmentOperationType.ASSIGN, None)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, AugAssign):
lhs = parse_expression(expression.target, caller_context)
rhs = parse_expression(expression.value, caller_context)
@ -450,10 +496,12 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = TupleExpression(tuple_vars)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, UnaryOp):
operand = parse_expression(expression.operand, caller_context)
op = UnaryOperationType.get_type(expression.op, isprefix=True) #TODO does vyper have postfix?
op = UnaryOperationType.get_type(
expression.op, isprefix=True
) # TODO does vyper have postfix?
parsed_expr = UnaryOperation(operand, op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
@ -476,9 +524,17 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
# We assume left operand in membership comparison cannot be Array type
conditions = deque()
if isinstance(expression.right, VyList):
inner_op = BinaryOperationType.get_type("!=") if expression.op == "NotIn" else BinaryOperationType.get_type("==")
outer_op = BinaryOperationType.get_type("&&") if expression.op == "NotIn" else BinaryOperationType.get_type("||")
inner_op = (
BinaryOperationType.get_type("!=")
if expression.op == "NotIn"
else BinaryOperationType.get_type("==")
)
outer_op = (
BinaryOperationType.get_type("&&")
if expression.op == "NotIn"
else BinaryOperationType.get_type("||")
)
for elem in expression.right.elements:
elem_expr = parse_expression(elem, caller_context)
print("elem", repr(elem_expr))
@ -491,15 +547,27 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
print(rhs.__class__.__name__)
if isinstance(rhs, Identifier):
if isinstance(rhs.value.type, ArrayType):
inner_op = BinaryOperationType.get_type("!=") if expression.op == "NotIn" else BinaryOperationType.get_type("==")
outer_op = BinaryOperationType.get_type("&&") if expression.op == "NotIn" else BinaryOperationType.get_type("||")
inner_op = (
BinaryOperationType.get_type("!=")
if expression.op == "NotIn"
else BinaryOperationType.get_type("==")
)
outer_op = (
BinaryOperationType.get_type("&&")
if expression.op == "NotIn"
else BinaryOperationType.get_type("||")
)
enum_members = rhs.value.type.length_value.value
for i in range(enum_members):
elem_expr = IndexAccess(rhs, Literal(str(i), ElementaryType("uint256")))
elem_expr.set_offset(rhs.source_mapping, caller_context.compilation_unit)
elem_expr.set_offset(
rhs.source_mapping, caller_context.compilation_unit
)
parsed_expr = BinaryOperation(lhs, elem_expr, inner_op)
parsed_expr.set_offset(lhs.source_mapping, caller_context.compilation_unit)
parsed_expr.set_offset(
lhs.source_mapping, caller_context.compilation_unit
)
conditions.append(parsed_expr)
# elif isinstance(rhs.value.type, UserDefinedType):
@ -507,8 +575,12 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
assert False
else:
# This is an indexaccess like hashmap[address, Roles]
inner_op = BinaryOperationType.get_type("|") #if expression.op == "NotIn" else BinaryOperationType.get_type("==")
outer_op = BinaryOperationType.get_type("&") #if expression.op == "NotIn" else BinaryOperationType.get_type("||")
inner_op = BinaryOperationType.get_type(
"|"
) # if expression.op == "NotIn" else BinaryOperationType.get_type("==")
outer_op = BinaryOperationType.get_type(
"&"
) # if expression.op == "NotIn" else BinaryOperationType.get_type("||")
# x, _ = find_variable(expression.right.value.attr, caller_context)
# print(x)
@ -520,7 +592,10 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
enum_members = rhs.expression_left.value.type.type_to.type.values
# for each value, create a literal with value = 2 ^ n (0 indexed)
# and then translate to bitmasking
enum_values = [Literal(str(2 ** n), ElementaryType("uint256")) for n in range(len(enum_members))]
enum_values = [
Literal(str(2**n), ElementaryType("uint256"))
for n in range(len(enum_members))
]
inner_lhs = enum_values[0]
for expr in enum_values[1:]:
inner_lhs = BinaryOperation(inner_lhs, expr, inner_op)
@ -530,8 +605,6 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr.set_offset(lhs.source_mapping, caller_context.compilation_unit)
return parsed_expr
while len(conditions) > 1:
lhs = conditions.pop()
rhs = conditions.pop()
@ -543,7 +616,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
else:
rhs = parse_expression(expression.right, caller_context)
op = BinaryOperationType.get_type(expression.op)
parsed_expr = BinaryOperation(lhs, rhs, op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
@ -556,7 +629,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = BinaryOperation(lhs, rhs, op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, Assert):
# Treat assert the same as a Solidity `require`.
# TODO rename from `SolidityFunction` to `Builtin`?
@ -566,19 +639,22 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
args = [parse_expression(expression.test, caller_context)]
else:
func = SolidityFunction("require(bool,string)")
args = [parse_expression(expression.test, caller_context), parse_expression(expression.msg, caller_context)]
args = [
parse_expression(expression.test, caller_context),
parse_expression(expression.msg, caller_context),
]
parsed_expr = CallExpression(Identifier(func), args, type_str)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, Subscript):
left_expression = parse_expression(expression.value, caller_context)
right_expression = parse_expression(expression.slice.value, caller_context)
parsed_expr = IndexAccess(left_expression, right_expression)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, BoolOp):
lhs = parse_expression(expression.values[0], caller_context)
rhs = parse_expression(expression.values[1], caller_context)

@ -36,7 +36,6 @@ if TYPE_CHECKING:
# CallerContext =Union["ContractSolc", "FunctionSolc", "CustomErrorSolc", "StructureTopLevelSolc"]
def _find_variable_in_function_parser(
var_name: str,
function_parser: Optional["FunctionSolc"],
@ -51,8 +50,6 @@ def _find_variable_in_function_parser(
return None
def _find_in_contract(
var_name: str,
contract: Optional[Contract],
@ -67,8 +64,6 @@ def _find_in_contract(
if var_name in contract_variables:
return contract_variables[var_name]
functions = {f.name: f for f in contract.functions if not f.is_shadowed}
# print(functions)
if var_name in functions:
@ -106,7 +101,6 @@ def _find_in_contract(
if var_name in enums:
return enums[var_name]
return None
@ -157,6 +151,7 @@ def find_variable(
# structure/enums cannot be shadowed
from slither.vyper_parsing.declarations.contract import ContractVyper
from slither.vyper_parsing.declarations.function import FunctionVyper
print("caller_context")
print(caller_context)
print(caller_context.__class__.__name__)
@ -208,5 +203,5 @@ def find_variable(
print(next_context.events_as_dict)
if f"{var_name}()" in next_context.events_as_dict:
return next_context.events_as_dict[f"{var_name}()"], False
raise VariableNotFound(f"Variable not found: {var_name} (context {caller_context})")

@ -11,11 +11,12 @@ from slither.core.solidity_types.user_defined_type import UserDefinedType
from slither.core.declarations.function_contract import FunctionContract
def parse_type(annotation: Union[Name, Subscript, Call], caller_context):
from slither.vyper_parsing.expressions.expression_parsing import parse_expression
if isinstance(caller_context, FunctionContract):
contract = caller_context.contract
contract = caller_context.contract
else:
contract = caller_context
@ -41,17 +42,16 @@ def parse_type(annotation: Union[Name, Subscript, Call], caller_context):
elif isinstance(annotation.value, Subscript):
type_ = parse_type(annotation.value, caller_context)
elif isinstance(annotation.value, Name):
# TODO it is weird that the ast_type is `Index` when it's a type annotation and not an expression, so we grab the value.
# Subscript(src='13:10:0', node_id=7, value=Name(src='13:6:0', node_id=8, id='String'), slice=Index(src='13:10:0', node_id=12, value=Int(src='20:2:0', node_id=10, value=64)))
type_ = parse_type(annotation.value, caller_context)
if annotation.value.id == "String":
return type_
length = parse_expression(annotation.slice.value, caller_context)
return ArrayType(type_, length)
elif isinstance(annotation, Call):
return parse_type(annotation.args[0], caller_context)
@ -63,8 +63,6 @@ def parse_type(annotation: Union[Name, Subscript, Call], caller_context):
if lname in ElementaryTypeName:
return ElementaryType(lname)
if name in contract.structures_as_dict:
return UserDefinedType(contract.structures_as_dict[name])

@ -27,7 +27,6 @@ class LocalVariableVyper:
self._variable.set_location("default")
@property
def underlying_variable(self) -> LocalVariable:
return self._variable

@ -5,6 +5,7 @@ from slither.vyper_parsing.ast.types import VariableDecl
from slither.vyper_parsing.type_parsing import parse_type
from slither.vyper_parsing.expressions.expression_parsing import parse_expression
class StateVariableVyper:
def __init__(self, variable: StateVariable, variable_data: VariableDecl) -> None:
self._variable: StateVariable = variable
@ -16,7 +17,7 @@ class StateVariableVyper:
if variable_data.value is not None:
self._variable.initialized = True
self._initializedNotParsed = variable_data.value
self._initializedNotParsed = variable_data.value
@property
def underlying_variable(self) -> StateVariable:
@ -24,7 +25,7 @@ class StateVariableVyper:
def analyze(self, contract) -> None:
self._variable.type = parse_type(self._elem_to_parse, contract)
if self._variable.initialized:
self._variable.expression = parse_expression(self._initializedNotParsed, contract)
self._initializedNotParsed = None

@ -51,9 +51,9 @@ class VyperCompilationUnit:
def analyze_contracts(self) -> None:
if not self._parsed:
raise SlitherException("Parse the contract before running analyses")
for contract, contract_parser in self._underlying_contract_to_parser.items():
# State variables are analyzed for all contracts because interfaces may
# State variables are analyzed for all contracts because interfaces may
# reference them, specifically, constants.
contract_parser.analyze_state_variables()
@ -73,7 +73,7 @@ class VyperCompilationUnit:
func.generate_slithir_and_analyze()
contract.convert_expression_to_slithir_ssa()
self._compilation_unit.propagate_function_calls()
for contract in self._compilation_unit.contracts:
contract.fix_phi()

Loading…
Cancel
Save