Merge branch 'dev' into feature/rewrite-uninitialized-state-with-ir

pull/49/head
Feist Josselin 6 years ago committed by GitHub
commit b81b137481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 107
      scripts/travis_test.sh
  2. 40
      slither/__main__.py
  3. 105
      slither/core/cfg/node.py
  4. 74
      slither/core/declarations/function.py
  5. 21
      slither/slither.py
  6. 12
      slither/slithir/convert.py
  7. 5
      slither/slithir/operations/unpack.py
  8. 5
      slither/slithir/variables/reference.py
  9. 26
      slither/solc_parsing/cfg/node.py
  10. 101
      slither/solc_parsing/declarations/contract.py
  11. 18
      slither/solc_parsing/declarations/event.py
  12. 388
      slither/solc_parsing/declarations/function.py
  13. 17
      slither/solc_parsing/declarations/modifier.py
  14. 176
      slither/solc_parsing/expressions/expression_parsing.py
  15. 61
      slither/solc_parsing/slitherSolc.py
  16. 53
      slither/solc_parsing/solidity_types/type_parsing.py
  17. 46
      slither/solc_parsing/variables/variable_declaration.py
  18. 35
      slither/utils/expression_manipulations.py

@ -2,84 +2,35 @@
### Test Detectors ### Test Detectors
# test_slither file.sol --detect-detectors number_results
slither tests/uninitialized.sol --disable-solc-warnings --detect-uninitialized-state test_slither(){
if [ $? -ne 3 ]; then slither "$1" --disable-solc-warnings "$2"
exit 1 if [ $? -ne "$3" ]; then
fi exit 1
fi
# contains also the test for the suicidal detector
slither tests/backdoor.sol --disable-solc-warnings --detect-backdoor slither "$1" --disable-solc-warnings "$2" --compact-ast
if [ $? -ne 1 ]; then if [ $? -ne "$3" ]; then
exit 1 exit 1
fi fi
}
slither tests/pragma.0.4.24.sol --disable-solc-warnings --detect-pragma
if [ $? -ne 1 ]; then test_slither tests/uninitialized.sol "--detect-uninitialized-state" 1
exit 1 test_slither tests/backdoor.sol "--detect-backdoor" 1
fi test_slither tests/backdoor.sol "--detect-suicidal" 1
test_slither tests/pragma.0.4.24.sol "--detect-pragma" 1
slither tests/old_solc.sol.json --solc-ast --detect-solc-version test_slither tests/old_solc.sol.json "--detect-solc-version" 1
if [ $? -ne 1 ]; then test_slither tests/reentrancy.sol "--detect-reentrancy" 1
exit 1 test_slither tests/uninitialized_storage_pointer.sol "--detect-uninitialized-storage" 1
fi test_slither tests/tx_origin.sol "--detect-tx-origin" 2
test_slither tests/unused_state.sol "--detect-unused-state" 1
slither tests/reentrancy.sol --disable-solc-warnings --detect-reentrancy test_slither tests/locked_ether.sol "--detect-locked-ether" 1
if [ $? -ne 1 ]; then test_slither tests/arbitrary_send.sol "--detect-arbitrary-send" 2
exit 1 test_slither tests/inline_assembly_contract.sol "--detect-assembly" 1
fi test_slither tests/inline_assembly_library.sol "--detect-assembly" 2
test_slither tests/naming_convention.sol "--detect-naming-convention" 10
slither tests/uninitialized_storage_pointer.sol --disable-solc-warnings --detect-uninitialized-storage test_slither tests/low_level_calls.sol "--detect-low-level-calls" 1
if [ $? -ne 1 ]; then test_slither tests/const_state_variables.sol "--detect-const-candidates-state" 2
exit 1
fi
slither tests/tx_origin.sol --disable-solc-warnings --detect-tx-origin
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/unused_state.sol --detect-unused-state
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/locked_ether.sol --detect-locked-ether
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/arbitrary_send.sol --disable-solc-warnings --detect-arbitrary-send
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/inline_assembly_contract.sol --disable-solc-warnings --detect-assembly
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/inline_assembly_library.sol --disable-solc-warnings --detect-assembly
if [ $? -ne 2 ]; then
exit 1
fi
slither tests/naming_convention.sol --disable-solc-warnings --detect-naming-convention
if [ $? -ne 10 ]; then
exit 1
fi
slither tests/low_level_calls.sol --disable-solc-warnings --detect-low-level-calls
if [ $? -ne 1 ]; then
exit 1
fi
slither tests/const_state_variables.sol --detect-const-candidates-state
if [ $? -ne 2 ]; then
exit 1
fi
### Test scripts ### Test scripts

@ -15,6 +15,7 @@ from slither.detectors.abstract_detector import (AbstractDetector,
classification_txt) classification_txt)
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from slither.slither import Slither from slither.slither import Slither
from slither.utils.colors import red
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("Slither") logger = logging.getLogger("Slither")
@ -52,8 +53,14 @@ def process(filename, args, detector_classes, printer_classes):
Returns: Returns:
list(result), int: Result list and number of contracts analyzed list(result), int: Result list and number of contracts analyzed
""" """
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args) ast = '--ast-json'
if args.compact_ast:
ast = '--ast-compact-json'
slither = Slither(filename, args.solc, args.disable_solc_warnings, args.solc_args, ast)
return _process(slither, detector_classes, printer_classes)
def _process(slither, detector_classes, printer_classes):
for detector_cls in detector_classes: for detector_cls in detector_classes:
slither.register_detector(detector_cls) slither.register_detector(detector_cls)
@ -76,6 +83,28 @@ def process(filename, args, detector_classes, printer_classes):
return results, analyzed_contracts_count return results, analyzed_contracts_count
def process_truffle(dirname, args, detector_classes, printer_classes):
if not os.path.isdir(os.path.join(dirname, 'build'))\
or not os.path.isdir(os.path.join(dirname, 'build', 'contracts')):
logger.info(red('No truffle build directory found, did you run `truffle compile`?'))
return (0,0)
filenames = glob.glob(os.path.join(dirname,'build','contracts', '*.json'))
all_contracts = []
for filename in filenames:
with open(filename) as f:
contract_loaded = json.load(f)
all_contracts += contract_loaded['ast']['nodes']
contract = {
"nodeType": "SourceUnit",
"nodes" : all_contracts}
slither = Slither(contract, args.solc, args.disable_solc_warnings, args.solc_args)
return _process(slither, detector_classes, printer_classes)
def output_json(results, filename): def output_json(results, filename):
with open(filename, 'w') as f: with open(filename, 'w') as f:
@ -194,6 +223,9 @@ def main_impl(all_detector_classes, all_printer_classes):
if os.path.isfile(filename): if os.path.isfile(filename):
(results, number_contracts) = process(filename, args, detector_classes, printer_classes) (results, number_contracts) = process(filename, args, detector_classes, printer_classes)
elif os.path.isfile(os.path.join(filename, 'truffle.js')):
(results, number_contracts) = process_truffle(filename, args, detector_classes, printer_classes)
elif os.path.isdir(filename) or len(globbed_filenames) > 0: elif os.path.isdir(filename) or len(globbed_filenames) > 0:
extension = "*.sol" if not args.solc_ast else "*.json" extension = "*.sol" if not args.solc_ast else "*.json"
filenames = glob.glob(os.path.join(filename, extension)) filenames = glob.glob(os.path.join(filename, extension))
@ -209,6 +241,7 @@ def main_impl(all_detector_classes, all_printer_classes):
# output_json(results, args.json) # output_json(results, args.json)
# exit(results) # exit(results)
else: else:
raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename)) raise Exception("Unrecognised file/dir path: '#{filename}'".format(filename=filename))
@ -319,6 +352,11 @@ def parse_args(detector_classes, printer_classes):
action="store_true", action="store_true",
default=False) default=False)
parser.add_argument('--compact-ast',
help=argparse.SUPPRESS,
action='store_true',
default=False)
return parser.parse_args() return parser.parse_args()

