fix and refactor comparison operator, add support for raise

pull/2099/head
alpharush 1 year ago
parent 81cb124e2d
commit 4845a3bd26
  1. 429
      slither/vyper_parsing/declarations/function.py
  2. 167
      slither/vyper_parsing/expressions/expression_parsing.py
  3. 56
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_in_bar__0.txt
  4. 66
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_in_baz__0.txt
  5. 33
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_in_foo__0.txt
  6. 8
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_precedence_fb__0.txt
  7. 12
      tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_precedence_foo__0.txt
  8. 17
      tests/e2e/vyper_parsing/test_data/in.vy

@ -158,9 +158,11 @@ class FunctionVyper:
if body and not isinstance(body[0], Pass):
self._function.is_implemented = True
self._function.is_empty = False
self._parse_cfg(body)
else:
self._function.is_implemented = False
self._function.is_empty = True
for local_var_parser in self._local_variables_parser:
local_var_parser.analyze(self._function)
@ -218,264 +220,257 @@ class FunctionVyper:
###################################################################################
###################################################################################
def _parse_cfg(self, cfg: Dict) -> None:
def _parse_cfg(self, cfg: List[ASTNode]) -> 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)
if cfg:
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()
local_var.set_function(self._function)
local_var.set_offset(expr.src, self._function.compilation_unit)
local_var_parser = LocalVariableVyper(local_var, expr)
self._add_local_variable(local_var_parser)
new_node = self._new_node(NodeType.VARIABLE, expr.src, scope)
if expr.value is not None:
local_var.initialized = True
new_node.add_unparsed_expression(expr.value)
new_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(curr_node, new_node)
curr_node = entry_node
for expr in cfg:
curr_node = new_node
def parse_statement(curr_node, expr):
if isinstance(expr, AnnAssign):
local_var = LocalVariable()
local_var.set_function(self._function)
local_var.set_offset(expr.src, self._function.compilation_unit)
elif isinstance(expr, (AugAssign, Assign)):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr)
link_underlying_nodes(curr_node, new_node)
local_var_parser = LocalVariableVyper(local_var, expr)
self._add_local_variable(local_var_parser)
curr_node = new_node
new_node = self._new_node(NodeType.VARIABLE, expr.src, scope)
if expr.value is not None:
local_var.initialized = True
new_node.add_unparsed_expression(expr.value)
new_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(curr_node, new_node)
elif isinstance(expr, Expr):
# TODO This is a workaround to handle Vyper putting payable/view in the function body...
if not isinstance(expr.value, Name):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr.value)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
curr_node = new_node
elif isinstance(expr, (AugAssign, Assign)):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr)
link_underlying_nodes(curr_node, new_node)
elif isinstance(expr, For):
curr_node = new_node
node_startLoop = self._new_node(NodeType.STARTLOOP, expr.src, scope)
link_underlying_nodes(curr_node, node_startLoop)
elif isinstance(expr, Expr):
# TODO This is a workaround to handle Vyper putting payable/view in the function body...
if not isinstance(expr.value, Name):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr.value)
link_underlying_nodes(curr_node, new_node)
local_var = LocalVariable()
local_var.set_function(self._function)
local_var.set_offset(expr.src, self._function.compilation_unit)
curr_node = new_node
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)
local_var.initialized = True
counter_node.add_unparsed_expression(counter_var.value)
counter_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(node_startLoop, counter_node)
node_condition = None
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
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
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=[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]
elif isinstance(expr, For):
node_startLoop = self._new_node(NodeType.STARTLOOP, expr.src, scope)
link_underlying_nodes(curr_node, node_startLoop)
local_var = LocalVariable()
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),
)
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)
local_var.initialized = True
counter_node.add_unparsed_expression(counter_var.value)
counter_node.underlying_node.add_variable_declaration(local_var)
link_underlying_nodes(node_startLoop, counter_node)
node_condition = None
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
iter_expr = expr.iter
loop_iterator = list(
filter(
lambda x: x._variable.name == iter_expr.attr,
self._contract_parser._variables_parser,
)
else:
loop_var_annotation = loop_iterator._elem_to_parse.value
)[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]
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(
# 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,
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"),
)
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
]
else:
raise NotImplementedError
# link
link_underlying_nodes(counter_node, node_condition)
loop_var_annotation = loop_iterator._elem_to_parse.value
# We update the index variable or range variable in the loop body
expr.body.insert(0, loop_var)
body_node = None
new_node = node_condition
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)
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,
)
loop_increment = AugAssign(
elif isinstance(expr.iter, Call): # range
range_val = expr.iter.args[0]
cond_expr = Compare(
"-1:-1:-1",
-1,
target=Name("-1:-1:-1", -1, "counter_var"),
op="+=",
value=Int("-1:-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"),
)
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)
else:
raise NotImplementedError
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
new_node = node_condition
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),
)
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
elif isinstance(expr, Continue):
pass
elif isinstance(expr, Break):
pass
elif isinstance(expr, Return):
new_node = self._new_node(NodeType.RETURN, expr.src, scope)
if expr.value is not None:
new_node.add_unparsed_expression(expr.value)
link_underlying_nodes(node_condition, node_endLoop)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
curr_node = node_endLoop
elif isinstance(expr, Assert):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr)
elif isinstance(expr, Continue):
pass
elif isinstance(expr, Break):
pass
elif isinstance(expr, Return):
new_node = self._new_node(NodeType.RETURN, expr.src, scope)
if expr.value is not None:
new_node.add_unparsed_expression(expr.value)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
elif isinstance(expr, Log):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr.value)
elif isinstance(expr, Assert):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
elif isinstance(expr, If):
condition_node = self._new_node(NodeType.IF, expr.test.src, scope)
condition_node.add_unparsed_expression(expr.test)
elif isinstance(expr, Log):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr.value)
endIf_node = self._new_node(NodeType.ENDIF, expr.src, scope)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
true_node = None
new_node = condition_node
for stmt in expr.body:
true_node = parse_statement(new_node, stmt)
new_node = true_node
# link_underlying_nodes(condition_node, true_node)
link_underlying_nodes(true_node, endIf_node)
elif isinstance(expr, If):
condition_node = self._new_node(NodeType.IF, expr.test.src, scope)
condition_node.add_unparsed_expression(expr.test)
false_node = None
new_node = condition_node
for stmt in expr.orelse:
false_node = parse_statement(new_node, stmt)
new_node = false_node
endIf_node = self._new_node(NodeType.ENDIF, expr.src, scope)
if false_node is not None:
# link_underlying_nodes(condition_node, false_node)
link_underlying_nodes(false_node, endIf_node)
true_node = None
new_node = condition_node
for stmt in expr.body:
true_node = parse_statement(new_node, stmt)
new_node = true_node
# link_underlying_nodes(condition_node, true_node)
link_underlying_nodes(true_node, endIf_node)
else:
link_underlying_nodes(condition_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
link_underlying_nodes(curr_node, condition_node)
curr_node = endIf_node
if false_node is not None:
# link_underlying_nodes(condition_node, false_node)
link_underlying_nodes(false_node, endIf_node)
elif isinstance(expr, Pass):
pass
elif isinstance(expr, Raise):
new_node = self._new_node(NodeType.EXPRESSION, expr.src, scope)
new_node.add_unparsed_expression(expr)
link_underlying_nodes(curr_node, new_node)
curr_node = new_node
else:
link_underlying_nodes(condition_node, endIf_node)
link_underlying_nodes(curr_node, condition_node)
curr_node = endIf_node
elif isinstance(expr, Pass):
pass
elif isinstance(expr, Raise):
print(expr)
# TODO
# assert False
pass
else:
print(f"isinstance(expr, {expr.__class__.__name__})")
assert False
return curr_node
else:
raise ParsingError(f"Statement not parsed {expr.__class__.__name__} {expr}")
curr_node = parse_statement(curr_node, expr)
# self._parse_block(cfg, node, self.underlying_function)
else:
self._function.is_empty = True
return curr_node
curr_node = parse_statement(curr_node, expr)
# endregion
###################################################################################

@ -69,6 +69,7 @@ from slither.vyper_parsing.ast.types import (
Assign,
AugAssign,
VyList,
Raise,
)
@ -192,6 +193,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
else:
arguments = [parse_expression(a, caller_context) for a in expression.args]
rets = None
if isinstance(called, Identifier):
print("called", called)
print("called.value", called.value.__class__.__name__)
@ -211,6 +213,7 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
elif isinstance(called.value, SolidityFunction):
rets = called.value.return_type
elif isinstance(called.value, Contract):
# Type conversions are not explicitly represented in the AST e.g. converting address to contract/ interface,
# so we infer that a type conversion is occurring if `called` is a `Contract` type.
@ -219,14 +222,12 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
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:
if rets is None:
rets = ["tuple()"]
def get_type_str(x):
@ -235,6 +236,13 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
return str(x.type)
print(rets)
# def vars_to_typestr(rets: List[Expression]) -> str:
# if len(rets) == 0:
# return ""
# if len(rets) == 1:
# return str(rets[0].type)
# return f"tuple({','.join(str(ret.type) for ret in rets)})"
type_str = (
get_type_str(rets[0])
if len(rets) == 1
@ -347,30 +355,48 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
if isinstance(expression, Compare):
lhs = parse_expression(expression.left, caller_context)
# We assume left operand in membership comparison cannot be Array type
if expression.op in ["In", "NotIn"]:
# If we see a membership operator e.g. x in [foo(), bar()] we rewrite it as if-else:
# if (x == foo()) {
# return true
# } else {
# if (x == bar()) {
# return true
# } else {
# return false
# }
# }
# We assume left operand in membership comparison cannot be Array type
# If we see a membership operator e.g. x in [foo(), bar()], we convert it to logical operations
# like (x == foo() || x == bar()) or (x != foo() && x != bar()) for "not in"
# TODO consider rewriting as if-else to accurately represent the precedence of potential side-effects
conditions = deque()
if isinstance(expression.right, VyList):
rhs = parse_expression(expression.right, caller_context)
is_tuple = isinstance(rhs, TupleExpression)
is_array = isinstance(rhs, Identifier) and isinstance(rhs.value.type, ArrayType)
if is_array:
assert rhs.value.type.is_fixed_array
if is_tuple or is_array:
length = len(rhs.expressions) if is_tuple else rhs.value.type.length_value.value
inner_op = (
BinaryOperationType.get_type("!=")
if expression.op == "NotIn"
else BinaryOperationType.get_type("==")
)
for i in range(length):
elem_expr = (
rhs.expressions[i]
if is_tuple
else IndexAccess(rhs, Literal(str(i), ElementaryType("uint256")))
)
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)
conditions.append(parsed_expr)
outer_op = (
BinaryOperationType.get_type("&&")
if expression.op == "NotIn"
else BinaryOperationType.get_type("||")
)
while len(conditions) > 1:
lhs = conditions.pop()
rhs = conditions.pop()
conditions.appendleft(BinaryOperation(lhs, rhs, outer_op))
return conditions.pop()
for elem in expression.right.elements:
elem_expr = parse_expression(elem, caller_context)
@ -378,79 +404,31 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr = BinaryOperation(lhs, elem_expr, inner_op)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
conditions.append(parsed_expr)
else:
rhs = parse_expression(expression.right, caller_context)
print(rhs)
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("||")
)
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
)
parsed_expr = BinaryOperation(lhs, elem_expr, inner_op)
parsed_expr.set_offset(
lhs.source_mapping, caller_context.compilation_unit
)
conditions.append(parsed_expr)
# elif isinstance(rhs.value.type, UserDefinedType):
else:
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("||")
# x, _ = find_variable(expression.right.value.attr, caller_context)
# print(x)
# print(x.type.type_to)
# print(x.type.type_to.__class__)
print(repr(rhs))
print(rhs)
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))
]
inner_lhs = enum_values[0]
for expr in enum_values[1:]:
inner_lhs = BinaryOperation(inner_lhs, expr, inner_op)
conditions.append(inner_lhs)
parsed_expr = BinaryOperation(lhs, conditions[0], outer_op)
parsed_expr.set_offset(lhs.source_mapping, caller_context.compilation_unit)
return parsed_expr
while len(conditions) > 1:
lhs = conditions.pop()
rhs = conditions.pop()
conditions.appendleft(BinaryOperation(lhs, rhs, outer_op))
return conditions.pop()
else: # enum type membership check https://docs.vyperlang.org/en/stable/types.html?h#id18
is_member_op = (
BinaryOperationType.get_type("==")
if expression.op == "NotIn"
else BinaryOperationType.get_type("!=")
)
# If all bits are cleared, then the lhs is not a member of the enum
# This allows representing membership in multiple enum members
# For example, if enum Foo has members A (1), B (2), and C (4), then
# (x in [Foo.A, Foo.B]) is equivalent to (x & (Foo.A | Foo.B) != 0),
# where (Foo.A | Foo.B) evaluates to 3.
# Thus, when x is 3, (x & (Foo.A | Foo.B) != 0) is true.
enum_bit_mask = BinaryOperation(
TypeConversion(lhs, ElementaryType("uint256")),
TypeConversion(rhs, ElementaryType("uint256")),
BinaryOperationType.get_type("&"),
)
membership_check = BinaryOperation(
enum_bit_mask, Literal("0", ElementaryType("uint256")), is_member_op
)
membership_check.set_offset(lhs.source_mapping, caller_context.compilation_unit)
return membership_check
else:
else: # a regular logical operator
rhs = parse_expression(expression.right, caller_context)
op = BinaryOperationType.get_type(expression.op)
@ -501,4 +479,17 @@ def parse_expression(expression: Dict, caller_context) -> "Expression":
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
if isinstance(expression, Raise):
type_str = "tuple()"
func = (
SolidityFunction("revert()")
if expression.exc is None
else SolidityFunction("revert(string)")
)
args = [] if expression.exc is None else [parse_expression(expression.exc, caller_context)]
parsed_expr = CallExpression(Identifier(func), args, type_str)
parsed_expr.set_offset(expression.src, caller_context.compilation_unit)
return parsed_expr
raise ParsingError(f"Expression not parsed {expression}")

