Exemple #1
0
def mult(left, right, result, types):
    """Constraints for the multiplication operation
    
    Cases:
        - Number_1 * Number_2 --> Stronger(Number_1, Number_2)
        - Int * Sequence --> Sequence
        - Sequence * Int --> Sequence
        
    Ex:
        - 1 * 2.0
        - 3 * [1, 2]
        - b"string" * 4
    """
    return [
        And(left != types.none, right != types.none),
        Or([
            # multiplication of two booleans is an integer. Handle it separately
            And(left == types.bool, right == types.bool, result == types.int),
            And(
                Or(left != types.bool, right != types.bool),
                Or(
                    And(types.subtype(left, types.seq),
                        types.subtype(right, types.int), result == left),
                    And(types.subtype(left, types.int),
                        types.subtype(right, types.seq), result == right),
                    And(types.subtype(left, types.complex),
                        types.subtype(right, left), result == left),
                    And(types.subtype(right, types.complex),
                        types.subtype(left, right), result == right),
                ))
        ] + overloading_axioms(left, right, result, "__mul__", types))
    ]
Exemple #2
0
def arithmetic(left, right, result, magic_method, is_mod, types):
    """Constraints for arithmetic operation

    Cases:
        - Number_1 (op) Number_2 --> Stronger(Number_1, Number_2)
        - String formatting
        
    Ex:
        - 2 ** 3.0
        - 3 - 4
        - "Case #%i: %i" % (u, v)
    """
    axioms = [
        And(types.subtype(left, types.complex), types.subtype(right, left),
            result == left),
        And(types.subtype(right, types.complex), types.subtype(left, right),
            result == right),
    ] + overloading_axioms(left, right, result, magic_method, types)

    if is_mod:
        axioms += [
            And(Or(left == types.string, left == types.bytes), result == left)
        ]

    return [And(left != types.none, right != types.none), Or(axioms)]
Exemple #3
0
def instancemethod_call(instance, args, result, attr, types, tvs):
    """Constraints for calls on instances

    There are two cases:
    - The called is an instance method
    - The called is a normal instance attribute which happens to be callable

    In the first case, check that it appears in the class instance methods and is not static method
    In the second case, check that it does not appear in the class instance methods but appears in the
        class attributes
        
    Add the receiver argument in the first case only
    `
    """
    axioms = []
    for t in types.all_types:
        # Check that attr is an instance method and "staticmethod" is not of its decorators,
        # if so, add call axioms with a receiver
        if attr in types.class_to_funcs[t]:
            decorators = types.class_to_funcs[t][attr][1]
            if "staticmethod" not in decorators and "property" not in decorators:
                attr_type = types.instance_attributes[t][attr]

                receiver_subtype = False
                if t in types.config.class_type_params:
                    type_func = types.classes[
                        t] if t not in ALIASES else getattr(
                            types.type_sort, ALIASES[t])
                    rec_type = type_func(
                        tvs[:len(types.config.class_type_params[t])])
                    receiver_subtype = types.subtype(instance, rec_type)
                    axioms.append(
                        And(
                            receiver_subtype,
                            Or(*generic_call_axioms(attr_type, list(args),
                                                    result, types, tvs))))
                else:
                    axioms.append(
                        And(
                            instance == types.type_sort.type_arg_0(
                                types.all_types[t]),
                            Or(
                                function_call_axioms(attr_type, args, result,
                                                     types))), )

        # Otherwise, check if it is an instance attribute, if so add call axioms with no receiver
        elif attr in types.instance_attributes[t]:
            attr_type = types.instance_attributes[t][attr]
            axioms.append(
                And(
                    instance == types.type_sort.type_arg_0(types.all_types[t]),
                    Or(
                        function_call_axioms(attr_type, args[1:], result,
                                             types) +
                        class_call_axioms(attr_type, args[1:], result, types)))
            )
    return axioms
