Beispiel #1
0
 def compare(self):
     left = Expr.parse_value_expr(self.expr.left, self.context)
     right = Expr.parse_value_expr(self.expr.comparators[0], self.context)
     if isinstance(self.expr.ops[0], ast.In) and \
        isinstance(right.typ, ListType):
         if not are_units_compatible(
                 left.typ, right.typ.subtype) and not are_units_compatible(
                     right.typ.subtype, left.typ):
             raise TypeMismatchException(
                 "Can't use IN comparison with different types!", self.expr)
         return self.build_in_comparator()
     else:
         if not are_units_compatible(
                 left.typ, right.typ) and not are_units_compatible(
                     right.typ, left.typ):
             raise TypeMismatchException(
                 "Can't compare values with different units!", self.expr)
     if len(self.expr.ops) != 1:
         raise StructureException(
             "Cannot have a comparison with more than two elements",
             self.expr)
     if isinstance(self.expr.ops[0], ast.Gt):
         op = 'sgt'
     elif isinstance(self.expr.ops[0], ast.GtE):
         op = 'sge'
     elif isinstance(self.expr.ops[0], ast.LtE):
         op = 'sle'
     elif isinstance(self.expr.ops[0], ast.Lt):
         op = 'slt'
     elif isinstance(self.expr.ops[0], ast.Eq):
         op = 'eq'
     elif isinstance(self.expr.ops[0], ast.NotEq):
         op = 'ne'
     else:
         raise Exception("Unsupported comparison operator")
     if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
         if op not in ('eq', 'ne'):
             raise TypeMismatchException("Invalid type for comparison op",
                                         self.expr)
     ltyp, rtyp = left.typ.typ, right.typ.typ
     if ltyp == rtyp:
         return LLLnode.from_list([op, left, right],
                                  typ='bool',
                                  pos=getpos(self.expr))
     elif ltyp == 'decimal' and rtyp == 'int128':
         return LLLnode.from_list(
             [op, left, ['mul', right, DECIMAL_DIVISOR]],
             typ='bool',
             pos=getpos(self.expr))
     elif ltyp == 'int128' and rtyp == 'decimal':
         return LLLnode.from_list(
             [op, ['mul', left, DECIMAL_DIVISOR], right],
             typ='bool',
             pos=getpos(self.expr))
     else:
         raise TypeMismatchException(
             "Unsupported types for comparison: %r %r" % (ltyp, rtyp),
             self.expr)
Beispiel #2
0
 def compare(self):
     left = Expr.parse_value_expr(self.expr.left, self.context)
     right = Expr.parse_value_expr(self.expr.comparators[0], self.context)
     if isinstance(self.expr.ops[0], ast.In) and \
        isinstance(right.typ, ListType):
         if not are_units_compatible(
                 left.typ, right.typ.subtype) and not are_units_compatible(
                     right.typ.subtype, left.typ):
             raise TypeMismatchException(
                 "Can't use IN comparison with different types!", self.expr)
         return self.build_in_comparator()
     else:
         if not are_units_compatible(
                 left.typ, right.typ) and not are_units_compatible(
                     right.typ, left.typ):
             raise TypeMismatchException(
                 "Can't compare values with different units!", self.expr)
     if len(self.expr.ops) != 1:
         raise StructureException(
             "Cannot have a comparison with more than two elements",
             self.expr)
     if isinstance(self.expr.ops[0], ast.Gt):
         op = 'sgt'
     elif isinstance(self.expr.ops[0], ast.GtE):
         op = 'sge'
     elif isinstance(self.expr.ops[0], ast.LtE):
         op = 'sle'
     elif isinstance(self.expr.ops[0], ast.Lt):
         op = 'slt'
     elif isinstance(self.expr.ops[0], ast.Eq):
         op = 'eq'
     elif isinstance(self.expr.ops[0], ast.NotEq):
         op = 'ne'
     else:
         raise Exception("Unsupported comparison operator")
     if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
         if op not in ('eq', 'ne'):
             raise TypeMismatchException("Invalid type for comparison op",
                                         self.expr)
     left_type, right_type = left.typ.typ, right.typ.typ
     if (left_type in ('decimal', 'int128') or right_type
             in ('decimal', 'int128')) and left_type != right_type:
         raise TypeMismatchException(
             'Implicit conversion from {} to {} disallowed, please convert.'
             .format(left_type, right_type), self.expr)
     if left_type == right_type:
         return LLLnode.from_list([op, left, right],
                                  typ='bool',
                                  pos=getpos(self.expr))
     else:
         raise TypeMismatchException(
             "Unsupported types for comparison: %r %r" %
             (left_type, right_type), self.expr)
Beispiel #3
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, ast.Not):
            # Note that in the case of bool, num, address, decimal, uint256 AND bytes32,
            # a zero entry represents false, all others represent true
            return LLLnode.from_list(["iszero", operand],
                                     typ='bool',
                                     pos=getpos(self.expr))
        elif isinstance(self.expr.op, ast.USub):
            if not is_numeric_type(operand.typ):
                raise TypeMismatchException(
                    "Unsupported type for negation: %r" % operand.typ, operand)

            if operand.typ.is_literal and 'int' in operand.typ.typ:
                num = ast.Num(0 - operand.value)
                num.source_code = self.expr.source_code
                num.lineno = self.expr.lineno
                num.col_offset = self.expr.col_offset
                return Expr.parse_value_expr(num, self.context)

            return LLLnode.from_list(["sub", 0, operand],
                                     typ=operand.typ,
                                     pos=getpos(self.expr))
        else:
            raise StructureException(
                "Only the 'not' unary operator is supported")
Beispiel #4
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, ast.Not):
            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
                return LLLnode.from_list(["iszero", operand],
                                         typ='bool',
                                         pos=getpos(self.expr))
            else:
                raise TypeMismatchException(
                    "Only bool is supported for not operation, %r supplied." %
                    operand.typ,
                    self.expr,
                )
        elif isinstance(self.expr.op, ast.USub):
            if not is_numeric_type(operand.typ):
                raise TypeMismatchException(
                    "Unsupported type for negation: %r" % operand.typ,
                    operand,
                )

            if operand.typ.is_literal and 'int' in operand.typ.typ:
                num = ast.Num(0 - operand.value)
                num.source_code = self.expr.source_code
                num.lineno = self.expr.lineno
                num.col_offset = self.expr.col_offset
                return Expr.parse_value_expr(num, self.context)

            return LLLnode.from_list(["sub", 0, operand],
                                     typ=operand.typ,
                                     pos=getpos(self.expr))
        else:
            raise StructureException(
                "Only the 'not' unary operator is supported")
Beispiel #5
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, vy_ast.Not):
            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
                return LLLnode.from_list(["iszero", operand],
                                         typ='bool',
                                         pos=getpos(self.expr))
            else:
                raise TypeMismatch(
                    f"Only bool is supported for not operation, {operand.typ} supplied.",
                    self.expr,
                )
        elif isinstance(self.expr.op, vy_ast.USub):
            if not is_numeric_type(operand.typ):
                raise TypeMismatch(
                    f"Unsupported type for negation: {operand.typ}",
                    self.expr,
                )

            # Clamp on minimum integer value as we cannot negate that value
            # (all other integer values are fine)
            min_int_val = get_min_val_for_type(operand.typ.typ)
            return LLLnode.from_list(
                ["sub", 0, ["clampgt", operand, min_int_val]],
                typ=operand.typ,
                pos=getpos(self.expr))
        else:
            raise StructureException(
                "Only the 'not' or 'neg' unary operators are supported")
