more builtins; support params, state vars in loop iterators

pull/2099/head
alpharush 1 year ago
parent 75c3389ace
commit 2f94d1c251
  1. 13
      slither/core/declarations/solidity_variables.py
  2. 4
      slither/vyper_parsing/declarations/contract.py
  3. 24
      slither/vyper_parsing/declarations/function.py
  4. 43
      slither/vyper_parsing/expressions/expression_parsing.py
  5. 8
      slither/vyper_parsing/variables/local_variable.py

@ -16,6 +16,7 @@ SOLIDITY_VARIABLES = {
"tx": "",
"block": "",
"super": "",
"chain": "",
}
SOLIDITY_VARIABLES_COMPOSED = {
@ -86,7 +87,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"create_from_blueprint()":[],
"empty()":[],
"convert()":[], # TODO make type conversion
"len()":[],
"len()":["uint256"],
"method_id()":[],
"unsafe_sub()": [],
"unsafe_add()": [],
@ -97,7 +98,15 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"min_value()":[],
"concat()":[],
"ecrecover()":[],
"isqrt()":[]
"isqrt()":[],
"range()":[],
"min()":[],
"max()":[],
"shift()":[],
"abs()":[],
"raw_call()":[],
"_abi_encode()":[],
"slice()":[],
}

@ -85,7 +85,7 @@ class ContractVyper:
self._contract.file_scope.contracts[contract.name] = contract
elif isinstance(node, InterfaceDef):
# TODO This needs to be done lazily as interfaces can refer to constant state variables
# This needs to be done lazily as interfaces can refer to constant state variables
contract = Contract(self._contract.compilation_unit, self._contract.file_scope)
contract.set_offset(node.src, self._contract.compilation_unit)
contract.is_interface = True
@ -158,7 +158,7 @@ class ContractVyper:
func.set_contract(self._contract)
func.set_contract_declarer(self._contract)
func_parser = FunctionVyper(func, function)
func_parser = FunctionVyper(func, function, self)
self._contract.add_function(func)
self._functions_parser.append(func_parser)

@ -30,6 +30,7 @@ class FunctionVyper:
self,
function: Function,
function_data: Dict,
contract_parser: "ContractVyper",
) -> None:
self._node_to_NodeVyper: Dict[Node, NodeVyper] = {}
@ -40,6 +41,7 @@ class FunctionVyper:
self._function.id = function_data.node_id
self._local_variables_parser: List = []
self._contract_parser = contract_parser
for decorator in function_data.decorators:
if not hasattr(decorator, "id"):
@ -255,18 +257,28 @@ class FunctionVyper:
new_node.add_unparsed_expression(counter_var.value)
new_node.underlying_node.add_variable_declaration(local_var)
if isinstance(expr.iter, Name):
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
iter_expr = expr.iter
loop_iterator = list(filter(lambda x: x._variable.name == iter_expr.attr, self._contract_parser._variables_parser))[0]
else: # local
iter_expr = expr.iter
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=[expr.iter], 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)
# HACK
# The loop variable is not annotated so we infer its type by looking at the type of the iterator
loop_iterator = list(filter(lambda x: x._variable.name == expr.iter.id, self._local_variables_parser))[0]
# Assumes `Subscript`
# TODO this should go in the body of the loop: expr.body.insert(0, ...)
if loop_iterator._elem_to_parse.value.id == "DynArray":
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)
local_var = LocalVariable()

@ -282,7 +282,10 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
called = parse_expression(expression.func, caller_context)
if isinstance(called, Identifier) and isinstance(called.value, SolidityFunction):
if called.value.name == "convert()":
if called.value.name == "empty()":
type_to = parse_type(expression.args[0], caller_context)
return CallExpression(called, [], str(type_to))
elif called.value.name == "convert()":
arg = parse_expression(expression.args[0], caller_context)
type_to = parse_type(expression.args[1], caller_context)
return TypeConversion(arg, type_to)
@ -294,9 +297,8 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
elif called.value.name== "max_value()":
type_to = parse_type(expression.args[0], caller_context)
member_type = str(type_to)
x = MemberAccess("max", member_type, CallExpression(Identifier(SolidityFunction("type()")), [ElementaryTypeNameExpression(type_to)], member_type))
print(x)
return x
# TODO return Literal
return MemberAccess("max", member_type, CallExpression(Identifier(SolidityFunction("type()")), [ElementaryTypeNameExpression(type_to)], member_type))
if expression.args and isinstance(expression.args[0], VyDict):
@ -320,7 +322,10 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
else:
rets = ["tuple()"]
elif isinstance(called, MemberAccess) and called.type is not None:
# (recover_type_2) Propagate the type collected to the `CallExpression`
# see recover_type_1
rets = called.type
else:
rets = ["tuple()"]
@ -330,7 +335,8 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
return str(x.type)
type_str = get_type_str(rets[0]) if len(rets) == 1 else f"tuple({','.join(map(get_type_str, rets))})"
print(CallExpression(called, arguments, type_str))
print(type_str)
return CallExpression(called, arguments, type_str)
if isinstance(expression, Attribute):
@ -352,8 +358,25 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
else:
expr = parse_expression(expression.value, caller_context)
member_access = MemberAccess(member_name, None, expr)
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
# its enclosing `CallExpression`
# try: TODO this is using the wrong caller_context and needs to be interface instead of self namespace
# var, was_created = find_variable(member_name, caller_context)
# 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))})"
# member_name_ret_type = type_str
# except:
# pass
member_access = MemberAccess(member_name, member_name_ret_type, expr)
return member_access
@ -373,9 +396,9 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
rhs = parse_expression(expression.value, caller_context)
op = AssignmentOperationType.get_type(expression.op)
return BinaryOperation(lhs, rhs, op)
return AssignmentOperation(lhs, rhs, op, None)
if isinstance(expression, Tuple):
if isinstance(expression, (Tuple, VyList)):
tuple_vars = [parse_expression(x, caller_context) for x in expression.elements]
return TupleExpression(tuple_vars)

@ -9,13 +9,17 @@ class LocalVariableVyper:
def __init__(self, variable: LocalVariable, variable_data: Union[Arg, Name]) -> None:
self._variable: LocalVariable = variable
if isinstance(variable_data, (Arg, )):
if isinstance(variable_data, Arg):
self._variable.name = variable_data.arg
self._elem_to_parse = variable_data.annotation
elif isinstance(variable_data, (AnnAssign, )):
elif isinstance(variable_data, AnnAssign):
self._variable.name = variable_data.target.id
self._elem_to_parse = variable_data.annotation
elif isinstance(variable_data, Name):
self._variable.name = variable_data.id
self._elem_to_parse = variable_data
else:
# param Subscript
self._variable.name = ""
self._elem_to_parse = variable_data

Loading…
Cancel
Save