slither.utils.expression_manipulations
We use protected member, to avoid having setter in the expression as they should be immutable
1""" 2 We use protected member, to avoid having setter in the expression 3 as they should be immutable 4""" 5import copy 6from typing import Union, Callable 7 8from slither.all_exceptions import SlitherException 9from slither.core.expressions import UnaryOperation 10from slither.core.expressions.assignment_operation import AssignmentOperation 11from slither.core.expressions.binary_operation import BinaryOperation 12from slither.core.expressions.call_expression import CallExpression 13from slither.core.expressions.conditional_expression import ConditionalExpression 14from slither.core.expressions.elementary_type_name_expression import ElementaryTypeNameExpression 15from slither.core.expressions.expression import Expression 16from slither.core.expressions.identifier import Identifier 17from slither.core.expressions.index_access import IndexAccess 18from slither.core.expressions.literal import Literal 19from slither.core.expressions.member_access import MemberAccess 20from slither.core.expressions.new_array import NewArray 21from slither.core.expressions.new_contract import NewContract 22from slither.core.expressions.tuple_expression import TupleExpression 23from slither.core.expressions.type_conversion import TypeConversion 24from slither.core.expressions.new_elementary_type import NewElementaryType 25 26# pylint: disable=protected-access 27def f_expressions( 28 e: Union[AssignmentOperation, BinaryOperation, TupleExpression], 29 x: Union[Identifier, Literal, MemberAccess, IndexAccess], 30) -> None: 31 e._expressions.append(x) 32 33 34def f_call(e: CallExpression, x: ElementaryTypeNameExpression) -> None: 35 e._arguments.append(x) 36 37 38def f_call_value(e: CallExpression, x): 39 e._value = x 40 41 42def f_call_gas(e: CallExpression, x): 43 e._gas = x 44 45 46def f_expression(e: Union[TypeConversion, UnaryOperation, MemberAccess], x: CallExpression) -> None: 47 e._expression = x 48 49 50def f_called(e: CallExpression, x: Identifier) -> None: 51 e._called = x 52 53 54class SplitTernaryExpression: 55 def __init__(self, expression: Union[AssignmentOperation, ConditionalExpression]) -> None: 56 57 if isinstance(expression, ConditionalExpression): 58 self.true_expression = copy.copy(expression.then_expression) 59 self.false_expression = copy.copy(expression.else_expression) 60 self.condition = copy.copy(expression.if_expression) 61 else: 62 self.true_expression = copy.copy(expression) 63 self.false_expression = copy.copy(expression) 64 self.condition = None 65 self.copy_expression(expression, self.true_expression, self.false_expression) 66 67 def conditional_not_ahead( 68 self, 69 next_expr: Expression, 70 true_expression: Union[AssignmentOperation, MemberAccess], 71 false_expression: Union[AssignmentOperation, MemberAccess], 72 f: Callable, 73 ) -> bool: 74 # look ahead for parenthetical expression (.. ? .. : ..) 75 if ( 76 isinstance(next_expr, TupleExpression) 77 and len(next_expr.expressions) == 1 78 and isinstance(next_expr.expressions[0], ConditionalExpression) 79 ): 80 next_expr = next_expr.expressions[0] 81 82 if isinstance(next_expr, ConditionalExpression): 83 f(true_expression, copy.copy(next_expr.then_expression)) 84 f(false_expression, copy.copy(next_expr.else_expression)) 85 self.condition = copy.copy(next_expr.if_expression) 86 return False 87 88 f(true_expression, copy.copy(next_expr)) 89 f(false_expression, copy.copy(next_expr)) 90 return True 91 92 def copy_expression( 93 self, expression: Expression, true_expression: Expression, false_expression: Expression 94 ) -> None: 95 if self.condition: 96 return 97 98 if isinstance(expression, ConditionalExpression): 99 raise SlitherException("Nested ternary operator not handled") 100 101 if isinstance( 102 expression, 103 ( 104 Literal, 105 Identifier, 106 NewArray, 107 NewContract, 108 ElementaryTypeNameExpression, 109 NewElementaryType, 110 ), 111 ): 112 return 113 114 if isinstance( 115 expression, (AssignmentOperation, BinaryOperation, TupleExpression, IndexAccess) 116 ): 117 true_expression._expressions = [] 118 false_expression._expressions = [] 119 self.convert_expressions(expression, true_expression, false_expression) 120 121 elif isinstance(expression, CallExpression): 122 next_expr = expression.called 123 self.convert_call_expression(expression, next_expr, true_expression, false_expression) 124 125 elif isinstance(expression, (TypeConversion, UnaryOperation, MemberAccess)): 126 next_expr = expression.expression 127 if self.conditional_not_ahead( 128 next_expr, true_expression, false_expression, f_expression 129 ): 130 self.copy_expression( 131 expression.expression, 132 true_expression.expression, 133 false_expression.expression, 134 ) 135 136 else: 137 raise SlitherException( 138 f"Ternary operation not handled {expression}({type(expression)})" 139 ) 140 141 def convert_expressions( 142 self, 143 expression: Union[AssignmentOperation, BinaryOperation, TupleExpression], 144 true_expression: Expression, 145 false_expression: Expression, 146 ) -> None: 147 for next_expr in expression.expressions: 148 # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? 149 # montyly: this might happen with unnamed tuple (ex: (,,,) = f()), but it needs to be checked 150 if next_expr is not None: 151 152 if self.conditional_not_ahead( 153 next_expr, true_expression, false_expression, f_expressions 154 ): 155 # always on last arguments added 156 self.copy_expression( 157 next_expr, 158 true_expression.expressions[-1], 159 false_expression.expressions[-1], 160 ) 161 else: 162 true_expression.expressions.append(None) 163 false_expression.expressions.append(None) 164 165 def convert_index_access( 166 self, next_expr: IndexAccess, true_expression: Expression, false_expression: Expression 167 ) -> None: 168 # create an index access for each branch 169 # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } 170 for expr in next_expr.expressions: 171 if self.conditional_not_ahead(expr, true_expression, false_expression, f_expressions): 172 self.copy_expression( 173 expr, 174 true_expression.expressions[-1], 175 false_expression.expressions[-1], 176 ) 177 178 def convert_call_expression( 179 self, 180 expression: CallExpression, 181 next_expr: Expression, 182 true_expression: Expression, 183 false_expression: Expression, 184 ) -> None: 185 # case of lib 186 # (.. ? .. : ..).add 187 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): 188 self.copy_expression(next_expr, true_expression.called, false_expression.called) 189 190 # In order to handle ternaries in both call options, gas and value, we return early if the 191 # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). 192 if expression.call_gas: 193 # case of (..).func{gas: .. ? .. : ..}() 194 next_expr = expression.call_gas 195 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call_gas): 196 self.copy_expression( 197 next_expr, 198 true_expression.call_gas, 199 false_expression.call_gas, 200 ) 201 else: 202 return 203 204 if expression.call_value: 205 # case of (..).func{value: .. ? .. : ..}() 206 next_expr = expression.call_value 207 if self.conditional_not_ahead( 208 next_expr, true_expression, false_expression, f_call_value 209 ): 210 self.copy_expression( 211 next_expr, 212 true_expression.call_value, 213 false_expression.call_value, 214 ) 215 else: 216 return 217 218 true_expression._arguments = [] 219 false_expression._arguments = [] 220 221 for expr in expression.arguments: 222 if self.conditional_not_ahead(expr, true_expression, false_expression, f_call): 223 # always on last arguments added 224 self.copy_expression( 225 expr, 226 true_expression.arguments[-1], 227 false_expression.arguments[-1], 228 )
def
f_expressions( e: Union[slither.core.expressions.assignment_operation.AssignmentOperation, slither.core.expressions.binary_operation.BinaryOperation, slither.core.expressions.tuple_expression.TupleExpression], x: Union[slither.core.expressions.identifier.Identifier, slither.core.expressions.literal.Literal, slither.core.expressions.member_access.MemberAccess, slither.core.expressions.index_access.IndexAccess]) -> None:
def
f_called( e: slither.core.expressions.call_expression.CallExpression, x: slither.core.expressions.identifier.Identifier) -> None:
class
SplitTernaryExpression:
55class SplitTernaryExpression: 56 def __init__(self, expression: Union[AssignmentOperation, ConditionalExpression]) -> None: 57 58 if isinstance(expression, ConditionalExpression): 59 self.true_expression = copy.copy(expression.then_expression) 60 self.false_expression = copy.copy(expression.else_expression) 61 self.condition = copy.copy(expression.if_expression) 62 else: 63 self.true_expression = copy.copy(expression) 64 self.false_expression = copy.copy(expression) 65 self.condition = None 66 self.copy_expression(expression, self.true_expression, self.false_expression) 67 68 def conditional_not_ahead( 69 self, 70 next_expr: Expression, 71 true_expression: Union[AssignmentOperation, MemberAccess], 72 false_expression: Union[AssignmentOperation, MemberAccess], 73 f: Callable, 74 ) -> bool: 75 # look ahead for parenthetical expression (.. ? .. : ..) 76 if ( 77 isinstance(next_expr, TupleExpression) 78 and len(next_expr.expressions) == 1 79 and isinstance(next_expr.expressions[0], ConditionalExpression) 80 ): 81 next_expr = next_expr.expressions[0] 82 83 if isinstance(next_expr, ConditionalExpression): 84 f(true_expression, copy.copy(next_expr.then_expression)) 85 f(false_expression, copy.copy(next_expr.else_expression)) 86 self.condition = copy.copy(next_expr.if_expression) 87 return False 88 89 f(true_expression, copy.copy(next_expr)) 90 f(false_expression, copy.copy(next_expr)) 91 return True 92 93 def copy_expression( 94 self, expression: Expression, true_expression: Expression, false_expression: Expression 95 ) -> None: 96 if self.condition: 97 return 98 99 if isinstance(expression, ConditionalExpression): 100 raise SlitherException("Nested ternary operator not handled") 101 102 if isinstance( 103 expression, 104 ( 105 Literal, 106 Identifier, 107 NewArray, 108 NewContract, 109 ElementaryTypeNameExpression, 110 NewElementaryType, 111 ), 112 ): 113 return 114 115 if isinstance( 116 expression, (AssignmentOperation, BinaryOperation, TupleExpression, IndexAccess) 117 ): 118 true_expression._expressions = [] 119 false_expression._expressions = [] 120 self.convert_expressions(expression, true_expression, false_expression) 121 122 elif isinstance(expression, CallExpression): 123 next_expr = expression.called 124 self.convert_call_expression(expression, next_expr, true_expression, false_expression) 125 126 elif isinstance(expression, (TypeConversion, UnaryOperation, MemberAccess)): 127 next_expr = expression.expression 128 if self.conditional_not_ahead( 129 next_expr, true_expression, false_expression, f_expression 130 ): 131 self.copy_expression( 132 expression.expression, 133 true_expression.expression, 134 false_expression.expression, 135 ) 136 137 else: 138 raise SlitherException( 139 f"Ternary operation not handled {expression}({type(expression)})" 140 ) 141 142 def convert_expressions( 143 self, 144 expression: Union[AssignmentOperation, BinaryOperation, TupleExpression], 145 true_expression: Expression, 146 false_expression: Expression, 147 ) -> None: 148 for next_expr in expression.expressions: 149 # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? 150 # montyly: this might happen with unnamed tuple (ex: (,,,) = f()), but it needs to be checked 151 if next_expr is not None: 152 153 if self.conditional_not_ahead( 154 next_expr, true_expression, false_expression, f_expressions 155 ): 156 # always on last arguments added 157 self.copy_expression( 158 next_expr, 159 true_expression.expressions[-1], 160 false_expression.expressions[-1], 161 ) 162 else: 163 true_expression.expressions.append(None) 164 false_expression.expressions.append(None) 165 166 def convert_index_access( 167 self, next_expr: IndexAccess, true_expression: Expression, false_expression: Expression 168 ) -> None: 169 # create an index access for each branch 170 # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } 171 for expr in next_expr.expressions: 172 if self.conditional_not_ahead(expr, true_expression, false_expression, f_expressions): 173 self.copy_expression( 174 expr, 175 true_expression.expressions[-1], 176 false_expression.expressions[-1], 177 ) 178 179 def convert_call_expression( 180 self, 181 expression: CallExpression, 182 next_expr: Expression, 183 true_expression: Expression, 184 false_expression: Expression, 185 ) -> None: 186 # case of lib 187 # (.. ? .. : ..).add 188 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): 189 self.copy_expression(next_expr, true_expression.called, false_expression.called) 190 191 # In order to handle ternaries in both call options, gas and value, we return early if the 192 # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). 193 if expression.call_gas: 194 # case of (..).func{gas: .. ? .. : ..}() 195 next_expr = expression.call_gas 196 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call_gas): 197 self.copy_expression( 198 next_expr, 199 true_expression.call_gas, 200 false_expression.call_gas, 201 ) 202 else: 203 return 204 205 if expression.call_value: 206 # case of (..).func{value: .. ? .. : ..}() 207 next_expr = expression.call_value 208 if self.conditional_not_ahead( 209 next_expr, true_expression, false_expression, f_call_value 210 ): 211 self.copy_expression( 212 next_expr, 213 true_expression.call_value, 214 false_expression.call_value, 215 ) 216 else: 217 return 218 219 true_expression._arguments = [] 220 false_expression._arguments = [] 221 222 for expr in expression.arguments: 223 if self.conditional_not_ahead(expr, true_expression, false_expression, f_call): 224 # always on last arguments added 225 self.copy_expression( 226 expr, 227 true_expression.arguments[-1], 228 false_expression.arguments[-1], 229 )
SplitTernaryExpression( expression: Union[slither.core.expressions.assignment_operation.AssignmentOperation, slither.core.expressions.conditional_expression.ConditionalExpression])
56 def __init__(self, expression: Union[AssignmentOperation, ConditionalExpression]) -> None: 57 58 if isinstance(expression, ConditionalExpression): 59 self.true_expression = copy.copy(expression.then_expression) 60 self.false_expression = copy.copy(expression.else_expression) 61 self.condition = copy.copy(expression.if_expression) 62 else: 63 self.true_expression = copy.copy(expression) 64 self.false_expression = copy.copy(expression) 65 self.condition = None 66 self.copy_expression(expression, self.true_expression, self.false_expression)
def
conditional_not_ahead( self, next_expr: slither.core.expressions.expression.Expression, true_expression: Union[slither.core.expressions.assignment_operation.AssignmentOperation, slither.core.expressions.member_access.MemberAccess], false_expression: Union[slither.core.expressions.assignment_operation.AssignmentOperation, slither.core.expressions.member_access.MemberAccess], f: Callable) -> bool:
68 def conditional_not_ahead( 69 self, 70 next_expr: Expression, 71 true_expression: Union[AssignmentOperation, MemberAccess], 72 false_expression: Union[AssignmentOperation, MemberAccess], 73 f: Callable, 74 ) -> bool: 75 # look ahead for parenthetical expression (.. ? .. : ..) 76 if ( 77 isinstance(next_expr, TupleExpression) 78 and len(next_expr.expressions) == 1 79 and isinstance(next_expr.expressions[0], ConditionalExpression) 80 ): 81 next_expr = next_expr.expressions[0] 82 83 if isinstance(next_expr, ConditionalExpression): 84 f(true_expression, copy.copy(next_expr.then_expression)) 85 f(false_expression, copy.copy(next_expr.else_expression)) 86 self.condition = copy.copy(next_expr.if_expression) 87 return False 88 89 f(true_expression, copy.copy(next_expr)) 90 f(false_expression, copy.copy(next_expr)) 91 return True
def
copy_expression( self, expression: slither.core.expressions.expression.Expression, true_expression: slither.core.expressions.expression.Expression, false_expression: slither.core.expressions.expression.Expression) -> None:
93 def copy_expression( 94 self, expression: Expression, true_expression: Expression, false_expression: Expression 95 ) -> None: 96 if self.condition: 97 return 98 99 if isinstance(expression, ConditionalExpression): 100 raise SlitherException("Nested ternary operator not handled") 101 102 if isinstance( 103 expression, 104 ( 105 Literal, 106 Identifier, 107 NewArray, 108 NewContract, 109 ElementaryTypeNameExpression, 110 NewElementaryType, 111 ), 112 ): 113 return 114 115 if isinstance( 116 expression, (AssignmentOperation, BinaryOperation, TupleExpression, IndexAccess) 117 ): 118 true_expression._expressions = [] 119 false_expression._expressions = [] 120 self.convert_expressions(expression, true_expression, false_expression) 121 122 elif isinstance(expression, CallExpression): 123 next_expr = expression.called 124 self.convert_call_expression(expression, next_expr, true_expression, false_expression) 125 126 elif isinstance(expression, (TypeConversion, UnaryOperation, MemberAccess)): 127 next_expr = expression.expression 128 if self.conditional_not_ahead( 129 next_expr, true_expression, false_expression, f_expression 130 ): 131 self.copy_expression( 132 expression.expression, 133 true_expression.expression, 134 false_expression.expression, 135 ) 136 137 else: 138 raise SlitherException( 139 f"Ternary operation not handled {expression}({type(expression)})" 140 )
def
convert_expressions( self, expression: Union[slither.core.expressions.assignment_operation.AssignmentOperation, slither.core.expressions.binary_operation.BinaryOperation, slither.core.expressions.tuple_expression.TupleExpression], true_expression: slither.core.expressions.expression.Expression, false_expression: slither.core.expressions.expression.Expression) -> None:
142 def convert_expressions( 143 self, 144 expression: Union[AssignmentOperation, BinaryOperation, TupleExpression], 145 true_expression: Expression, 146 false_expression: Expression, 147 ) -> None: 148 for next_expr in expression.expressions: 149 # TODO: can we get rid of `NoneType` expressions in `TupleExpression`? 150 # montyly: this might happen with unnamed tuple (ex: (,,,) = f()), but it needs to be checked 151 if next_expr is not None: 152 153 if self.conditional_not_ahead( 154 next_expr, true_expression, false_expression, f_expressions 155 ): 156 # always on last arguments added 157 self.copy_expression( 158 next_expr, 159 true_expression.expressions[-1], 160 false_expression.expressions[-1], 161 ) 162 else: 163 true_expression.expressions.append(None) 164 false_expression.expressions.append(None)
def
convert_index_access( self, next_expr: slither.core.expressions.index_access.IndexAccess, true_expression: slither.core.expressions.expression.Expression, false_expression: slither.core.expressions.expression.Expression) -> None:
166 def convert_index_access( 167 self, next_expr: IndexAccess, true_expression: Expression, false_expression: Expression 168 ) -> None: 169 # create an index access for each branch 170 # x[if cond ? 1 : 2] -> if cond { x[1] } else { x[2] } 171 for expr in next_expr.expressions: 172 if self.conditional_not_ahead(expr, true_expression, false_expression, f_expressions): 173 self.copy_expression( 174 expr, 175 true_expression.expressions[-1], 176 false_expression.expressions[-1], 177 )
def
convert_call_expression( self, expression: slither.core.expressions.call_expression.CallExpression, next_expr: slither.core.expressions.expression.Expression, true_expression: slither.core.expressions.expression.Expression, false_expression: slither.core.expressions.expression.Expression) -> None:
179 def convert_call_expression( 180 self, 181 expression: CallExpression, 182 next_expr: Expression, 183 true_expression: Expression, 184 false_expression: Expression, 185 ) -> None: 186 # case of lib 187 # (.. ? .. : ..).add 188 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_called): 189 self.copy_expression(next_expr, true_expression.called, false_expression.called) 190 191 # In order to handle ternaries in both call options, gas and value, we return early if the 192 # conditional is not ahead to rewrite both ternaries (see `_rewrite_ternary_as_if_else`). 193 if expression.call_gas: 194 # case of (..).func{gas: .. ? .. : ..}() 195 next_expr = expression.call_gas 196 if self.conditional_not_ahead(next_expr, true_expression, false_expression, f_call_gas): 197 self.copy_expression( 198 next_expr, 199 true_expression.call_gas, 200 false_expression.call_gas, 201 ) 202 else: 203 return 204 205 if expression.call_value: 206 # case of (..).func{value: .. ? .. : ..}() 207 next_expr = expression.call_value 208 if self.conditional_not_ahead( 209 next_expr, true_expression, false_expression, f_call_value 210 ): 211 self.copy_expression( 212 next_expr, 213 true_expression.call_value, 214 false_expression.call_value, 215 ) 216 else: 217 return 218 219 true_expression._arguments = [] 220 false_expression._arguments = [] 221 222 for expr in expression.arguments: 223 if self.conditional_not_ahead(expr, true_expression, false_expression, f_call): 224 # always on last arguments added 225 self.copy_expression( 226 expr, 227 true_expression.arguments[-1], 228 false_expression.arguments[-1], 229 )