Beispiel #6
0
 def parse_UnaryOp(self):
     operand = Expr.parse_value_expr(self.expr.operand, self.context)
     if isinstance(self.expr.op, vy_ast.Not):
         if isinstance(operand.typ, BaseType) and operand.typ.typ == "bool":
             return LLLnode.from_list(["iszero", operand], typ="bool", pos=getpos(self.expr))
     elif isinstance(self.expr.op, vy_ast.USub) and is_numeric_type(operand.typ):
         # Clamp on minimum integer value as we cannot negate that value
         # (all other integer values are fine)
         min_int_val = get_min_val_for_type(operand.typ.typ)
         return LLLnode.from_list(
             ["sub", 0, ["clampgt", operand, min_int_val]],
             typ=operand.typ,
             pos=getpos(self.expr),
         )
Beispiel #7
0
    def unary_operations(self):
        operand = Expr.parse_value_expr(self.expr.operand, self.context)
        if isinstance(self.expr.op, ast.Not):
            if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool':
                return LLLnode.from_list(["iszero", operand],
                                         typ='bool',
                                         pos=getpos(self.expr))
            else:
                raise TypeMismatchException(
                    f"Only bool is supported for not operation, {operand.typ} supplied.",
                    self.expr,
                )
        elif isinstance(self.expr.op, ast.USub):
            # Must be a signed integer
            if not is_numeric_type(
                    operand.typ) or operand.typ.typ.lower().startswith('u'):
                raise TypeMismatchException(
                    f"Unsupported type for negation: {operand.typ}",
                    operand,
                )

            if operand.typ.is_literal and 'int' in operand.typ.typ:
                num = ast.Num(n=0 - operand.value)
                num.source_code = self.expr.source_code
                num.lineno = self.expr.lineno
                num.col_offset = self.expr.col_offset
                num.end_lineno = self.expr.end_lineno
                num.end_col_offset = self.expr.end_col_offset
                return Expr.parse_value_expr(num, self.context)

            # Clamp on minimum integer value as we cannot negate that value
            # (all other integer values are fine)
            min_int_val = get_min_val_for_type(operand.typ.typ)
            return LLLnode.from_list(
                ["sub", 0, ["clampgt", operand, min_int_val]],
                typ=operand.typ,
                pos=getpos(self.expr))
        else:
            raise StructureException(
                "Only the 'not' or 'neg' unary operators are supported")
Beispiel #8
0
    def arithmetic(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):
            raise TypeMismatchException(
                f"Unsupported types for arithmetic op: {left.typ} {right.typ}",
                self.expr,
            )

        arithmetic_pair = {left.typ.typ, right.typ.typ}

        # Special Case: Simplify any literal to literal arithmetic at compile time.
        if left.typ.is_literal and right.typ.is_literal and \
           isinstance(right.value, int) and isinstance(left.value, int) and \
           arithmetic_pair.issubset({'uint256', 'int128'}):

            if isinstance(self.expr.op, vy_ast.Add):
                val = left.value + right.value
            elif isinstance(self.expr.op, vy_ast.Sub):
                val = left.value - right.value
            elif isinstance(self.expr.op, vy_ast.Mult):
                val = left.value * right.value
            elif isinstance(self.expr.op, vy_ast.Pow):
                val = left.value ** right.value
            elif isinstance(self.expr.op, (vy_ast.Div, vy_ast.Mod)):
                if right.value == 0:
                    raise ZeroDivisionException(
                        "integer division or modulo by zero",
                        self.expr,
                    )
                if isinstance(self.expr.op, vy_ast.Div):
                    val = left.value // right.value
                elif isinstance(self.expr.op, vy_ast.Mod):
                    # modified modulo logic to remain consistent with EVM behaviour
                    val = abs(left.value) % abs(right.value)
                    if left.value < 0:
                        val = -val
            else:
                raise ParserException(
                    f'Unsupported literal operator: {type(self.expr.op)}',
                    self.expr,
                )

            num = vy_ast.Int(n=val)
            num.full_source_code = self.expr.full_source_code
            num.node_source_code = self.expr.node_source_code
            num.lineno = self.expr.lineno
            num.col_offset = self.expr.col_offset
            num.end_lineno = self.expr.end_lineno
            num.end_col_offset = self.expr.end_col_offset

            return Expr.parse_value_expr(num, self.context)

        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):
            raise TypeMismatchException(
                "Cannot perform exponentiation on decimal values.",
                self.expr,
            )

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            raise TypeMismatchException(
                f"Cannot implicitly convert {left.typ.typ} to {right.typ.typ}.",
                self.expr,
            )

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (vy_ast.Add, vy_ast.Sub)):
            if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}:
                raise TypeMismatchException(
                    f"Unit mismatch: {left.typ.unit} {right.typ.unit}",
                    self.expr,
                )
            if (
                left.typ.positional and
                right.typ.positional and
                isinstance(self.expr.op, vy_ast.Add)
            ):
                raise TypeMismatchException(
                    "Cannot add two positional units!",
                    self.expr,
                )
            new_unit = left.typ.unit or right.typ.unit

            # xor, as subtracting two positionals gives a delta
            new_positional = left.typ.positional ^ right.typ.positional

            new_typ = BaseType(ltyp, new_unit, new_positional)

            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']

            else:
                raise Exception(f"Unsupported Operation '{op}({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Mult):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot multiply positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit)
            new_typ = BaseType(ltyp, new_unit)

            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]]]
            else:
                raise Exception(f"Unsupported Operation 'mul({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Div):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot divide by 0.", self.expr)
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot divide positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
            new_typ = BaseType(ltyp, new_unit)
            if ltyp == rtyp == 'uint256':
                arith = ['div', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'int128':
                arith = ['sdiv', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'decimal':
                arith = ['sdiv',
                         # TODO check overflow cases, also should it be smul
                         ['mul', 'l', DECIMAL_DIVISOR],
                         ['clamp_nonzero', 'r']]

            else:
                raise Exception(f"Unsupported Operation 'div({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot calculate modulus of 0.", self.expr)
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as modulus arguments!",
                    self.expr,
                )

            if not are_units_compatible(left.typ, right.typ) and not (left.typ.unit or right.typ.unit):  # noqa: E501
                raise TypeMismatchException("Modulus arguments must have same unit", self.expr)
            new_unit = left.typ.unit or right.typ.unit
            new_typ = BaseType(ltyp, new_unit)

            if ltyp == rtyp == 'uint256':
                arith = ['mod', 'l', ['clamp_nonzero', 'r']]
            elif ltyp == rtyp:
                # TODO should this be regular mod
                arith = ['smod', 'l', ['clamp_nonzero', 'r']]

            else:
                raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, vy_ast.Pow):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as exponential arguments!",
                    self.expr,
                )
            if right.typ.unit:
                raise TypeMismatchException(
                    "Cannot use unit values as exponents",
                    self.expr,
                )
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(self.expr.right, vy_ast.Name):
                raise TypeMismatchException(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            new_unit = left.typ.unit
            if left.typ.unit and not isinstance(self.expr.right, vy_ast.Name):
                new_unit = {left.typ.unit.copy().popitem()[0]: self.expr.right.n}
            new_typ = BaseType(ltyp, new_unit)

            if ltyp == rtyp == 'uint256':
                arith = ['seq',
                         ['assert',
                             ['or',
                                 # r == 1 | iszero(r)
                                 # could be simplified to ~(r & 1)
                                 ['or', ['eq', 'r', 1], ['iszero', 'r']],
                                 ['lt', 'l', ['exp', 'l', 'r']]]],
                         ['exp', 'l', 'r']]
            elif ltyp == rtyp == 'int128':
                arith = ['exp', 'l', 'r']

            else:
                raise TypeMismatchException('Only whole number exponents are supported', self.expr)
        else:
            raise ParserException(f"Unsupported binary operator: {self.expr.op}", self.expr)

        p = ['seq']

        if new_typ.typ == 'int128':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINNUM],
                arith,
                ['mload', MemoryPositions.MAXNUM],
            ])
        elif new_typ.typ == 'decimal':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINDECIMAL],
                arith,
                ['mload', MemoryPositions.MAXDECIMAL],
            ])
        elif new_typ.typ == 'uint256':
            p.append(arith)
        else:
            raise Exception(f"{arith} {new_typ}")

        p = ['with', 'l', left, ['with', 'r', right, p]]
        return LLLnode.from_list(p, typ=new_typ, pos=pos)
