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)) # set constant so that revert reason str is well behaved try: tmp = self.context.constancy self.context.constancy = Constancy.Constant msg_lll = Expr(msg, self.context).lll_node finally: self.context.constancy = tmp # TODO this is probably useful in codegen.core # compare with eval_seq. def _get_last(lll): if len(lll.args) == 0: return lll.value return _get_last(lll.args[-1]) # TODO maybe use ensure_in_memory if msg_lll.location != "memory": buf = self.context.new_internal_variable(msg_lll.typ) instantiate_msg = make_byte_array_copier(buf, msg_lll) else: buf = _get_last(msg_lll) if not isinstance(buf, int): raise CompilerPanic(f"invalid bytestring {buf}\n{self}") instantiate_msg = msg_lll # offset of bytes in (bytes,) method_id = util.abi_method_id("Error(string)") # abi encode method_id + bytestring assert buf >= 36, "invalid buffer" # we don't mind overwriting other memory because we are # getting out of here anyway. _runtime_length = ["mload", buf] revert_seq = [ "seq", instantiate_msg, zero_pad(buf), ["mstore", buf - 64, method_id], ["mstore", buf - 32, 0x20], [ "revert", buf - 36, ["add", 4 + 32 + 32, ["ceil32", _runtime_length]] ], ] if test_expr is not None: 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 _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 _annotated_method_id(abi_sig): method_id = util.abi_method_id(abi_sig) annotation = f"{hex(method_id)}: {abi_sig}" return LLLnode(method_id, annotation=annotation)