@ -5,6 +5,7 @@ import logging
from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable
from slither.visitors.expression.expression_printer import ExpressionPrinter from slither.visitors.expression.expression_printer import ExpressionPrinter
from slither.visitors.expression.read_var import ReadVar from slither.visitors.expression.read_var import ReadVar
@ -12,10 +13,17 @@ from slither.visitors.expression.write_var import WriteVar
from slither.core.children.child_function import ChildFunction from slither.core.children.child_function import ChildFunction
from slither.core.declarations.solidity_variables import SolidityFunction from slither.core.declarations.solidity_variables import SolidityVariable, SolidityFunction
from slither.slithir.convert import convert_expression from slither.slithir.convert import convert_expression
from slither.slithir.operations import OperationWithLValue, Index, Member, LowLevelCall, SolidityCall, HighLevelCall, InternalCall, LibraryCall
from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable, TupleVariable
from slither.core.declarations import Contract
logger = logging.getLogger("Node") logger = logging.getLogger("Node")
class NodeType: class NodeType:
@ -103,7 +111,10 @@ class Node(SourceMapping, ChildFunction):
self._vars_written = [] self._vars_written = []
self._vars_read = [] self._vars_read = []
self._internal_calls = [] self._internal_calls = []
self._external_calls = [] self._solidity_calls = []
self._high_level_calls = []
self._low_level_calls = []
self._external_calls_as_expressions = []
self._irs = [] self._irs = []
self._state_vars_written = [] self._state_vars_written = []
@ -176,16 +187,43 @@ class Node(SourceMapping, ChildFunction):
@property @property
def internal_calls(self): def internal_calls(self):
""" """
list(Function or SolidityFunction): List of function calls (that does not create a transaction) list(Function or SolidityFunction): List of internal/soldiity function calls
""" """
return list(self._internal_calls) return list(self._internal_calls)
@property @property
def external_calls(self): def solidity_calls(self):
"""
list(SolidityFunction): List of Soldity calls
"""
return list(self._internal_calls)
@property
def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
""" """
list(CallExpression): List of message calls (that creates a transaction) list(CallExpression): List of message calls (that creates a transaction)
""" """
return self._external_calls return self._external_calls_as_expressions
@property @property
def calls_as_expression(self): def calls_as_expression(self):
@ -226,10 +264,7 @@ class Node(SourceMapping, ChildFunction):
Returns: Returns:
bool: True if the node has a require or assert call bool: True if the node has a require or assert call
""" """
return self.internal_calls and\ return any(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'] for c in self.internal_calls)
any(isinstance(c, SolidityFunction) and\
(c.name in ['require(bool)', 'require(bool,string)', 'assert(bool)'])\
for c in self.internal_calls)
def contains_if(self): def contains_if(self):
""" """
@ -328,3 +363,55 @@ class Node(SourceMapping, ChildFunction):
if self.expression: if self.expression:
expression = self.expression expression = self.expression
self._irs = convert_expression(expression, self) self._irs = convert_expression(expression, self)
self._find_read_write_call()
def _find_read_write_call(self):
def is_slithir_var(var):
return isinstance(var, (Constant, ReferenceVariable, TemporaryVariable, TupleVariable))
for ir in self.irs:
self._vars_read += [v for v in ir.read if not is_slithir_var(v)]
if isinstance(ir, OperationWithLValue):
if isinstance(ir, (Index, Member)):
continue # Don't consider Member and Index operations -> ReferenceVariable
var = ir.lvalue
# If its a reference, we loop until finding the origin
if isinstance(var, (ReferenceVariable)):
while isinstance(var, ReferenceVariable):
var = var.points_to
# Only store non-slithIR variables
if not is_slithir_var(var):
self._vars_written.append(var)
if isinstance(ir, InternalCall):
self._internal_calls.append(ir.function)
if isinstance(ir, SolidityCall):
# TODO: consider removing dependancy of solidity_call to internal_call
self._solidity_calls.append(ir.function)
self._internal_calls.append(ir.function)
if isinstance(ir, LowLevelCall):
assert isinstance(ir.destination, (Variable, SolidityVariable))
self._low_level_calls.append((ir.destination, ir.function_name.value))
elif isinstance(ir, (HighLevelCall)) and not isinstance(ir, LibraryCall):
if isinstance(ir.destination.type, Contract):
self._high_level_calls.append((ir.destination.type, ir.function))
else:
self._high_level_calls.append((ir.destination.type.type, ir.function))
elif isinstance(ir, LibraryCall):
assert isinstance(ir.destination, Contract)
self._high_level_calls.append((ir.destination, ir.function))
self._vars_read = list(set(self._vars_read))
self._state_vars_read = [v for v in self._vars_read if isinstance(v, StateVariable)]
self._solidity_vars_read = [v for v in self._vars_read if isinstance(v, SolidityVariable)]
self._vars_written = list(set(self._vars_written))
self._state_vars_written = [v for v in self._vars_written if isinstance(v, StateVariable)]
self._internal_calls = list(set(self._internal_calls))
self._solidity_calls = list(set(self._solidity_calls))
self._high_level_calls = list(set(self._high_level_calls))
self._low_level_calls = list(set(self._low_level_calls))

@ -44,7 +44,10 @@ class Function(ChildContract, SourceMapping):
self._solidity_vars_read = [] self._solidity_vars_read = []
self._state_vars_written = [] self._state_vars_written = []
self._internal_calls = [] self._internal_calls = []
self._external_calls = [] self._solidity_calls = []
self._low_level_calls = []
self._high_level_calls = []
self._external_calls_as_expressions = []
self._expression_vars_read = [] self._expression_vars_read = []
self._expression_vars_written = [] self._expression_vars_written = []
self._expression_calls = [] self._expression_calls = []
@ -239,14 +242,42 @@ class Function(ChildContract, SourceMapping):
return list(self._internal_calls) return list(self._internal_calls)
@property @property
def external_calls(self): def solidity_calls(self):
"""
list(SolidityFunction): List of Soldity calls
"""
return list(self._internal_calls)
@property
def high_level_calls(self):
"""
list((Contract, Function|Variable)):
List of high level calls (external calls).
A variable is called in case of call to a public state variable
Include library calls
"""
return list(self._high_level_calls)
@property
def low_level_calls(self):
"""
list((Variable|SolidityVariable, str)): List of low_level call
A low level call is defined by
- the variable called
- the name of the function (call/delegatecall/codecall)
"""
return list(self._low_level_calls)
@property
def external_calls_as_expressions(self):
""" """
list(ExpressionCall): List of message calls (that creates a transaction) list(ExpressionCall): List of message calls (that creates a transaction)
""" """
return list(self._external_calls) return list(self._external_calls_as_expressions)
@property @property
def calls_as_expression(self): def calls_as_expressions(self):
return self._expression_calls return self._expression_calls
@property @property
@ -353,6 +384,7 @@ class Function(ChildContract, SourceMapping):
calls = [x for x in calls if x] calls = [x for x in calls if x]
calls = [item for sublist in calls for item in sublist] calls = [item for sublist in calls for item in sublist]
# Remove dupplicate if they share the same string representation # Remove dupplicate if they share the same string representation
# TODO: check if groupby is still necessary here
calls = [next(obj) for i, obj in\ calls = [next(obj) for i, obj in\
groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))] groupby(sorted(calls, key=lambda x: str(x)), lambda x: str(x))]
self._expression_calls = calls self._expression_calls = calls
@ -364,12 +396,30 @@ class Function(ChildContract, SourceMapping):
groupby(sorted(internal_calls, key=lambda x: str(x)), lambda x: str(x))] groupby(sorted(internal_calls, key=lambda x: str(x)), lambda x: str(x))]
self._internal_calls = internal_calls self._internal_calls = internal_calls
external_calls = [x.external_calls for x in self.nodes] self._solidity_calls = [c for c in internal_calls if isinstance(c, SolidityFunction)]
external_calls = [x for x in external_calls if x]
external_calls = [item for sublist in external_calls for item in sublist] low_level_calls = [x.low_level_calls for x in self.nodes]
external_calls = [next(obj) for i, obj in low_level_calls = [x for x in low_level_calls if x]
groupby(sorted(external_calls, key=lambda x: str(x)), lambda x: str(x))] low_level_calls = [item for sublist in low_level_calls for item in sublist]
self._external_calls = external_calls low_level_calls = [next(obj) for i, obj in
groupby(sorted(low_level_calls, key=lambda x: str(x)), lambda x: str(x))]
self._low_level_calls = low_level_calls
high_level_calls = [x.high_level_calls for x in self.nodes]
high_level_calls = [x for x in high_level_calls if x]
high_level_calls = [item for sublist in high_level_calls for item in sublist]
high_level_calls = [next(obj) for i, obj in
groupby(sorted(high_level_calls, key=lambda x: str(x)), lambda x: str(x))]
self._high_level_calls = high_level_calls
external_calls_as_expressions = [x.external_calls_as_expressions for x in self.nodes]
external_calls_as_expressions = [x for x in external_calls_as_expressions if x]
external_calls_as_expressions = [item for sublist in external_calls_as_expressions for item in sublist]
external_calls_as_expressions = [next(obj) for i, obj in
groupby(sorted(external_calls_as_expressions, key=lambda x: str(x)), lambda x: str(x))]
self._external_calls_as_expressions = external_calls_as_expressions
def _explore_functions(self, f_new_values): def _explore_functions(self, f_new_values):
@ -567,14 +617,14 @@ class Function(ChildContract, SourceMapping):
Return the function summary Return the function summary
Returns: Returns:
(str, str, list(str), list(str), listr(str), list(str), list(str); (str, str, list(str), list(str), listr(str), list(str), list(str);
name, visibility, modifiers, vars read, vars written, internal_calls, external_calls name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
""" """
return (self.name, self.visibility, return (self.name, self.visibility,
[str(x) for x in self.modifiers], [str(x) for x in self.modifiers],
[str(x) for x in self.state_variables_read + self.solidity_variables_read], [str(x) for x in self.state_variables_read + self.solidity_variables_read],
[str(x) for x in self.state_variables_written], [str(x) for x in self.state_variables_written],
[str(x) for x in self.internal_calls], [str(x) for x in self.internal_calls],
[str(x) for x in self.external_calls]) [str(x) for x in self.external_calls_as_expressions])
def is_protected(self): def is_protected(self):
""" """

@ -17,16 +17,21 @@ logger_printer = logging.getLogger("Printers")
class Slither(SlitherSolc): class Slither(SlitherSolc):
def __init__(self, filename, solc='solc', disable_solc_warnings=False, solc_arguments=''): def __init__(self, contract, solc='solc', disable_solc_warnings=False, solc_arguments='', ast_format='--ast-json'):
self._detectors = [] self._detectors = []
self._printers = [] self._printers = []
stdout = self._run_solc(filename, solc, disable_solc_warnings, solc_arguments) # json text provided
if isinstance(contract, dict):
super(Slither, self).__init__(filename) super(Slither, self).__init__('')
self._parse_contracts_from_loaded_json(contract, '')
# .json or .sol provided
else:
contracts_json = self._run_solc(contract, solc, disable_solc_warnings, solc_arguments, ast_format)
super(Slither, self).__init__(contract)
for d in stdout: for c in contracts_json:
self._parse_contracts_from_json(d) self._parse_contracts_from_json(c)
self._analyze_contracts() self._analyze_contracts()
@ -76,7 +81,7 @@ class Slither(SlitherSolc):
"You can't register {!r} twice.".format(cls) "You can't register {!r} twice.".format(cls)
) )
def _run_solc(self, filename, solc, disable_solc_warnings, solc_arguments): def _run_solc(self, filename, solc, disable_solc_warnings, solc_arguments, ast_format):
if not os.path.isfile(filename): if not os.path.isfile(filename):
logger.error('{} does not exist (are you in the correct directory?)'.format(filename)) logger.error('{} does not exist (are you in the correct directory?)'.format(filename))
exit(-1) exit(-1)
@ -93,7 +98,7 @@ class Slither(SlitherSolc):
logger.info('Empty AST file: %s', filename) logger.info('Empty AST file: %s', filename)
sys.exit(-1) sys.exit(-1)
else: else:
cmd = [solc, filename, '--ast-json'] cmd = [solc, filename, ast_format]
if solc_arguments: if solc_arguments:
# To parse, we first split the string on each '--' # To parse, we first split the string on each '--'
solc_args = solc_arguments.split('--') solc_args = solc_arguments.split('--')

@ -283,6 +283,8 @@ def get_sig(ir):
for arg in ir.arguments: for arg in ir.arguments:
if isinstance(arg, (list,)): if isinstance(arg, (list,)):
type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg)) type_arg = '{}[{}]'.format(get_type(arg[0].type), len(arg))
elif isinstance(arg, Function):
type_arg = arg.signature_str
else: else:
type_arg = get_type(arg.type) type_arg = get_type(arg.type)
args.append(type_arg) args.append(type_arg)
@ -544,8 +546,14 @@ def remove_temporary(result):
return result return result
def remove_unused(result): def remove_unused(result):
removed = True removed = True
if not result:
return result
# dont remove the last elem, as it may be used by RETURN
last_elem = result[-1]
while removed: while removed:
removed = False removed = False
@ -562,7 +570,7 @@ def remove_unused(result):
for ins in result: for ins in result:
if isinstance(ins, Member): if isinstance(ins, Member):
if not ins.lvalue.name in to_keep: if not ins.lvalue.name in to_keep and ins != last_elem:
to_remove.append(ins) to_remove.append(ins)
removed = True removed = True

@ -28,4 +28,7 @@ class Unpack(OperationWithLValue):
return self._idx return self._idx
def __str__(self): def __str__(self):
return "{} = UNPACK {} index: {} ".format(self.lvalue, self.tuple, self.index) return "{}({})= UNPACK {} index: {} ".format(self.lvalue,
self.lvalue.type,
self.tuple,
self.index)

@ -1,6 +1,6 @@
from slither.core.children.child_node import ChildNode from slither.core.children.child_node import ChildNode
from slither.core.declarations import Contract, Enum, SolidityVariableComposed from slither.core.declarations import Contract, Enum, SolidityVariable
from slither.core.variables.variable import Variable from slither.core.variables.variable import Variable
@ -36,8 +36,7 @@ class ReferenceVariable(ChildNode, Variable):
# Member or Index operator # Member or Index operator
from slither.slithir.utils.utils import is_valid_lvalue from slither.slithir.utils.utils import is_valid_lvalue
assert is_valid_lvalue(points_to) \ assert is_valid_lvalue(points_to) \
or points_to == SolidityVariableComposed('msg.data') \ or isinstance(points_to, (SolidityVariable, Contract, Enum))
or isinstance(points_to, (Contract, Enum))
self._points_to = points_to self._points_to = points_to