Beispiel #9
0
    def arithmetic(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):
            raise TypeMismatch(
                f"Unsupported types for arithmetic op: {left.typ} {right.typ}",
                self.expr,
            )

        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):
            raise TypeMismatch(
                "Cannot perform exponentiation on decimal values.",
                self.expr,
            )

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            raise TypeMismatch(
                f"Cannot implicitly convert {left.typ.typ} to {right.typ.typ}.",
                self.expr,
            )

        ltyp, rtyp = left.typ.typ, right.typ.typ
        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']

            else:
                raise Exception(
                    f"Unsupported Operation '{op}({ltyp}, {rtyp})'")

        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]
                    ]
                ]
            else:
                raise Exception(f"Unsupported Operation 'mul({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Div):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot divide by 0.", self.expr)

            new_typ = BaseType(ltyp)
            if ltyp == rtyp == 'uint256':
                arith = ['div', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'int128':
                arith = ['sdiv', 'l', ['clamp_nonzero', 'r']]

            elif ltyp == rtyp == 'decimal':
                arith = [
                    'sdiv',
                    # TODO check overflow cases, also should it be smul
                    ['mul', 'l', DECIMAL_DIVISOR],
                    ['clamp_nonzero', 'r']
                ]

            else:
                raise Exception(f"Unsupported Operation 'div({ltyp}, {rtyp})'")

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                raise ZeroDivisionException("Cannot calculate modulus of 0.",
                                            self.expr)

            new_typ = BaseType(ltyp)

            if ltyp == rtyp == 'uint256':
                arith = ['mod', 'l', ['clamp_nonzero', 'r']]
            elif ltyp == rtyp:
                # TODO should this be regular mod
                arith = ['smod', 'l', ['clamp_nonzero', 'r']]

            else:
                raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, vy_ast.Pow):
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(
                    self.expr.right, vy_ast.Name):
                raise TypeMismatch(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            new_typ = BaseType(ltyp)

            if ltyp == rtyp == 'uint256':
                arith = [
                    'seq',
                    [
                        'assert',
                        [
                            'or',
                            # r == 1 | iszero(r)
                            # could be simplified to ~(r & 1)
                            ['or', ['eq', 'r', 1], ['iszero', 'r']],
                            ['lt', 'l', ['exp', 'l', 'r']]
                        ]
                    ],
                    ['exp', 'l', 'r']
                ]
            elif ltyp == rtyp == 'int128':
                arith = ['exp', 'l', 'r']

            else:
                raise TypeMismatch('Only whole number exponents are supported',
                                   self.expr)
        else:
            raise StructureException(
                f"Unsupported binary operator: {self.expr.op}", self.expr)

        p = ['seq']

        if new_typ.typ == 'int128':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINNUM],
                arith,
                ['mload', MemoryPositions.MAXNUM],
            ])
        elif new_typ.typ == 'decimal':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINDECIMAL],
                arith,
                ['mload', MemoryPositions.MAXDECIMAL],
            ])
        elif new_typ.typ == 'uint256':
            p.append(arith)
        else:
            raise Exception(f"{arith} {new_typ}")

        p = ['with', 'l', left, ['with', 'r', right, p]]
        return LLLnode.from_list(p, typ=new_typ, pos=pos)
Beispiel #10
0
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if right.value is None:
            raise InvalidLiteral(
                'Comparison to None is not allowed, compare against a default value.',
                self.expr,
            )

        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            # TODO: Can this if branch be removed ^
            pass

        elif isinstance(self.expr.op, vy_ast.In) and isinstance(
                right.typ, ListType):
            if left.typ != right.typ.subtype:
                raise TypeMismatch(
                    "Can't use IN comparison with different types!",
                    self.expr,
                )
            return self.build_in_comparator()

        if isinstance(self.expr.op, vy_ast.Gt):
            op = 'sgt'
        elif isinstance(self.expr.op, vy_ast.GtE):
            op = 'sge'
        elif isinstance(self.expr.op, vy_ast.LtE):
            op = 'sle'
        elif isinstance(self.expr.op, vy_ast.Lt):
            op = 'slt'
        elif isinstance(self.expr.op, vy_ast.Eq):
            op = 'eq'
        elif isinstance(self.expr.op, vy_ast.NotEq):
            op = 'ne'
        else:
            raise Exception("Unsupported comparison operator")

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.right, self.context).lll_node

            length_mismatch = (left.typ.maxlen != right.typ.maxlen)
            left_over_32 = left.typ.maxlen > 32
            right_over_32 = right.typ.maxlen > 32
            if length_mismatch or left_over_32 or right_over_32:
                left_keccak = keccak256_helper(self.expr, [left], None,
                                               self.context)
                right_keccak = keccak256_helper(self.expr, [right], None,
                                                self.context)

                if op == 'eq' or op == 'ne':
                    return LLLnode.from_list(
                        [op, left_keccak, right_keccak],
                        typ='bool',
                        pos=getpos(self.expr),
                    )

                else:
                    raise StructureException(
                        "Can only compare strings/bytes of length shorter",
                        " than 32 bytes other than equality comparisons",
                        self.expr,
                    )

            else:

                def load_bytearray(side):
                    if side.location == 'memory':
                        return ['mload', ['add', 32, side]]
                    elif side.location == 'storage':
                        return ['sload', ['add', 1, ['sha3_32', side]]]

                return LLLnode.from_list(
                    [op, load_bytearray(left),
                     load_bytearray(right)],
                    typ='bool',
                    pos=getpos(self.expr),
                )

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ('eq', 'ne'):
                raise TypeMismatch("Invalid type for comparison op", self.expr)
        left_type, right_type = left.typ.typ, right.typ.typ

        # Special Case: comparison of a literal integer. If in valid range allow it to be compared.
        if {left_type, right_type} == {'int128', 'uint256'} and {
                left.typ.is_literal, right.typ.is_literal
        } == {True, False}:  # noqa: E501

            comparison_allowed = False
            if left.typ.is_literal and SizeLimits.in_bounds(
                    right_type, left.value):
                comparison_allowed = True
            elif right.typ.is_literal and SizeLimits.in_bounds(
                    left_type, right.value):
                comparison_allowed = True
            op = self._signed_to_unsigned_comparision_op(op)

            if comparison_allowed:
                return LLLnode.from_list([op, left, right],
                                         typ='bool',
                                         pos=getpos(self.expr))

        elif {left_type, right_type} == {'uint256', 'uint256'}:
            op = self._signed_to_unsigned_comparision_op(op)
        elif (left_type in ('decimal', 'int128') or right_type in
              ('decimal', 'int128')) and left_type != right_type:  # noqa: E501
            raise TypeMismatch(
                f'Implicit conversion from {left_type} to {right_type} disallowed, please convert.',
                self.expr,
            )

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ='bool',
                                     pos=getpos(self.expr))
        else:
            raise TypeMismatch(
                f"Unsupported types for comparison: {left_type} {right_type}",
                self.expr,
            )
