Esempio n. 1
0
def transform_assert_stmt(builder: IRBuilder, a: AssertStmt) -> None:
    if builder.options.strip_asserts:
        return
    cond = builder.accept(a.expr)
    ok_block, error_block = BasicBlock(), BasicBlock()
    builder.add_bool_branch(cond, ok_block, error_block)
    builder.activate_block(error_block)
    if a.msg is None:
        # Special case (for simpler generated code)
        builder.add(
            RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, None,
                               a.line))
    elif isinstance(a.msg, StrExpr):
        # Another special case
        builder.add(
            RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, a.msg.value,
                               a.line))
    else:
        # The general case -- explicitly construct an exception instance
        message = builder.accept(a.msg)
        exc_type = builder.load_module_attr_by_fullname(
            'builtins.AssertionError', a.line)
        exc = builder.py_call(exc_type, [message], a.line)
        builder.call_c(raise_exception_op, [exc], a.line)
    builder.add(Unreachable())
    builder.activate_block(ok_block)
Esempio n. 2
0
def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
    """Generate the '__get__' method for a callable class."""
    line = fn_info.fitem.line
    with builder.enter_method(fn_info.callable_class.ir,
                              '__get__',
                              object_rprimitive,
                              fn_info,
                              self_type=object_rprimitive):
        instance = builder.add_argument('instance', object_rprimitive)
        builder.add_argument('owner', object_rprimitive)

        # If accessed through the class, just return the callable
        # object. If accessed through an object, create a new bound
        # instance method object.
        instance_block, class_block = BasicBlock(), BasicBlock()
        comparison = builder.translate_is_op(builder.read(instance),
                                             builder.none_object(), 'is', line)
        builder.add_bool_branch(comparison, class_block, instance_block)

        builder.activate_block(class_block)
        builder.add(Return(builder.self()))

        builder.activate_block(instance_block)
        builder.add(
            Return(
                builder.call_c(
                    method_new_op,
                    [builder.self(), builder.read(instance)], line)))
Esempio n. 3
0
def setup_non_ext_dict(builder: IRBuilder,
                       cdef: ClassDef,
                       metaclass: Value,
                       bases: Value) -> Value:
    """Initialize the class dictionary for a non-extension class.

    This class dictionary is passed to the metaclass constructor.
    """
    # Check if the metaclass defines a __prepare__ method, and if so, call it.
    has_prepare = builder.call_c(py_hasattr_op,
                                [metaclass,
                                builder.load_str('__prepare__')], cdef.line)

    non_ext_dict = Register(dict_rprimitive)

    true_block, false_block, exit_block, = BasicBlock(), BasicBlock(), BasicBlock()
    builder.add_bool_branch(has_prepare, true_block, false_block)

    builder.activate_block(true_block)
    cls_name = builder.load_str(cdef.name)
    prepare_meth = builder.py_get_attr(metaclass, '__prepare__', cdef.line)
    prepare_dict = builder.py_call(prepare_meth, [cls_name, bases], cdef.line)
    builder.assign(non_ext_dict, prepare_dict, cdef.line)
    builder.goto(exit_block)

    builder.activate_block(false_block)
    builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line)
    builder.goto(exit_block)
    builder.activate_block(exit_block)

    return non_ext_dict
Esempio n. 4
0
def faster_min_max(builder: IRBuilder, expr: CallExpr,
                   callee: RefExpr) -> Optional[Value]:
    if expr.arg_kinds == [ARG_POS, ARG_POS]:
        x, y = builder.accept(expr.args[0]), builder.accept(expr.args[1])
        result = Register(builder.node_type(expr))
        # CPython evaluates arguments reversely when calling min(...) or max(...)
        if callee.fullname == 'builtins.min':
            comparison = builder.binary_op(y, x, '<', expr.line)
        else:
            comparison = builder.binary_op(y, x, '>', expr.line)

        true_block, false_block, next_block = BasicBlock(), BasicBlock(
        ), BasicBlock()
        builder.add_bool_branch(comparison, true_block, false_block)

        builder.activate_block(true_block)
        builder.assign(result, builder.coerce(y, result.type, expr.line),
                       expr.line)
        builder.goto(next_block)

        builder.activate_block(false_block)
        builder.assign(result, builder.coerce(x, result.type, expr.line),
                       expr.line)
        builder.goto(next_block)

        builder.activate_block(next_block)
        return result
    return None
