mirror of https://github.com/crytic/slither
parent
64ecdbec9a
commit
aae3a0e642
@ -0,0 +1,24 @@ |
|||||||
|
from slither.core.solidity_types import ElementaryType |
||||||
|
from slither.slithir.operations.lvalue import OperationWithLValue |
||||||
|
from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue |
||||||
|
|
||||||
|
|
||||||
|
class CodeSize(OperationWithLValue): |
||||||
|
|
||||||
|
def __init__(self, value, lvalue): |
||||||
|
assert is_valid_rvalue(value) |
||||||
|
assert is_valid_lvalue(lvalue) |
||||||
|
self._value = value |
||||||
|
self._lvalue = lvalue |
||||||
|
lvalue.set_type(ElementaryType('uint256')) |
||||||
|
|
||||||
|
@property |
||||||
|
def read(self): |
||||||
|
return [self._value] |
||||||
|
|
||||||
|
@property |
||||||
|
def value(self): |
||||||
|
return self._value |
||||||
|
|
||||||
|
def __str__(self): |
||||||
|
return "{} -> CODESIZE {}".format(self.lvalue, self.value) |
@ -0,0 +1,268 @@ |
|||||||
|
from slither.core.declarations.solidity_variables import SOLIDITY_FUNCTIONS |
||||||
|
from slither.core.expressions import BinaryOperationType, UnaryOperationType |
||||||
|
|
||||||
|
# taken from https://github.com/ethereum/solidity/blob/356cc91084114f840da66804b2a9fc1ac2846cff/libevmasm/Instruction.cpp#L180 |
||||||
|
evm_opcodes = [ |
||||||
|
"STOP", |
||||||
|
"ADD", |
||||||
|
"SUB", |
||||||
|
"MUL", |
||||||
|
"DIV", |
||||||
|
"SDIV", |
||||||
|
"MOD", |
||||||
|
"SMOD", |
||||||
|
"EXP", |
||||||
|
"NOT", |
||||||
|
"LT", |
||||||
|
"GT", |
||||||
|
"SLT", |
||||||
|
"SGT", |
||||||
|
"EQ", |
||||||
|
"ISZERO", |
||||||
|
"AND", |
||||||
|
"OR", |
||||||
|
"XOR", |
||||||
|
"BYTE", |
||||||
|
"SHL", |
||||||
|
"SHR", |
||||||
|
"SAR", |
||||||
|
"ADDMOD", |
||||||
|
"MULMOD", |
||||||
|
"SIGNEXTEND", |
||||||
|
"KECCAK256", |
||||||
|
"ADDRESS", |
||||||
|
"BALANCE", |
||||||
|
"ORIGIN", |
||||||
|
"CALLER", |
||||||
|
"CALLVALUE", |
||||||
|
"CALLDATALOAD", |
||||||
|
"CALLDATASIZE", |
||||||
|
"CALLDATACOPY", |
||||||
|
"CODESIZE", |
||||||
|
"CODECOPY", |
||||||
|
"GASPRICE", |
||||||
|
"EXTCODESIZE", |
||||||
|
"EXTCODECOPY", |
||||||
|
"RETURNDATASIZE", |
||||||
|
"RETURNDATACOPY", |
||||||
|
"EXTCODEHASH", |
||||||
|
"BLOCKHASH", |
||||||
|
"COINBASE", |
||||||
|
"TIMESTAMP", |
||||||
|
"NUMBER", |
||||||
|
"DIFFICULTY", |
||||||
|
"GASLIMIT", |
||||||
|
"CHAINID", |
||||||
|
"SELFBALANCE", |
||||||
|
"POP", |
||||||
|
"MLOAD", |
||||||
|
"MSTORE", |
||||||
|
"MSTORE8", |
||||||
|
"SLOAD", |
||||||
|
"SSTORE", |
||||||
|
"JUMP", |
||||||
|
"JUMPI", |
||||||
|
"PC", |
||||||
|
"MSIZE", |
||||||
|
"GAS", |
||||||
|
"JUMPDEST", |
||||||
|
"PUSH1", |
||||||
|
"PUSH2", |
||||||
|
"PUSH3", |
||||||
|
"PUSH4", |
||||||
|
"PUSH5", |
||||||
|
"PUSH6", |
||||||
|
"PUSH7", |
||||||
|
"PUSH8", |
||||||
|
"PUSH9", |
||||||
|
"PUSH10", |
||||||
|
"PUSH11", |
||||||
|
"PUSH12", |
||||||
|
"PUSH13", |
||||||
|
"PUSH14", |
||||||
|
"PUSH15", |
||||||
|
"PUSH16", |
||||||
|
"PUSH17", |
||||||
|
"PUSH18", |
||||||
|
"PUSH19", |
||||||
|
"PUSH20", |
||||||
|
"PUSH21", |
||||||
|
"PUSH22", |
||||||
|
"PUSH23", |
||||||
|
"PUSH24", |
||||||
|
"PUSH25", |
||||||
|
"PUSH26", |
||||||
|
"PUSH27", |
||||||
|
"PUSH28", |
||||||
|
"PUSH29", |
||||||
|
"PUSH30", |
||||||
|
"PUSH31", |
||||||
|
"PUSH32", |
||||||
|
"DUP1", |
||||||
|
"DUP2", |
||||||
|
"DUP3", |
||||||
|
"DUP4", |
||||||
|
"DUP5", |
||||||
|
"DUP6", |
||||||
|
"DUP7", |
||||||
|
"DUP8", |
||||||
|
"DUP9", |
||||||
|
"DUP10", |
||||||
|
"DUP11", |
||||||
|
"DUP12", |
||||||
|
"DUP13", |
||||||
|
"DUP14", |
||||||
|
"DUP15", |
||||||
|
"DUP16", |
||||||
|
"SWAP1", |
||||||
|
"SWAP2", |
||||||
|
"SWAP3", |
||||||
|
"SWAP4", |
||||||
|
"SWAP5", |
||||||
|
"SWAP6", |
||||||
|
"SWAP7", |
||||||
|
"SWAP8", |
||||||
|
"SWAP9", |
||||||
|
"SWAP10", |
||||||
|
"SWAP11", |
||||||
|
"SWAP12", |
||||||
|
"SWAP13", |
||||||
|
"SWAP14", |
||||||
|
"SWAP15", |
||||||
|
"SWAP16", |
||||||
|
"LOG0", |
||||||
|
"LOG1", |
||||||
|
"LOG2", |
||||||
|
"LOG3", |
||||||
|
"LOG4", |
||||||
|
"CREATE", |
||||||
|
"CALL", |
||||||
|
"CALLCODE", |
||||||
|
"STATICCALL", |
||||||
|
"RETURN", |
||||||
|
"DELEGATECALL", |
||||||
|
"CREATE2", |
||||||
|
"REVERT", |
||||||
|
"INVALID", |
||||||
|
"SELFDESTRUCT", |
||||||
|
] |
||||||
|
|
||||||
|
yul_funcs = [ |
||||||
|
"datasize", |
||||||
|
"dataoffset", |
||||||
|
"datacopy", |
||||||
|
"setimmutable", |
||||||
|
"loadimmutable", |
||||||
|
] |
||||||
|
|
||||||
|
builtins = [x.lower() for x in evm_opcodes if not ( |
||||||
|
x.startswith("PUSH") or |
||||||
|
x.startswith("SWAP") or |
||||||
|
x.startswith("DUP") or |
||||||
|
x == "JUMP" or |
||||||
|
x == "JUMPI" or |
||||||
|
x == "JUMPDEST" |
||||||
|
)] + yul_funcs |
||||||
|
|
||||||
|
function_args = { |
||||||
|
'byte': [2, 1], |
||||||
|
'addmod': [3, 1], |
||||||
|
'mulmod': [3, 1], |
||||||
|
'signextend': [2, 1], |
||||||
|
'keccak256': [2, 1], |
||||||
|
'pc': [0, 1], |
||||||
|
'pop': [1, 0], |
||||||
|
'mload': [1, 1], |
||||||
|
'mstore': [2, 0], |
||||||
|
'mstore8': [2, 0], |
||||||
|
'sload': [1, 1], |
||||||
|
'sstore': [2, 0], |
||||||
|
'msize': [1, 1], |
||||||
|
'gas': [0, 1], |
||||||
|
'address': [0, 1], |
||||||
|
'balance': [1, 1], |
||||||
|
'selfbalance': [0, 1], |
||||||
|
'caller': [0, 1], |
||||||
|
'callvalue': [0, 1], |
||||||
|
'calldataload': [1, 1], |
||||||
|
'calldatasize': [0, 1], |
||||||
|
'calldatacopy': [3, 0], |
||||||
|
'codesize': [0, 1], |
||||||
|
'codecopy': [3, 0], |
||||||
|
'extcodesize': [1, 1], |
||||||
|
'extcodecopy': [4, 0], |
||||||
|
'returndatasize': [0, 1], |
||||||
|
'returndatacopy': [3, 0], |
||||||
|
'extcodehash': [1, 1], |
||||||
|
'create': [3, 1], |
||||||
|
'create2': [4, 1], |
||||||
|
'call': [7, 1], |
||||||
|
'callcode': [7, 1], |
||||||
|
'delegatecall': [6, 1], |
||||||
|
'staticcall': [6, 1], |
||||||
|
'return': [2, 0], |
||||||
|
'revert': [2, 0], |
||||||
|
'selfdestruct': [1, 0], |
||||||
|
'invalid': [0, 0], |
||||||
|
'log0': [2, 0], |
||||||
|
'log1': [3, 0], |
||||||
|
'log2': [4, 0], |
||||||
|
'log3': [5, 0], |
||||||
|
'log4': [6, 0], |
||||||
|
'chainid': [0, 1], |
||||||
|
'origin': [0, 1], |
||||||
|
'gasprice': [0, 1], |
||||||
|
'blockhash': [1, 1], |
||||||
|
'coinbase': [0, 1], |
||||||
|
'timestamp': [0, 1], |
||||||
|
'number': [0, 1], |
||||||
|
'difficulty': [0, 1], |
||||||
|
'gaslimit': [0, 1], |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
def format_function_descriptor(name): |
||||||
|
if name not in function_args: |
||||||
|
return name + "()" |
||||||
|
|
||||||
|
return name + "(" + ",".join(["uint256"] * function_args[name][0]) + ")" |
||||||
|
|
||||||
|
|
||||||
|
for k, v in function_args.items(): |
||||||
|
SOLIDITY_FUNCTIONS[format_function_descriptor(k)] = ['uint256'] * v[1] |
||||||
|
|
||||||
|
unary_ops = { |
||||||
|
'not': UnaryOperationType.TILD, |
||||||
|
'iszero': UnaryOperationType.BANG, |
||||||
|
} |
||||||
|
|
||||||
|
binary_ops = { |
||||||
|
'add': BinaryOperationType.ADDITION, |
||||||
|
'sub': BinaryOperationType.SUBTRACTION, |
||||||
|
'mul': BinaryOperationType.MULTIPLICATION, |
||||||
|
'div': BinaryOperationType.DIVISION, |
||||||
|
'sdiv': BinaryOperationType.DIVISION_SIGNED, |
||||||
|
'mod': BinaryOperationType.MODULO, |
||||||
|
'smod': BinaryOperationType.MODULO_SIGNED, |
||||||
|
'exp': BinaryOperationType.POWER, |
||||||
|
'lt': BinaryOperationType.LESS, |
||||||
|
'gt': BinaryOperationType.GREATER, |
||||||
|
'slt': BinaryOperationType.LESS_SIGNED, |
||||||
|
'sgt': BinaryOperationType.GREATER_SIGNED, |
||||||
|
'eq': BinaryOperationType.EQUAL, |
||||||
|
'and': BinaryOperationType.AND, |
||||||
|
'or': BinaryOperationType.OR, |
||||||
|
'xor': BinaryOperationType.CARET, |
||||||
|
'shl': BinaryOperationType.LEFT_SHIFT, |
||||||
|
'shr': BinaryOperationType.RIGHT_SHIFT, |
||||||
|
'sar': BinaryOperationType.RIGHT_SHIFT_ARITHMETIC, |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class YulBuiltin: |
||||||
|
def __init__(self, name): |
||||||
|
self._name = name |
||||||
|
|
||||||
|
@property |
||||||
|
def name(self): |
||||||
|
return self._name |
@ -0,0 +1,525 @@ |
|||||||
|
import json |
||||||
|
|
||||||
|
from slither.core.cfg.node import NodeType, link_nodes |
||||||
|
from slither.core.declarations import Function, SolidityFunction, SolidityVariable |
||||||
|
from slither.core.expressions import ( |
||||||
|
Literal, |
||||||
|
AssignmentOperation, |
||||||
|
AssignmentOperationType, |
||||||
|
Identifier, CallExpression, TupleExpression, BinaryOperation, UnaryOperation, |
||||||
|
) |
||||||
|
from slither.core.solidity_types import ElementaryType |
||||||
|
from slither.core.variables.local_variable import LocalVariable |
||||||
|
from slither.exceptions import SlitherException |
||||||
|
from slither.solc_parsing.yul.evm_functions import * |
||||||
|
|
||||||
|
|
||||||
|
class YulLocalVariable(LocalVariable): |
||||||
|
|
||||||
|
def __init__(self, ast): |
||||||
|
super(LocalVariable, self).__init__() |
||||||
|
|
||||||
|
assert (ast['nodeType'] == 'YulTypedName') |
||||||
|
self._name = ast['name'] |
||||||
|
self._type = ElementaryType('uint256') |
||||||
|
|
||||||
|
self._location = 'memory' |
||||||
|
|
||||||
|
|
||||||
|
class YulFunction(Function): |
||||||
|
|
||||||
|
def __init__(self, ast, root): |
||||||
|
super(YulFunction, self).__init__() |
||||||
|
|
||||||
|
assert (ast['nodeType'] == 'YulFunctionDefinition') |
||||||
|
|
||||||
|
self._contract = root.function.contract |
||||||
|
self._contract_declarer = root.function.contract_declarer |
||||||
|
|
||||||
|
self._name = ast['name'] |
||||||
|
self._scope = root.yul_path |
||||||
|
self._counter_nodes = 0 |
||||||
|
|
||||||
|
self._is_implemented = True |
||||||
|
self._contains_assembly = True |
||||||
|
|
||||||
|
self._node_solc = root.function.node_solc() |
||||||
|
|
||||||
|
self._entry_point = self.new_node(NodeType.ASSEMBLY, ast['src']) |
||||||
|
self._entry_point.set_yul_child(root, ast['name']) |
||||||
|
|
||||||
|
self._ast = ast |
||||||
|
self.set_offset(ast['src'], root.function.slither) |
||||||
|
|
||||||
|
def convert_body(self): |
||||||
|
node = self.new_node(NodeType.ENTRYPOINT, self._ast['src']) |
||||||
|
link_nodes(self.entry_point, node) |
||||||
|
|
||||||
|
for param in self._ast.get('parameters', []): |
||||||
|
node = convert_yul(self.entry_point, node, param) |
||||||
|
self._parameters.append(self.entry_point.get_yul_local_variable_from_name(param['name'])) |
||||||
|
|
||||||
|
for ret in self._ast.get('returnVariables', []): |
||||||
|
node = convert_yul(self.entry_point, node, ret) |
||||||
|
self._returns.append(self.entry_point.get_yul_local_variable_from_name(ret['name'])) |
||||||
|
|
||||||
|
convert_yul(self.entry_point, node, self._ast['body']) |
||||||
|
|
||||||
|
def parse_body(self): |
||||||
|
for node in self.nodes: |
||||||
|
node.analyze_expressions(self) |
||||||
|
|
||||||
|
def node_solc(self): |
||||||
|
return self._node_solc |
||||||
|
|
||||||
|
def new_node(self, node_type, src): |
||||||
|
node = self._node_solc(node_type, self._counter_nodes) |
||||||
|
node.set_offset(src, self.slither) |
||||||
|
node.set_function(self) |
||||||
|
self._counter_nodes += 1 |
||||||
|
self._nodes.append(node) |
||||||
|
return node |
||||||
|
|
||||||
|
|
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
# region Block conversion |
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
|
||||||
|
""" |
||||||
|
The functions in this region, at a high level, will extract the control flow |
||||||
|
structures and metadata from the input AST. These include things like function |
||||||
|
definitions and local variables. |
||||||
|
|
||||||
|
Each function takes three parameters: |
||||||
|
1) root is a NodeSolc of NodeType.ASSEMBLY, and stores information at the |
||||||
|
local scope. In Yul, variables are scoped to the function they're |
||||||
|
declared in (except for variables outside the assembly block) |
||||||
|
2) parent is the last node in the CFG. new nodes should be linked against |
||||||
|
this node |
||||||
|
3) ast is a dictionary and is the current node in the Yul ast being converted |
||||||
|
|
||||||
|
Each function must return a single parameter: |
||||||
|
1) A NodeSolc representing the new end of the CFG |
||||||
|
|
||||||
|
The entrypoint is the function at the end of this region, `convert_yul`, which |
||||||
|
dispatches to a specialized function based on a lookup dictionary. |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_block(root, parent, ast): |
||||||
|
for statement in ast["statements"]: |
||||||
|
parent = convert_yul(root, parent, statement) |
||||||
|
return parent |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_function_definition(root, parent, ast): |
||||||
|
f = YulFunction(ast, root) |
||||||
|
|
||||||
|
root.function.contract._functions[root.format_canonical_yul_name(f.name)] = f |
||||||
|
|
||||||
|
f.convert_body() |
||||||
|
f.parse_body() |
||||||
|
|
||||||
|
return parent |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_variable_declaration(root, parent, ast): |
||||||
|
for variable_ast in ast['variables']: |
||||||
|
parent = convert_yul(root, parent, variable_ast) |
||||||
|
|
||||||
|
node = parent.function.new_node(NodeType.EXPRESSION, ast["src"]) |
||||||
|
node.add_unparsed_yul_expression(root, ast) |
||||||
|
link_nodes(parent, node) |
||||||
|
|
||||||
|
return node |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_assignment(root, parent, ast): |
||||||
|
node = parent.function.new_node(NodeType.EXPRESSION, ast["src"]) |
||||||
|
node.add_unparsed_yul_expression(root, ast) |
||||||
|
link_nodes(parent, node) |
||||||
|
return node |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_expression_statement(root, parent, ast): |
||||||
|
src = ast['src'] |
||||||
|
expression_ast = ast['expression'] |
||||||
|
|
||||||
|
expression = parent.function.new_node(NodeType.EXPRESSION, src) |
||||||
|
expression.add_unparsed_yul_expression(root, expression_ast) |
||||||
|
link_nodes(parent, expression) |
||||||
|
|
||||||
|
return expression |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_if(root, parent, ast): |
||||||
|
# we're cheating and pretending that yul supports if/else so we can convert switch cleaner |
||||||
|
|
||||||
|
src = ast['src'] |
||||||
|
condition_ast = ast['condition'] |
||||||
|
true_body_ast = ast['body'] |
||||||
|
false_body_ast = ast['false_body'] if 'false_body' in ast else None |
||||||
|
|
||||||
|
condition = parent.function.new_node(NodeType.IF, src) |
||||||
|
end = parent.function.new_node(NodeType.ENDIF, src) |
||||||
|
|
||||||
|
condition.add_unparsed_yul_expression(root, condition_ast) |
||||||
|
|
||||||
|
true_body = convert_yul(root, condition, true_body_ast) |
||||||
|
|
||||||
|
if false_body_ast: |
||||||
|
false_body = convert_yul(root, condition, false_body_ast) |
||||||
|
link_nodes(false_body, end) |
||||||
|
else: |
||||||
|
link_nodes(condition, end) |
||||||
|
|
||||||
|
link_nodes(parent, condition) |
||||||
|
link_nodes(true_body, end) |
||||||
|
|
||||||
|
return end |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_switch(root, parent, ast): |
||||||
|
""" |
||||||
|
This is unfortunate. We don't really want a switch in our IR so we're going to |
||||||
|
translate it into a series of if/else statements. |
||||||
|
""" |
||||||
|
cases_ast = ast['cases'] |
||||||
|
expression_ast = ast['expression'] |
||||||
|
|
||||||
|
# this variable stores the result of the expression so we don't accidentally compute it more than once |
||||||
|
switch_expr_var = 'switch_expr_{}'.format(ast['src'].replace(':', '_')) |
||||||
|
|
||||||
|
rewritten_switch = { |
||||||
|
'nodeType': 'YulBlock', |
||||||
|
'src': ast['src'], |
||||||
|
'statements': [ |
||||||
|
{ |
||||||
|
'nodeType': 'YulVariableDeclaration', |
||||||
|
'src': expression_ast['src'], |
||||||
|
'variables': [ |
||||||
|
{ |
||||||
|
'nodeType': 'YulTypedName', |
||||||
|
'src': expression_ast['src'], |
||||||
|
'name': switch_expr_var, |
||||||
|
'type': '', |
||||||
|
}, |
||||||
|
], |
||||||
|
'value': expression_ast, |
||||||
|
}, |
||||||
|
], |
||||||
|
} |
||||||
|
|
||||||
|
last_if = None |
||||||
|
|
||||||
|
default_ast = None |
||||||
|
|
||||||
|
for case_ast in cases_ast: |
||||||
|
body_ast = case_ast['body'] |
||||||
|
value_ast = case_ast['value'] |
||||||
|
|
||||||
|
if value_ast == 'default': |
||||||
|
default_ast = case_ast |
||||||
|
continue |
||||||
|
|
||||||
|
current_if = { |
||||||
|
'nodeType': 'YulIf', |
||||||
|
'src': case_ast['src'], |
||||||
|
'condition': { |
||||||
|
'nodeType': 'YulFunctionCall', |
||||||
|
'src': case_ast['src'], |
||||||
|
'functionName': { |
||||||
|
'nodeType': 'YulIdentifier', |
||||||
|
'src': case_ast['src'], |
||||||
|
'name': 'eq', |
||||||
|
}, |
||||||
|
'arguments': [ |
||||||
|
{ |
||||||
|
'nodeType': 'YulIdentifier', |
||||||
|
'src': case_ast['src'], |
||||||
|
'name': switch_expr_var, |
||||||
|
}, |
||||||
|
value_ast, |
||||||
|
] |
||||||
|
}, |
||||||
|
'body': body_ast, |
||||||
|
} |
||||||
|
|
||||||
|
if last_if: |
||||||
|
last_if['false_body'] = current_if |
||||||
|
else: |
||||||
|
rewritten_switch['statements'].append(current_if) |
||||||
|
|
||||||
|
last_if = current_if |
||||||
|
|
||||||
|
if default_ast: |
||||||
|
body_ast = default_ast['body'] |
||||||
|
|
||||||
|
if last_if: |
||||||
|
last_if['false_body'] = body_ast |
||||||
|
else: |
||||||
|
rewritten_switch['statements'].append(body_ast) |
||||||
|
|
||||||
|
return convert_yul(root, parent, rewritten_switch) |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_for_loop(root, parent, ast): |
||||||
|
pre_ast = ast['pre'] |
||||||
|
condition_ast = ast['condition'] |
||||||
|
post_ast = ast['post'] |
||||||
|
body_ast = ast['body'] |
||||||
|
|
||||||
|
start_loop = parent.function.new_node(NodeType.STARTLOOP, ast['src']) |
||||||
|
end_loop = parent.function.new_node(NodeType.ENDLOOP, ast['src']) |
||||||
|
|
||||||
|
link_nodes(parent, start_loop) |
||||||
|
|
||||||
|
pre = convert_yul(root, start_loop, pre_ast) |
||||||
|
|
||||||
|
condition = parent.function.new_node(NodeType.IFLOOP, condition_ast['src']) |
||||||
|
condition.add_unparsed_yul_expression(root, condition_ast) |
||||||
|
link_nodes(pre, condition) |
||||||
|
|
||||||
|
link_nodes(condition, end_loop) |
||||||
|
|
||||||
|
body = convert_yul(root, condition, body_ast) |
||||||
|
|
||||||
|
post = convert_yul(root, body, post_ast) |
||||||
|
|
||||||
|
link_nodes(post, condition) |
||||||
|
|
||||||
|
return end_loop |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_break(root, parent, ast): |
||||||
|
break_ = parent.function.new_node(NodeType.BREAK, ast['src']) |
||||||
|
link_nodes(parent, break_) |
||||||
|
return break_ |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_continue(root, parent, ast): |
||||||
|
continue_ = parent.function.new_node(NodeType.CONTINUE, ast['src']) |
||||||
|
link_nodes(parent, continue_) |
||||||
|
return continue_ |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_leave(root, parent, ast): |
||||||
|
leave = parent.function.new_node(NodeType.RETURN, ast['src']) |
||||||
|
link_nodes(parent, leave) |
||||||
|
return leave |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_typed_name(root, parent, ast): |
||||||
|
var = YulLocalVariable(ast) |
||||||
|
var.set_function(root.function) |
||||||
|
var.set_offset(ast['src'], root.slither) |
||||||
|
|
||||||
|
root.add_yul_local_variable(var) |
||||||
|
|
||||||
|
node = parent.function.new_node(NodeType.VARIABLE, ast['src']) |
||||||
|
node.add_variable_declaration(var) |
||||||
|
link_nodes(parent, node) |
||||||
|
|
||||||
|
return node |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul_unsupported(root, parent, ast): |
||||||
|
raise SlitherException(f"no converter available for {ast['nodeType']} {json.dumps(ast, indent=2)}") |
||||||
|
|
||||||
|
|
||||||
|
def convert_yul(root, parent, ast): |
||||||
|
return converters.get(ast['nodeType'], convert_yul_unsupported)(root, parent, ast) |
||||||
|
|
||||||
|
|
||||||
|
converters = { |
||||||
|
'YulBlock': convert_yul_block, |
||||||
|
'YulFunctionDefinition': convert_yul_function_definition, |
||||||
|
'YulVariableDeclaration': convert_yul_variable_declaration, |
||||||
|
'YulAssignment': convert_yul_assignment, |
||||||
|
'YulExpressionStatement': convert_yul_expression_statement, |
||||||
|
'YulIf': convert_yul_if, |
||||||
|
'YulSwitch': convert_yul_switch, |
||||||
|
'YulForLoop': convert_yul_for_loop, |
||||||
|
'YulBreak': convert_yul_break, |
||||||
|
'YulContinue': convert_yul_continue, |
||||||
|
'YulLeave': convert_yul_leave, |
||||||
|
'YulTypedName': convert_yul_typed_name, |
||||||
|
} |
||||||
|
|
||||||
|
# endregion |
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
|
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
# region Expression parsing |
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
|
||||||
|
""" |
||||||
|
The functions in this region parse the AST into expressions. |
||||||
|
|
||||||
|
Each function takes three parameters: |
||||||
|
1) root is the same root as above |
||||||
|
2) node is the CFG node which stores this expression |
||||||
|
3) ast is the same ast as above |
||||||
|
|
||||||
|
Each function must return a single parameter: |
||||||
|
1) The operation that was parsed, or None |
||||||
|
|
||||||
|
The entrypoint is the function at the end of this region, `parse_yul`, which |
||||||
|
dispatches to a specialized function based on a lookup dictionary. |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
def _parse_yul_assignment_common(root, node, ast, key): |
||||||
|
lhs = [parse_yul(root, node, arg) for arg in ast[key]] |
||||||
|
rhs = parse_yul(root, node, ast['value']) |
||||||
|
|
||||||
|
return AssignmentOperation(vars_to_val(lhs), rhs, AssignmentOperationType.ASSIGN, vars_to_typestr(lhs)) |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_variable_declaration(root, node, ast): |
||||||
|
""" |
||||||
|
We already created variables in the conversion phase, so just do |
||||||
|
the assignment |
||||||
|
""" |
||||||
|
|
||||||
|
if not ast['value']: |
||||||
|
return None |
||||||
|
|
||||||
|
return _parse_yul_assignment_common(root, node, ast, 'variables') |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_assignment(root, node, ast): |
||||||
|
return _parse_yul_assignment_common(root, node, ast, 'variableNames') |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_function_call(root, node, ast): |
||||||
|
args = [parse_yul(root, node, arg) for arg in ast['arguments']] |
||||||
|
ident = parse_yul(root, node, ast['functionName']) |
||||||
|
|
||||||
|
if isinstance(ident.value, YulBuiltin): |
||||||
|
name = ident.value.name |
||||||
|
if name in binary_ops: |
||||||
|
if name in ['shl', 'shr', 'sar']: |
||||||
|
# lmao ok |
||||||
|
return BinaryOperation(args[1], args[0], binary_ops[name]) |
||||||
|
|
||||||
|
return BinaryOperation(args[0], args[1], binary_ops[name]) |
||||||
|
|
||||||
|
if name in unary_ops: |
||||||
|
return UnaryOperation(args[0], unary_ops[name]) |
||||||
|
|
||||||
|
ident = Identifier(SolidityFunction(format_function_descriptor(ident.value.name))) |
||||||
|
|
||||||
|
if isinstance(ident.value, Function): |
||||||
|
return CallExpression(ident, args, vars_to_typestr(ident.value.returns)) |
||||||
|
elif isinstance(ident.value, SolidityFunction): |
||||||
|
return CallExpression(ident, args, vars_to_typestr(ident.value.return_type)) |
||||||
|
else: |
||||||
|
raise SlitherException(f"unexpected function call target type {str(type(ident.value))}") |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_identifier(root, node, ast): |
||||||
|
name = ast['name'] |
||||||
|
|
||||||
|
if name in builtins: |
||||||
|
return Identifier(YulBuiltin(name)) |
||||||
|
|
||||||
|
# check function-scoped variables |
||||||
|
variable = root.function.get_local_variable_from_name(name) |
||||||
|
if variable: |
||||||
|
return Identifier(variable) |
||||||
|
|
||||||
|
# check yul-scoped variable |
||||||
|
variable = root.get_yul_local_variable_from_name(name) |
||||||
|
if variable: |
||||||
|
return Identifier(variable) |
||||||
|
|
||||||
|
# check yul-scoped function |
||||||
|
# note that a function can recurse into itself, so we have two canonical names |
||||||
|
# to check (but only one of them can be valid) |
||||||
|
|
||||||
|
functions = root.function.contract_declarer._functions |
||||||
|
|
||||||
|
canonical_name = root.format_canonical_yul_name(name) |
||||||
|
if canonical_name in functions: |
||||||
|
return Identifier(functions[canonical_name]) |
||||||
|
|
||||||
|
canonical_name = root.format_canonical_yul_name(name, -1) |
||||||
|
if canonical_name in functions: |
||||||
|
return Identifier(functions[canonical_name]) |
||||||
|
|
||||||
|
# check for magic suffixes |
||||||
|
if name.endswith("_slot"): |
||||||
|
potential_name = name[:-5] |
||||||
|
var = root.function.contract.get_state_variable_from_name(potential_name) |
||||||
|
if var: |
||||||
|
return Identifier(SolidityVariable(name)) |
||||||
|
if name.endswith("_offset"): |
||||||
|
potential_name = name[:-7] |
||||||
|
var = root.function.contract.get_state_variable_from_name(potential_name) |
||||||
|
if var: |
||||||
|
return Identifier(SolidityVariable(name)) |
||||||
|
|
||||||
|
raise SlitherException(f"unresolved reference to identifier {name}") |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_literal(root, node, ast): |
||||||
|
type_ = ast['type'] |
||||||
|
value = ast['value'] |
||||||
|
|
||||||
|
if not type_: |
||||||
|
type_ = 'bool' if value in ['true', 'false'] else 'uint256' |
||||||
|
|
||||||
|
return Literal(value, ElementaryType(type_)) |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_typed_name(root, node, ast): |
||||||
|
var = root.get_yul_local_variable_from_name(ast['name']) |
||||||
|
|
||||||
|
i = Identifier(var) |
||||||
|
i._type = var.type |
||||||
|
return i |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul_unsupported(root, node, ast): |
||||||
|
raise SlitherException(f"no parser available for {ast['nodeType']} {json.dumps(ast, indent=2)}") |
||||||
|
|
||||||
|
|
||||||
|
def parse_yul(root, node, ast): |
||||||
|
op = parsers.get(ast['nodeType'], parse_yul_unsupported)(root, node, ast) |
||||||
|
if op: |
||||||
|
op.set_offset(ast["src"], root.slither) |
||||||
|
return op |
||||||
|
|
||||||
|
|
||||||
|
parsers = { |
||||||
|
'YulVariableDeclaration': parse_yul_variable_declaration, |
||||||
|
'YulAssignment': parse_yul_assignment, |
||||||
|
'YulFunctionCall': parse_yul_function_call, |
||||||
|
'YulIdentifier': parse_yul_identifier, |
||||||
|
'YulTypedName': parse_yul_typed_name, |
||||||
|
'YulLiteral': parse_yul_literal, |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
# endregion |
||||||
|
################################################################################### |
||||||
|
################################################################################### |
||||||
|
|
||||||
|
def vars_to_typestr(rets): |
||||||
|
if len(rets) == 0: |
||||||
|
return "" |
||||||
|
if len(rets) == 1: |
||||||
|
return str(rets[0].type) |
||||||
|
return "tuple({})".format(",".join(str(ret.type) for ret in rets)) |
||||||
|
|
||||||
|
|
||||||
|
def vars_to_val(vars): |
||||||
|
if len(vars) == 1: |
||||||
|
return vars[0] |
||||||
|
return TupleExpression(vars) |
Loading…
Reference in new issue