Beispiel #11
0
    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)
Beispiel #12
0
    def parse_Compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if right.value is None:
            return

        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            # TODO: Can this if branch be removed ^
            pass

        elif isinstance(self.expr.op,
                        (vy_ast.In, vy_ast.NotIn)) and isinstance(
                            right.typ, ListType):
            return self.build_in_comparator()

        if isinstance(self.expr.op, vy_ast.Gt):
            op = "sgt"
        elif isinstance(self.expr.op, vy_ast.GtE):
            op = "sge"
        elif isinstance(self.expr.op, vy_ast.LtE):
            op = "sle"
        elif isinstance(self.expr.op, vy_ast.Lt):
            op = "slt"
        elif isinstance(self.expr.op, vy_ast.Eq):
            op = "eq"
        elif isinstance(self.expr.op, vy_ast.NotEq):
            op = "ne"
        else:
            return

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.right, self.context).lll_node

            length_mismatch = left.typ.maxlen != right.typ.maxlen
            left_over_32 = left.typ.maxlen > 32
            right_over_32 = right.typ.maxlen > 32
            if length_mismatch or left_over_32 or right_over_32:
                left_keccak = keccak256_helper(self.expr, [left], None,
                                               self.context)
                right_keccak = keccak256_helper(self.expr, [right], None,
                                                self.context)

                if op == "eq" or op == "ne":
                    return LLLnode.from_list(
                        [op, left_keccak, right_keccak],
                        typ="bool",
                        pos=getpos(self.expr),
                    )

                else:
                    return

            else:

                def load_bytearray(side):
                    if side.location == "memory":
                        return ["mload", ["add", 32, side]]
                    elif side.location == "storage":
                        return ["sload", ["add", 1, side]]

                return LLLnode.from_list(
                    [op, load_bytearray(left),
                     load_bytearray(right)],
                    typ="bool",
                    pos=getpos(self.expr),
                )

        # Compare other types.
        if is_numeric_type(left.typ) and is_numeric_type(right.typ):
            if left.typ.typ == right.typ.typ == "uint256":
                # this works because we only have one unsigned integer type
                # in the future if others are added, this logic must be expanded
                op = self._signed_to_unsigned_comparision_op(op)

        elif op not in ("eq", "ne"):
            return

        return LLLnode.from_list([op, left, right],
                                 typ="bool",
                                 pos=getpos(self.expr))
