def _pack_arguments(contract_sig, args, context, pos): # abi encoding just treats all args as a big tuple args_tuple_t = TupleType([x.typ for x in args]) args_as_tuple = LLLnode.from_list(["multi"] + [x for x in args], typ=args_tuple_t) args_abi_t = args_tuple_t.abi_type # sanity typecheck - make sure the arguments can be assigned dst_tuple_t = TupleType([arg.typ for arg in contract_sig.args][:len(args)]) check_assign(dummy_node_for_type(dst_tuple_t), args_as_tuple) if contract_sig.return_type is not None: return_abi_t = calculate_type_for_external_return( contract_sig.return_type).abi_type # we use the same buffer for args and returndata, # so allocate enough space here for the returndata too. buflen = max(args_abi_t.size_bound(), return_abi_t.size_bound()) else: buflen = args_abi_t.size_bound() buflen += 32 # padding for the method id buf_t = get_type_for_exact_size(buflen) buf = context.new_internal_variable(buf_t) args_ofst = buf + 28 args_len = args_abi_t.size_bound() + 4 abi_signature = contract_sig.name + dst_tuple_t.abi_type.selector_name() # layout: # 32 bytes | args # 0x..00<method_id_4bytes> | args # the reason for the left padding is just so the alignment is easier. # if we were only targeting constantinople, we could align # to buf (and also keep code size small) by using # (mstore buf (shl signature.method_id 224)) mstore_method_id = [["mstore", buf, util.abi_method_id(abi_signature)]] if len(args) == 0: encode_args = ["pass"] else: encode_args = abi_encode(buf + 32, args_as_tuple, context, pos, bufsz=buflen) return buf, mstore_method_id + [encode_args], args_ofst, args_len
def parse_Tuple(self): tuple_elements = [ Expr(x, self.context).ir_node for x in self.expr.elements ] typ = TupleType([x.typ for x in tuple_elements], is_literal=True) multi_ir = IRnode.from_list(["multi"] + tuple_elements, typ=typ) return multi_ir
def parse_Tuple(self): tuple_elements = [ Expr(x, self.context).lll_node for x in self.expr.elements ] typ = TupleType([x.typ for x in tuple_elements], is_literal=True) multi_lll = LLLnode.from_list(["multi"] + tuple_elements, typ=typ, pos=getpos(self.expr)) return multi_lll
def test_type_storage_sizes(): assert BaseType("int128").storage_size_in_words == 1 assert ByteArrayType(12).storage_size_in_words == 2 assert ByteArrayType(33).storage_size_in_words == 3 assert SArrayType(BaseType("int128"), 10).storage_size_in_words == 10 _tuple = TupleType([BaseType("int128"), BaseType("decimal")]) assert _tuple.storage_size_in_words == 2 _struct = StructType({ "a": BaseType("int128"), "b": BaseType("decimal") }, "Foo") assert _struct.storage_size_in_words == 2 # Don't allow unknown types. with raises(Exception): _ = int.storage_size_in_words # Maps are not supported for function arguments or outputs with raises(Exception): _ = MappingType(BaseType("int128"), BaseType("int128")).storage_size_in_words
def test_canonicalize_type(): # TODO add more types # Test ABI format of multiple args. c = TupleType([BaseType("int128"), BaseType("address")]) assert c.abi_type.selector_name() == "(int128,address)"
def test_tuple_node_types(): node1 = TupleType([BaseType("int128"), BaseType("decimal")]) node2 = TupleType([BaseType("int128"), BaseType("decimal")]) assert node1 == node2 assert str(node1) == "(int128, decimal)"
def calculate_type_for_external_return(ir_typ): if needs_external_call_wrap(ir_typ): return TupleType([ir_typ]) return ir_typ
def ir_tuple_from_args(args): typ = TupleType([x.typ for x in args]) return IRnode.from_list(["multi"] + [x for x in args], typ=typ)
def lll_for_self_call(stmt_expr, context): from vyper.codegen.expr import Expr # TODO rethink this circular import pos = getpos(stmt_expr) # ** Internal Call ** # Steps: # - copy arguments into the soon-to-be callee # - allocate return buffer # - push jumpdest (callback ptr) and return buffer location # - jump to label # - (private function will fill return buffer and jump back) method_name = stmt_expr.func.attr pos_args_lll = [Expr(x, context).lll_node for x in stmt_expr.args] sig, kw_vals = context.lookup_internal_function(method_name, pos_args_lll) kw_args_lll = [Expr(x, context).lll_node for x in kw_vals] args_lll = pos_args_lll + kw_args_lll args_tuple_t = TupleType([x.typ for x in args_lll]) args_as_tuple = LLLnode.from_list(["multi"] + [x for x in args_lll], typ=args_tuple_t) # register callee to help calculate our starting frame offset context.register_callee(sig.frame_size) if context.is_constant() and sig.mutability not in ("view", "pure"): raise StateAccessViolation( f"May not call state modifying function " f"'{method_name}' within {context.pp_constancy()}.", getpos(stmt_expr), ) # TODO move me to type checker phase if not sig.internal: raise StructureException("Cannot call external functions via 'self'", stmt_expr) return_label = _generate_label(f"{sig.internal_function_label}_call") # allocate space for the return buffer # TODO allocate in stmt and/or expr.py if sig.return_type is not None: return_buffer = LLLnode.from_list( context.new_internal_variable(sig.return_type), annotation=f"{return_label}_return_buf" ) else: return_buffer = None # note: dst_tuple_t != args_tuple_t dst_tuple_t = TupleType([arg.typ for arg in sig.args]) args_dst = LLLnode(sig.frame_start, typ=dst_tuple_t, location="memory") # if one of the arguments is a self call, the argument # buffer could get borked. to prevent against that, # write args to a temporary buffer until all the arguments # are fully evaluated. if args_as_tuple.contains_self_call: copy_args = ["seq"] # TODO deallocate me tmp_args_buf = LLLnode( context.new_internal_variable(dst_tuple_t), typ=dst_tuple_t, location="memory", ) copy_args.append( # --> args evaluate here <-- make_setter(tmp_args_buf, args_as_tuple, pos) ) copy_args.append(make_setter(args_dst, tmp_args_buf, pos)) else: copy_args = make_setter(args_dst, args_as_tuple, pos) goto_op = ["goto", sig.internal_function_label] # pass return buffer to subroutine if return_buffer is not None: goto_op += [return_buffer] # pass return label to subroutine goto_op += [push_label_to_stack(return_label)] call_sequence = [ "seq", copy_args, goto_op, ["label", return_label, ["var_list"], "pass"], ] if return_buffer is not None: # push return buffer location to stack call_sequence += [return_buffer] o = LLLnode.from_list( call_sequence, typ=sig.return_type, location="memory", pos=pos, annotation=stmt_expr.get("node_source_code"), add_gas_estimate=sig.gas, ) o.is_self_call = True return o
def calculate_type_for_external_return(lll_typ): if _needs_external_call_wrap(lll_typ): return TupleType([lll_typ]) return lll_typ