def byte_array_to_num( arg, expr, out_type, offset=32, ): if arg.location == "memory": lengetter = LLLnode.from_list(["mload", "_sub"], typ=BaseType("int128")) first_el_getter = LLLnode.from_list(["mload", ["add", 32, "_sub"]], typ=BaseType("int128")) elif arg.location == "storage": lengetter = LLLnode.from_list(["sload", ["sha3_32", "_sub"]], typ=BaseType("int128")) first_el_getter = LLLnode.from_list( ["sload", ["add", 1, ["sha3_32", "_sub"]]], typ=BaseType("int128") ) if out_type == "int128": result = int128_clamp(["div", "_el1", ["exp", 256, ["sub", 32, "_len"]]]) elif out_type == "uint256": result = ["div", "_el1", ["exp", 256, ["sub", offset, "_len"]]] return LLLnode.from_list( [ "with", "_sub", arg, [ "with", "_el1", first_el_getter, ["with", "_len", ["clamp", 0, lengetter, 32], result], ], ], typ=BaseType(out_type), annotation=f"bytearray to number ({out_type})", )
def parse_BinOp(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): return pos = getpos(self.expr) types = {left.typ.typ, right.typ.typ} literals = {left.typ.is_literal, right.typ.is_literal} # If one value of the operation is a literal, we recast it to match the non-literal type. # We know this is OK because types were already verified in the actual typechecking pass. # This is a temporary solution to not break parser while we work toward removing types # altogether at this stage of complition. @iamdefinitelyahuman if literals == {True, False } and len(types) > 1 and "decimal" not in types: if left.typ.is_literal and SizeLimits.in_bounds( right.typ.typ, left.value): left = LLLnode.from_list( left.value, typ=BaseType(right.typ.typ, None, is_literal=True), pos=pos, ) elif right.typ.is_literal and SizeLimits.in_bounds( left.typ.typ, right.value): right = LLLnode.from_list( right.value, typ=BaseType(left.typ.typ, None, is_literal=True), pos=pos, ) ltyp, rtyp = left.typ.typ, right.typ.typ if ltyp != rtyp: # Sanity check - ensure that we aren't dealing with different types # This should be unreachable due to the type check pass return arith = None if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)): new_typ = BaseType(ltyp) if ltyp == "uint256": if isinstance(self.expr.op, vy_ast.Add): # safeadd arith = [ "seq", ["assert", ["ge", ["add", "l", "r"], "l"]], ["add", "l", "r"] ] elif isinstance(self.expr.op, vy_ast.Sub): # safesub arith = [ "seq", ["assert", ["ge", "l", "r"]], ["sub", "l", "r"] ] elif ltyp == "int256": if isinstance(self.expr.op, vy_ast.Add): op, comp1, comp2 = "add", "sge", "slt" else: op, comp1, comp2 = "sub", "sle", "sgt" if right.typ.is_literal: if right.value >= 0: arith = [ "seq", ["assert", [comp1, [op, "l", "r"], "l"]], [op, "l", "r"] ] else: arith = [ "seq", ["assert", [comp2, [op, "l", "r"], "l"]], [op, "l", "r"] ] else: arith = [ "with", "ans", [op, "l", "r"], [ "seq", [ "assert", [ "or", [ "and", ["sge", "r", 0], [comp1, "ans", "l"] ], [ "and", ["slt", "r", 0], [comp2, "ans", "l"] ], ], ], "ans", ], ] elif ltyp in ("decimal", "int128"): op = "add" if isinstance(self.expr.op, vy_ast.Add) else "sub" arith = [op, "l", "r"] elif isinstance(self.expr.op, vy_ast.Mult): new_typ = BaseType(ltyp) if ltyp == "uint256": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["div", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp == "int256": if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) if not left.typ.is_literal and not right.typ.is_literal: bounds_check = [ "assert", [ "or", ["ne", "l", ["not", 0]], ["ne", "r", upper_bound] ], ] elif left.typ.is_literal and left.value == -1: bounds_check = ["assert", ["ne", "r", upper_bound]] elif right.typ.is_literal and right.value == -(2**255): bounds_check = ["assert", ["ne", "l", ["not", 0]]] else: bounds_check = "pass" arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", bounds_check, [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp == "int128": arith = ["mul", "l", "r"] elif ltyp == "decimal": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], ["sdiv", "ans", DECIMAL_DIVISOR], ], ] elif isinstance(self.expr.op, vy_ast.Div): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp == "uint256": arith = ["div", "l", divisor] elif ltyp == "int256": if version_check(begin="constantinople"): upper_bound = ["shl", 255, 1] else: upper_bound = -(2**255) if not left.typ.is_literal and not right.typ.is_literal: bounds_check = [ "assert", [ "or", ["ne", "r", ["not", 0]], ["ne", "l", upper_bound] ], ] elif left.typ.is_literal and left.value == -(2**255): bounds_check = ["assert", ["ne", "r", ["not", 0]]] elif right.typ.is_literal and right.value == -1: bounds_check = ["assert", ["ne", "l", upper_bound]] else: bounds_check = "pass" arith = ["seq", bounds_check, ["sdiv", "l", divisor]] elif ltyp in ("int128", "int256"): arith = ["sdiv", "l", divisor] elif ltyp == "decimal": arith = [ "sdiv", ["mul", "l", DECIMAL_DIVISOR], divisor, ] elif isinstance(self.expr.op, vy_ast.Mod): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp == "uint256": arith = ["mod", "l", divisor] else: arith = ["smod", "l", divisor] elif isinstance(self.expr.op, vy_ast.Pow): new_typ = BaseType(ltyp) if self.expr.left.get("value") == 1: return LLLnode.from_list([1], typ=new_typ, pos=pos) if self.expr.left.get("value") == 0: return LLLnode.from_list(["iszero", right], typ=new_typ, pos=pos) if ltyp == "int128": is_signed = True num_bits = 128 elif ltyp == "int256": is_signed = True num_bits = 256 else: is_signed = False num_bits = 256 if isinstance(self.expr.left, vy_ast.Int): value = self.expr.left.value upper_bound = calculate_largest_power(value, num_bits, is_signed) + 1 # for signed integers, this also prevents negative values clamp = ["lt", right, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) elif isinstance(self.expr.right, vy_ast.Int): value = self.expr.right.value upper_bound = calculate_largest_base(value, num_bits, is_signed) + 1 if is_signed: clamp = [ "and", ["slt", left, upper_bound], ["sgt", left, -upper_bound] ] else: clamp = ["lt", left, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) else: # `a ** b` where neither `a` or `b` are known # TODO this is currently unreachable, once we implement a way to do it safely # remove the check in `vyper/context/types/value/numeric.py` return if arith is None: return p = ["seq"] if new_typ.typ == "int128": p.append(int128_clamp(arith)) elif new_typ.typ == "decimal": p.append([ "clamp", ["mload", MemoryPositions.MINDECIMAL], arith, ["mload", MemoryPositions.MAXDECIMAL], ]) elif new_typ.typ in ("uint256", "int256"): p.append(arith) else: return p = ["with", "l", left, ["with", "r", right, p]] return LLLnode.from_list(p, typ=new_typ, pos=pos)
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 == "bytes32": 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: return LLLnode.from_list( int128_clamp(in_arg), typ=BaseType("int128"), pos=getpos(expr), ) 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, expr, "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)) else: return LLLnode.from_list( ["uclample", in_arg, ["mload", MemoryPositions.MAXNUM]], typ=BaseType("int128"), pos=getpos(expr), ) elif input_type == "decimal": return LLLnode.from_list( int128_clamp(["sdiv", in_arg, DECIMAL_DIVISOR]), typ=BaseType("int128"), pos=getpos(expr), ) elif input_type == "bool": return LLLnode.from_list(in_arg, typ=BaseType("int128"), pos=getpos(expr)) else: raise InvalidLiteral(f"Invalid input for int128: {in_arg}", expr)
def parse_BinOp(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): return arithmetic_pair = {left.typ.typ, right.typ.typ} pos = getpos(self.expr) # Special case with uint256 were int literal may be casted. if arithmetic_pair == {"uint256", "int128"}: # Check right side literal. if right.typ.is_literal and SizeLimits.in_bounds( "uint256", right.value): right = LLLnode.from_list( right.value, typ=BaseType("uint256", None, is_literal=True), pos=pos, ) # Check left side literal. elif left.typ.is_literal and SizeLimits.in_bounds( "uint256", left.value): left = LLLnode.from_list( left.value, typ=BaseType("uint256", None, is_literal=True), pos=pos, ) if left.typ.typ == "decimal" and isinstance(self.expr.op, vy_ast.Pow): return # Only allow explicit conversions to occur. if left.typ.typ != right.typ.typ: return ltyp, rtyp = left.typ.typ, right.typ.typ arith = None if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)): new_typ = BaseType(ltyp) op = "add" if isinstance(self.expr.op, vy_ast.Add) else "sub" if ltyp == "uint256" and isinstance(self.expr.op, vy_ast.Add): # safeadd arith = [ "seq", ["assert", ["ge", ["add", "l", "r"], "l"]], ["add", "l", "r"] ] elif ltyp == "uint256" and isinstance(self.expr.op, vy_ast.Sub): # safesub arith = [ "seq", ["assert", ["ge", "l", "r"]], ["sub", "l", "r"] ] elif ltyp == rtyp: arith = [op, "l", "r"] elif isinstance(self.expr.op, vy_ast.Mult): new_typ = BaseType(ltyp) if ltyp == rtyp == "uint256": arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["div", "ans", "l"], "r"], ["iszero", "l"] ] ], "ans", ], ] elif ltyp == rtyp == "int128": # TODO should this be 'smul' (note edge cases in YP for smul) arith = ["mul", "l", "r"] elif ltyp == rtyp == "decimal": # TODO should this be smul arith = [ "with", "ans", ["mul", "l", "r"], [ "seq", [ "assert", [ "or", ["eq", ["sdiv", "ans", "l"], "r"], ["iszero", "l"] ] ], ["sdiv", "ans", DECIMAL_DIVISOR], ], ] elif isinstance(self.expr.op, vy_ast.Div): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp == rtyp == "uint256": arith = ["div", "l", divisor] elif ltyp == rtyp == "int128": arith = ["sdiv", "l", divisor] elif ltyp == rtyp == "decimal": arith = [ "sdiv", # TODO check overflow cases, also should it be smul ["mul", "l", DECIMAL_DIVISOR], divisor, ] elif isinstance(self.expr.op, vy_ast.Mod): if right.typ.is_literal and right.value == 0: return new_typ = BaseType(ltyp) if right.typ.is_literal: divisor = "r" else: # only apply the non-zero clamp when r is not a constant divisor = ["clamp_nonzero", "r"] if ltyp == rtyp == "uint256": arith = ["mod", "l", divisor] elif ltyp == rtyp: # TODO should this be regular mod arith = ["smod", "l", divisor] elif isinstance(self.expr.op, vy_ast.Pow): if ltyp != "int128" and ltyp != "uint256" and isinstance( self.expr.right, vy_ast.Name): return new_typ = BaseType(ltyp) if self.expr.left.get("value") == 1: return LLLnode.from_list([1], typ=new_typ, pos=pos) if self.expr.left.get("value") == 0: return LLLnode.from_list(["iszero", right], typ=new_typ, pos=pos) if ltyp == "int128": is_signed = True num_bits = 128 else: is_signed = False num_bits = 256 if isinstance(self.expr.left, vy_ast.Int): value = self.expr.left.value upper_bound = calculate_largest_power(value, num_bits, is_signed) + 1 # for signed integers, this also prevents negative values clamp = ["lt", right, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) elif isinstance(self.expr.right, vy_ast.Int): value = self.expr.right.value upper_bound = calculate_largest_base(value, num_bits, is_signed) + 1 if is_signed: clamp = [ "and", ["slt", left, upper_bound], ["sgt", left, -upper_bound] ] else: clamp = ["lt", left, upper_bound] return LLLnode.from_list( ["seq", ["assert", clamp], ["exp", left, right]], typ=new_typ, pos=pos, ) else: # `a ** b` where neither `a` or `b` are known # TODO this is currently unreachable, once we implement a way to do it safely # remove the check in `vyper/context/types/value/numeric.py` return if arith is None: return p = ["seq"] if new_typ.typ == "int128": p.append(int128_clamp(arith)) elif new_typ.typ == "decimal": p.append([ "clamp", ["mload", MemoryPositions.MINDECIMAL], arith, ["mload", MemoryPositions.MAXDECIMAL], ]) elif new_typ.typ == "uint256": p.append(arith) else: return p = ["with", "l", left, ["with", "r", right, p]] return LLLnode.from_list(p, typ=new_typ, pos=pos)
def to_decimal(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == "Bytes": if in_arg.typ.maxlen > 32: raise TypeMismatch( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to decimal", expr, ) num = byte_array_to_num(in_arg, expr, "int128") return LLLnode.from_list(["mul", num, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: if input_type == "uint256": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteral( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: return LLLnode.from_list( [ "uclample", ["mul", in_arg, DECIMAL_DIVISOR], ["mload", MemoryPositions.MAXDECIMAL], ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "address": return LLLnode.from_list( [ "mul", [ "signextend", 15, ["and", in_arg, (SizeLimits.ADDRSIZE - 1)] ], DECIMAL_DIVISOR, ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "bytes32": if in_arg.typ.is_literal: if not SizeLimits.in_bounds("int128", (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteral( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: return LLLnode.from_list( [ "clamp", ["mload", MemoryPositions.MINDECIMAL], ["mul", in_arg, DECIMAL_DIVISOR], ["mload", MemoryPositions.MAXDECIMAL], ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type == "int256": return LLLnode.from_list( [ "seq", int128_clamp(in_arg), ["mul", in_arg, DECIMAL_DIVISOR] ], typ=BaseType("decimal"), pos=getpos(expr), ) elif input_type in ("int128", "bool"): return LLLnode.from_list(["mul", in_arg, DECIMAL_DIVISOR], typ=BaseType("decimal"), pos=getpos(expr)) else: raise InvalidLiteral(f"Invalid input for decimal: {in_arg}", expr)