Beispiel #13
0
    def arithmetic(self):
        pre_alloc_left, left = self.arithmetic_get_reference(self.expr.left)
        pre_alloc_right, right = self.arithmetic_get_reference(self.expr.right)

        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            raise TypeMismatchException(
                f"Unsupported types for arithmetic op: {left.typ} {right.typ}",
                self.expr,
            )

        arithmetic_pair = {left.typ.typ, right.typ.typ}

        # Special Case: Simplify any literal to literal arithmetic at compile time.
        if left.typ.is_literal and right.typ.is_literal and \
           isinstance(right.value, int) and isinstance(left.value, int) and \
           arithmetic_pair.issubset({'uint256', 'int128'}):

            if isinstance(self.expr.op, ast.Add):
                val = left.value + right.value
            elif isinstance(self.expr.op, ast.Sub):
                val = left.value - right.value
            elif isinstance(self.expr.op, ast.Mult):
                val = left.value * right.value
            elif isinstance(self.expr.op, ast.Div):
                val = left.value // right.value
            elif isinstance(self.expr.op, ast.Mod):
                val = left.value % right.value
            elif isinstance(self.expr.op, ast.Pow):
                val = left.value ** right.value
            else:
                raise ParserException(
                    f'Unsupported literal operator: {str(type(self.expr.op))}',
                    self.expr,
                )

            num = ast.Num(n=val)
            num.source_code = self.expr.source_code
            num.lineno = self.expr.lineno
            num.col_offset = self.expr.col_offset
            num.end_lineno = self.expr.end_lineno
            num.end_col_offset = self.expr.end_col_offset

            return Expr.parse_value_expr(num, self.context)

        # 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=getpos(self.expr),
                )

            # 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=getpos(self.expr),
                )

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            raise TypeMismatchException(
                f"Cannot implicitly convert {left.typ.typ} to {right.typ.typ}.",
                self.expr,
            )

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (ast.Add, ast.Sub)):
            if left.typ.unit != right.typ.unit and left.typ.unit != {} and right.typ.unit != {}:
                raise TypeMismatchException(
                    f"Unit mismatch: {left.typ.unit} {right.typ.unit}",
                    self.expr,
                )
            if left.typ.positional and right.typ.positional and isinstance(self.expr.op, ast.Add):
                raise TypeMismatchException(
                    "Cannot add two positional units!",
                    self.expr,
                )
            new_unit = left.typ.unit or right.typ.unit

            # xor, as subtracting two positionals gives a delta
            new_positional = left.typ.positional ^ right.typ.positional

            op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub'
            if ltyp == 'uint256' and isinstance(self.expr.op, ast.Add):
                o = LLLnode.from_list([
                    'seq',
                    # Checks that: a + b >= a
                    ['assert', ['ge', ['add', left, right], left]],
                    ['add', left, right],
                ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr))
            elif ltyp == 'uint256' and isinstance(self.expr.op, ast.Sub):
                o = LLLnode.from_list([
                    'seq',
                    # Checks that: a >= b
                    ['assert', ['ge', left, right]],
                    ['sub', left, right]
                ], typ=BaseType('uint256', new_unit, new_positional), pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list(
                    [op, left, right],
                    typ=BaseType(ltyp, new_unit, new_positional),
                    pos=getpos(self.expr),
                )
            else:
                raise Exception(f"Unsupported Operation '{op}({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, ast.Mult):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot multiply positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'if',
                    ['eq', left, 0],
                    [0],
                    [
                        'seq', ['assert', ['eq', ['div', ['mul', left, right], left], right]],
                        ['mul', left, right]
                    ],
                ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(
                    ['mul', left, right],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with', 'r', right, [
                        'with', 'l', left, [
                            'with', 'ans', ['mul', 'l', 'r'],
                            [
                                'seq',
                                [
                                    'assert',
                                    ['or', ['eq', ['sdiv', 'ans', 'l'], 'r'], ['iszero', 'l']]
                                ],
                                ['sdiv', 'ans', DECIMAL_DIVISOR],
                            ],
                        ],
                    ],
                ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr))
            else:
                raise Exception(f"Unsupported Operation 'mul({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, ast.Div):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot divide positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'seq',
                    # Checks that:  b != 0
                    ['assert', right],
                    ['div', left, right],
                ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(
                    ['sdiv', left, ['clamp_nonzero', right]],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with', 'l', left, [
                        'with', 'r', ['clamp_nonzero', right], [
                            'sdiv',
                            ['mul', 'l', DECIMAL_DIVISOR],
                            'r',
                        ],
                    ]
                ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr))
            else:
                raise Exception(f"Unsupported Operation 'div({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, ast.Mod):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as modulus arguments!",
                    self.expr,
                )
            if not are_units_compatible(left.typ, right.typ) and not (left.typ.unit or right.typ.unit):  # noqa: E501
                raise TypeMismatchException("Modulus arguments must have same unit", self.expr)
            new_unit = left.typ.unit or right.typ.unit
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'seq',
                    ['assert', right],
                    ['mod', left, right]
                ], typ=BaseType('uint256', new_unit), pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list(
                    ['smod', left, ['clamp_nonzero', right]],
                    typ=BaseType(ltyp, new_unit),
                    pos=getpos(self.expr),
                )
            else:
                raise Exception(f"Unsupported Operation 'mod({ltyp}, {rtyp})'")
        elif isinstance(self.expr.op, ast.Pow):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as exponential arguments!",
                    self.expr,
                )
            if right.typ.unit:
                raise TypeMismatchException(
                    "Cannot use unit values as exponents",
                    self.expr,
                )
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(self.expr.right, ast.Name):
                raise TypeMismatchException(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr,
                )
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'seq',
                    [
                        'assert',
                        [
                            'or',
                            ['or', ['eq', right, 1], ['iszero', right]],
                            ['lt', left, ['exp', left, right]]
                        ],
                    ],
                    ['exp', left, right],
                ], typ=BaseType('uint256'), pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                new_unit = left.typ.unit
                if left.typ.unit and not isinstance(self.expr.right, ast.Name):
                    new_unit = {left.typ.unit.copy().popitem()[0]: self.expr.right.n}
                o = LLLnode.from_list(
                    ['exp', left, right],
                    typ=BaseType('int128', new_unit),
                    pos=getpos(self.expr),
                )
            else:
                raise TypeMismatchException('Only whole number exponents are supported', self.expr)
        else:
            raise ParserException(f"Unsupported binary operator: {self.expr.op}", self.expr)

        p = ['seq']

        if pre_alloc_left:
            p.append(pre_alloc_left)
        if pre_alloc_right:
            p.append(pre_alloc_right)

        if o.typ.typ == 'int128':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINNUM],
                o,
                ['mload', MemoryPositions.MAXNUM],
            ])
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        elif o.typ.typ == 'decimal':
            p.append([
                'clamp',
                ['mload', MemoryPositions.MINDECIMAL],
                o,
                ['mload', MemoryPositions.MAXDECIMAL],
            ])
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        if o.typ.typ == 'uint256':
            p.append(o)
            return LLLnode.from_list(p, typ=o.typ, pos=getpos(self.expr))
        else:
            raise Exception(f"{o} {o.typ}")
Beispiel #14
0
    def parse_Compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.right, self.context)

        if right.value is None:
            return

        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            # TODO: Can this if branch be removed ^
            pass

        elif isinstance(self.expr.op, vy_ast.In) and isinstance(
                right.typ, ListType):
            if left.typ != right.typ.subtype:
                return
            return self.build_in_comparator()

        if isinstance(self.expr.op, vy_ast.Gt):
            op = "sgt"
        elif isinstance(self.expr.op, vy_ast.GtE):
            op = "sge"
        elif isinstance(self.expr.op, vy_ast.LtE):
            op = "sle"
        elif isinstance(self.expr.op, vy_ast.Lt):
            op = "slt"
        elif isinstance(self.expr.op, vy_ast.Eq):
            op = "eq"
        elif isinstance(self.expr.op, vy_ast.NotEq):
            op = "ne"
        else:
            return

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayLike) and isinstance(
                right.typ, ByteArrayLike):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.right, self.context).lll_node

            length_mismatch = left.typ.maxlen != right.typ.maxlen
            left_over_32 = left.typ.maxlen > 32
            right_over_32 = right.typ.maxlen > 32
            if length_mismatch or left_over_32 or right_over_32:
                left_keccak = keccak256_helper(self.expr, [left], None,
                                               self.context)
                right_keccak = keccak256_helper(self.expr, [right], None,
                                                self.context)

                if op == "eq" or op == "ne":
                    return LLLnode.from_list(
                        [op, left_keccak, right_keccak],
                        typ="bool",
                        pos=getpos(self.expr),
                    )

                else:
                    return

            else:

                def load_bytearray(side):
                    if side.location == "memory":
                        return ["mload", ["add", 32, side]]
                    elif side.location == "storage":
                        return ["sload", ["add", 1, ["sha3_32", side]]]

                return LLLnode.from_list(
                    [op, load_bytearray(left),
                     load_bytearray(right)],
                    typ="bool",
                    pos=getpos(self.expr),
                )

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ("eq", "ne"):
                return
        left_type, right_type = left.typ.typ, right.typ.typ

        # Special Case: comparison of a literal integer. If in valid range allow it to be compared.
        if {left_type, right_type} == {"int128", "uint256"} and {
                left.typ.is_literal,
                right.typ.is_literal,
        } == {
                True,
                False,
        }:  # noqa: E501

            comparison_allowed = False
            if left.typ.is_literal and SizeLimits.in_bounds(
                    right_type, left.value):
                comparison_allowed = True
            elif right.typ.is_literal and SizeLimits.in_bounds(
                    left_type, right.value):
                comparison_allowed = True
            op = self._signed_to_unsigned_comparision_op(op)

            if comparison_allowed:
                return LLLnode.from_list([op, left, right],
                                         typ="bool",
                                         pos=getpos(self.expr))

        elif {left_type, right_type} == {"uint256", "uint256"}:
            op = self._signed_to_unsigned_comparision_op(op)
        elif (left_type in ("decimal", "int128") or right_type in
              ("decimal", "int128")) and left_type != right_type:  # noqa: E501
            return

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ="bool",
                                     pos=getpos(self.expr))