Exemple #4
0
def one_type_instantiation(class_name, args, result, types, tvs):
    """Constraints for class instantiation, if the class name is known
    
    :param class_name: The class to be instantiated
    :param args: the types of the arguments passed to the class instantiation
    :param result: The resulting instance from instantiation
    :param types: Z3Types object for this inference program
    """
    if class_name in types.abstract_types:
        return Or()
    init_args_count = types.class_to_funcs[class_name]["__init__"][0]

    # Get the __init__ function of the this class
    init_func = types.instance_attributes[class_name]["__init__"]

    if class_name not in types.config.class_type_params:

        # Get the instance accessor from the type_sort data type.
        instance = getattr(types.type_sort,
                           "type_arg_0")(types.all_types[class_name])

        # Assert that it's a call to this __init__ function

        # Get the default args count
        defaults_accessor = getattr(
            types.type_sort, "func_{}_defaults_args".format(init_args_count))
        default_count = defaults_accessor(init_func)

        rem_args_count = init_args_count - len(args) - 1
        rem_args = []
        for i in range(rem_args_count):
            arg_idx = len(args) + i + 2
            # Get the default arg type
            arg_accessor = getattr(
                types.type_sort,
                "func_{}_arg_{}".format(init_args_count, arg_idx))
            rem_args.append(arg_accessor(init_func))

        all_args = (instance, ) + args + tuple(rem_args) + (
            types.none, )  # The return type of __init__ is None
        z3_func_args = (default_count, ) + all_args
        return And(
            result == instance,
            init_func == types.funcs[len(args) + len(rem_args) +
                                     1](z3_func_args),
            default_count >= rem_args_count)
    else:

        rec_type = types.classes[class_name](
            tvs[:len(types.config.class_type_params[class_name])])
        generic_receiver_type = result == rec_type
        generic_args = (rec_type, ) + args
        # Assert that it's a call to this __init__ function
        return And(
            generic_receiver_type,
            Or(*generic_call_axioms(init_func, generic_args, types.none, types,
                                    tvs)))
Exemple #5
0
def add(left, right, result, types):
    """Constraints for the addition operation
    
    Cases:
        - Number_1 + Number_2 --> Stronger(Number_1, Number_2)
        - Sequence + Sequence --> Sequence
    
    Ex:
        - 1 + 2.0
        - [1, 2, 3] + [4]
        - "string" + "string2"
    
    TODO: Tuples addition
    """
    return [
        And(left != types.none, right != types.none),
        Or([
            And(types.subtype(left, types.complex), types.subtype(right, left),
                result == left),
            And(types.subtype(right, types.complex), types.subtype(
                left, right), result == right),
            And(types.subtype(left, types.seq), left == right, left == result),
            And(
                left == types.list(types.list_type(left)),
                right == left,
                result == left,
            ),
            And(left == types.string, right == types.string, result ==
                types.string)
        ] + overloading_axioms(left, right, result, "__add__", types)),
    ]
Exemple #6
0
def for_loop(iterable, target, types):
    """Constraints for for-loop iterable and iteration target"""
    return [
        Or(iterable == types.list(target), iterable == types.set(target),
           iterable == types.dict(target, types.dict_value_type(iterable)),
           And(iterable == types.string, target == types.string),
           And(iterable == types.bytes, target == types.bytes))
    ]
Exemple #7
0
def delete_subscript(indexed, types):
    """Constraints for subscript deletion
    
    Prevent subscript deletion of tuples, strings and bytes (Immutable sequences)
    """
    return [
        Not(
            Or(indexed == types.string, indexed == types.bytes,
               types.subtype(indexed, types.tuple)))
    ]
Exemple #8
0
def div(left, right, result, types):
    """Constraints for the division operation

    Cases:
        - Number_1 / Number_2 --> Stronger(types.float, Stronger(Number_1, Number2))
        
    Ex:
        - True / 7
        - 3 / (1 + 2j)
    """
    return [
        And(left != types.none, right != types.none),
        And(types.subtype(left, types.complex),
            types.subtype(right, types.complex)),
        Implies(Or(left == types.complex, right == types.complex),
                result == types.complex),
        Implies(Not(Or(left == types.complex, right == types.complex)),
                result == types.float)
    ]
