def to_address(expr, arg, out_typ): # question: should this be allowed? if is_integer_type(arg.typ): if arg.typ._int_info.is_signed: _FAIL(arg.typ, out_typ, expr) # TODO once we introduce uint160, we can just check that arg # is unsigned, and then call to_int(expr, arg, uint160) because # the logic is equivalent. # disallow casting from Bytes[N>20] _check_bytes(expr, arg, out_typ, 32) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if arg_typ.maxlen > 20: arg = int_clamp(arg, 160, signed=False) if is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=False) # clamp after shift if info.m > 20: arg = int_clamp(arg, 160, signed=False) elif is_integer_type(arg.typ): arg_info = arg.typ._int_info if arg_info.bits > 160 or arg_info.is_signed: arg = int_clamp(arg, 160, signed=False) return IRnode.from_list(arg, typ=out_typ)
def to_int(expr, arg, out_typ): int_info = out_typ._int_info assert int_info.bits % 8 == 0 _check_bytes(expr, arg, out_typ, 32) if isinstance(expr, vy_ast.Constant): return _literal_int(expr, arg.typ, out_typ) elif isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_typ.maxlen * 8 > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=int_info.is_signed) if arg_info.m_bits > int_info.bits: arg = int_clamp(arg, int_info.bits, signed=int_info.is_signed) elif is_decimal_type(arg.typ): arg = _fixed_to_int(arg, out_typ) elif is_integer_type(arg.typ): arg = _int_to_int(arg, out_typ) elif is_base_type(arg.typ, "address"): if int_info.is_signed: # TODO if possible, refactor to move this validation close to the entry of the function _FAIL(arg.typ, out_typ, expr) if int_info.bits < 160: arg = int_clamp(arg, int_info.bits, signed=False) return IRnode.from_list(arg, typ=out_typ)
def to_enum(expr, arg, out_typ): if not is_base_type(arg.typ, "uint256"): _FAIL(arg.typ, out_typ, expr) if len(out_typ.members) < 256: arg = int_clamp(arg, bits=len(out_typ.members), signed=False) return IRnode.from_list(arg, typ=out_typ)
def _int_to_int(arg, out_typ): arg_info = arg.typ._int_info out_info = out_typ._int_info # do the same thing as # _clamp_numeric_convert(arg, arg_info.bounds, out_info.bounds, arg_info.is_signed) # but with better code size and gas. if arg_info.is_signed and not out_info.is_signed: # e.g. (clample (clampge arg 0) (2**128 - 1)) # note that when out_info.bits == 256, # (clample arg 2**256 - 1) does not make sense. # see similar assertion in _clamp_numeric_convert. if out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # note: because of the usage of signed=False, and the fact # that out_bits < 256 in this branch, below implies # not only (clample arg 2**128 - 1) but also (clampge arg 0). arg = int_clamp(arg, out_info.bits, signed=False) else: # note: this also works for out_bits == 256. arg = clamp("sge", arg, 0) elif not arg_info.is_signed and out_info.is_signed: # e.g. (uclample (uclampge arg 0) (2**127 - 1)) # (note that (uclampge arg 0) always evaluates to true.) arg = int_clamp(arg, out_info.bits - 1, signed=False) elif out_info.bits < arg_info.bits: assert out_info.bits < 256, "unreachable" # narrowing conversion, signs are the same. # we can just use regular int clampers. arg = int_clamp(arg, out_info.bits, out_info.is_signed) else: # widening conversion, signs are the same. # we do not have to do any clamps. assert arg_info.is_signed == out_info.is_signed and out_info.bits >= arg_info.bits return IRnode.from_list(arg, typ=out_typ)
def _num_clamp(arg, dst_info, arg_info): if dst_info.is_signed != arg_info.is_signed: arg = _signedness_clamp(arg, arg_info.bits) # if, not elif (could be two clamps!) # TODO is it possible to make this more efficient? if dst_info.bits < arg_info.bits: arg = int_clamp(arg, dst_info.bits, dst_info.is_signed) return arg
def to_int128(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "num_literal": if isinstance(in_arg, int): if not SizeLimits.in_bounds("int128", in_arg): raise InvalidLiteral(f"Number out of range: {in_arg}") return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) elif isinstance(in_arg, Decimal): if not SizeLimits.in_bounds("int128", math.trunc(in_arg)): raise InvalidLiteral( f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list(math.trunc(in_arg), typ=BaseType("int128"), pos=getpos(expr)) else: raise InvalidLiteral(f"Unknown numeric literal type: {in_arg}") elif input_type in ("bytes32", "int256"): if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", in_arg.value): raise InvalidLiteral(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: # cast to output type so clamp_basetype works in_arg = LLLnode.from_list(in_arg, typ="int128") return LLLnode.from_list( clamp_basetype(in_arg), typ=BaseType("int128"), pos=getpos(expr), ) # CMC 20211020: what is the purpose of this .. it lops off 32 bits elif input_type == "address": return LLLnode.from_list( ["signextend", 15, ["and", in_arg, (SizeLimits.ADDRSIZE - 1)]], typ=BaseType("int128"), pos=getpos(expr), ) elif input_type in ("String", "Bytes"): if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to int128", expr, ) return byte_array_to_num(in_arg, "int128") elif input_type == "uint256": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", in_arg.value): raise InvalidLiteral(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) # !! do not use clamp_basetype. check that 0 <= input <= MAX_INT128. res = int_clamp(in_arg, 127, signed=False) return LLLnode.from_list( res, typ="int128", pos=getpos(expr), ) elif input_type == "decimal": # cast to int128 so clamp_basetype works res = LLLnode.from_list(["sdiv", in_arg, DECIMAL_DIVISOR], typ="int128") return LLLnode.from_list(clamp_basetype(res), typ="int128", pos=getpos(expr)) elif input_type in ("bool", "uint8"): # note: for int8, would need signextend return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: raise InvalidLiteral(f"Invalid input for int128: {in_arg}", expr)
def _signedness_clamp(arg, bits): return int_clamp(arg, bits=bits - 1, signed=False)