Several improvements in tuples and abi.decode support

abi.decode:
- Better IR output
- Propagate types (fix #529 third cases)
- Correct parsing in abi.decode parameters in case of arrays

Tuples:
- Improve support of tuple singleton (the generated IR was missing an
Unpack operation)
pull/548/head
Josselin 4 years ago
parent 5cea11612e
commit 53dc37388e
  1. 5
      slither/core/expressions/elementary_type_name_expression.py
  2. 6
      slither/slithir/convert.py
  3. 19
      slither/slithir/operations/solidity_call.py
  4. 2
      slither/solc_parsing/declarations/function.py
  5. 8
      slither/solc_parsing/expressions/expression_parsing.py
  6. 9
      slither/visitors/slithir/expression_to_slithir.py

@ -15,5 +15,10 @@ class ElementaryTypeNameExpression(Expression):
def type(self) -> Type: def type(self) -> Type:
return self._type return self._type
@type.setter
def type(self, new_type: Type):
assert isinstance(new_type, Type)
self._type = new_type
def __str__(self): def __str__(self):
return str(self._type) return str(self._type)

@ -803,6 +803,12 @@ def convert_to_solidity_func(ir):
new_ir.set_node(ir.node) new_ir.set_node(ir.node)
if isinstance(call.return_type, list) and len(call.return_type) == 1: if isinstance(call.return_type, list) and len(call.return_type) == 1:
new_ir.lvalue.set_type(call.return_type[0]) new_ir.lvalue.set_type(call.return_type[0])
elif (isinstance(new_ir.lvalue, TupleVariable) and
call == SolidityFunction("abi.decode()") and
len(new_ir.arguments) == 2 and
isinstance(new_ir.arguments[1], list)):
types = [x for x in new_ir.arguments[1]]
new_ir.lvalue.set_type(types)
else: else:
new_ir.lvalue.set_type(call.return_type) new_ir.lvalue.set_type(call.return_type)
return new_ir return new_ir

@ -30,8 +30,17 @@ class SolidityCall(Call, OperationWithLValue):
return self._type_call return self._type_call
def __str__(self): def __str__(self):
args = [str(a) for a in self.arguments] if (self.function == SolidityFunction("abi.decode()") and
return str(self.lvalue) +' = SOLIDITY_CALL {}({})'.format(self.function.full_name, ','.join(args)) len(self.arguments) == 2 and
# return str(self.lvalue) +' = INTERNALCALL {} (arg {})'.format(self.function, isinstance(self.arguments[1], list)):
# self.nbr_arguments) args = str(self.arguments[0]) + '(' + ','.join([str(a) for a in self.arguments[1]]) + ')'
else:
args = ','.join([str(a) for a in self.arguments])
lvalue = ''
if self.lvalue:
if isinstance(self.lvalue.type, (list,)):
lvalue = '{}({}) = '.format(self.lvalue, ','.join(str(x) for x in self.lvalue.type))
else:
lvalue = '{}({}) = '.format(self.lvalue, self.lvalue.type)
return lvalue + 'SOLIDITY_CALL {}({})'.format(self.function.full_name, args)

@ -631,6 +631,8 @@ class FunctionSolc:
i = 0 i = 0
new_node = node new_node = node
for variable in variables: for variable in variables:
if variable is None:
continue
init = inits[i] init = inits[i]
src = variable["src"] src = variable["src"]
i = i + 1 i = i + 1

@ -421,7 +421,6 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
# | Expression '?' Expression ':' Expression # | Expression '?' Expression ':' Expression
# | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression # | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression
# | PrimaryExpression # | PrimaryExpression
# The AST naming does not follow the spec # The AST naming does not follow the spec
name = expression[caller_context.get_key()] name = expression[caller_context.get_key()]
is_compact_ast = caller_context.is_compact_ast is_compact_ast = caller_context.is_compact_ast
@ -646,7 +645,12 @@ def parse_expression(expression: Dict, caller_context: CallerContext) -> "Expres
# if abi.decode is used # if abi.decode is used
# For example, abi.decode(data, ...(uint[]) ) # For example, abi.decode(data, ...(uint[]) )
if right is None: if right is None:
return parse_expression(left, caller_context) ret = parse_expression(left, caller_context)
# Nested array are not yet available in abi.decode
if isinstance(ret, ElementaryTypeNameExpression):
old_type = ret.type
ret.type = ArrayType(old_type, None)
return ret
left_expression = parse_expression(left, caller_context) left_expression = parse_expression(left, caller_context)
right_expression = parse_expression(right, caller_context) right_expression = parse_expression(right, caller_context)

@ -5,6 +5,7 @@ from slither.core.expressions import (AssignmentOperationType,
UnaryOperationType, BinaryOperationType) UnaryOperationType, BinaryOperationType)
from slither.core.solidity_types import ArrayType, ElementaryType from slither.core.solidity_types import ArrayType, ElementaryType
from slither.core.solidity_types.type import Type from slither.core.solidity_types.type import Type
from slither.core.variables.local_variable_init_from_tuple import LocalVariableInitFromTuple
from slither.slithir.operations import (Assignment, Binary, BinaryType, Delete, from slither.slithir.operations import (Assignment, Binary, BinaryType, Delete,
Index, InitArray, InternalCall, Member, Index, InitArray, InternalCall, Member,
NewArray, NewContract, NewArray, NewContract,
@ -130,6 +131,14 @@ class ExpressionToSlithIR(ExpressionVisitor):
operation.set_expression(expression) operation.set_expression(expression)
self._result.append(operation) self._result.append(operation)
set_val(expression, None) set_val(expression, None)
# Tuple with only one element. We need to convert the assignment to a Unpack
# Ex:
# (uint a,,) = g()
elif isinstance(left, LocalVariableInitFromTuple) and left.tuple_index:
operation = Unpack(left, right, left.tuple_index)
operation.set_expression(expression)
self._result.append(operation)
set_val(expression, None)
else: else:
# Init of array, like # Init of array, like
# uint8[2] var = [1,2]; # uint8[2] var = [1,2];

Loading…
Cancel
Save