Exemple #9
0
def index(indexed, ind, result, types):
    """Constraints for index subscript
    
    Cases:
        - List[t] --> t
        - str --> str
        - bytes --> bytes
        - Dict{t1: t2} --> t2
        - Tuple(t1, t2, t3, ..., tn) --> Super(t1, t2, t3, ..., tn)
    """

    # Tuple indexing
    # Assert that 'indexed' can be a tuple of an arbitrary length, where the result is the super-type of its elements.
    t = []
    for cur_len in range(1, len(types.tuples)):
        tuple_args = [
            getattr(types.type_sort, "tuple_{}_arg_{}".format(cur_len,
                                                              i + 1))(indexed)
            for i in range(cur_len)
        ]
        t.append(
            And(indexed == types.tuples[cur_len](*tuple_args),
                *[types.subtype(x, result) for x in tuple_args]))

    t.extend(overloading_axioms(indexed, ind, result, '__getitem__', types))

    return [
        Or([
            And(indexed == types.dict(types.dict_key_type(indexed), result),
                types.subtype(ind, types.dict_key_type(indexed))),
            And(types.subtype(ind, types.int), indexed == types.list(result)),
            And(types.subtype(ind, types.int), indexed == types.string, result
                == types.string),
            And(types.subtype(ind, types.int), indexed == types.bytes, result
                == types.bytes),
        ] + t)
    ], Or([
        indexed == types.dict(ind, result),
        And(ind == types.int, indexed == types.list(result)),
        And(ind == types.int, indexed == types.string, result == types.string),
        And(ind == types.int, indexed == types.bytes, result == types.bytes),
    ])
Exemple #10
0
def call(called, args, result, types, tvs):
    """Constraints for calls
    
    Cases:
        - Function call
        - Class instantiation
        - Callable classes
    """
    r1 = function_call_axioms(called, args, result, types)
    r2 = instance_axioms(called, args, result, types, tvs)
    r3 = class_call_axioms(called, args, result, types)
    r4 = generic_call_axioms(called, args, result, types, tvs)
    return [Or((r1 + r2 + r3 + r4))]
Exemple #11
0
def slicing(lower, upper, step, sliced, result, types):
    """Constraints for slicing subscript
    
    Cases:
        - Sequence --> Sequence
    """
    return [
        And(
            types.subtype(lower, types.int), types.subtype(upper, types.int),
            types.subtype(step, types.int),
            Or(sliced == types.string, sliced == types.bytes,
               types.subtype(sliced, types.tuple),
               sliced == types.list(types.list_type(sliced))),
            result == sliced)
    ]
Exemple #12
0
def staticmethod_call(class_type, args, result, attr, types):
    """Constraints for staticmethod calls
    
    Assert with all classes which has the method `attr` which has decorator `staticmethod`
    """
    axioms = []
    for t in types.all_types:
        # Check that attr is a method and "staticmethod" is one of its decorators
        if attr in types.class_to_funcs[t]:
            decorators = types.class_to_funcs[t][attr][1]
            if "staticmethod" in decorators:
                attr_type = types.instance_attributes[t][attr]
                axioms.append(
                    And(
                        class_type == types.all_types[t],
                        Or(function_call_axioms(attr_type, args, result,
                                                types))))
    return axioms
Exemple #13
0
def comparison_axioms(left, right, method_name, types):
    """
    Constraints for ordering comparison (>, >=, <, <=)
    """
    return [
        And(left != types.none, right != types.none),
        Or([
            And(types.subtype(left, types.float),
                types.subtype(right, types.float)),
            And(left == types.list(types.list_type(left)), right == left),
            And(left == types.set(types.set_type(left)), right == types.set(
                types.set_type(right))),
            And(types.subtype(left, types.tuple),
                types.subtype(right, types.tuple)),
            And(left == types.string, right == types.string),
            And(left == types.bytes, right == types.bytes),
        ] + overloading_axioms(left, right, types.bool, method_name, types))
    ]