Beispiel #15
0
    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 ltyp == rtyp == "uint256":
                arith = ["div", "l", ["clamp_nonzero", "r"]]

            elif ltyp == rtyp == "int128":
                arith = ["sdiv", "l", ["clamp_nonzero", "r"]]

            elif ltyp == rtyp == "decimal":
                arith = [
                    "sdiv",
                    # TODO check overflow cases, also should it be smul
                    ["mul", "l", DECIMAL_DIVISOR],
                    ["clamp_nonzero", "r"],
                ]

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                return

            new_typ = BaseType(ltyp)

            if ltyp == rtyp == "uint256":
                arith = ["mod", "l", ["clamp_nonzero", "r"]]
            elif ltyp == rtyp:
                # TODO should this be regular mod
                arith = ["smod", "l", ["clamp_nonzero", "r"]]

        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 ltyp == rtyp == "uint256":
                arith = [
                    "seq",
                    [
                        "assert",
                        [
                            "or",
                            # r == 1 | iszero(r)
                            # could be simplified to ~(r & 1)
                            ["or", ["eq", "r", 1], ["iszero", "r"]],
                            ["lt", "l", ["exp", "l", "r"]],
                        ],
                    ],
                    ["exp", "l", "r"],
                ]
            elif ltyp == rtyp == "int128":
                arith = ["exp", "l", "r"]

        if arith is None:
            return

        p = ["seq"]
        if new_typ.typ == "int128":
            p.append([
                "clamp",
                ["mload", MemoryPositions.MINNUM],
                arith,
                ["mload", MemoryPositions.MAXNUM],
            ])
        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)