@ -45,25 +45,21 @@ class NodeSolc(Node):
expression = self.expression expression = self.expression
pp = ReadVar(expression) pp = ReadVar(expression)
self._expression_vars_read = pp.result() self._expression_vars_read = pp.result()
vars_read = [ExportValues(v).result() for v in self._expression_vars_read]
self._vars_read = [item for sublist in vars_read for item in sublist] # self._vars_read = [item for sublist in vars_read for item in sublist]
self._state_vars_read = [x for x in self.variables_read if\ # self._state_vars_read = [x for x in self.variables_read if\
isinstance(x, (StateVariable))] # isinstance(x, (StateVariable))]
self._solidity_vars_read = [x for x in self.variables_read if\ # self._solidity_vars_read = [x for x in self.variables_read if\
isinstance(x, (SolidityVariable))] # isinstance(x, (SolidityVariable))]
pp = WriteVar(expression) pp = WriteVar(expression)
self._expression_vars_written = pp.result() self._expression_vars_written = pp.result()
vars_written = [ExportValues(v).result() for v in self._expression_vars_written]
self._vars_written = [item for sublist in vars_written for item in sublist] # self._vars_written = [item for sublist in vars_written for item in sublist]
self._state_vars_written = [x for x in self.variables_written if\ # self._state_vars_written = [x for x in self.variables_written if\
isinstance(x, StateVariable)] # isinstance(x, StateVariable)]
pp = FindCalls(expression) pp = FindCalls(expression)
self._expression_calls = pp.result() self._expression_calls = pp.result()
calls = [ExportValues(c).result() for c in self.calls_as_expression] self._external_calls_as_expressions = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]
calls = [item for sublist in calls for item in sublist]
self._internal_calls = [c for c in calls if isinstance(c, (Function, SolidityFunction))]
self._external_calls = [c for c in self.calls_as_expression if not isinstance(c.called, Identifier)]

@ -37,8 +37,13 @@ class ContractSolc04(Contract):
self._is_analyzed = False self._is_analyzed = False
# Export info # Export info
self._name = self._data['attributes']['name'] if self.is_compact_ast:
self._name = self._data['name']
else:
self._name = self._data['attributes'][self.get_key()]
self._id = self._data['id'] self._id = self._data['id']
self._inheritance = [] self._inheritance = []
self._parse_contract_info() self._parse_contract_info()
@ -48,11 +53,27 @@ class ContractSolc04(Contract):
def is_analyzed(self): def is_analyzed(self):
return self._is_analyzed return self._is_analyzed
def get_key(self):
return self.slither.get_key()
def get_children(self, key='nodes'):
if self.is_compact_ast:
return key
return 'children'
@property
def is_compact_ast(self):
return self.slither.is_compact_ast
def set_is_analyzed(self, is_analyzed): def set_is_analyzed(self, is_analyzed):
self._is_analyzed = is_analyzed self._is_analyzed = is_analyzed
def _parse_contract_info(self): def _parse_contract_info(self):
if self.is_compact_ast:
attributes = self._data
else:
attributes = self._data['attributes'] attributes = self._data['attributes']
self.isInterface = False self.isInterface = False
if 'contractKind' in attributes: if 'contractKind' in attributes:
if attributes['contractKind'] == 'interface': if attributes['contractKind'] == 'interface':
@ -62,29 +83,29 @@ class ContractSolc04(Contract):
self.fullyImplemented = attributes['fullyImplemented'] self.fullyImplemented = attributes['fullyImplemented']
def _parse_contract_items(self): def _parse_contract_items(self):
if not 'children' in self._data: # empty contract if not self.get_children() in self._data: # empty contract
return return
for item in self._data['children']: for item in self._data[self.get_children()]:
if item['name'] == 'FunctionDefinition': if item[self.get_key()] == 'FunctionDefinition':
self._functionsNotParsed.append(item) self._functionsNotParsed.append(item)
elif item['name'] == 'EventDefinition': elif item[self.get_key()] == 'EventDefinition':
self._eventsNotParsed.append(item) self._eventsNotParsed.append(item)
elif item['name'] == 'InheritanceSpecifier': elif item[self.get_key()] == 'InheritanceSpecifier':
# we dont need to parse it as it is redundant # we dont need to parse it as it is redundant
# with self.linearizedBaseContracts # with self.linearizedBaseContracts
continue continue
elif item['name'] == 'VariableDeclaration': elif item[self.get_key()] == 'VariableDeclaration':
self._variablesNotParsed.append(item) self._variablesNotParsed.append(item)
elif item['name'] == 'EnumDefinition': elif item[self.get_key()] == 'EnumDefinition':
self._enumsNotParsed.append(item) self._enumsNotParsed.append(item)
elif item['name'] == 'ModifierDefinition': elif item[self.get_key()] == 'ModifierDefinition':
self._modifiersNotParsed.append(item) self._modifiersNotParsed.append(item)
elif item['name'] == 'StructDefinition': elif item[self.get_key()] == 'StructDefinition':
self._structuresNotParsed.append(item) self._structuresNotParsed.append(item)
elif item['name'] == 'UsingForDirective': elif item[self.get_key()] == 'UsingForDirective':
self._usingForNotParsed.append(item) self._usingForNotParsed.append(item)
else: else:
logger.error('Unknown contract item: '+item['name']) logger.error('Unknown contract item: '+item[self.get_key()])
exit(-1) exit(-1)
return return
@ -92,8 +113,19 @@ class ContractSolc04(Contract):
for father in self.inheritance: for father in self.inheritance:
self._using_for.update(father.using_for) self._using_for.update(father.using_for)
if self.is_compact_ast:
for using_for in self._usingForNotParsed: for using_for in self._usingForNotParsed:
children = using_for['children'] lib_name = parse_type(using_for['libraryName'], self)
if 'typeName' in using_for and using_for['typeName']:
type_name = parse_type(using_for['typeName'], self)
else:
type_name = '*'
if not type_name in self._using_for:
self.using_for[type_name] = []
self._using_for[type_name].append(lib_name)
else:
for using_for in self._usingForNotParsed:
children = using_for[self.get_children()]
assert children and len(children) <= 2 assert children and len(children) <= 2
if len(children) == 2: if len(children) == 2:
new = parse_type(children[0], self) new = parse_type(children[0], self)
@ -119,15 +151,22 @@ class ContractSolc04(Contract):
def _analyze_enum(self, enum): def _analyze_enum(self, enum):
# Enum can be parsed in one pass # Enum can be parsed in one pass
name = enum['attributes']['name'] if self.is_compact_ast:
name = enum['name']
canonicalName = enum['canonicalName']
else:
name = enum['attributes'][self.get_key()]
if 'canonicalName' in enum['attributes']: if 'canonicalName' in enum['attributes']:
canonicalName = enum['attributes']['canonicalName'] canonicalName = enum['attributes']['canonicalName']
else: else:
canonicalName = self.name + '.' + name canonicalName = self.name + '.' + name
values = [] values = []
for child in enum['children']: for child in enum[self.get_children('members')]:
assert child['name'] == 'EnumValue' assert child[self.get_key()] == 'EnumValue'
values.append(child['attributes']['name']) if self.is_compact_ast:
values.append(child['name'])
else:
values.append(child['attributes'][self.get_key()])
new_enum = Enum(name, canonicalName, values) new_enum = Enum(name, canonicalName, values)
new_enum.set_contract(self) new_enum.set_contract(self)
@ -135,14 +174,19 @@ class ContractSolc04(Contract):
self._enums[canonicalName] = new_enum self._enums[canonicalName] = new_enum
def _parse_struct(self, struct): def _parse_struct(self, struct):
name = struct['attributes']['name'] if self.is_compact_ast:
if 'canonicalName' in struct['attributes']: name = struct['name']
canonicalName = struct['attributes']['canonicalName'] attributes = struct
else:
name = struct['attributes'][self.get_key()]
attributes = struct['attributes']
if 'canonicalName' in attributes:
canonicalName = attributes['canonicalName']
else: else:
canonicalName = self.name + '.' + name canonicalName = self.name + '.' + name
if 'children' in struct: if self.get_children('members') in struct:
children = struct['children'] children = struct[self.get_children('members')]
else: else:
children = [] # empty struct children = [] # empty struct
st = StructureSolc(name, canonicalName, children) st = StructureSolc(name, canonicalName, children)
@ -171,7 +215,7 @@ class ContractSolc04(Contract):
self._events.update(father.events_as_dict()) self._events.update(father.events_as_dict())
for event_to_parse in self._eventsNotParsed: for event_to_parse in self._eventsNotParsed:
event = EventSolc(event_to_parse) event = EventSolc(event_to_parse, self)
event.analyze(self) event.analyze(self)
event.set_contract(self) event.set_contract(self)
self._events[event.full_name] = event self._events[event.full_name] = event
@ -189,6 +233,12 @@ class ContractSolc04(Contract):
self._variables[var.name] = var self._variables[var.name] = var
def analyze_constant_state_variables(self):
for var in self.variables:
if var.is_constant:
var.analyze(self)
return
def analyze_state_variables(self): def analyze_state_variables(self):
for var in self.variables: for var in self.variables:
var.analyze(self) var.analyze(self)
@ -196,7 +246,7 @@ class ContractSolc04(Contract):
def _parse_modifier(self, modifier): def _parse_modifier(self, modifier):
modif = ModifierSolc(modifier) modif = ModifierSolc(modifier, self)
modif.set_contract(self) modif.set_contract(self)
modif.set_offset(modifier['src'], self.slither) modif.set_offset(modifier['src'], self.slither)
self._modifiers_no_params.append(modif) self._modifiers_no_params.append(modif)
@ -210,8 +260,7 @@ class ContractSolc04(Contract):
return return
def _parse_function(self, function): def _parse_function(self, function):
func = FunctionSolc(function) func = FunctionSolc(function, self)
func.set_contract(self)
func.set_offset(function['src'], self.slither) func.set_offset(function['src'], self.slither)
self._functions_no_params.append(func) self._functions_no_params.append(func)

@ -9,18 +9,30 @@ class EventSolc(Event):
Event class Event class
""" """
def __init__(self, event): def __init__(self, event, contract):
super(EventSolc, self).__init__() super(EventSolc, self).__init__()
self._name = event['attributes']['name'] self._contract = contract
self._elems = []
self._elems = []
if self.is_compact_ast:
self._name = event['name']
elems = event['parameters']
assert elems['nodeType'] == 'ParameterList'
self._elemsNotParsed = elems['parameters']
else:
self._name = event['attributes']['name']
elems = event['children'][0] elems = event['children'][0]
assert elems['name'] == 'ParameterList' assert elems['name'] == 'ParameterList'
if 'children' in elems: if 'children' in elems:
self._elemsNotParsed = elems['children'] self._elemsNotParsed = elems['children']
else: else:
self._elemsNotParsed = [] self._elemsNotParsed = []
@property
def is_compact_ast(self):
return self.contract.is_compact_ast
def analyze(self, contract): def analyze(self, contract):
for elem_to_parse in self._elemsNotParsed: for elem_to_parse in self._elemsNotParsed:
elem = EventVariableSolc(elem_to_parse) elem = EventVariableSolc(elem_to_parse)

