Merge pull request #227 from crytic/dev-fix-types

Improve conversion of Literals
pull/229/head
Feist Josselin 6 years ago committed by GitHub
commit 96c128230b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      slither/core/declarations/function.py
  2. 9
      slither/core/declarations/structure.py
  3. 7
      slither/core/expressions/literal.py
  4. 4
      slither/core/solidity_types/array_type.py
  5. 1
      slither/core/variables/state_variable.py
  6. 74
      slither/slithir/convert.py
  7. 48
      slither/slithir/variables/constant.py
  8. 2
      slither/solc_parsing/declarations/structure.py
  9. 16
      slither/solc_parsing/expressions/expression_parsing.py
  10. 4
      slither/solc_parsing/solidity_types/type_parsing.py
  11. 31
      slither/utils/type.py
  12. 8
      slither/visitors/expression/constants_folding.py
  13. 3
      slither/visitors/slithir/expression_to_slithir.py

@ -86,6 +86,7 @@ class Function(ChildContract, SourceMapping):
self._reachable_from_nodes = set()
self._reachable_from_functions = set()
###################################################################################
###################################################################################
# region General properties
@ -1070,4 +1071,4 @@ class Function(ChildContract, SourceMapping):
def __str__(self):
return self._name
# endregion
# endregion

@ -10,6 +10,8 @@ class Structure(ChildContract, SourceMapping):
self._name = None
self._canonical_name = None
self._elems = None
# Name of the elements in the order of declaration
self._elems_ordered = None
@property
def canonical_name(self):
@ -23,5 +25,12 @@ class Structure(ChildContract, SourceMapping):
def elems(self):
return self._elems
@property
def elems_ordered(self):
ret = []
for e in self._elems_ordered:
ret.append(self._elems[e])
return ret
def __str__(self):
return self.name

@ -2,14 +2,19 @@ from slither.core.expressions.expression import Expression
class Literal(Expression):
def __init__(self, value):
def __init__(self, value, type):
super(Literal, self).__init__()
self._value = value
self._type = type
@property
def value(self):
return self._value
@property
def type(self):
return self._type
def __str__(self):
# be sure to handle any character
return str(self._value)

@ -10,7 +10,7 @@ class ArrayType(Type):
assert isinstance(t, Type)
if length:
if isinstance(length, int):
length = Literal(length)
length = Literal(length, 'uint256')
assert isinstance(length, Expression)
super(ArrayType, self).__init__()
self._type = t
@ -18,7 +18,7 @@ class ArrayType(Type):
if length:
if not isinstance(length, Literal):
cf = ConstantFolding(length)
cf = ConstantFolding(length, "uint256")
length = cf.result()
self._length_value = length
else:

@ -3,7 +3,6 @@ from slither.core.children.child_contract import ChildContract
class StateVariable(ChildContract, Variable):
@property
def canonical_name(self):
return '{}:{}'.format(self.contract.name, self.name)