Beispiel #16
0
    def arithmetic(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):
            raise TypeMismatchException(
                "Unsupported types for arithmetic op: %r %r" %
                (left.typ, right.typ), self.expr)

        arithmetic_pair = {left.typ.typ, right.typ.typ}

        # Special Case: Simplify any literal to literal arithmetic at compile time.
        if left.typ.is_literal and right.typ.is_literal and \
           isinstance(right.value, int) and isinstance(left.value, int):

            if isinstance(self.expr.op, ast.Add):
                val = left.value + right.value
            elif isinstance(self.expr.op, ast.Sub):
                val = left.value - right.value
            elif isinstance(self.expr.op, ast.Mult):
                val = left.value * right.value
            elif isinstance(self.expr.op, ast.Div):
                val = left.value // right.value
            elif isinstance(self.expr.op, ast.Mod):
                val = left.value % right.value
            elif isinstance(self.expr.op, ast.Pow):
                val = left.value**right.value
            else:
                raise ParserException(
                    'Unsupported literal operator: %s' %
                    str(type(self.expr.op)), self.expr)

            num = ast.Num(val)
            num.source_code = self.expr.source_code
            num.lineno = self.expr.lineno
            num.col_offset = self.expr.col_offset

            return Expr.parse_value_expr(num, self.context)

        # 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=getpos(self.expr))
                arithmetic_pair = {left.typ.typ, right.typ.typ}
            # 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=getpos(self.expr))
                arithmetic_pair = {left.typ.typ, right.typ.typ}

        # Only allow explicit conversions to occur.
        if left.typ.typ != right.typ.typ:
            raise TypeMismatchException(
                "Cannot implicitly convert {} to {}.".format(
                    left.typ.typ, right.typ.typ), self.expr)

        ltyp, rtyp = left.typ.typ, right.typ.typ
        if isinstance(self.expr.op, (ast.Add, ast.Sub)):
            if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None:
                raise TypeMismatchException(
                    "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit),
                    self.expr)
            if left.typ.positional and right.typ.positional and isinstance(
                    self.expr.op, ast.Add):
                raise TypeMismatchException("Cannot add two positional units!",
                                            self.expr)
            new_unit = left.typ.unit or right.typ.unit
            new_positional = left.typ.positional ^ right.typ.positional  # xor, as subtracting two positionals gives a delta
            op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub'
            if ltyp == 'uint256' and isinstance(self.expr.op, ast.Add):
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that: a + b >= a
                        ['assert', ['ge', ['add', left, right], left]],
                        ['add', left, right]
                    ],
                    typ=BaseType('uint256', new_unit, new_positional),
                    pos=getpos(self.expr))
            elif ltyp == 'uint256' and isinstance(self.expr.op, ast.Sub):
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that: a >= b
                        ['assert', ['ge', left, right]],
                        ['sub', left, right]
                    ],
                    typ=BaseType('uint256', new_unit, new_positional),
                    pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list([op, left, right],
                                      typ=BaseType(ltyp, new_unit,
                                                   new_positional),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation '%r(%r, %r)'" %
                                (op, ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Mult):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot multiply positional values!", self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that: a == 0 || a / b == b
                        [
                            'assert',
                            [
                                'or', ['iszero', left],
                                [
                                    'eq', ['div', ['mul', left, right], left],
                                    right
                                ]
                            ]
                        ],
                        ['mul', left, right]
                    ],
                    typ=BaseType('uint256', new_unit),
                    pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(['mul', left, right],
                                      typ=BaseType('int128', new_unit),
                                      pos=getpos(self.expr))
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with', 'r', right,
                    [
                        'with', 'l', left,
                        [
                            'with', 'ans', ['mul', 'l', 'r'],
                            [
                                'seq',
                                [
                                    'assert',
                                    [
                                        'or', [
                                            'eq', ['sdiv', 'ans', 'l'], 'r'
                                        ], ['iszero', 'l']
                                    ]
                                ], ['sdiv', 'ans', DECIMAL_DIVISOR]
                            ]
                        ]
                    ]
                ],
                                      typ=BaseType('decimal', new_unit),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation 'mul(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Div):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException("Cannot divide positional values!",
                                            self.expr)
            new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list(
                    [
                        'seq',
                        # Checks that:  b != 0
                        ['assert', right],
                        ['div', left, right]
                    ],
                    typ=BaseType('uint256', new_unit),
                    pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                o = LLLnode.from_list(['sdiv', left, ['clamp_nonzero', right]],
                                      typ=BaseType('int128', new_unit),
                                      pos=getpos(self.expr))
            elif ltyp == rtyp == 'decimal':
                o = LLLnode.from_list([
                    'with', 'l', left,
                    [
                        'with', 'r', ['clamp_nonzero', right],
                        ['sdiv', ['mul', 'l', DECIMAL_DIVISOR], 'r']
                    ]
                ],
                                      typ=BaseType('decimal', new_unit),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation 'div(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Mod):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as modulus arguments!",
                    self.expr)
            if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None:
                raise TypeMismatchException(
                    "Modulus arguments must have same unit", self.expr)
            new_unit = left.typ.unit or right.typ.unit
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list(
                    ['seq', ['assert', right], ['mod', left, right]],
                    typ=BaseType('uint256', new_unit),
                    pos=getpos(self.expr))
            elif ltyp == rtyp:
                o = LLLnode.from_list(['smod', left, ['clamp_nonzero', right]],
                                      typ=BaseType(ltyp, new_unit),
                                      pos=getpos(self.expr))
            else:
                raise Exception("Unsupported Operation 'mod(%r, %r)'" %
                                (ltyp, rtyp))
        elif isinstance(self.expr.op, ast.Pow):
            if left.typ.positional or right.typ.positional:
                raise TypeMismatchException(
                    "Cannot use positional values as exponential arguments!",
                    self.expr)
            if right.typ.unit:
                raise TypeMismatchException(
                    "Cannot use unit values as exponents", self.expr)
            if ltyp != 'int128' and ltyp != 'uint256' and isinstance(
                    self.expr.right, ast.Name):
                raise TypeMismatchException(
                    "Cannot use dynamic values as exponents, for unit base types",
                    self.expr)
            if ltyp == rtyp == 'uint256':
                o = LLLnode.from_list([
                    'seq',
                    [
                        'assert',
                        [
                            'or', ['or', ['eq', right, 1], ['iszero', right]],
                            ['lt', left, ['exp', left, right]]
                        ]
                    ], ['exp', left, right]
                ],
                                      typ=BaseType('uint256'),
                                      pos=getpos(self.expr))
            elif ltyp == rtyp == 'int128':
                new_unit = left.typ.unit
                if left.typ.unit and not isinstance(self.expr.right, ast.Name):
                    new_unit = {
                        left.typ.unit.copy().popitem()[0]: self.expr.right.n
                    }
                o = LLLnode.from_list(['exp', left, right],
                                      typ=BaseType('int128', new_unit),
                                      pos=getpos(self.expr))
            else:
                raise TypeMismatchException(
                    'Only whole number exponents are supported', self.expr)
        else:
            raise Exception("Unsupported binop: %r" % self.expr.op)
        if o.typ.typ == 'int128':
            return LLLnode.from_list([
                'clamp', ['mload', MemoryPositions.MINNUM], o,
                ['mload', MemoryPositions.MAXNUM]
            ],
                                     typ=o.typ,
                                     pos=getpos(self.expr))
        elif o.typ.typ == 'decimal':
            return LLLnode.from_list([
                'clamp', ['mload', MemoryPositions.MINDECIMAL], o,
                ['mload', MemoryPositions.MAXDECIMAL]
            ],
                                     typ=o.typ,
                                     pos=getpos(self.expr))
        if o.typ.typ == 'uint256':
            return o
        else:
            raise Exception("%r %r" % (o, o.typ))
Beispiel #17
0
 def arithmetic(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):
         raise TypeMismatchException(
             "Unsupported types for arithmetic op: %r %r" %
             (left.typ, right.typ), self.expr)
     ltyp, rtyp = left.typ.typ, right.typ.typ
     if isinstance(self.expr.op, (ast.Add, ast.Sub)):
         if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None:
             raise TypeMismatchException(
                 "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit),
                 self.expr)
         if left.typ.positional and right.typ.positional and isinstance(
                 self.expr.op, ast.Add):
             raise TypeMismatchException("Cannot add two positional units!",
                                         self.expr)
         new_unit = left.typ.unit or right.typ.unit
         new_positional = left.typ.positional ^ right.typ.positional  # xor, as subtracting two positionals gives a delta
         op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub'
         if ltyp == rtyp:
             o = LLLnode.from_list([op, left, right],
                                   typ=BaseType(ltyp, new_unit,
                                                new_positional),
                                   pos=getpos(self.expr))
         elif ltyp == 'int128' and rtyp == 'decimal':
             o = LLLnode.from_list(
                 [op, ['mul', left, DECIMAL_DIVISOR], right],
                 typ=BaseType('decimal', new_unit, new_positional),
                 pos=getpos(self.expr))
         elif ltyp == 'decimal' and rtyp == 'int128':
             o = LLLnode.from_list(
                 [op, left, ['mul', right, DECIMAL_DIVISOR]],
                 typ=BaseType('decimal', new_unit, new_positional),
                 pos=getpos(self.expr))
         else:
             raise Exception("Unsupported Operation '%r(%r, %r)'" %
                             (op, ltyp, rtyp))
     elif isinstance(self.expr.op, ast.Mult):
         if left.typ.positional or right.typ.positional:
             raise TypeMismatchException(
                 "Cannot multiply positional values!", self.expr)
         new_unit = combine_units(left.typ.unit, right.typ.unit)
         if ltyp == rtyp == 'int128':
             o = LLLnode.from_list(['mul', left, right],
                                   typ=BaseType('int128', new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == rtyp == 'decimal':
             o = LLLnode.from_list([
                 'with', 'r', right,
                 [
                     'with', 'l', left,
                     [
                         'with', 'ans', ['mul', 'l', 'r'],
                         [
                             'seq',
                             [
                                 'assert',
                                 [
                                     'or', [
                                         'eq', ['sdiv', 'ans', 'l'], 'r'
                                     ], ['not', 'l']
                                 ]
                             ], ['sdiv', 'ans', DECIMAL_DIVISOR]
                         ]
                     ]
                 ]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         elif (ltyp == 'int128'
               and rtyp == 'decimal') or (ltyp == 'decimal'
                                          and rtyp == 'int128'):
             o = LLLnode.from_list([
                 'with', 'r', right,
                 [
                     'with', 'l', left,
                     [
                         'with', 'ans', ['mul', 'l', 'r'],
                         [
                             'seq',
                             [
                                 'assert',
                                 [
                                     'or', [
                                         'eq', ['sdiv', 'ans', 'l'], 'r'
                                     ], ['not', 'l']
                                 ]
                             ], 'ans'
                         ]
                     ]
                 ]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         else:
             raise Exception("Unsupported Operation 'mul(%r, %r)'" %
                             (ltyp, rtyp))
     elif isinstance(self.expr.op, ast.Div):
         if left.typ.positional or right.typ.positional:
             raise TypeMismatchException("Cannot divide positional values!",
                                         self.expr)
         new_unit = combine_units(left.typ.unit, right.typ.unit, div=True)
         if ltyp == rtyp == 'int128':
             o = LLLnode.from_list([
                 'sdiv', ['mul', left, DECIMAL_DIVISOR],
                 ['clamp_nonzero', right]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == rtyp == 'decimal':
             o = LLLnode.from_list([
                 'with', 'l', left,
                 [
                     'with', 'r', ['clamp_nonzero', right],
                     ['sdiv', ['mul', 'l', DECIMAL_DIVISOR], 'r']
                 ]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == 'int128' and rtyp == 'decimal':
             o = LLLnode.from_list([
                 'sdiv', ['mul', left, DECIMAL_DIVISOR**2],
                 ['clamp_nonzero', right]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == 'decimal' and rtyp == 'int128':
             o = LLLnode.from_list(['sdiv', left, ['clamp_nonzero', right]],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         else:
             raise Exception("Unsupported Operation 'div(%r, %r)'" %
                             (ltyp, rtyp))
     elif isinstance(self.expr.op, ast.Mod):
         if left.typ.positional or right.typ.positional:
             raise TypeMismatchException(
                 "Cannot use positional values as modulus arguments!",
                 self.expr)
         if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None:
             raise TypeMismatchException(
                 "Modulus arguments must have same unit", self.expr)
         new_unit = left.typ.unit or right.typ.unit
         if ltyp == rtyp:
             o = LLLnode.from_list(['smod', left, ['clamp_nonzero', right]],
                                   typ=BaseType(ltyp, new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == 'decimal' and rtyp == 'int128':
             o = LLLnode.from_list([
                 'smod', left,
                 ['mul', ['clamp_nonzero', right], DECIMAL_DIVISOR]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         elif ltyp == 'int128' and rtyp == 'decimal':
             o = LLLnode.from_list([
                 'smod', ['mul', left, DECIMAL_DIVISOR],
                 ['clamp_nonzero', right]
             ],
                                   typ=BaseType('decimal', new_unit),
                                   pos=getpos(self.expr))
         else:
             raise Exception("Unsupported Operation 'mod(%r, %r)'" %
                             (ltyp, rtyp))
     elif isinstance(self.expr.op, ast.Pow):
         if left.typ.positional or right.typ.positional:
             raise TypeMismatchException(
                 "Cannot use positional values as exponential arguments!",
                 self.expr)
         if right.typ.unit:
             raise TypeMismatchException(
                 "Cannot use unit values as exponents", self.expr)
         if ltyp != 'int128' and isinstance(self.expr.right, ast.Name):
             raise TypeMismatchException(
                 "Cannot use dynamic values as exponents, for unit base types",
                 self.expr)
         if ltyp == rtyp == 'int128':
             new_unit = left.typ.unit
             if left.typ.unit and not isinstance(self.expr.right, ast.Name):
                 new_unit = {
                     left.typ.unit.copy().popitem()[0]: self.expr.right.n
                 }
             o = LLLnode.from_list(['exp', left, right],
                                   typ=BaseType('int128', new_unit),
                                   pos=getpos(self.expr))
         else:
             raise TypeMismatchException(
                 'Only whole number exponents are supported', self.expr)
     else:
         raise Exception("Unsupported binop: %r" % self.expr.op)
     if o.typ.typ == 'int128':
         return LLLnode.from_list([
             'clamp', ['mload', MemoryPositions.MINNUM], o,
             ['mload', MemoryPositions.MAXNUM]
         ],
                                  typ=o.typ,
                                  pos=getpos(self.expr))
     elif o.typ.typ == 'decimal':
         return LLLnode.from_list([
             'clamp', ['mload', MemoryPositions.MINDECIMAL], o,
             ['mload', MemoryPositions.MAXDECIMAL]
         ],
                                  typ=o.typ,
                                  pos=getpos(self.expr))
     else:
         raise Exception("%r %r" % (o, o.typ))
Beispiel #18
0
    def compare(self):
        left = Expr.parse_value_expr(self.expr.left, self.context)
        right = Expr.parse_value_expr(self.expr.comparators[0], self.context)

        if isinstance(left.typ, ByteArrayType) and isinstance(
                right.typ, ByteArrayType):
            if left.typ.maxlen != right.typ.maxlen:
                raise TypeMismatchException(
                    'Can only compare bytes of the same length', self.expr)
            if left.typ.maxlen > 32 or right.typ.maxlen > 32:
                raise ParserException(
                    'Can only compare bytes of length shorter than 32 bytes',
                    self.expr)
        elif isinstance(self.expr.ops[0], ast.In) and \
           isinstance(right.typ, ListType):
            if not are_units_compatible(
                    left.typ, right.typ.subtype) and not are_units_compatible(
                        right.typ.subtype, left.typ):
                raise TypeMismatchException(
                    "Can't use IN comparison with different types!", self.expr)
            return self.build_in_comparator()
        else:
            if not are_units_compatible(
                    left.typ, right.typ) and not are_units_compatible(
                        right.typ, left.typ):
                raise TypeMismatchException(
                    "Can't compare values with different units!", self.expr)

        if len(self.expr.ops) != 1:
            raise StructureException(
                "Cannot have a comparison with more than two elements",
                self.expr)
        if isinstance(self.expr.ops[0], ast.Gt):
            op = 'sgt'
        elif isinstance(self.expr.ops[0], ast.GtE):
            op = 'sge'
        elif isinstance(self.expr.ops[0], ast.LtE):
            op = 'sle'
        elif isinstance(self.expr.ops[0], ast.Lt):
            op = 'slt'
        elif isinstance(self.expr.ops[0], ast.Eq):
            op = 'eq'
        elif isinstance(self.expr.ops[0], ast.NotEq):
            op = 'ne'
        else:
            raise Exception("Unsupported comparison operator")

        # Compare (limited to 32) byte arrays.
        if isinstance(left.typ, ByteArrayType) and isinstance(
                left.typ, ByteArrayType):
            left = Expr(self.expr.left, self.context).lll_node
            right = Expr(self.expr.comparators[0], self.context).lll_node

            def load_bytearray(side):
                if side.location == 'memory':
                    return ['mload', ['add', 32, side]]
                elif side.location == 'storage':
                    return ['sload', ['add', 1, ['sha3_32', side]]]

            return LLLnode.from_list(
                [op, load_bytearray(left),
                 load_bytearray(right)],
                typ='bool',
                pos=getpos(self.expr))

        # Compare other types.
        if not is_numeric_type(left.typ) or not is_numeric_type(right.typ):
            if op not in ('eq', 'ne'):
                raise TypeMismatchException("Invalid type for comparison op",
                                            self.expr)
        left_type, right_type = left.typ.typ, right.typ.typ

        # Special Case: comparison of a literal integer. If in valid range allow it to be compared.
        if {left_type, right_type} == {'int128', 'uint256'} and {
                left.typ.is_literal, right.typ.is_literal
        } == {True, False}:

            comparison_allowed = False
            if left.typ.is_literal and SizeLimits.in_bounds(
                    right_type, left.value):
                comparison_allowed = True
            elif right.typ.is_literal and SizeLimits.in_bounds(
                    left_type, right.value):
                comparison_allowed = True
            op = self._signed_to_unsigned_comparision_op(op)

            if comparison_allowed:
                return LLLnode.from_list([op, left, right],
                                         typ='bool',
                                         pos=getpos(self.expr))

        elif {left_type, right_type} == {'uint256', 'uint256'}:
            op = self._signed_to_unsigned_comparision_op(op)
        elif (left_type in ('decimal', 'int128') or right_type
              in ('decimal', 'int128')) and left_type != right_type:
            raise TypeMismatchException(
                'Implicit conversion from {} to {} disallowed, please convert.'
                .format(left_type, right_type), self.expr)

        if left_type == right_type:
            return LLLnode.from_list([op, left, right],
                                     typ='bool',
                                     pos=getpos(self.expr))
        else:
            raise TypeMismatchException(
                "Unsupported types for comparison: %r %r" %
                (left_type, right_type), self.expr)
Beispiel #19
0
    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 ltyp == rtyp == "uint256":
                arith = ["div", "l", ["clamp_nonzero", "r"]]

            elif ltyp == rtyp == "int128":
                arith = ["sdiv", "l", ["clamp_nonzero", "r"]]

            elif ltyp == rtyp == "decimal":
                arith = [
                    "sdiv",
                    # TODO check overflow cases, also should it be smul
                    ["mul", "l", DECIMAL_DIVISOR],
                    ["clamp_nonzero", "r"],
                ]

        elif isinstance(self.expr.op, vy_ast.Mod):
            if right.typ.is_literal and right.value == 0:
                return

            new_typ = BaseType(ltyp)

            if ltyp == rtyp == "uint256":
                arith = ["mod", "l", ["clamp_nonzero", "r"]]
            elif ltyp == rtyp:
                # TODO should this be regular mod
                arith = ["smod", "l", ["clamp_nonzero", "r"]]

        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([
                "clamp",
                ["mload", MemoryPositions.MINNUM],
                arith,
                ["mload", MemoryPositions.MAXNUM],
            ])
        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)