@ -29,25 +29,46 @@ class FunctionSolc(Function):
""" """
# elems = [(type, name)] # elems = [(type, name)]
def __init__(self, function): def __init__(self, function, contract):
super(FunctionSolc, self).__init__() super(FunctionSolc, self).__init__()
self._name = function['attributes']['name'] self._contract = contract
if self.is_compact_ast:
self._name = function['name']
else:
self._name = function['attributes'][self.get_key()]
self._functionNotParsed = function self._functionNotParsed = function
self._params_was_analyzed = False self._params_was_analyzed = False
self._content_was_analyzed = False self._content_was_analyzed = False
self._counter_nodes = 0 self._counter_nodes = 0
def get_key(self):
return self.slither.get_key()
def get_children(self, key):
if self.is_compact_ast:
return key
return 'children'
@property
def is_compact_ast(self):
return self.slither.is_compact_ast
def _analyze_attributes(self): def _analyze_attributes(self):
if self.is_compact_ast:
attributes = self._functionNotParsed
else:
attributes = self._functionNotParsed['attributes'] attributes = self._functionNotParsed['attributes']
if 'payable' in attributes: if 'payable' in attributes:
self._payable = attributes['payable'] self._payable = attributes['payable']
elif 'stateMutability' in attributes: if 'stateMutability' in attributes:
if attributes['stateMutability'] == 'payable': if attributes['stateMutability'] == 'payable':
self._payable = True self._payable = True
elif attributes['stateMutability'] == 'pure': elif attributes['stateMutability'] == 'pure':
self._pure = True self._pure = True
self._view = True self._view = True
elif attributes['stateMutability'] == 'view':
self._view = True
if 'constant' in attributes: if 'constant' in attributes:
self._view = attributes['constant'] self._view = attributes['constant']
@ -80,43 +101,79 @@ class FunctionSolc(Function):
def _parse_if(self, ifStatement, node): def _parse_if(self, ifStatement, node):
# IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? # IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
falseStatement = None
children = ifStatement['children'] if self.is_compact_ast:
condition = ifStatement['condition']
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF) condition_node = self._new_node(NodeType.IF)
#condition = parse_expression(children[0], self) condition_node.add_unparsed_expression(condition)
link_nodes(node, condition_node)
trueStatement = self._parse_statement(ifStatement['trueBody'], condition_node)
if ifStatement['falseBody']:
falseStatement = self._parse_statement(ifStatement['falseBody'], condition_node)
else:
children = ifStatement[self.get_children('children')]
condition = children[0] condition = children[0]
# Note: check if the expression could be directly
# parsed here
condition_node = self._new_node(NodeType.IF)
condition_node.add_unparsed_expression(condition) condition_node.add_unparsed_expression(condition)
link_nodes(node, condition_node) link_nodes(node, condition_node)
trueStatement = self._parse_statement(children[1], condition_node) trueStatement = self._parse_statement(children[1], condition_node)
if len(children) == 3:
falseStatement = self._parse_statement(children[2], condition_node)
endIf_node = self._new_node(NodeType.ENDIF) endIf_node = self._new_node(NodeType.ENDIF)
link_nodes(trueStatement, endIf_node) link_nodes(trueStatement, endIf_node)
if len(children) == 3: if falseStatement:
falseStatement = self._parse_statement(children[2], condition_node)
link_nodes(falseStatement, endIf_node) link_nodes(falseStatement, endIf_node)
else: else:
link_nodes(condition_node, endIf_node) link_nodes(condition_node, endIf_node)
return endIf_node return endIf_node
# def _parse_if(self, ifStatement, node):
# # IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
#
# children = ifStatement[self.get_children('children')]
# condition_node = self._new_node(NodeType.IF)
# #condition = parse_expression(children[0], self)
# condition = children[0]
# condition_node.add_unparsed_expression(condition)
#
# link_nodes(node, condition_node)
#
# trueStatement = self._parse_statement(children[1], condition_node)
#
#
# endIf_node = self._new_node(NodeType.ENDIF)
# link_nodes(trueStatement, endIf_node)
#
# if len(children) == 3:
# falseStatement = self._parse_statement(children[2], condition_node)
#
# link_nodes(falseStatement, endIf_node)
#
# else:
# link_nodes(condition_node, endIf_node)
#
# return endIf_node
def _parse_while(self, whileStatement, node): def _parse_while(self, whileStatement, node):
# WhileStatement = 'while' '(' Expression ')' Statement # WhileStatement = 'while' '(' Expression ')' Statement
children = whileStatement['children']
node_startWhile = self._new_node(NodeType.STARTLOOP) node_startWhile = self._new_node(NodeType.STARTLOOP)
node_condition = self._new_node(NodeType.IFLOOP) node_condition = self._new_node(NodeType.IFLOOP)
#expression = parse_expression(children[0], self)
if self.is_compact_ast:
node_condition.add_unparsed_expression(whileStatement['condition'])
statement = self._parse_statement(whileStatement['body'], node_condition)
else:
children = whileStatement[self.get_children('children')]
expression = children[0] expression = children[0]
node_condition.add_unparsed_expression(expression) node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition) statement = self._parse_statement(children[1], node_condition)
node_endWhile = self._new_node(NodeType.ENDLOOP) node_endWhile = self._new_node(NodeType.ENDLOOP)
@ -128,9 +185,56 @@ class FunctionSolc(Function):
return node_endWhile return node_endWhile
def _parse_for_compact_ast(self, statement, node):
body = statement['body']
init_expression = statement['initializationExpression']
condition = statement['condition']
loop_expression = statement['loopExpression']
node_startLoop = self._new_node(NodeType.STARTLOOP)
node_endLoop = self._new_node(NodeType.ENDLOOP)
if init_expression:
node_init_expression = self._parse_statement(init_expression, node)
link_nodes(node_init_expression, node_startLoop)
else:
link_nodes(node, node_startLoop)
if condition:
node_condition = self._new_node(NodeType.IFLOOP)
node_condition.add_unparsed_expression(condition)
link_nodes(node_startLoop, node_condition)
link_nodes(node_condition, node_endLoop)
else:
node_condition = node_startLoop
node_body = self._parse_statement(body, node_condition)
if loop_expression:
node_LoopExpression = self._parse_statement(loop_expression, node_body)
link_nodes(node_LoopExpression, node_startLoop)
else:
link_nodes(node_body, node_startLoop)
if not condition:
if not loop_expression:
# TODO: fix case where loop has no expression
link_nodes(node_startLoop, node_endLoop)
else:
link_nodes(node_LoopExpression, node_endLoop)
return node_endLoop
def _parse_for(self, statement, node): def _parse_for(self, statement, node):
# ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement # ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
# the handling of loop in the legacy ast is too complex
# to integrate the comapct ast
# its cleaner to do it separately
if self.is_compact_ast:
return self._parse_for_compact_ast(statement, node)
hasInitExession = True hasInitExession = True
hasCondition = True hasCondition = True
hasLoopExpression = True hasLoopExpression = True
@ -153,11 +257,11 @@ class FunctionSolc(Function):
node_startLoop = self._new_node(NodeType.STARTLOOP) node_startLoop = self._new_node(NodeType.STARTLOOP)
node_endLoop = self._new_node(NodeType.ENDLOOP) node_endLoop = self._new_node(NodeType.ENDLOOP)
children = statement['children'] children = statement[self.get_children('children')]
if hasInitExession: if hasInitExession:
if len(children) >= 2: if len(children) >= 2:
if children[0]['name'] in ['VariableDefinitionStatement', if children[0][self.get_key()] in ['VariableDefinitionStatement',
'VariableDeclarationStatement', 'VariableDeclarationStatement',
'ExpressionStatement']: 'ExpressionStatement']:
node_initExpression = self._parse_statement(children[0], node) node_initExpression = self._parse_statement(children[0], node)
@ -176,7 +280,7 @@ class FunctionSolc(Function):
candidate = children[1] candidate = children[1]
else: else:
candidate = children[0] candidate = children[0]
if candidate['name'] not in ['VariableDefinitionStatement', if candidate[self.get_key()] not in ['VariableDefinitionStatement',
'VariableDeclarationStatement', 'VariableDeclarationStatement',
'ExpressionStatement']: 'ExpressionStatement']:
node_condition = self._new_node(NodeType.IFLOOP) node_condition = self._new_node(NodeType.IFLOOP)
@ -195,42 +299,45 @@ class FunctionSolc(Function):
node_LoopExpression = node_statement node_LoopExpression = node_statement
if hasLoopExpression: if hasLoopExpression:
if len(children) > 2: if len(children) > 2:
if children[-2]['name'] == 'ExpressionStatement': if children[-2][self.get_key()] == 'ExpressionStatement':
node_LoopExpression = self._parse_statement(children[-2], node_statement) node_LoopExpression = self._parse_statement(children[-2], node_statement)
if not hasCondition:
link_nodes(node_LoopExpression, node_endLoop)
if not hasCondition and not hasLoopExpression:
link_nodes(node, node_endLoop)
link_nodes(node_LoopExpression, node_startLoop) link_nodes(node_LoopExpression, node_startLoop)
return node_endLoop return node_endLoop
def _parse_dowhile(self, doWhilestatement, node): def _parse_dowhile(self, doWhilestatement, node):
children = doWhilestatement['children']
node_startDoWhile = self._new_node(NodeType.STARTLOOP) node_startDoWhile = self._new_node(NodeType.STARTLOOP)
node_condition = self._new_node(NodeType.IFLOOP)
if self.is_compact_ast:
node_condition.add_unparsed_expression(doWhilestatement['condition'])
statement = self._parse_statement(doWhilestatement['body'], node_condition)
else:
children = doWhilestatement[self.get_children('children')]
# same order in the AST as while # same order in the AST as while
node_condition = self._new_node(NodeType.IFLOOP)
#expression = parse_expression(children[0], self)
expression = children[0] expression = children[0]
node_condition.add_unparsed_expression(expression) node_condition.add_unparsed_expression(expression)
statement = self._parse_statement(children[1], node_condition) statement = self._parse_statement(children[1], node_condition)
node_endDoWhile = self._new_node(NodeType.ENDLOOP) node_endDoWhile = self._new_node(NodeType.ENDLOOP)
link_nodes(node, node_startDoWhile) link_nodes(node, node_startDoWhile)
link_nodes(node_startDoWhile, node_condition) link_nodes(node_startDoWhile, statement)
link_nodes(statement, node_condition) link_nodes(statement, node_condition)
link_nodes(node_condition, node_endDoWhile) link_nodes(node_condition, node_endDoWhile)
return node_endDoWhile return node_endDoWhile
def _parse_variable_definition(self, statement, node): def _parse_variable_definition(self, statement, node):
#assert len(statement['children']) == 1
# if there is, parse default value
#assert not 'attributes' in statement
try: try:
local_var = LocalVariableSolc(statement) local_var = LocalVariableSolc(statement)
#local_var = LocalVariableSolc(statement['children'][0], statement['children'][1::])
local_var.set_function(self) local_var.set_function(self)
local_var.set_offset(statement['src'], self.contract.slither) local_var.set_offset(statement['src'], self.contract.slither)
@ -243,10 +350,84 @@ class FunctionSolc(Function):
return new_node return new_node
except MultipleVariablesDeclaration: except MultipleVariablesDeclaration:
# Custom handling of var (a,b) = .. style declaration # Custom handling of var (a,b) = .. style declaration
if self.is_compact_ast:
variables = statement['declarations']
count = len(variables)
if statement['initialValue']['nodeType'] == 'TupleExpression':
inits = statement['initialValue']['components']
i = 0
new_node = node
for variable in variables:
init = inits[i]
src = variable['src']
i = i+1
new_statement = {'nodeType':'VariableDefinitionStatement',
'src': src,
'declarations':[variable],
'initialValue':init}
new_node = self._parse_variable_definition(new_statement, new_node)
else:
# If we have
# var (a, b) = f()
# we can split in multiple declarations, without init
# Then we craft one expression that does the assignment
variables = []
i = 0
new_node = node
for variable in statement['declarations']:
i = i+1
if variable:
src = variable['src']
# Create a fake statement to be consistent
new_statement = {'nodeType':'VariableDefinitionStatement',
'src': src,
'declarations':[variable]}
variables.append(variable)
new_node = self._parse_variable_definition_init_tuple(new_statement,
i,
new_node)
var_identifiers = []
# craft of the expression doing the assignement
for v in variables:
identifier = {
'nodeType':'Identifier',
'src': v['src'],
'name': v['name'],
'typeDescriptions': {
'typeString':v['typeDescriptions']['typeString']
}
}
var_identifiers.append(identifier)
tuple_expression = {'nodeType':'TupleExpression',
'src': statement['src'],
'components':var_identifiers}
expression = {
'nodeType' : 'Assignment',
'src':statement['src'],
'operator': '=',
'type':'tuple()',
'leftHandSide': tuple_expression,
'rightHandSide': statement['initialValue'],
'typeDescriptions': {'typeString':'tuple()'}
}
node = new_node
new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression)
link_nodes(node, new_node)
else:
count = 0 count = 0
children = statement['children'] children = statement[self.get_children('children')]
child = children[0] child = children[0]
while child['name'] == 'VariableDeclaration': while child[self.get_key()] == 'VariableDeclaration':
count = count +1 count = count +1
child = children[count] child = children[count]
@ -257,32 +438,32 @@ class FunctionSolc(Function):
variables_declaration = children[0:count] variables_declaration = children[0:count]
i = 0 i = 0
new_node = node new_node = node
if tuple_vars['name'] == 'TupleExpression': if tuple_vars[self.get_key()] == 'TupleExpression':
assert len(tuple_vars['children']) == count assert len(tuple_vars[self.get_children('children')]) == count
for variable in variables_declaration: for variable in variables_declaration:
init = tuple_vars['children'][i] init = tuple_vars[self.get_children('children')][i]
src = variable['src'] src = variable['src']
i= i+1 i = i+1
# Create a fake statement to be consistent # Create a fake statement to be consistent
new_statement = {'name':'VariableDefinitionStatement', new_statement = {self.get_key():'VariableDefinitionStatement',
'src': src, 'src': src,
'children':[variable, init]} self.get_children('children'):[variable, init]}
new_node = self._parse_variable_definition(new_statement, new_node) new_node = self._parse_variable_definition(new_statement, new_node)
else: else:
# If we have # If we have
# var (a, b) = f() # var (a, b) = f()
# we can split in multiple declarations, without init # we can split in multiple declarations, without init
# Then we craft one expression that does not assignment # Then we craft one expression that does the assignment
assert tuple_vars['name'] in ['FunctionCall', 'Conditional'] assert tuple_vars[self.get_key()] in ['FunctionCall', 'Conditional']
variables = [] variables = []
for variable in variables_declaration: for variable in variables_declaration:
src = variable['src'] src = variable['src']
i= i+1 i = i+1
# Create a fake statement to be consistent # Create a fake statement to be consistent
new_statement = {'name':'VariableDefinitionStatement', new_statement = {self.get_key():'VariableDefinitionStatement',
'src': src, 'src': src,
'children':[variable]} self.get_children('children'):[variable]}
variables.append(variable) variables.append(variable)
new_node = self._parse_variable_definition_init_tuple(new_statement, i, new_node) new_node = self._parse_variable_definition_init_tuple(new_statement, i, new_node)
@ -290,23 +471,23 @@ class FunctionSolc(Function):
# craft of the expression doing the assignement # craft of the expression doing the assignement
for v in variables: for v in variables:
identifier = { identifier = {
'name' : 'Identifier', self.get_key() : 'Identifier',
'src': v['src'], 'src': v['src'],
'attributes': { 'attributes': {
'value': v['attributes']['name'], 'value': v['attributes'][self.get_key()],
'type': v['attributes']['type']} 'type': v['attributes']['type']}
} }
var_identifiers.append(identifier) var_identifiers.append(identifier)
expression = { expression = {
'name' : 'Assignment', self.get_key() : 'Assignment',
'src':statement['src'], 'src':statement['src'],
'attributes': {'operator': '=', 'attributes': {'operator': '=',
'type':'tuple()'}, 'type':'tuple()'},
'children': self.get_children('children'):
[{'name': 'TupleExpression', [{self.get_key(): 'TupleExpression',
'src': statement['src'], 'src': statement['src'],
'children': var_identifiers}, self.get_children('children'): var_identifiers},
tuple_vars]} tuple_vars]}
node = new_node node = new_node
new_node = self._new_node(NodeType.EXPRESSION) new_node = self._new_node(NodeType.EXPRESSION)
@ -318,7 +499,7 @@ class FunctionSolc(Function):
def _parse_variable_definition_init_tuple(self, statement, index, node): def _parse_variable_definition_init_tuple(self, statement, index, node):
local_var = LocalVariableInitFromTupleSolc(statement, index) local_var = LocalVariableInitFromTupleSolc(statement, index)
#local_var = LocalVariableSolc(statement['children'][0], statement['children'][1::]) #local_var = LocalVariableSolc(statement[self.get_children('children')][0], statement[self.get_children('children')][1::])
local_var.set_function(self) local_var.set_function(self)
local_var.set_offset(statement['src'], self.contract.slither) local_var.set_offset(statement['src'], self.contract.slither)
@ -342,7 +523,7 @@ class FunctionSolc(Function):
# Throw | EmitStatement | SimpleStatement ) ';' # Throw | EmitStatement | SimpleStatement ) ';'
# SimpleStatement = VariableDefinition | ExpressionStatement # SimpleStatement = VariableDefinition | ExpressionStatement
name = statement['name'] name = statement[self.get_key()]
# SimpleStatement = VariableDefinition | ExpressionStatement # SimpleStatement = VariableDefinition | ExpressionStatement
if name == 'IfStatement': if name == 'IfStatement':
node = self._parse_if(statement, node) node = self._parse_if(statement, node)
@ -371,9 +552,13 @@ class FunctionSolc(Function):
elif name == 'Return': elif name == 'Return':
return_node = self._new_node(NodeType.RETURN) return_node = self._new_node(NodeType.RETURN)
link_nodes(node, return_node) link_nodes(node, return_node)
if 'children' in statement and statement['children']: if self.is_compact_ast:
assert len(statement['children']) == 1 if statement['expression']:
expression = statement['children'][0] return_node.add_unparsed_expression(statement['expression'])
else:
if self.get_children('children') in statement and statement[self.get_children('children')]:
assert len(statement[self.get_children('children')]) == 1
expression = statement[self.get_children('children')][0]
return_node.add_unparsed_expression(expression) return_node.add_unparsed_expression(expression)
node = return_node node = return_node
elif name == 'Throw': elif name == 'Throw':
@ -381,8 +566,11 @@ class FunctionSolc(Function):
link_nodes(node, throw_node) link_nodes(node, throw_node)
node = throw_node node = throw_node
elif name == 'EmitStatement': elif name == 'EmitStatement':
#expression = parse_expression(statement['children'][0], self) #expression = parse_expression(statement[self.get_children('children')][0], self)
expression = statement['children'][0] if self.is_compact_ast:
expression = statement['eventCall']
else:
expression = statement[self.get_children('children')][0]
new_node = self._new_node(NodeType.EXPRESSION) new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression) new_node.add_unparsed_expression(expression)
link_nodes(node, new_node) link_nodes(node, new_node)
@ -390,10 +578,13 @@ class FunctionSolc(Function):
elif name in ['VariableDefinitionStatement', 'VariableDeclarationStatement']: elif name in ['VariableDefinitionStatement', 'VariableDeclarationStatement']:
node = self._parse_variable_definition(statement, node) node = self._parse_variable_definition(statement, node)
elif name == 'ExpressionStatement': elif name == 'ExpressionStatement':
assert len(statement['children']) == 1 #assert len(statement[self.get_children('expression')]) == 1
assert not 'attributes' in statement #assert not 'attributes' in statement
#expression = parse_expression(statement['children'][0], self) #expression = parse_expression(statement[self.get_children('children')][0], self)
expression = statement['children'][0] if self.is_compact_ast:
expression = statement[self.get_children('expression')]
else:
expression = statement[self.get_children('expression')][0]
new_node = self._new_node(NodeType.EXPRESSION) new_node = self._new_node(NodeType.EXPRESSION)
new_node.add_unparsed_expression(expression) new_node.add_unparsed_expression(expression)
link_nodes(node, new_node) link_nodes(node, new_node)
@ -409,20 +600,30 @@ class FunctionSolc(Function):
Return: Return:
Node Node
''' '''
assert block['name'] == 'Block' assert block[self.get_key()] == 'Block'
for child in block['children']: if self.is_compact_ast:
node = self._parse_statement(child, node) statements = block['statements']
else:
statements = block[self.get_children('children')]
for statement in statements:
node = self._parse_statement(statement, node)
return node return node
def _parse_cfg(self, cfg): def _parse_cfg(self, cfg):
assert cfg['name'] == 'Block' assert cfg[self.get_key()] == 'Block'
node = self._new_node(NodeType.ENTRYPOINT) node = self._new_node(NodeType.ENTRYPOINT)
self._entry_point = node self._entry_point = node
if not cfg['children']: if self.is_compact_ast:
statements = cfg['statements']
else:
statements = cfg[self.get_children('children')]
if not statements:
self._is_empty = True self._is_empty = True
else: else:
self._is_empty = False self._is_empty = False
@ -464,7 +665,7 @@ class FunctionSolc(Function):
end_node = self._find_end_loop(node, []) end_node = self._find_end_loop(node, [])
if not end_node: if not end_node:
logger.error('Break in no-loop context {}'.format(node.nodeId())) logger.error('Break in no-loop context {}'.format(node))
exit(-1) exit(-1)
for son in node.sons: for son in node.sons:
@ -527,10 +728,15 @@ class FunctionSolc(Function):
self._nodes = [n for n in self.nodes if not n in to_remove] self._nodes = [n for n in self.nodes if not n in to_remove]
# #
def _parse_params(self, params): def _parse_params(self, params):
assert params[self.get_key()] == 'ParameterList'
assert params['name'] == 'ParameterList' if self.is_compact_ast:
for param in params['children']: params = params['parameters']
assert param['name'] == 'VariableDeclaration' else:
params = params[self.get_children('children')]
for param in params:
assert param[self.get_key()] == 'VariableDeclaration'
local_var = LocalVariableSolc(param) local_var = LocalVariableSolc(param)
@ -547,9 +753,15 @@ class FunctionSolc(Function):
def _parse_returns(self, returns): def _parse_returns(self, returns):
assert returns['name'] == 'ParameterList' assert returns[self.get_key()] == 'ParameterList'
for ret in returns['children']:
assert ret['name'] == 'VariableDeclaration' if self.is_compact_ast:
returns = returns['parameters']
else:
returns = returns[self.get_children('children')]
for ret in returns:
assert ret[self.get_key()] == 'VariableDeclaration'
local_var = LocalVariableSolc(ret) local_var = LocalVariableSolc(ret)
@ -580,8 +792,11 @@ class FunctionSolc(Function):
self._analyze_attributes() self._analyze_attributes()
children = self._functionNotParsed['children'] if self.is_compact_ast:
params = self._functionNotParsed['parameters']
returns = self._functionNotParsed['returnParameters']
else:
children = self._functionNotParsed[self.get_children('children')]
params = children[0] params = children[0]
returns = children[1] returns = children[1]
@ -596,15 +811,26 @@ class FunctionSolc(Function):
self._content_was_analyzed = True self._content_was_analyzed = True
children = self._functionNotParsed['children'] if self.is_compact_ast:
body = self._functionNotParsed['body']
if body and body[self.get_key()] == 'Block':
self._is_implemented = True
self._parse_cfg(body)
for modifier in self._functionNotParsed['modifiers']:
self._parse_modifier(modifier)
else:
children = self._functionNotParsed[self.get_children('children')]
self._is_implemented = False self._is_implemented = False
for child in children[2:]: for child in children[2:]:
if child['name'] == 'Block': if child[self.get_key()] == 'Block':
self._is_implemented = True self._is_implemented = True
self._parse_cfg(child) self._parse_cfg(child)
continue continue
assert child['name'] == 'ModifierInvocation' assert child[self.get_key()] == 'ModifierInvocation'
self._parse_modifier(child) self._parse_modifier(child)
@ -630,13 +856,13 @@ class FunctionSolc(Function):
break break
self._remove_alone_endif() self._remove_alone_endif()
self._analyze_read_write()
self._analyze_calls()
def convert_expression_to_slithir(self): def convert_expression_to_slithir(self):
for node in self.nodes: for node in self.nodes:
node.slithir_generation() node.slithir_generation()
transform_slithir_vars_to_ssa(self) transform_slithir_vars_to_ssa(self)
self._analyze_read_write()
self._analyze_calls()
def split_ternary_node(self, node, condition, true_expr, false_expr): def split_ternary_node(self, node, condition, true_expr, false_expr):