Exemple #14
0
def generator(iterable, target, types):
    """Constraints for comprehension generators
    
    Ex:
        - [x for x in [1, 2]]
        - [x for x in {1, 2}]
        - [x for y in ["st", "st"] for x in y]
        - [x for x in {1: "a", 2: "b"}]
    """
    # TODO tuples
    return [
        Or(
            iterable == types.list(target),
            iterable == types.set(target),
            And(iterable == types.string, target == types.string),
            And(iterable == types.bytes, target == types.bytes),
            iterable == types.dict(target, types.dict_value_type(iterable)),
        )
    ]
Exemple #15
0
def class_call_axioms(called, args, result, types):
    """Constraints for callable classes
    
    Assert with all classes which have the method `__call__`.
    """
    axioms = []
    for t in types.all_types:
        # Check that `__call__` is a method in the current class.
        if "__call__" in types.class_to_funcs[t]:
            call_type = types.instance_attributes[t]["__call__"]
            instance = getattr(types.type_sort,
                               "type_arg_0")(types.all_types[t])
            args_types = (instance, ) + args
            axioms.append(
                And(
                    called == instance,
                    Or(
                        function_call_axioms(call_type, args_types, result,
                                             types))))
    return axioms
Exemple #16
0
def attribute(instance, attr, result, types):
    """Constraints for attribute access
    
    Assert with all classes having the attribute attr
    """
    axioms = []
    for t in types.all_types:
        if t in types.instance_attributes and attr in types.instance_attributes[
                t]:
            # instance access. Ex: A().x
            attr_type = types.instance_attributes[t][attr]
            if t in types.config.class_type_params:
                tps = types.config.class_type_params[t]
                accessors = [
                    ctb[1:] for ctb in types.config.class_to_base
                    if isinstance(ctb, tuple) and ctb[0] == t
                ]
                accessors = accessors[0]
                args = [
                    getattr(types.type_sort, a)(instance) for a in accessors
                ]
                params = [
                    getattr(types, "generic{}_tv{}".format(len(tps),
                                                           i + 1))(attr_type)
                    for i, _ in enumerate(tps)
                ]
                gen_func = getattr(types, "generic{}_func".format(
                    len(tps)))(attr_type)

                generic_attr_type = getattr(types.type_sort,
                                            'is_generic{}'.format(
                                                len(tps)))(attr_type)

                type_instance = types.classes[t](args)
                substituted = gen_func
                for arg, param in zip(args, params):
                    substituted = types.subst(substituted, param, arg)
                axioms.append(
                    And(instance == type_instance, generic_attr_type,
                        result == substituted))
                continue
            type_instance = getattr(types.type_sort,
                                    "type_arg_0")(types.all_types[t])

            # Check if it is a property access
            if attr in types.class_to_funcs[t] and "property" in \
                    types.class_to_funcs[t][attr][1]:
                # Set the attribute type to be the return type of the property method
                method_type = types.instance_attributes[t][attr]
                arg_accessor = getattr(types.type_sort, "func_1_arg_1")
                axioms.append(
                    And(
                        instance == type_instance,
                        method_type == types.funcs[1](
                            0, arg_accessor(method_type), result)))
            else:
                attr_type = types.instance_attributes[t][attr]
                axioms.append(
                    And(instance == type_instance, result == attr_type))
        if t in types.class_attributes and attr in types.class_attributes[t]:
            # class access. Ex: A.x
            class_type = types.all_types[t]
            attr_type = types.class_attributes[t][attr]
            axioms.append(And(instance == class_type, result == attr_type))
    return Or(axioms)