Esempio n. 5
0
def gen_calls_to_correct_impl(
    builder: IRBuilder,
    impl_to_use: Value,
    arg_info: ArgInfo,
    fitem: FuncDef,
    line: int,
) -> None:
    current_func_decl = builder.mapper.func_to_decl[fitem]

    def gen_native_func_call_and_return(fdef: FuncDef) -> None:
        func_decl = builder.mapper.func_to_decl[fdef]
        ret_val = builder.builder.call(func_decl, arg_info.args,
                                       arg_info.arg_kinds, arg_info.arg_names,
                                       line)
        coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line)
        builder.add(Return(coerced))

    typ, src = builtin_names['builtins.int']
    int_type_obj = builder.add(LoadAddress(typ, src, line))
    is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line)

    native_call, non_native_call = BasicBlock(), BasicBlock()
    builder.add_bool_branch(is_int, native_call, non_native_call)
    builder.activate_block(native_call)

    passed_id = builder.add(Unbox(impl_to_use, int_rprimitive, line))

    native_ids = get_native_impl_ids(builder, fitem)
    for impl, i in native_ids.items():
        call_impl, next_impl = BasicBlock(), BasicBlock()

        current_id = builder.load_int(i)
        builder.builder.compare_tagged_condition(
            passed_id,
            current_id,
            '==',
            call_impl,
            next_impl,
            line,
        )

        # Call the registered implementation
        builder.activate_block(call_impl)

        gen_native_func_call_and_return(impl)
        builder.activate_block(next_impl)

    # We've already handled all the possible integer IDs, so we should never get here
    builder.add(Unreachable())

    builder.activate_block(non_native_call)
    ret_val = builder.py_call(impl_to_use, arg_info.args, line,
                              arg_info.arg_kinds, arg_info.arg_names)
    coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line)
    builder.add(Return(coerced))
Esempio n. 6
0
def populate_switch_for_generator_class(builder: IRBuilder) -> None:
    cls = builder.fn_info.generator_class
    line = builder.fn_info.fitem.line

    builder.activate_block(cls.switch_block)
    for label, true_block in enumerate(cls.continuation_blocks):
        false_block = BasicBlock()
        comparison = builder.binary_op(cls.next_label_reg,
                                       builder.add(LoadInt(label)), '==', line)
        builder.add_bool_branch(comparison, true_block, false_block)
        builder.activate_block(false_block)

    builder.add(
        RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, line))
    builder.add(Unreachable())
Esempio n. 7
0
def add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int) -> None:
    """Add error handling blocks to a generator class.

    Generates blocks to check if error flags are set while calling the
    helper method for generator functions, and raises an exception if
    those flags are set.
    """
    cls = builder.fn_info.generator_class
    assert cls.exc_regs is not None
    exc_type, exc_val, exc_tb = cls.exc_regs

    # Check to see if an exception was raised.
    error_block = BasicBlock()
    ok_block = BasicBlock()
    comparison = builder.translate_is_op(exc_type, builder.none_object(), 'is not', line)
    builder.add_bool_branch(comparison, error_block, ok_block)

    builder.activate_block(error_block)
    builder.call_c(raise_exception_with_tb_op, [exc_type, exc_val, exc_tb], line)
    builder.add(Unreachable())
    builder.goto_and_activate(ok_block)
Esempio n. 8
0
def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
    """Generate the '__get__' method for a callable class."""
    line = fn_info.fitem.line
    builder.enter(fn_info)

    vself = builder.read(
        builder.environment.add_local_reg(Var(SELF_NAME), object_rprimitive,
                                          True))
    instance = builder.environment.add_local_reg(Var('instance'),
                                                 object_rprimitive, True)
    builder.environment.add_local_reg(Var('owner'), object_rprimitive, True)

    # If accessed through the class, just return the callable
    # object. If accessed through an object, create a new bound
    # instance method object.
    instance_block, class_block = BasicBlock(), BasicBlock()
    comparison = builder.translate_is_op(builder.read(instance),
                                         builder.none_object(), 'is', line)
    builder.add_bool_branch(comparison, class_block, instance_block)

    builder.activate_block(class_block)
    builder.add(Return(vself))

    builder.activate_block(instance_block)
    builder.add(
        Return(
            builder.call_c(method_new_op,
                           [vself, builder.read(instance)], line)))

    blocks, env, _, fn_info = builder.leave()

    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),
                         RuntimeArg('instance', object_rprimitive),
                         RuntimeArg('owner', object_rprimitive)),
                        object_rprimitive)
    get_fn_decl = FuncDecl('__get__', fn_info.callable_class.ir.name,
                           builder.module_name, sig)
    get_fn_ir = FuncIR(get_fn_decl, blocks, env)
    fn_info.callable_class.ir.methods['__get__'] = get_fn_ir
    builder.functions.append(get_fn_ir)
