def clamp_basetype(ir_node): t = ir_node.typ if not isinstance(t, BaseType): raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest # copy of the input ir_node = unwrap_location(ir_node) if isinstance(t, EnumType): bits = len(t.members) # assert x >> bits == 0 ret = int_clamp(ir_node, bits, signed=False) elif is_integer_type(t) or is_decimal_type(t): if t._num_info.bits == 256: ret = ir_node else: ret = int_clamp(ir_node, t._num_info.bits, signed=t._num_info.is_signed) elif is_bytes_m_type(t): if t._bytes_info.m == 32: ret = ir_node # special case, no clamp. else: ret = bytes_clamp(ir_node, t._bytes_info.m) elif t.typ in ("address", ): ret = int_clamp(ir_node, 160) elif t.typ in ("bool", ): ret = int_clamp(ir_node, 1) else: # pragma: nocover raise CompilerPanic(f"{t} passed to clamp_basetype") return IRnode.from_list(ret, typ=ir_node.typ)
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) ret = b.resolve(ret) return IRnode.from_list(ret)
def parse_Hex(self): hexstr = self.expr.value t = self.expr._metadata.get("type") n_bytes = (len(hexstr) - 2) // 2 # e.g. "0x1234" is 2 bytes if t is not None: inferred_type = new_type_to_old_type(self.expr._metadata["type"]) # This branch is a band-aid to deal with bytes20 vs address literals # TODO handle this properly in the type checker elif len(hexstr) == 42: inferred_type = BaseType("address", is_literal=True) else: inferred_type = BaseType(f"bytes{n_bytes}", is_literal=True) if is_base_type(inferred_type, "address"): # sanity check typechecker did its job assert len(hexstr) == 42 and is_checksum_encoded(hexstr) typ = BaseType("address") return IRnode.from_list(int(self.expr.value, 16), typ=typ) elif is_bytes_m_type(inferred_type): assert n_bytes == inferred_type._bytes_info.m # bytes_m types are left padded with zeros val = int(hexstr, 16) << 8 * (32 - n_bytes) typ = BaseType(f"bytes{n_bytes}", is_literal=True) return IRnode.from_list(val, typ=typ)
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 clamp_basetype(ir_node): t = ir_node.typ if not isinstance(t, BaseType): raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest # copy of the input ir_node = unwrap_location(ir_node) if is_integer_type(t) or is_decimal_type(t): if t._num_info.bits == 256: return ir_node else: return int_clamp(ir_node, t._num_info.bits, signed=t._num_info.is_signed) if is_bytes_m_type(t): if t._bytes_info.m == 32: return ir_node # special case, no clamp. else: return bytes_clamp(ir_node, t._bytes_info.m) if t.typ in ("address", ): return int_clamp(ir_node, 160) if t.typ in ("bool", ): return int_clamp(ir_node, 1) raise CompilerPanic(f"{t} passed to clamp_basetype") # pragma: notest
def _type_class_of(typ): if is_integer_type(typ): return "int" if is_bytes_m_type(typ): return "bytes_m" if is_decimal_type(typ): return "decimal" if isinstance(typ, BaseType): return typ.typ # e.g., "bool" if isinstance(typ, ByteArrayType): return "bytes" if isinstance(typ, StringType): return "string"
def to_decimal(expr, arg, out_typ): # question: is converting from Bytes to decimal allowed? _check_bytes(expr, arg, out_typ, max_bytes_allowed=16) if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if arg_typ.maxlen * 8 > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) # TODO revisit this condition once we have more decimal types # and decimal bounds expand # will be something like: if info.m_bits > 168 if info.m_bits > 128: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): int_info = arg.typ._int_info arg = _int_to_fixed(arg, out_typ) out_info = out_typ._decimal_info if int_info.bits > out_info.bits: # TODO: _num_clamp probably not necessary bc already # clamped in _int_to_fixed arg = _num_clamp(arg, out_info, int_info) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest
def to_bytes_m(expr, arg, out_typ): out_info = out_typ._bytes_info _check_bytes(expr, arg, out_typ, max_bytes_allowed=out_info.m) if isinstance(arg.typ, ByteArrayType): bytes_val = LOAD(bytes_data_ptr(arg)) # zero out any dirty bytes (which can happen in the last # word of a bytearray) len_ = get_bytearray_length(arg) num_zero_bits = IRnode.from_list(["mul", ["sub", 32, len_], 8]) with num_zero_bits.cache_when_complex("bits") as (b, num_zero_bits): arg = shl(num_zero_bits, shr(num_zero_bits, bytes_val)) arg = b.resolve(arg) elif is_bytes_m_type(arg.typ): arg_info = arg.typ._bytes_info # clamp if it's a downcast if arg_info.m > out_info.m: arg = bytes_clamp(arg, out_info.m) elif is_integer_type(arg.typ) or is_base_type(arg.typ, "address"): int_bits = arg.typ._int_info.bits if out_info.m_bits < int_bits: # question: allow with runtime clamp? # arg = int_clamp(m_bits, signed=int_info.signed) _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) elif is_decimal_type(arg.typ): if out_info.m_bits < arg.typ._decimal_info.bits: _FAIL(arg.typ, out_typ, expr) # note: neg numbers not OOB. keep sign bit arg = shl(256 - out_info.m_bits, arg) else: # bool arg = shl(256 - out_info.m_bits, arg) return IRnode.from_list(arg, typ=out_typ)
def convert(expr, context): if len(expr.args) != 2: raise StructureException( "The convert function expects two parameters.", expr) arg_ast = expr.args[0] arg = Expr(arg_ast, context).ir_node original_arg = arg out_typ = context.parse_type(expr.args[1]) if isinstance(arg.typ, BaseType): arg = unwrap_location(arg) with arg.cache_when_complex("arg") as (b, arg): if is_base_type(out_typ, "bool"): ret = to_bool(arg_ast, arg, out_typ) elif is_base_type(out_typ, "address"): ret = to_address(arg_ast, arg, out_typ) elif isinstance(out_typ, EnumType): ret = to_enum(arg_ast, arg, out_typ) elif is_integer_type(out_typ): ret = to_int(arg_ast, arg, out_typ) elif is_bytes_m_type(out_typ): ret = to_bytes_m(arg_ast, arg, out_typ) elif is_decimal_type(out_typ): ret = to_decimal(arg_ast, arg, out_typ) elif isinstance(out_typ, ByteArrayType): ret = to_bytes(arg_ast, arg, out_typ) elif isinstance(out_typ, StringType): ret = to_string(arg_ast, arg, out_typ) else: raise StructureException(f"Conversion to {out_typ} is invalid.", arg_ast) # test if arg actually changed. if not, we do not need to use # unwrap_location (this can reduce memory traffic for downstream # operations which are in-place, like the returndata routine) test_arg = IRnode.from_list(arg, typ=out_typ) if test_arg == ret: original_arg.typ = out_typ return original_arg return IRnode.from_list(b.resolve(ret))
def _bytes_to_num(arg, out_typ, signed): # converting a bytestring to a number: # bytestring and bytes_m are right-padded with zeroes, int is left-padded. # convert by shr or sar the number of zero bytes (converted to bits) # e.g. "abcd000000000000" -> bitcast(000000000000abcd, output_type) if isinstance(arg.typ, ByteArrayLike): _len = get_bytearray_length(arg) arg = LOAD(bytes_data_ptr(arg)) num_zero_bits = ["mul", 8, ["sub", 32, _len]] elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info num_zero_bits = 8 * (32 - info.m) else: raise CompilerPanic("unreachable") # pragma: notest if signed: ret = sar(num_zero_bits, arg) else: ret = shr(num_zero_bits, arg) annotation = (f"__intrinsic__byte_array_to_num({out_typ})", ) return IRnode.from_list(ret, annotation=annotation)
def to_decimal(expr, arg, out_typ): _check_bytes(expr, arg, out_typ, 32) out_info = out_typ._decimal_info if isinstance(expr, vy_ast.Constant): return _literal_decimal(expr, arg.typ, out_typ) if isinstance(arg.typ, ByteArrayType): arg_typ = arg.typ arg = _bytes_to_num(arg, out_typ, signed=True) if arg_typ.maxlen * 8 > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_bytes_m_type(arg.typ): info = arg.typ._bytes_info arg = _bytes_to_num(arg, out_typ, signed=True) if info.m_bits > 168: arg = IRnode.from_list(arg, typ=out_typ) arg = clamp_basetype(arg) return IRnode.from_list(arg, typ=out_typ) elif is_integer_type(arg.typ): arg = _int_to_fixed(arg, out_typ) return IRnode.from_list(arg, typ=out_typ) elif is_base_type(arg.typ, "bool"): # TODO: consider adding _int_info to bool so we can use _int_to_fixed arg = ["mul", arg, 10**out_info.decimals] return IRnode.from_list(arg, typ=out_typ) else: raise CompilerPanic("unreachable") # pragma: notest