Exemple #1
0
def safe_div(x, y):
    num_info = x.typ._num_info
    typ = x.typ

    ok = [1]  # true

    if is_decimal_type(x.typ):
        lo, hi = num_info.bounds
        if max(abs(lo), abs(hi)) * num_info.divisor > 2**256 - 1:
            # stub to prevent us from adding fixed point numbers we don't know
            # how to deal with
            raise UnimplementedException(
                "safe_mul for decimal{num_info.bits}x{num_info.decimals}")
        x = ["mul", x, num_info.divisor]

    DIV = "sdiv" if num_info.is_signed else "div"
    res = IRnode.from_list([DIV, x, clamp("gt", y, 0)], typ=typ)
    with res.cache_when_complex("res") as (b1, res):

        # TODO: refactor this condition / push some things into the optimizer
        if num_info.is_signed and num_info.bits == 256:
            if version_check(begin="constantinople"):
                upper_bound = ["shl", 255, 1]
            else:
                upper_bound = -(2**255)

            if not x.is_literal and not y.typ.is_literal:
                ok = ["or", ["ne", y, ["not", 0]], ["ne", x, upper_bound]]
            # TODO push these rules into the optimizer
            elif x.is_literal and x.value == -(2**255):
                ok = ["ne", y, ["not", 0]]
            elif y.is_literal and y.value == -1:
                ok = ["ne", x, upper_bound]
            else:
                # x or y is a literal, and not an evil value.
                pass

        elif num_info.is_signed and is_integer_type(typ):
            lo, hi = num_info.bounds
            # we need to throw on min_value(typ) / -1,
            # but we can skip if one of the operands is a literal and not
            # the evil value
            can_skip_clamp = (x.is_literal
                              and x.value != lo) or (y.is_literal
                                                     and y.value != -1)
            if not can_skip_clamp:
                # clamp_basetype has fewer ops than the int256 rule.
                res = clamp_basetype(res)

        elif is_decimal_type(typ):
            # always clamp decimals, since decimal division can actually
            # result in something larger than either operand (e.g. 1.0 / 0.1)
            # TODO maybe use safe_mul
            res = clamp_basetype(res)

        check = IRnode.from_list(["assert", ok], error_msg="safemul")
        return IRnode.from_list(b1.resolve(["seq", check, res]))
Exemple #2
0
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)
Exemple #3
0
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)
Exemple #4
0
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)
Exemple #5
0
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
Exemple #6
0
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"
Exemple #7
0
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)
Exemple #8
0
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))
Exemple #9
0
def safe_mul(x, y):
    # precondition: x.typ.typ == y.typ.typ
    num_info = x.typ._num_info

    # optimizer rules work better for the safemul checks below
    # if second operand is literal
    if x.is_literal:
        tmp = x
        x = y
        y = tmp

    res = IRnode.from_list(["mul", x, y], typ=x.typ.typ)

    DIV = "sdiv" if num_info.is_signed else "div"

    with res.cache_when_complex("ans") as (b1, res):

        ok = [1]  # True

        if num_info.bits > 128:  # check overflow mod 256
            # assert (res/y == x | y == 0)
            ok = ["or", ["eq", [DIV, res, y], x], ["iszero", y]]

        # int256
        if num_info.is_signed and num_info.bits == 256:
            # special case:
            # in the above sdiv check, if (r==-1 and l==-2**255),
            # -2**255<res> / -1<r> will return -2**255<l>.
            # need to check: not (r == -1 and l == -2**255)
            if version_check(begin="constantinople"):
                upper_bound = ["shl", 255, 1]
            else:
                upper_bound = -(2**255)

            check_x = ["ne", x, upper_bound]
            check_y = ["ne", ["not", y], 0]

            if not x.is_literal and not y.is_literal:
                # TODO can simplify this condition?
                ok = ["and", ok, ["or", check_x, check_y]]

            # TODO push some of this constant folding into optimizer
            elif x.is_literal and x.value == -(2**255):
                ok = ["and", ok, check_y]
            elif y.is_literal and y.value == -1:
                ok = ["and", ok, check_x]
            else:
                # x or y is a literal, and we have determined it is
                # not an evil value
                pass

        if is_decimal_type(res.typ):
            res = IRnode.from_list([DIV, res, num_info.divisor], typ=res.typ)

        # check overflow mod <bits>
        # NOTE: if 128 < bits < 256, `x * y` could be between
        # MAX_<bits> and 2**256 OR it could overflow past 2**256.
        # so, we check for overflow in mod 256 AS WELL AS mod <bits>
        # (if bits == 256, clamp_basetype is a no-op)
        res = clamp_basetype(res)

        check = IRnode.from_list(["assert", ok], error_msg="safediv")
        res = IRnode.from_list(["seq", check, res], typ=res.typ)

        return b1.resolve(res)