Skip to content
Permalink
Browse files

- Improve literals parsing and Constant conversion:

        - add bool type to constant
        - convert integer using 'e' (1e10)
        - convert constant uint -> int when possible
- Api added:
        - Constant.original_value: str version of the constant expression
        - Structure.elems_ordered: keep original structure declaration order
  • Loading branch information...
montyly committed May 6, 2019
1 parent 0fd77f9 commit fbd1ddb5fc7199aaff0a6f1dd73440b1d29f046b
@@ -86,6 +86,7 @@ def __init__(self):
self._reachable_from_nodes = set()
self._reachable_from_functions = set()


###################################################################################
###################################################################################
# region General properties
@@ -1070,4 +1071,4 @@ def _analyze_calls(self):
def __str__(self):
return self._name

# endregion
# endregion
@@ -10,6 +10,8 @@ def __init__(self):
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 @@ def name(self):
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 @@

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,15 +10,15 @@ def __init__(self, t, length):
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
self._length = length

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 @@

class StateVariable(ChildContract, Variable):


@property
def canonical_name(self):
return '{}:{}'.format(self.contract.name, self.name)
@@ -7,7 +7,9 @@
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 @@
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 @@ def value(self):
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 @@ def __init__(self, name, canonicalName, elems):
self._name = name
self._canonical_name = canonicalName
self._elems = {}
self._elems_ordered = []

self._elemsNotParsed = elems

@@ -28,5 +29,6 @@ def analyze(self):
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,16 +20,20 @@ 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:
raise NotConstant
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 @@ def _post_index_access(self, expression):
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)

0 comments on commit fbd1ddb

Please sign in to comment.
You can’t perform that action at this time.