@ -19,8 +19,10 @@ class ModifierSolc(Modifier, FunctionSolc):
self._analyze_attributes() self._analyze_attributes()
if self.is_compact_ast:
params = self._functionNotParsed['parameters']
else:
children = self._functionNotParsed['children'] children = self._functionNotParsed['children']
params = children[0] params = children[0]
if params: if params:
@ -32,6 +34,15 @@ class ModifierSolc(Modifier, FunctionSolc):
self._content_was_analyzed = True self._content_was_analyzed = True
if self.is_compact_ast:
body = self._functionNotParsed['body']
if body and body[self.get_key()] == 'Block':
self._is_implemented = True
self._parse_cfg(body)
else:
children = self._functionNotParsed['children'] children = self._functionNotParsed['children']
self._isImplemented = False self._isImplemented = False
@ -39,7 +50,7 @@ class ModifierSolc(Modifier, FunctionSolc):
assert len(children) == 2 assert len(children) == 2
block = children[1] block = children[1]
assert block['name'] == 'Block' assert block['name'] == 'Block'
self._isImplemented = False self._isImplemented = True
self._parse_cfg(block) self._parse_cfg(block)
for local_vars in self.variables: for local_vars in self.variables:
@ -52,7 +63,7 @@ class ModifierSolc(Modifier, FunctionSolc):
self._analyze_calls() self._analyze_calls()
def _parse_statement(self, statement, node): def _parse_statement(self, statement, node):
name = statement['name'] name = statement[self.get_key()]
if name == 'PlaceholderStatement': if name == 'PlaceholderStatement':
placeholder_node = self._new_node(NodeType.PLACEHOLDER) placeholder_node = self._new_node(NodeType.PLACEHOLDER)
link_nodes(node, placeholder_node) link_nodes(node, placeholder_node)