Esempio n. 9
0
def generate_singledispatch_dispatch_function(
    builder: IRBuilder,
    main_singledispatch_function_name: str,
    fitem: FuncDef,
) -> None:
    line = fitem.line
    current_func_decl = builder.mapper.func_to_decl[fitem]
    arg_info = get_args(builder, current_func_decl.sig.args, line)

    dispatch_func_obj = builder.self()

    arg_type = builder.builder.get_type_of_obj(arg_info.args[0], line)
    dispatch_cache = builder.builder.get_attr(dispatch_func_obj,
                                              'dispatch_cache',
                                              dict_rprimitive, line)
    call_find_impl, use_cache, call_func = BasicBlock(), BasicBlock(
    ), BasicBlock()
    get_result = builder.call_c(dict_get_method_with_none,
                                [dispatch_cache, arg_type], line)
    is_not_none = builder.translate_is_op(get_result, builder.none_object(),
                                          'is not', line)
    impl_to_use = Register(object_rprimitive)
    builder.add_bool_branch(is_not_none, use_cache, call_find_impl)

    builder.activate_block(use_cache)
    builder.assign(impl_to_use, get_result, line)
    builder.goto(call_func)

    builder.activate_block(call_find_impl)
    find_impl = builder.load_module_attr_by_fullname('functools._find_impl',
                                                     line)
    registry = load_singledispatch_registry(builder, dispatch_func_obj, line)
    uncached_impl = builder.py_call(find_impl, [arg_type, registry], line)
    builder.call_c(dict_set_item_op, [dispatch_cache, arg_type, uncached_impl],
                   line)
    builder.assign(impl_to_use, uncached_impl, line)
    builder.goto(call_func)

    builder.activate_block(call_func)
    gen_calls_to_correct_impl(builder, impl_to_use, arg_info, fitem, line)
Esempio n. 10
0
def generate_singledispatch_dispatch_function(
    builder: IRBuilder,
    main_singledispatch_function_name: str,
    fitem: FuncDef,
) -> None:
    impls = builder.singledispatch_impls[fitem]
    line = fitem.line
    current_func_decl = builder.mapper.func_to_decl[fitem]
    arg_info = get_args(builder, current_func_decl.sig.args, line)

    def gen_func_call_and_return(
        func_name: str,
        fdef: FuncDef,
        fullname: Optional[str] = None
    ) -> None:
        if is_decorated(builder, fdef):
            func = load_func(builder, func_name, fullname, line)
            ret_val = builder.builder.py_call(
                func, arg_info.args, line, arg_info.arg_kinds, arg_info.arg_names
            )
        else:
            func_decl = builder.mapper.func_to_decl[fdef]
            ret_val = builder.builder.call(
                func_decl, arg_info.args, arg_info.arg_kinds, arg_info.arg_names, line
            )
        coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line)
        builder.nonlocal_control[-1].gen_return(builder, coerced, line)

    # Add all necessary imports of other modules that have registered functions in other modules
    # We're doing this in a separate pass over the implementations because that avoids the
    # complexity and code size implications of generating this import before every call to a
    # registered implementation that might need this imported
    for _, impl in impls:
        if not is_decorated(builder, impl):
            continue
        module_name = impl.fullname.rsplit('.')[0]
        if module_name not in builder.imports:
            # We need to generate an import here because the module needs to be imported before we
            # try loading the function from it
            builder.gen_import(module_name, line)

    # Sort the list of implementations so that we check any subclasses before we check the classes
    # they inherit from, to better match singledispatch's behavior of going through the argument's
    # MRO, and using the first implementation it finds
    for dispatch_type, impl in sort_with_subclasses_first(impls):
        call_impl, next_impl = BasicBlock(), BasicBlock()
        should_call_impl = check_if_isinstance(builder, arg_info.args[0], dispatch_type, line)
        builder.add_bool_branch(should_call_impl, call_impl, next_impl)

        # Call the registered implementation
        builder.activate_block(call_impl)

        # The shortname of a function is just '{class}.{func_name}', and we don't support
        # singledispatchmethod yet, so that is always the same as the function name
        name = short_id_from_name(impl.name, impl.name, impl.line)
        gen_func_call_and_return(name, impl, fullname=impl.fullname)
        builder.activate_block(next_impl)

    # We don't pass fullname here because getting the fullname of the main generated singledispatch
    # function isn't easy, and we don't need it because the fullname is only needed for making sure
    # we load the function from another module instead of the globals dict if it's defined in
    # another module, which will never be true for the main singledispatch function (it's always
    # generated in the same module as the dispatch function)
    gen_func_call_and_return(main_singledispatch_function_name, fitem)