def gen_tuple_return(stmt, context, sub): # Is from a call expression. if sub.args and len(sub.args[0].args) > 0 and ( sub.args[0].args[0].value == "call" or sub.args[0].args[0].value == "staticcall"): # self-call to external. mem_pos = sub mem_size = get_size_of_type(sub.typ) * 32 return LLLnode.from_list(["return", mem_pos, mem_size], typ=sub.typ) elif sub.annotation and "Internal Call" in sub.annotation: mem_pos = sub.args[ -1].value if sub.value == "seq_unchecked" else sub.args[0].args[-1] mem_size = get_size_of_type(sub.typ) * 32 # Add zero padder if bytes are present in output. zero_padder = ["pass"] byte_arrays = [(i, x) for i, x in enumerate(sub.typ.tuple_members()) if isinstance(x, ByteArrayLike)] if byte_arrays: i, x = byte_arrays[-1] zero_padder = zero_pad(bytez_placeholder=[ "add", mem_pos, ["mload", mem_pos + i * 32] ]) return LLLnode.from_list( ["seq"] + [sub] + [zero_padder] + [make_return_stmt(stmt, context, mem_pos, mem_size)], typ=sub.typ, pos=getpos(stmt), valency=0, ) abi_typ = abi_type_of(context.return_type) # according to the ABI, return types are ALWAYS tuples even if # only one element is being returned. # https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector-and-argument-encoding # "and the return values v_1, ..., v_k of f are encoded as # # enc((v_1, ..., v_k)) # i.e. the values are combined into a tuple and encoded. # " # therefore, wrap it in a tuple if it's not already a tuple. # (big difference between returning `(bytes,)` and `bytes`. abi_typ = ensure_tuple(abi_typ) abi_bytes_needed = abi_typ.static_size() + abi_typ.dynamic_size_bound() dst, _ = context.memory_allocator.increase_memory(abi_bytes_needed) return_buffer = LLLnode(dst, location="memory", annotation="return_buffer", typ=context.return_type) check_assign(return_buffer, sub, pos=getpos(stmt)) encode_out = abi_encode(return_buffer, sub, pos=getpos(stmt), returns=True) load_return_len = ["mload", MemoryPositions.FREE_VAR_SPACE] os = [ "seq", ["mstore", MemoryPositions.FREE_VAR_SPACE, encode_out], make_return_stmt(stmt, context, return_buffer, load_return_len), ] return LLLnode.from_list(os, typ=None, pos=getpos(stmt), valency=0)
def calculate_arg_totals(self): """ Calculate base arguments, and totals. """ code = self.func_ast_code self.base_args = [] self.total_default_args = 0 if hasattr(code.args, "defaults"): self.total_default_args = len(code.args.defaults) if self.total_default_args > 0: # all argument w/o defaults self.base_args = self.args[:-self.total_default_args] else: # No default args, so base_args = args. self.base_args = self.args # All default argument name/type definitions. self.default_args = code.args.args[ -self.total_default_args:] # noqa: E203 # Keep all the value to assign to default parameters. self.default_values = dict( zip([arg.arg for arg in self.default_args], code.args.defaults)) # Calculate the total sizes in memory the function arguments will take use. # Total memory size of all arguments (base + default together). self.max_copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayLike) else get_size_of_type(arg.typ) * 32 for arg in self.args ]) # Total memory size of base arguments (arguments exclude default parameters). self.base_copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayLike) else get_size_of_type(arg.typ) * 32 for arg in self.base_args ])
def parse_func(code, _globals, sigs, origcode, _custom_units, _vars=None): if _vars is None: _vars = {} sig = FunctionSignature.from_definition(code, sigs=sigs, custom_units=_custom_units) # Check for duplicate variables with globals for arg in sig.args: if arg.name in _globals: raise VariableDeclarationException("Variable name duplicated between function arguments and globals: " + arg.name) # Create a context context = Context(vars=_vars, globals=_globals, sigs=sigs, return_type=sig.output_type, is_constant=sig.const, is_payable=sig.payable, origcode=origcode, custom_units=_custom_units) # Copy calldata to memory for fixed-size arguments copy_size = sum([32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in sig.args]) context.next_mem += copy_size if not len(sig.args): copier = 'pass' elif sig.name == '__init__': copier = ['codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen', copy_size] else: copier = ['calldatacopy', MemoryPositions.RESERVED_MEMORY, 4, copy_size] clampers = [copier] # Add asserts for payable and internal if not sig.payable: clampers.append(['assert', ['iszero', 'callvalue']]) if sig.private: clampers.append(['assert', ['eq', 'caller', 'address']]) # Fill in variable positions for arg in sig.args: clampers.append(make_clamper(arg.pos, context.next_mem, arg.typ, sig.name == '__init__')) if isinstance(arg.typ, ByteArrayType): context.vars[arg.name] = VariableRecord(arg.name, context.next_mem, arg.typ, False) context.next_mem += 32 * get_size_of_type(arg.typ) else: context.vars[arg.name] = VariableRecord(arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ, False) # Create "clampers" (input well-formedness checkers) # Return function body if sig.name == '__init__': o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code)) else: method_id_node = LLLnode.from_list(sig.method_id, pos=getpos(code), annotation='%s' % sig.name) o = LLLnode.from_list(['if', ['eq', ['mload', 0], method_id_node], ['seq'] + clampers + [parse_body(c, context) for c in code.body] + ['stop'] ], typ=None, pos=getpos(code)) # Check for at leasts one return statement if necessary. if context.return_type and context.function_return_count == 0: raise StructureException( "Missing return statement in function '%s' " % sig.name, code ) o.context = context o.total_gas = o.gas + calc_mem_gas(o.context.next_mem) o.func_name = sig.name return o
def external_contract_call_expr(expr, context, contract_name, contract_address): if contract_name not in context.sigs: raise VariableDeclarationException("Contract not declared yet: %s" % contract_name) method_name = expr.func.attr if method_name not in context.sigs[contract_name]: raise VariableDeclarationException( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" % method_name) sig = context.sigs[contract_name][method_name] inargs, inargsize = pack_arguments( sig, [parse_expr(arg, context) for arg in expr.args], context) output_placeholder = context.new_placeholder(typ=sig.output_type) if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayType): returner = output_placeholder + 32 else: raise TypeMismatchException( "Invalid output type: %r" % sig.output_type, expr) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]] ] if context.is_constant: sub.append([ 'assert', [ 'staticcall', 'gas', contract_address, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32 ] ]) else: sub.append([ 'assert', [ 'call', 'gas', contract_address, 0, inargs, inargsize, output_placeholder, get_size_of_type(sig.output_type) * 32 ] ]) sub.extend([0, returner]) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(expr)) return o
def _call_make_placeholder(stmt_expr, context, sig): if sig.output_type is None: return 0, 0, 0 output_placeholder = context.new_placeholder(typ=sig.output_type) output_size = get_size_of_type(sig.output_type) * 32 if isinstance(sig.output_type, BaseType): returner = output_placeholder elif isinstance(sig.output_type, ByteArrayLike): returner = output_placeholder elif isinstance(sig.output_type, TupleLike): # incase of struct we need to decode the output and then return it returner = ["seq"] decoded_placeholder = context.new_placeholder(typ=sig.output_type) decoded_node = LLLnode(decoded_placeholder, typ=sig.output_type, location="memory") output_node = LLLnode(output_placeholder, typ=sig.output_type, location="memory") returner.append(abi_decode(decoded_node, output_node)) returner.extend([decoded_placeholder]) elif isinstance(sig.output_type, ListType): returner = output_placeholder else: raise TypeCheckFailure(f"Invalid output type: {sig.output_type}") return output_placeholder, returner, output_size
def serialise_var_rec(var_rec): if isinstance(var_rec.typ, ByteArrayType): type_str = 'bytes[%s]' % var_rec.typ.maxlen _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, TupleType): type_str = 'tuple' _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, MappingType): type_str = str(var_rec.typ) _size = 0 else: type_str = var_rec.typ.typ _size = get_size_of_type(var_rec.typ) * 32 out = {'type': type_str, 'size': _size, 'position': var_rec.pos} return out
def make_clamper(datapos, mempos, typ, is_init=False): if not is_init: data_decl = ['calldataload', ['add', 4, datapos]] copier = lambda pos, sz: ['calldatacopy', mempos, ['add', 4, pos], sz] else: data_decl = ['codeload', ['add', '~codelen', datapos]] copier = lambda pos, sz: ['codecopy', mempos, ['add', '~codelen', pos], sz] # Numbers: make sure they're in range if is_base_type(typ, 'int128'): return LLLnode.from_list(['clamp', ['mload', MemoryPositions.MINNUM], data_decl, ['mload', MemoryPositions.MAXNUM]], typ=typ, annotation='checking int128 input') # Booleans: make sure they're zero or one elif is_base_type(typ, 'bool'): return LLLnode.from_list(['uclamplt', data_decl, 2], typ=typ, annotation='checking bool input') # Addresses: make sure they're in range elif is_base_type(typ, 'address'): return LLLnode.from_list(['uclamplt', data_decl, ['mload', MemoryPositions.ADDRSIZE]], typ=typ, annotation='checking address input') # Bytes: make sure they have the right size elif isinstance(typ, ByteArrayType): return LLLnode.from_list(['seq', copier(data_decl, 32 + typ.maxlen), ['assert', ['le', ['calldataload', ['add', 4, data_decl]], typ.maxlen]]], typ=None, annotation='checking bytearray input') # Lists: recurse elif isinstance(typ, ListType): o = [] for i in range(typ.count): offset = get_size_of_type(typ.subtype) * 32 * i o.append(make_clamper(datapos + offset, mempos + offset, typ.subtype, is_init)) return LLLnode.from_list(['seq'] + o, typ=None, annotation='checking list input') # Otherwise don't make any checks else: return LLLnode.from_list('pass')
def size(self): if hasattr(self.typ, "size_in_bytes"): # temporary requirement to support both new and old type objects # we divide by 32 here because the returned value is denominated # in "slots" of 32 bytes each return math.ceil(self.typ.size_in_bytes / 32) return get_size_of_type(self.typ)
def _assert_reason(self, test_expr, msg): if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE": return LLLnode.from_list(["assert_unreachable", test_expr], typ=None, pos=getpos(msg)) reason_str_type = ByteArrayType(len(msg.value.strip())) sig_placeholder = self.context.new_internal_variable(BaseType(32)) arg_placeholder = self.context.new_internal_variable(BaseType(32)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) revert_seq = [ "seq", ["mstore", sig_placeholder, method_id], ["mstore", arg_placeholder, 32], placeholder_bytes, [ "revert", sig_placeholder + 28, int(4 + get_size_of_type(reason_str_type) * 32) ], ] if test_expr: lll_node = ["if", ["iszero", test_expr], revert_seq] else: lll_node = revert_seq return LLLnode.from_list(lll_node, typ=None, pos=getpos(self.stmt))
def _assert_reason(self, test_expr, msg): if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE": return LLLnode.from_list(["assert_unreachable", test_expr], typ=None, pos=getpos(msg)) reason_str = msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) assert_reason = [ "seq", ["mstore", sig_placeholder, method_id], ["mstore", arg_placeholder, 32], placeholder_bytes, [ "assert_reason", test_expr, int(sig_placeholder + 28), int(4 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
def new_internal_variable(self, typ: NodeType) -> int: """ Allocate memory for an internal variable. Arguments --------- typ : NodeType Variable type, used to determine the size of memory allocation Returns ------- int Memory offset for the variable """ # internal variable names begin with a number sign so there is no chance for collision var_id = self._internal_var_iter self._internal_var_iter += 1 name = f"#internal_{var_id}" if hasattr(typ, "size_in_bytes"): # temporary requirement to support both new and old type objects var_size = typ.size_in_bytes # type: ignore else: var_size = 32 * get_size_of_type(typ) return self._new_variable(name, typ, var_size, True)
def get_external_call_output(sig, context): if not sig.output_type: return 0, 0, [] output_placeholder = context.new_internal_variable(typ=sig.output_type) output_size = get_size_of_type(sig.output_type) * 32 if isinstance(sig.output_type, BaseType): returner = [0, output_placeholder] elif isinstance(sig.output_type, ByteArrayLike): returner = [0, output_placeholder + 32] elif isinstance(sig.output_type, TupleLike): # incase of struct we need to decode the output and then return it returner = ["seq"] decoded_placeholder = context.new_internal_variable( typ=sig.output_type) decoded_node = LLLnode(decoded_placeholder, typ=sig.output_type, location="memory") output_node = LLLnode(output_placeholder, typ=sig.output_type, location="memory") returner.append(abi_decode(decoded_node, output_node)) returner.extend([0, decoded_placeholder]) elif isinstance(sig.output_type, ListType): returner = [0, output_placeholder] else: raise TypeCheckFailure(f"Invalid output type: {sig.output_type}") return output_placeholder, output_size, returner
def new_variable(self, name, typ, pos=None): if self.is_valid_varname(name, pos): self.vars[name] = VariableRecord(name, self.next_mem, typ, True, self.blockscopes.copy()) pos = self.next_mem self.next_mem += 32 * get_size_of_type(typ) return pos
def _assert_reason(self, test_expr, msg): if isinstance(msg, ast.Name) and msg.id == 'UNREACHABLE': return self._assert_unreachable(test_expr, msg) if not isinstance(msg, ast.Str): raise StructureException( 'Reason parameter of assert needs to be a literal string ' '(or UNREACHABLE constant).', msg) if len(msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) assert_reason = [ 'seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, [ 'assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
def new_variable(self, name: str, typ: NodeType, pos: VyperNode = None) -> int: """ Allocate memory for a user-defined variable. Arguments --------- name : str Name of the variable typ : NodeType Variable type, used to determine the size of memory allocation pos : VyperNode AST node corresponding to the location where the variable was created, used for annotating exceptions Returns ------- int Memory offset for the variable """ if hasattr(typ, "size_in_bytes"): # temporary requirement to support both new and old type objects var_size = typ.size_in_bytes # type: ignore else: var_size = 32 * get_size_of_type(typ) return self._new_variable(name, typ, var_size, False)
def parse_assert(self): with self.context.assertion_scope(): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if not self.is_bool_expr(test_expr): raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test) if self.stmt.msg: if not isinstance(self.stmt.msg, ast.Str): raise StructureException( 'Reason parameter of assert needs to be a literal string.', self.stmt.msg ) if len(self.stmt.msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = self.stmt.msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(self.stmt.msg, self.context).lll_node method_id = fourbytes_to_int(sha3(b"Error(string)")[:4]) assert_reason = [ 'seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, [ 'assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt)) else: return LLLnode.from_list(['assert', test_expr], typ=None, pos=getpos(self.stmt))
def pack_arguments(signature, args, context, pos): placeholder_typ = ByteArrayType(maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException("Wrong number of args for: %s (%s args, expected %s)" % (signature.name, actual_arg_count, expected_arg_count)) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append(make_setter(LLLnode.from_list(placeholder + staticarray_offset + 32 + i * 32, typ=typ), arg, 'memory', pos=pos)) elif isinstance(typ, ByteArrayType): setters.append(['mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz']) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list(['add', placeholder + 32, '_poz'], typ=typ, location='memory') setters.append(['with', '_s', arg, ['seq', make_byte_array_copier(target, arg_copy), ['set', '_poz', ['add', 32, ['add', '_poz', get_length(arg_copy)]]]]]) needpos = True elif isinstance(typ, ListType): target = LLLnode.from_list([placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory') setters.append(make_setter(target, arg, 'memory', pos=pos)) staticarray_offset += 32 * (typ.count - 1) else: raise TypeMismatchException("Cannot pack argument of type %r" % typ) if needpos: return LLLnode.from_list(['with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + [placeholder + 28]], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28 else: return LLLnode.from_list(['seq'] + setters + [placeholder + 28], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28
def from_declaration(cls, code, custom_units=None): name = code.target.id pos = 0 if not is_varname_valid(name, custom_units=custom_units): raise EventDeclarationException("Event name invalid: " + name) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] arg = keys[i].id is_indexed = False # Check to see if argument is a topic if isinstance(typ, ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, ast.Subscript) and getattr( typ.value, 'id', None ) == 'bytes' and typ.slice.value.n > 32 and is_indexed: raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( "Maximum of 3 topics {} given".format(topics_count - 1), arg) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidTypeException("Argument must have type", arg) if not is_varname_valid(arg, custom_units): raise VariableDeclarationException( "Argument name invalid or reserved: " + arg, arg) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg) parsed_type = parse_type(typ, None, custom_units=custom_units) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ')' # noqa F812 event_id = bytes_to_int(sha3(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def new_variable(self, name, typ): if not is_varname_valid(name, custom_units=self.custom_units): raise VariableDeclarationException("Variable name invalid or reserved: " + name) if name in self.vars or name in self.globals: raise VariableDeclarationException("Duplicate variable name: %s" % name) self.vars[name] = VariableRecord(name, self.next_mem, typ, True, self.blockscopes.copy()) pos = self.next_mem self.next_mem += 32 * get_size_of_type(typ) return pos
def test_get_size_of_type(): assert get_size_of_type(BaseType('int128')) == 1 assert get_size_of_type(ByteArrayType(12)) == 3 assert get_size_of_type(ByteArrayType(33)) == 4 assert get_size_of_type(ListType(BaseType('int128'), 10)) == 10 _tuple = TupleType([BaseType('int128'), BaseType('decimal')]) assert get_size_of_type(_tuple) == 2 _struct = StructType({'a': BaseType('int128'), 'b': BaseType('decimal')}) assert get_size_of_type(_struct) == 2 # Don't allow unknow types. with raises(Exception): get_size_of_type(int) # Maps are not supported for function arguments or outputs with raises(Exception): get_size_of_type(MappingType(BaseType('int128'), BaseType('int128')))
def _make_array_index_setter(target, target_token, pos, location, offset): if location == "memory" and isinstance(target.value, int): offset = target.value + 32 * get_size_of_type(target.typ.subtype) * offset return LLLnode.from_list([offset], typ=target.typ.subtype, location=location, pos=pos) else: return add_variable_offset( target_token, LLLnode.from_list(offset, typ="int128"), pos=pos, array_bounds_check=False, )
def call_make_placeholder(stmt_expr, context, sig): if sig.output_type is None: return 0, 0, 0 output_placeholder = context.new_placeholder(typ=sig.output_type) out_size = get_size_of_type(sig.output_type) * 32 returner = output_placeholder if not sig.private and isinstance(sig.output_type, ByteArrayLike): returner = output_placeholder + 32 return output_placeholder, returner, out_size
def get_external_contract_call_output(sig, context): if not sig.output_type: return 0, 0, [] output_placeholder = context.new_placeholder(typ=sig.output_type) output_size = get_size_of_type(sig.output_type) * 32 if isinstance(sig.output_type, BaseType): returner = [0, output_placeholder] elif isinstance(sig.output_type, ByteArrayType): returner = [0, output_placeholder + 32] else: raise TypeMismatchException("Invalid output type: %s" % sig.output_type) return output_placeholder, output_size, returner
def new_variable(self, name, typ, internal_var=False, pos=None): # mangle internally generated variables so they cannot collide # with user variables. if internal_var: name = self._mangle(name) if internal_var or self.is_valid_varname(name, pos): var_size = 32 * get_size_of_type(typ) var_pos, _ = self.memory_allocator.increase_memory(var_size) self.vars[name] = VariableRecord( name=name, pos=var_pos, typ=typ, mutable=True, blockscopes=self.blockscopes.copy(), ) return var_pos
def new_variable(self, name, typ, pos=None): if self.is_valid_varname(name, pos): var_size = 32 * get_size_of_type(typ) var_pos, _ = self.memory_allocator.increase_memory(var_size) self.vars[name] = VariableRecord( name, var_pos, typ, True, self.blockscopes.copy(), ) return var_pos
def serialise_var_rec(var_rec): if isinstance(var_rec.typ, ByteArrayType): type_str = "bytes[%s]" % var_rec.typ.maxlen _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, StringType): type_str = "string[%s]" % var_rec.typ.maxlen _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, StructType): type_str = "struct[%s]" % var_rec.typ.name _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, TupleType): type_str = "tuple" _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, ListType): type_str = str(var_rec.typ) _size = get_size_of_type(var_rec.typ) * 32 elif isinstance(var_rec.typ, MappingType): type_str = str(var_rec.typ) _size = 0 else: type_str = str(var_rec.typ) _size = get_size_of_type(var_rec.typ) * 32 out = {"type": type_str, "size": _size, "position": var_rec.pos} return out
def from_declaration(cls, class_node, global_ctx): name = class_node.name pos = 0 check_valid_varname( name, global_ctx._structs, global_ctx._constants, pos=class_node, error_prefix="Event name invalid. ", exc=EventDeclarationException, ) args = [] indexed_list = [] if len(class_node.body) != 1 or not isinstance(class_node.body[0], vy_ast.Pass): for node in class_node.body: arg_item = node.target arg = node.target.id typ = node.annotation if isinstance(typ, vy_ast.Call) and typ.get("func.id") == "indexed": indexed_list.append(True) typ = typ.args[0] else: indexed_list.append(False) check_valid_varname( arg, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise TypeCheckFailure( f"Duplicate function argument name: {arg}") # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = (name + "(" + ",".join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ")") # noqa F812 event_id = bytes_to_int(keccak256(bytes(sig, "utf-8"))) return cls(name, args, indexed_list, event_id, sig)
def test_get_size_of_type(): assert get_size_of_type(BaseType("int128")) == 1 assert get_size_of_type(ByteArrayType(12)) == 3 assert get_size_of_type(ByteArrayType(33)) == 4 assert get_size_of_type(ListType(BaseType("int128"), 10)) == 10 _tuple = TupleType([BaseType("int128"), BaseType("decimal")]) assert get_size_of_type(_tuple) == 2 _struct = StructType({ "a": BaseType("int128"), "b": BaseType("decimal") }, "Foo") assert get_size_of_type(_struct) == 2 # Don't allow unknown types. with raises(Exception): get_size_of_type(int) # Maps are not supported for function arguments or outputs with raises(Exception): get_size_of_type(MappingType(BaseType("int128"), BaseType("int128")))
def pack_arguments(signature, args, context, pos, return_placeholder=True): placeholder_typ = ByteArrayType(maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException("Wrong number of args for: %s (%s args, expected %s)" % (signature.name, actual_arg_count, expected_arg_count)) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append(make_setter(LLLnode.from_list(placeholder + staticarray_offset + 32 + i * 32, typ=typ), arg, 'memory', pos=pos, in_function_call=True)) elif isinstance(typ, ByteArrayType): setters.append(['mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz']) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list(['add', placeholder + 32, '_poz'], typ=typ, location='memory') setters.append(['with', '_s', arg, ['seq', make_byte_array_copier(target, arg_copy, pos), ['set', '_poz', ['add', 32, ['ceil32', ['add', '_poz', get_length(arg_copy)]]]]]]) needpos = True elif isinstance(typ, (StructType, ListType)): if has_dynamic_data(typ): raise TypeMismatchException("Cannot pack bytearray in struct") target = LLLnode.from_list([placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory') setters.append(make_setter(target, arg, 'memory', pos=pos)) if (isinstance(typ, ListType)): count = typ.count else: count = len(typ.tuple_items()) staticarray_offset += 32 * (count - 1) else: raise TypeMismatchException("Cannot pack argument of type %r" % typ) # For private call usage, doesn't use a returner. returner = [[placeholder + 28]] if return_placeholder else [] if needpos: return ( LLLnode.from_list(['with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + returner], typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32 ) else: return ( LLLnode.from_list(['seq'] + setters + returner, typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32 )
def get_external_call_output(sig, context): if not sig.output_type: return 0, 0, [] output_placeholder = context.new_placeholder(typ=sig.output_type) output_size = get_size_of_type(sig.output_type) * 32 if isinstance(sig.output_type, BaseType): returner = [0, output_placeholder] elif isinstance(sig.output_type, ByteArrayLike): returner = [0, output_placeholder + 32] elif isinstance(sig.output_type, TupleLike): returner = [0, output_placeholder] elif isinstance(sig.output_type, ListType): returner = [0, output_placeholder] else: raise TypeCheckFailure(f"Invalid output type: {sig.output_type}") return output_placeholder, output_size, returner