@ -2,45 +2,57 @@ digraph{
0[label="Node Type: ENTRY_POINT 0
"];
0->1;
1[label="Node Type: NEW VARIABLE 1
1[label="Node Type: IF 1
EXPRESSION:
a = 0
uint256(x) & uint256(self.roles[self]) != 0
IRs:
a(int128) := 0(uint256)"];
1->2;
2[label="Node Type: NEW VARIABLE 2
TMP_10 = CONVERT x to uint256
REF_4(in.Roles) -> roles[self]
TMP_11 = CONVERT REF_4 to uint256
TMP_12(uint256) = TMP_10 & TMP_11
TMP_13(bool) = TMP_12 != 0
CONDITION TMP_13"];
1->3[label="True"];
1->2[label="False"];
2[label="Node Type: END_IF 2
"];
2->4;
3[label="Node Type: RETURN 3
EXPRESSION:
b = 0
True
IRs:
b(int128) := 0(uint256)"];
2->3;
3[label="Node Type: IF 3
RETURN True"];
3->2;
4[label="Node Type: IF 4
EXPRESSION:
x & 1 | 2
uint256(x) & uint256(self.roles[self]) == 0
IRs:
TMP_0(uint256) = 1 | 2
TMP_1(in.Roles) = x & TMP_0
CONDITION TMP_1"];
3->5[label="True"];
3->4[label="False"];
4[label="Node Type: END_IF 4
TMP_14 = CONVERT x to uint256
REF_5(in.Roles) -> roles[self]
TMP_15 = CONVERT REF_5 to uint256
TMP_16(uint256) = TMP_14 & TMP_15
TMP_17(bool) = TMP_16 == 0
CONDITION TMP_17"];
4->6[label="True"];
4->5[label="False"];
5[label="Node Type: END_IF 5
"];
4->6;
5[label="Node Type: RETURN 5
5->7;
6[label="Node Type: RETURN 6
EXPRESSION:
True
False
IRs:
RETURN True"];
5->4;
6[label="Node Type: RETURN 6
RETURN False"];
6->5;
7[label="Node Type: RETURN 7
EXPRESSION:
False

@ -0,0 +1,66 @@
digraph{
0[label="Node Type: ENTRY_POINT 0
"];
0->1;
1[label="Node Type: IF 1
EXPRESSION:
uint256(x) & uint256(Roles.A | Roles.B) != 0
IRs:
TMP_0 = CONVERT x to uint256
REF_0(in.Roles) -> Roles.A
REF_1(in.Roles) -> Roles.B
TMP_1(in.Roles) = REF_0 | REF_1
TMP_2 = CONVERT TMP_1 to uint256
TMP_3(uint256) = TMP_0 & TMP_2
TMP_4(bool) = TMP_3 != 0
CONDITION TMP_4"];
1->3[label="True"];
1->2[label="False"];
2[label="Node Type: END_IF 2
"];
2->4;
3[label="Node Type: RETURN 3
EXPRESSION:
True
IRs:
RETURN True"];
3->2;
4[label="Node Type: IF 4
EXPRESSION:
uint256(x) & uint256(Roles.A | Roles.B) == 0
IRs:
TMP_5 = CONVERT x to uint256
REF_2(in.Roles) -> Roles.A
REF_3(in.Roles) -> Roles.B
TMP_6(in.Roles) = REF_2 | REF_3
TMP_7 = CONVERT TMP_6 to uint256
TMP_8(uint256) = TMP_5 & TMP_7
TMP_9(bool) = TMP_8 == 0
CONDITION TMP_9"];
4->6[label="True"];
4->5[label="False"];
5[label="Node Type: END_IF 5
"];
5->7;
6[label="Node Type: RETURN 6
EXPRESSION:
False
IRs:
RETURN False"];
6->5;
7[label="Node Type: RETURN 7
EXPRESSION:
False
IRs:
RETURN False"];
}

@ -24,10 +24,10 @@ EXPRESSION:
x == b || x == a
IRs:
TMP_2(bool) = x == b
TMP_3(bool) = x == a
TMP_4(bool) = TMP_2 || TMP_3
CONDITION TMP_4"];
TMP_18(bool) = x == b
TMP_19(bool) = x == a
TMP_20(bool) = TMP_18 || TMP_19
CONDITION TMP_20"];
3->5[label="True"];
3->4[label="False"];
4[label="Node Type: END_IF 4
@ -41,7 +41,30 @@ True
IRs:
RETURN True"];
5->4;
6[label="Node Type: RETURN 6
6[label="Node Type: IF 6
EXPRESSION:
x != b && x != a
IRs:
TMP_21(bool) = x != b
TMP_22(bool) = x != a
TMP_23(bool) = TMP_21 && TMP_22
CONDITION TMP_23"];
6->8[label="True"];
6->7[label="False"];
7[label="Node Type: END_IF 7
"];
7->9;
8[label="Node Type: EXPRESSION 8
EXPRESSION:
revert(string)(nope)
IRs:
TMP_24(None) = SOLIDITY_CALL revert(string)(nope)"];
8->7;
9[label="Node Type: RETURN 9
EXPRESSION:
False

@ -1,4 +1,12 @@
digraph{
0[label="Node Type: ENTRY_POINT 0
"];
0->1;
1[label="Node Type: EXPRESSION 1
EXPRESSION:
revert()()
IRs:
TMP_0(None) = SOLIDITY_CALL revert()()"];
}

@ -8,10 +8,10 @@ EXPRESSION:
x != self.fb() && x != self.fa()
IRs:
TMP_0(uint256) = INTERNAL_CALL, precedence.fb()()
TMP_1(bool) = x != TMP_0
TMP_2(uint256) = INTERNAL_CALL, precedence.fa()()
TMP_3(bool) = x != TMP_2
TMP_4(bool) = TMP_1 && TMP_3
RETURN TMP_4"];
TMP_1(uint256) = INTERNAL_CALL, precedence.fb()()
TMP_2(bool) = x != TMP_1
TMP_3(uint256) = INTERNAL_CALL, precedence.fa()()
TMP_4(bool) = x != TMP_3
TMP_5(bool) = TMP_2 && TMP_4
RETURN TMP_5"];
}

@ -4,13 +4,23 @@ enum Roles:
roles: public(HashMap[address, Roles])
@external
def baz(x: Roles) -> bool:
if x in (Roles.A | Roles.B):
return True
if x not in (Roles.A | Roles.B):
return False
return False
@external
def bar(x: Roles) -> bool:
a: int128 = 0
b: int128 = 0
if x in self.roles[self]:
return True
if x not in self.roles[self]:
return False
return False
@external
@ -20,4 +30,7 @@ def foo(x: int128) -> bool:
if x in [a, b]:
return True
if x not in [a, b]:
raise "nope"
return False
Loading…
Cancel
Save