@ -7,7 +7,9 @@ from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import (ArrayType, ElementaryType,
FunctionType, MappingType,
UserDefinedType)
from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable
from slither.slithir.operations import (Assignment, Balance, Binary,
BinaryType, Call, Condition, Delete,
EventCall, HighLevelCall, Index,
@ -30,6 +32,7 @@ from slither.slithir.variables import (Constant, ReferenceVariable,
TemporaryVariable)
from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
from slither.utils.function import get_function_id
from slither.utils.type import export_nested_types_from_variable
logger = logging.getLogger('ConvertToIR')
@ -39,7 +42,8 @@ def convert_expression(expression, node):
from slither.core.cfg.node import NodeType
if isinstance(expression, Literal) and node.type in [NodeType.IF, NodeType.IFLOOP]:
result = [Condition(Constant(expression.value))]
cst = Constant(expression.value, expression.type)
result = [Condition(cst)]
return result
if isinstance(expression, Identifier) and node.type in [NodeType.IF, NodeType.IFLOOP]:
result = [Condition(expression.value)]
@ -599,7 +603,7 @@ def convert_to_push(ir, node):
ir = Push(ir.destination, val)
length = Literal(len(operation.init_values))
length = Literal(len(operation.init_values), 'uint256')
t = operation.init_values[0].type
ir.lvalue.set_type(ArrayType(t, length))
@ -822,6 +826,71 @@ def remove_unused(result):
result = [i for i in result if not i in to_remove]
return result
# endregion
###################################################################################
###################################################################################
# region Constant type conversioh
###################################################################################
###################################################################################
def convert_constant_types(irs):
"""
late conversion of uint -> type for constant (Literal)
:param irs:
:return:
"""
# TODO: implement instances lookup for events, NewContract
was_changed = True
while was_changed:
was_changed = False
for ir in irs:
if isinstance(ir, Assignment):
if isinstance(ir.lvalue.type, ElementaryType):
if ir.lvalue.type.type in ElementaryTypeInt:
if ir.rvalue.type.type != 'int256':
ir.rvalue.set_type(ElementaryType('int256'))
was_changed = True
if isinstance(ir, Binary):
if isinstance(ir.lvalue.type, ElementaryType):
if ir.lvalue.type.type in ElementaryTypeInt:
for r in ir.read:
if r.type.type != 'int256':
r.set_type(ElementaryType('int256'))
was_changed = True
if isinstance(ir, (HighLevelCall, InternalCall)):
func = ir.function
if isinstance(func, StateVariable):
types = export_nested_types_from_variable(func)
else:
types = [p.type for p in func.parameters]
for idx, arg in enumerate(ir.arguments):
t = types[idx]
if isinstance(t, ElementaryType):
if t.type in ElementaryTypeInt:
if arg.type.type != 'int256':
arg.set_type(ElementaryType('int256'))
was_changed = True
if isinstance(ir, NewStructure):
st = ir.structure
for idx, arg in enumerate(ir.arguments):
e = st.elems_ordered[idx]
if isinstance(e.type, ElementaryType):
if e.type.type in ElementaryTypeInt:
if arg.type.type != 'int256':
arg.set_type(ElementaryType('int256'))
was_changed = True
if isinstance(ir, InitArray):
if isinstance(ir.lvalue.type, ArrayType):
if isinstance(ir.lvalue.type.type, ElementaryType):
if ir.lvalue.type.type.type in ElementaryTypeInt:
for r in ir.read:
if r.type.type != 'int256':
r.set_type(ElementaryType('int256'))
was_changed = True
# endregion
###################################################################################
###################################################################################
@ -839,6 +908,7 @@ def apply_ir_heuristics(irs, node):
irs = propagate_type_and_convert_call(irs, node)
irs = remove_unused(irs)
find_references_origin(irs)
convert_constant_types(irs)
return irs

@ -1,17 +1,41 @@
from .variable import SlithIRVariable
from slither.core.solidity_types.elementary_type import ElementaryType
from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint
class Constant(SlithIRVariable):
def __init__(self, val):
def __init__(self, val, type=None):
super(Constant, self).__init__()
assert isinstance(val, str)
if val.isdigit():
self._type = ElementaryType('uint256')
self._val = int(val)
self._original_value = val
if type:
assert isinstance(type, ElementaryType)
self._type = type
if type.type in Int + Uint:
if val.startswith('0x'):
self._val = int(val, 16)
else:
if 'e' in val:
base, expo = val.split('e')
self._val = int(float(base)* (10 ** int(expo)))
elif 'E' in val:
base, expo = val.split('E')
self._val = int(float(base) * (10 ** int(expo)))
else:
self._val = int(val)
elif type.type == 'bool':
self._val = val == 'true'
else:
self._val = val
else:
self._type = ElementaryType('string')
self._val = val
if val.isdigit():
self._type = ElementaryType('uint256')
self._val = int(val)
else:
self._type = ElementaryType('string')
self._val = val
@property
def value(self):
@ -20,10 +44,18 @@ class Constant(SlithIRVariable):
If the expression was an hexadecimal delcared as hex'...'
return a str
Returns:
(str, int)
(str | int | bool)
'''
return self._val
@property
def original_value(self):
'''
Return the string representation of the value
:return: str
'''
return self._original_value
def __str__(self):
return str(self.value)

@ -16,6 +16,7 @@ class StructureSolc(Structure):
self._name = name
self._canonical_name = canonicalName
self._elems = {}
self._elems_ordered = []
self._elemsNotParsed = elems
@ -28,5 +29,6 @@ class StructureSolc(Structure):
elem.analyze(self.contract)
self._elems[elem.name] = elem
self._elems_ordered.append(elem.name)
self._elemsNotParsed = []

@ -479,6 +479,12 @@ def parse_expression(expression, caller_context):
value = str(convert_subdenomination(value, expression['subdenomination']))
elif not value and value != "":
value = '0x'+expression['hexValue']
type = expression['typeDescriptions']['typeString']
# Length declaration for array was None until solc 0.5.5
if type is None:
if expression['kind'] == 'number':
type = 'int_const'
else:
value = expression['attributes']['value']
if value:
@ -489,7 +495,15 @@ def parse_expression(expression, caller_context):
# see https://solidity.readthedocs.io/en/v0.4.25/types.html?highlight=hex#hexadecimal-literals
assert 'hexvalue' in expression['attributes']
value = '0x'+expression['attributes']['hexvalue']
literal = Literal(value)
type = expression['attributes']['type']
if type.startswith('int_const '):
type = ElementaryType('uint256')
elif type.startswith('bool'):
type = ElementaryType('bool')
else:
type = ElementaryType('string')
literal = Literal(value, type)
return literal
elif name == 'Identifier':

@ -32,7 +32,7 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
if name_elementary in ElementaryTypeName:
depth = name.count('[')
if depth:
return ArrayType(ElementaryType(name_elementary), Literal(depth))
return ArrayType(ElementaryType(name_elementary), Literal(depth, 'uint256'))
else:
return ElementaryType(name_elementary)
# We first look for contract
@ -78,7 +78,7 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
depth+=1
var_type = next((st for st in all_structures if st.contract.name+"."+st.name == name_struct), None)
if var_type:
return ArrayType(UserDefinedType(var_type), Literal(depth))
return ArrayType(UserDefinedType(var_type), Literal(depth, 'uint256'))
if not var_type:
var_type = next((f for f in contract.functions if f.name == name), None)

@ -0,0 +1,31 @@
from slither.core.solidity_types import (ArrayType, MappingType, ElementaryType)
def _add_mapping_parameter(t, l):
while isinstance(t, MappingType):
l.append(t.type_from)
t = t.type_to
_add_array_parameter(t, l)
def _add_array_parameter(t, l):
while isinstance(t, ArrayType):
l.append(ElementaryType('uint256'))
t = t.type
def export_nested_types_from_variable(variable):
"""
Export the list of nested types (mapping/array)
:param variable:
:return: list(Type)
"""
l = []
if isinstance(variable.type, MappingType):
t = variable.type
_add_mapping_parameter(t, l)
if isinstance(variable.type, ArrayType):
v = variable
_add_array_parameter(v.type, l)
return l

@ -20,8 +20,12 @@ def set_val(expression, val):
class ConstantFolding(ExpressionVisitor):
def __init__(self, expression, type):
super(ConstantFolding, self).__init__(expression)
self._type = type
def result(self):
return Literal(int(get_val(self._expression)))
return Literal(int(get_val(self._expression)), self._type)
def _post_identifier(self, expression):
if not expression.value.is_constant:
@ -29,7 +33,7 @@ class ConstantFolding(ExpressionVisitor):
expr = expression.value.expression
# assumption that we won't have infinite loop
if not isinstance(expr, Literal):
cf = ConstantFolding(expr)
cf = ConstantFolding(expr, self._type)
expr = cf.result()
set_val(expression, int(expr.value))

@ -173,7 +173,8 @@ class ExpressionToSlithIR(ExpressionVisitor):
set_val(expression, val)
def _post_literal(self, expression):
set_val(expression, Constant(expression.value))
cst = Constant(expression.value, expression.type)
set_val(expression, cst)
def _post_member_access(self, expression):
expr = get(expression.expression)

Loading…
Cancel
Save