@ -127,16 +127,30 @@ def find_variable(var_name, caller_context):
def parse_call(expression, caller_context): def parse_call(expression, caller_context):
attributes = expression['attributes']
if caller_context.is_compact_ast:
attributes = expression
type_conversion = expression['kind'] == 'typeConversion'
type_return = attributes['typeDescriptions']['typeString']
else:
attributes = expression['attributes']
type_conversion = attributes['type_conversion'] type_conversion = attributes['type_conversion']
type_return = attributes['type']
children = expression['children']
if type_conversion: if type_conversion:
assert len(children) == 2 type_call = parse_type(UnknownType(type_return), caller_context)
type_call = parse_type(UnknownType(attributes['type']), caller_context)
if caller_context.is_compact_ast:
type_info = expression['expression']
assert len(expression['arguments']) == 1
expression_to_parse = expression['arguments'][0]
else:
children = expression['children']
assert len(children) == 2
type_info = children[0] type_info = children[0]
expression_to_parse = children[1]
assert type_info['name'] in ['ElementaryTypenameExpression', assert type_info['name'] in ['ElementaryTypenameExpression',
'ElementaryTypeNameExpression', 'ElementaryTypeNameExpression',
'Identifier', 'Identifier',
@ -144,26 +158,36 @@ def parse_call(expression, caller_context):
'IndexAccess', 'IndexAccess',
'MemberAccess'] 'MemberAccess']
expression = parse_expression(children[1], caller_context) expression = parse_expression(expression_to_parse, caller_context)
t = TypeConversion(expression, type_call) t = TypeConversion(expression, type_call)
return t return t
assert children if caller_context.is_compact_ast:
called = parse_expression(expression['expression'], caller_context)
type_call = attributes['type'] arguments = []
if expression['arguments']:
arguments = [parse_expression(a, caller_context) for a in expression['arguments']]
else:
children = expression['children']
called = parse_expression(children[0], caller_context) called = parse_expression(children[0], caller_context)
arguments = [parse_expression(a, caller_context) for a in children[1::]] arguments = [parse_expression(a, caller_context) for a in children[1::]]
if isinstance(called, SuperCallExpression): if isinstance(called, SuperCallExpression):
return SuperCallExpression(called, arguments, type_call) return SuperCallExpression(called, arguments, type_return)
return CallExpression(called, arguments, type_call) return CallExpression(called, arguments, type_return)
def parse_super_name(expression): def parse_super_name(expression, is_compact_ast):
if is_compact_ast:
assert expression['nodeType'] == 'MemberAccess'
attributes = expression
base_name = expression['memberName']
arguments = expression['typeDescriptions']['typeString']
else:
assert expression['name'] == 'MemberAccess' assert expression['name'] == 'MemberAccess'
attributes = expression['attributes'] attributes = expression['attributes']
base_name = attributes['member_name'] base_name = attributes['member_name']
arguments = attributes['type'] arguments = attributes['type']
assert arguments.startswith('function ') assert arguments.startswith('function ')
# remove function (...() # remove function (...()
arguments = arguments[len('function '):] arguments = arguments[len('function '):]
@ -237,22 +261,36 @@ def parse_expression(expression, caller_context):
# | PrimaryExpression # | PrimaryExpression
# The AST naming does not follow the spec # The AST naming does not follow the spec
name = expression['name'] name = expression[caller_context.get_key()]
is_compact_ast = caller_context.is_compact_ast
if name == 'UnaryOperation': if name == 'UnaryOperation':
if is_compact_ast:
attributes = expression
else:
attributes = expression['attributes'] attributes = expression['attributes']
assert 'prefix' in attributes assert 'prefix' in attributes
operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix']) operation_type = UnaryOperationType.get_type(attributes['operator'], attributes['prefix'])
if is_compact_ast:
expression = parse_expression(expression['subExpression'], caller_context)
else:
assert len(expression['children']) == 1 assert len(expression['children']) == 1
expression = parse_expression(expression['children'][0], caller_context) expression = parse_expression(expression['children'][0], caller_context)
unary_op = UnaryOperation(expression, operation_type) unary_op = UnaryOperation(expression, operation_type)
return unary_op return unary_op
elif name == 'BinaryOperation': elif name == 'BinaryOperation':
if is_compact_ast:
attributes = expression
else:
attributes = expression['attributes'] attributes = expression['attributes']
operation_type = BinaryOperationType.get_type(attributes['operator']) operation_type = BinaryOperationType.get_type(attributes['operator'])
if is_compact_ast:
left_expression = parse_expression(expression['leftExpression'], caller_context)
right_expression = parse_expression(expression['rightExpression'], caller_context)
else:
assert len(expression['children']) == 2 assert len(expression['children']) == 2
left_expression = parse_expression(expression['children'][0], caller_context) left_expression = parse_expression(expression['children'][0], caller_context)
right_expression = parse_expression(expression['children'][1], caller_context) right_expression = parse_expression(expression['children'][1], caller_context)
@ -274,6 +312,9 @@ def parse_expression(expression, caller_context):
Note: this is only possible with Solidity >= 0.4.12 Note: this is only possible with Solidity >= 0.4.12
""" """
if is_compact_ast:
expressions = [parse_expression(e, caller_context) if e else None for e in expression['components']]
else:
if 'children' not in expression : if 'children' not in expression :
attributes = expression['attributes'] attributes = expression['attributes']
components = attributes['components'] components = attributes['components']
@ -294,22 +335,34 @@ def parse_expression(expression, caller_context):
return t return t
elif name == 'Conditional': elif name == 'Conditional':
if is_compact_ast:
if_expression = parse_expression(expression['condition'], caller_context)
then_expression = parse_expression(expression['trueExpression'], caller_context)
else_expression = parse_expression(expression['falseExpression'], caller_context)
else:
children = expression['children'] children = expression['children']
assert len(children) == 3 assert len(children) == 3
if_expression = parse_expression(children[0], caller_context) if_expression = parse_expression(children[0], caller_context)
then_expression = parse_expression(children[1], caller_context) then_expression = parse_expression(children[1], caller_context)
else_expression = parse_expression(children[2], caller_context) else_expression = parse_expression(children[2], caller_context)
conditional = ConditionalExpression(if_expression, then_expression, else_expression) conditional = ConditionalExpression(if_expression, then_expression, else_expression)
#print(conditional)
return conditional return conditional
elif name == 'Assignment': elif name == 'Assignment':
if is_compact_ast:
left_expression = parse_expression(expression['leftHandSide'], caller_context)
right_expression = parse_expression(expression['rightHandSide'], caller_context)
operation_type = AssignmentOperationType.get_type(expression['operator'])
operation_return_type = expression['typeDescriptions']['typeString']
else:
attributes = expression['attributes'] attributes = expression['attributes']
children = expression['children'] children = expression['children']
assert len(expression['children']) == 2 assert len(expression['children']) == 2
left_expression = parse_expression(children[0], caller_context) left_expression = parse_expression(children[0], caller_context)
right_expression = parse_expression(children[1], caller_context) right_expression = parse_expression(children[1], caller_context)
operation_type = AssignmentOperationType.get_type(attributes['operator']) operation_type = AssignmentOperationType.get_type(attributes['operator'])
operation_return_type = attributes['type'] operation_return_type = attributes['type']
@ -318,6 +371,12 @@ def parse_expression(expression, caller_context):
elif name == 'Literal': elif name == 'Literal':
assert 'children' not in expression assert 'children' not in expression
if is_compact_ast:
value = expression['value']
if not value:
value = '0x'+expression['hexValue']
else:
value = expression['attributes']['value'] value = expression['attributes']['value']
if value is None: if value is None:
# for literal declared as hex # for literal declared as hex
@ -329,9 +388,17 @@ def parse_expression(expression, caller_context):
elif name == 'Identifier': elif name == 'Identifier':
assert 'children' not in expression assert 'children' not in expression
t = None
if caller_context.is_compact_ast:
value = expression['name']
t = expression['typeDescriptions']['typeString']
else:
value = expression['attributes']['value'] value = expression['attributes']['value']
if 'type' in expression['attributes']: if 'type' in expression['attributes']:
t = expression['attributes']['type'] t = expression['attributes']['type']
if t: if t:
found = re.findall('[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t) found = re.findall('[struct|enum|function|modifier] \(([\[\] ()a-zA-Z0-9\.,_]*)\)', t)
assert len(found) <= 1 assert len(found) <= 1
@ -345,22 +412,34 @@ def parse_expression(expression, caller_context):
return identifier return identifier
elif name == 'IndexAccess': elif name == 'IndexAccess':
if is_compact_ast:
index_type = expression['typeDescriptions']['typeString']
left = expression['baseExpression']
right = expression['indexExpression']
else:
index_type = expression['attributes']['type'] index_type = expression['attributes']['type']
children = expression['children'] children = expression['children']
assert len(children) == 2 assert len(children) == 2
left_expression = parse_expression(children[0], caller_context) left = children[0]
right_expression = parse_expression(children[1], caller_context) right = children[1]
left_expression = parse_expression(left, caller_context)
right_expression = parse_expression(right, caller_context)
index = IndexAccess(left_expression, right_expression, index_type) index = IndexAccess(left_expression, right_expression, index_type)
return index return index
elif name == 'MemberAccess': elif name == 'MemberAccess':
if caller_context.is_compact_ast:
member_name = expression['memberName']
member_type = expression['typeDescriptions']['typeString']
member_expression = parse_expression(expression['expression'], caller_context)
else:
member_name = expression['attributes']['member_name'] member_name = expression['attributes']['member_name']
member_type = expression['attributes']['type'] member_type = expression['attributes']['type']
children = expression['children'] children = expression['children']
assert len(children) == 1 assert len(children) == 1
member_expression = parse_expression(children[0], caller_context) member_expression = parse_expression(children[0], caller_context)
if str(member_expression) == 'super': if str(member_expression) == 'super':
super_name = parse_super_name(expression) super_name = parse_super_name(expression, is_compact_ast)
if isinstance(caller_context, Contract): if isinstance(caller_context, Contract):
inheritance = caller_context.inheritance inheritance = caller_context.inheritance
else: else:
@ -384,6 +463,9 @@ def parse_expression(expression, caller_context):
elif name == 'ElementaryTypeNameExpression': elif name == 'ElementaryTypeNameExpression':
# nop exression # nop exression
# uint; # uint;
if is_compact_ast:
value = expression['typeName']
else:
assert 'children' not in expression assert 'children' not in expression
value = expression['attributes']['value'] value = expression['attributes']['value']
t = parse_type(UnknownType(value), caller_context) t = parse_type(UnknownType(value), caller_context)
@ -393,45 +475,65 @@ def parse_expression(expression, caller_context):
# NewExpression is not a root expression, it's always the child of another expression # NewExpression is not a root expression, it's always the child of another expression
elif name == 'NewExpression': elif name == 'NewExpression':
new_type = expression['attributes']['type']
if is_compact_ast:
type_name = expression['typeName']
else:
children = expression['children'] children = expression['children']
assert len(children) == 1 assert len(children) == 1
#new_expression = parse_expression(children[0]) type_name = children[0]
child = children[0]
if child['name'] == 'ArrayTypeName': if type_name[caller_context.get_key()] == 'ArrayTypeName':
depth = 0 depth = 0
while child['name'] == 'ArrayTypeName': while type_name[caller_context.get_key()] == 'ArrayTypeName':
# Note: dont conserve the size of the array if provided # Note: dont conserve the size of the array if provided
#assert len(child['children']) == 1 # We compute it directly
child = child['children'][0] if is_compact_ast:
type_name = type_name['baseType']
else:
type_name = type_name['children'][0]
depth += 1 depth += 1
if type_name[caller_context.get_key()] == 'ElementaryTypeName':
if child['name'] == 'ElementaryTypeName': if is_compact_ast:
array_type = ElementaryType(child['attributes']['name']) array_type = ElementaryType(type_name['name'])
elif child['name'] == 'UserDefinedTypeName': else:
array_type = parse_type(UnknownType(child['attributes']['name']), caller_context) array_type = ElementaryType(type_name['attributes']['name'])
elif type_name[caller_context.get_key()] == 'UserDefinedTypeName':
if is_compact_ast:
array_type = parse_type(UnknownType(type_name['name']), caller_context)
else:
array_type = parse_type(UnknownType(type_name['attributes']['name']), caller_context)
else: else:
logger.error('Incorrect type array {}'.format(child)) logger.error('Incorrect type array {}'.format(type_name))
exit(-1) exit(-1)
array = NewArray(depth, array_type) array = NewArray(depth, array_type)
return array return array
if child['name'] == 'ElementaryTypeName': if type_name[caller_context.get_key()] == 'ElementaryTypeName':
elem_type = ElementaryType(child['attributes']['name']) if is_compact_ast:
elem_type = ElementaryType(type_name['name'])
else:
elem_type = ElementaryType(type_name['attributes']['name'])
new_elem = NewElementaryType(elem_type) new_elem = NewElementaryType(elem_type)
return new_elem return new_elem
assert child['name'] == 'UserDefinedTypeName' assert type_name[caller_context.get_key()] == 'UserDefinedTypeName'
contract_name = child['attributes']['name'] if is_compact_ast:
contract_name = type_name['name']
else:
contract_name = type_name['attributes']['name']
new = NewContract(contract_name) new = NewContract(contract_name)
return new return new
elif name == 'ModifierInvocation': elif name == 'ModifierInvocation':
if is_compact_ast:
called = parse_expression(expression['modifierName'], caller_context)
arguments = []
if expression['arguments']:
arguments = [parse_expression(a, caller_context) for a in expression['arguments']]
else:
children = expression['children'] children = expression['children']
called = parse_expression(children[0], caller_context) called = parse_expression(children[0], caller_context)
arguments = [parse_expression(a, caller_context) for a in children[1::]] arguments = [parse_expression(a, caller_context) for a in children[1::]]

@ -18,7 +18,29 @@ class SlitherSolc(Slither):
self._contracts_by_id = {} self._contracts_by_id = {}
self._analyzed = False self._analyzed = False
self._is_compact_ast = False
def get_key(self):
if self._is_compact_ast:
return 'nodeType'
return 'name'
def get_children(self):
if self._is_compact_ast:
return 'nodes'
return 'children'
@property
def is_compact_ast(self):
return self._is_compact_ast
def _parse_contracts_from_json(self, json_data): def _parse_contracts_from_json(self, json_data):
try:
data_loaded = json.loads(json_data)
self._parse_contracts_from_loaded_json(data_loaded['ast'], data_loaded['sourcePath'])
return True
except ValueError:
first = json_data.find('{') first = json_data.find('{')
if first != -1: if first != -1:
last = json_data.rfind('}') + 1 last = json_data.rfind('}') + 1
@ -26,50 +48,64 @@ class SlitherSolc(Slither):
json_data = json_data[first:last] json_data = json_data[first:last]
data_loaded = json.loads(json_data) data_loaded = json.loads(json_data)
self._parse_contracts_from_loaded_json(data_loaded, filename)
return True
return False
def _parse_contracts_from_loaded_json(self, data_loaded, filename):
if 'nodeType' in data_loaded:
self._is_compact_ast = True
if data_loaded['name'] == 'root': if data_loaded[self.get_key()] == 'root':
self._solc_version = '0.3' self._solc_version = '0.3'
logger.error('solc <0.4 is not supported') logger.error('solc <0.4 is not supported')
return return
elif data_loaded['name'] == 'SourceUnit': elif data_loaded[self.get_key()] == 'SourceUnit':
self._solc_version = '0.4' self._solc_version = '0.4'
self._parse_source_unit(data_loaded, filename) self._parse_source_unit(data_loaded, filename)
else: else:
logger.error('solc version is not supported') logger.error('solc version is not supported')
return return
for contract_data in data_loaded['children']: for contract_data in data_loaded[self.get_children()]:
# if self.solc_version == '0.3': # if self.solc_version == '0.3':
# assert contract_data['name'] == 'Contract' # assert contract_data[self.get_key()] == 'Contract'
# contract = ContractSolc03(self, contract_data) # contract = ContractSolc03(self, contract_data)
if self.solc_version == '0.4': if self.solc_version == '0.4':
assert contract_data['name'] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective'] assert contract_data[self.get_key()] in ['ContractDefinition', 'PragmaDirective', 'ImportDirective']
if contract_data['name'] == 'ContractDefinition': if contract_data[self.get_key()] == 'ContractDefinition':
contract = ContractSolc04(self, contract_data) contract = ContractSolc04(self, contract_data)
if 'src' in contract_data: if 'src' in contract_data:
contract.set_offset(contract_data['src'], self) contract.set_offset(contract_data['src'], self)
self._contractsNotParsed.append(contract) self._contractsNotParsed.append(contract)
elif contract_data['name'] == 'PragmaDirective': elif contract_data[self.get_key()] == 'PragmaDirective':
if self._is_compact_ast:
pragma = Pragma(contract_data['literals'])
else:
pragma = Pragma(contract_data['attributes']["literals"]) pragma = Pragma(contract_data['attributes']["literals"])
pragma.set_offset(contract_data['src'], self) pragma.set_offset(contract_data['src'], self)
self._pragma_directives.append(pragma) self._pragma_directives.append(pragma)
elif contract_data['name'] == 'ImportDirective': elif contract_data[self.get_key()] == 'ImportDirective':
if self.is_compact_ast:
import_directive = Import(contract_data["absolutePath"])
else:
import_directive = Import(contract_data['attributes']["absolutePath"]) import_directive = Import(contract_data['attributes']["absolutePath"])
import_directive.set_offset(contract_data['src'], self) import_directive.set_offset(contract_data['src'], self)
self._import_directives.append(import_directive) self._import_directives.append(import_directive)
return True
return False
def _parse_source_unit(self, data, filename): def _parse_source_unit(self, data, filename):
if data['name'] != 'SourceUnit': if data[self.get_key()] != 'SourceUnit':
return -1 # handle solc prior 0.3.6 return -1 # handle solc prior 0.3.6
# match any char for filename # match any char for filename
# filename can contain space, /, -, .. # filename can contain space, /, -, ..
name = re.findall('=* (.+) =*', filename) name = re.findall('=* (.+) =*', filename)
if name:
assert len(name) == 1 assert len(name) == 1
name = name[0] name = name[0]
else:
name =filename
sourceUnit = -1 # handle old solc, or error sourceUnit = -1 # handle old solc, or error
if 'src' in data: if 'src' in data:
@ -215,6 +251,9 @@ class SlitherSolc(Slither):
contract.set_is_analyzed(True) contract.set_is_analyzed(True)
def _analyze_struct_events(self, contract): def _analyze_struct_events(self, contract):
contract.analyze_constant_state_variables()
# Struct can refer to enum, or state variables # Struct can refer to enum, or state variables
contract.analyze_structs() contract.analyze_structs()
# Event can refer to struct # Event can refer to struct

@ -41,6 +41,8 @@ def _find_from_type_name(name, contract, contracts, structures, enums):
name_contract = name name_contract = name
if name_contract.startswith('contract '): if name_contract.startswith('contract '):
name_contract = name_contract[len('contract '):] name_contract = name_contract[len('contract '):]
if name_contract.startswith('library '):
name_contract = name_contract[len('library '):]
var_type = next((c for c in contracts if c.name == name_contract), None) var_type = next((c for c in contracts if c.name == name_contract), None)
if not var_type: if not var_type:
@ -131,6 +133,14 @@ def parse_type(t, caller_context):
logger.error('Incorrect caller context') logger.error('Incorrect caller context')
exit(-1) exit(-1)
is_compact_ast = caller_context.is_compact_ast
if is_compact_ast:
key = 'nodeType'
else:
key = 'name'
structures = contract.structures structures = contract.structures
enums = contract.enums enums = contract.enums
contracts = contract.slither.contracts contracts = contract.slither.contracts
@ -138,14 +148,23 @@ def parse_type(t, caller_context):
if isinstance(t, UnknownType): if isinstance(t, UnknownType):
return _find_from_type_name(t.name, contract, contracts, structures, enums) return _find_from_type_name(t.name, contract, contracts, structures, enums)
elif t['name'] == 'ElementaryTypeName': elif t[key] == 'ElementaryTypeName':
return ElementaryType(t['attributes']['name']) if is_compact_ast:
return ElementaryType(t['name'])
return ElementaryType(t['attributes'][key])
elif t['name'] == 'UserDefinedTypeName': elif t[key] == 'UserDefinedTypeName':
return _find_from_type_name(t['attributes']['name'], contract, contracts, structures, enums) if is_compact_ast:
return _find_from_type_name(t['typeDescriptions']['typeString'], contract, contracts, structures, enums)
return _find_from_type_name(t['attributes'][key], contract, contracts, structures, enums)
elif t['name'] == 'ArrayTypeName': elif t[key] == 'ArrayTypeName':
length = None length = None
if is_compact_ast:
if t['length']:
length = parse_expression(t['length'], caller_context)
array_type = parse_type(t['baseType'], contract)
else:
if len(t['children']) == 2: if len(t['children']) == 2:
length = parse_expression(t['children'][1], caller_context) length = parse_expression(t['children'][1], caller_context)
else: else:
@ -153,8 +172,12 @@ def parse_type(t, caller_context):
array_type = parse_type(t['children'][0], contract) array_type = parse_type(t['children'][0], contract)
return ArrayType(array_type, length) return ArrayType(array_type, length)
elif t['name'] == 'Mapping': elif t[key] == 'Mapping':
if is_compact_ast:
mappingFrom = parse_type(t['keyType'], contract)
mappingTo = parse_type(t['valueType'], contract)
else:
assert len(t['children']) == 2 assert len(t['children']) == 2
mappingFrom = parse_type(t['children'][0], contract) mappingFrom = parse_type(t['children'][0], contract)
@ -162,23 +185,29 @@ def parse_type(t, caller_context):
return MappingType(mappingFrom, mappingTo) return MappingType(mappingFrom, mappingTo)
elif t['name'] == 'FunctionTypeName': elif t[key] == 'FunctionTypeName':
assert len(t['children']) == 2
if is_compact_ast:
params = t['parameterTypes']
return_values = t['returnParameterTypes']
index = 'parameters'
else:
assert len(t['children']) == 2
params = t['children'][0] params = t['children'][0]
return_values = t['children'][1] return_values = t['children'][1]
index = 'children'
assert params['name'] == 'ParameterList' assert params[key] == 'ParameterList'
assert return_values['name'] == 'ParameterList' assert return_values[key] == 'ParameterList'
params_vars = [] params_vars = []
return_values_vars = [] return_values_vars = []
for p in params['children']: for p in params[index]:
var = FunctionTypeVariableSolc(p) var = FunctionTypeVariableSolc(p)
var.set_offset(p['src'], caller_context.slither) var.set_offset(p['src'], caller_context.slither)
var.analyze(caller_context) var.analyze(caller_context)
params_vars.append(var) params_vars.append(var)
for p in return_values['children']: for p in return_values[index]:
var = FunctionTypeVariableSolc(p) var = FunctionTypeVariableSolc(p)
var.set_offset(p['src'], caller_context.slither) var.set_offset(p['src'], caller_context.slither)

@ -33,7 +33,28 @@ class VariableDeclarationSolc(Variable):
self._elem_to_parse = None self._elem_to_parse = None
self._initializedNotParsed = None self._initializedNotParsed = None
if var['name'] in ['VariableDeclarationStatement', 'VariableDefinitionStatement']: self._is_compact_ast = False
if 'nodeType' in var:
self._is_compact_ast = True
nodeType = var['nodeType']
if nodeType in ['VariableDeclarationStatement', 'VariableDefinitionStatement']:
if len(var['declarations'])>1:
raise MultipleVariablesDeclaration
init = None
if 'initialValue' in var:
init = var['initialValue']
self._init_from_declaration(var['declarations'][0], init)
elif nodeType == 'VariableDeclaration':
self._init_from_declaration(var, var['value'])
else:
logger.error('Incorrect variable declaration type {}'.format(nodeType))
exit(-1)
else:
nodeType = var['name']
if nodeType in ['VariableDeclarationStatement', 'VariableDefinitionStatement']:
if len(var['children']) == 2: if len(var['children']) == 2:
init = var['children'][1] init = var['children'][1]
elif len(var['children']) == 1: elif len(var['children']) == 1:
@ -45,10 +66,10 @@ class VariableDeclarationSolc(Variable):
exit(-1) exit(-1)
declaration = var['children'][0] declaration = var['children'][0]
self._init_from_declaration(declaration, init) self._init_from_declaration(declaration, init)
elif var['name'] == 'VariableDeclaration': elif nodeType == 'VariableDeclaration':
self._init_from_declaration(var, None) self._init_from_declaration(var, None)
else: else:
logger.error('Incorrect variable declaration type {}'.format(var['name'])) logger.error('Incorrect variable declaration type {}'.format(nodeType))
exit(-1) exit(-1)
@property @property
@ -66,13 +87,17 @@ class VariableDeclarationSolc(Variable):
self._visibility = 'internal' self._visibility = 'internal'
def _init_from_declaration(self, var, init): def _init_from_declaration(self, var, init):
if self._is_compact_ast:
attributes = var
self._typeName = attributes['typeDescriptions']['typeString']
else:
assert len(var['children']) <= 2 assert len(var['children']) <= 2
assert var['name'] == 'VariableDeclaration' assert var['name'] == 'VariableDeclaration'
attributes = var['attributes'] attributes = var['attributes']
self._name = attributes['name']
self._typeName = attributes['type'] self._typeName = attributes['type']
self._name = attributes['name']
self._arrayDepth = 0 self._arrayDepth = 0
self._isMapping = False self._isMapping = False
self._mappingFrom = None self._mappingFrom = None
@ -85,6 +110,12 @@ class VariableDeclarationSolc(Variable):
self._analyze_variable_attributes(attributes) self._analyze_variable_attributes(attributes)
if self._is_compact_ast:
if var['typeName']:
self._elem_to_parse = var['typeName']
else:
self._elem_to_parse = UnknownType(var['typeDescriptions']['typeString'])
else:
if not var['children']: if not var['children']:
# It happens on variable declared inside loop declaration # It happens on variable declared inside loop declaration
try: try:
@ -95,6 +126,11 @@ class VariableDeclarationSolc(Variable):
else: else:
self._elem_to_parse = var['children'][0] self._elem_to_parse = var['children'][0]
if self._is_compact_ast:
self._initializedNotParsed = init
if init:
self._initialized = True
else:
if init: # there are two way to init a var local in the AST if init: # there are two way to init a var local in the AST
assert len(var['children']) <= 1 assert len(var['children']) <= 1
self._initialized = True self._initialized = True

@ -28,6 +28,9 @@ def f_call(e, x):
def f_expression(e, x): def f_expression(e, x):
e._expression = x e._expression = x
def f_called(e, x):
e._called = x
class SplitTernaryExpression(object): class SplitTernaryExpression(object):
def __init__(self, expression): def __init__(self, expression):
@ -63,9 +66,18 @@ class SplitTernaryExpression(object):
if isinstance(expression, ConditionalExpression): if isinstance(expression, ConditionalExpression):
raise Exception('Nested ternary operator not handled') raise Exception('Nested ternary operator not handled')
if isinstance(expression, (Literal, Identifier, IndexAccess, MemberAccess)): if isinstance(expression, (Literal, Identifier, IndexAccess)):
return None return None
# case of lib
# (.. ? .. : ..).add
if isinstance(expression, MemberAccess):
next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(next_expr,
true_expression.expression,
false_expression.expression)
elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)): elif isinstance(expression, (AssignmentOperation, BinaryOperation, TupleExpression)):
true_expression._expressions = [] true_expression._expressions = []
false_expression._expressions = [] false_expression._expressions = []
@ -73,12 +85,19 @@ class SplitTernaryExpression(object):
for next_expr in expression.expressions: for next_expr in expression.expressions:
if self.apply_copy(next_expr, true_expression, false_expression, f_expressions): if self.apply_copy(next_expr, true_expression, false_expression, f_expressions):
# always on last arguments added # always on last arguments added
self.copy_expression(next_expr, true_expression.expressions[-1], false_expression.expressions[-1]) self.copy_expression(next_expr,
true_expression.expressions[-1],
false_expression.expressions[-1])
elif isinstance(expression, CallExpression): elif isinstance(expression, CallExpression):
next_expr = expression.called next_expr = expression.called
true_expression._called = copy.copy(next_expr)
false_expression._called = copy.copy(next_expr) # case of lib
# (.. ? .. : ..).add
if self.apply_copy(next_expr, true_expression, false_expression, f_called):
self.copy_expression(next_expr,
true_expression.called,
false_expression.called)
true_expression._arguments = [] true_expression._arguments = []
false_expression._arguments = [] false_expression._arguments = []
@ -86,12 +105,16 @@ class SplitTernaryExpression(object):
for next_expr in expression.arguments: for next_expr in expression.arguments:
if self.apply_copy(next_expr, true_expression, false_expression, f_call): if self.apply_copy(next_expr, true_expression, false_expression, f_call):
# always on last arguments added # always on last arguments added
self.copy_expression(next_expr, true_expression.arguments[-1], false_expression.arguments[-1]) self.copy_expression(next_expr,
true_expression.arguments[-1],
false_expression.arguments[-1])
elif isinstance(expression, TypeConversion): elif isinstance(expression, TypeConversion):
next_expr = expression.expression next_expr = expression.expression
if self.apply_copy(next_expr, true_expression, false_expression, f_expression): if self.apply_copy(next_expr, true_expression, false_expression, f_expression):
self.copy_expression(expression.expression, true_expression.expression, false_expression.expression) self.copy_expression(expression.expression,
true_expression.expression,
false_expression.expression)
else: else:
raise Exception('Ternary operation not handled {}'.format(type(expression))) raise Exception('Ternary operation not handled {}'.format(type(expression)))

Loading…
Cancel
Save