예제 #1
0
def transform_try_finally_stmt(builder: IRBuilder, try_body: GenFunc,
                               finally_body: GenFunc) -> None:
    """Generalized try/finally handling that takes functions to gen the bodies.

    The point of this is to also be able to support with."""
    # Finally is a big pain, because there are so many ways that
    # exits can occur. We emit 10+ basic blocks for every finally!

    err_handler, main_entry, return_entry, finally_block = (BasicBlock(),
                                                            BasicBlock(),
                                                            BasicBlock(),
                                                            BasicBlock())

    # Compile the body of the try
    ret_reg = try_finally_try(builder, err_handler, return_entry, main_entry,
                              try_body)

    # Set up the entry blocks for the finally statement
    old_exc = try_finally_entry_blocks(builder, err_handler, return_entry,
                                       main_entry, finally_block, ret_reg)

    # Compile the body of the finally
    cleanup_block, finally_control = try_finally_body(builder, finally_block,
                                                      finally_body, ret_reg,
                                                      old_exc)

    # Resolve the control flow out of the finally block
    out_block = try_finally_resolve_control(builder, cleanup_block,
                                            finally_control, old_exc, ret_reg)

    builder.activate_block(out_block)
예제 #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)))
예제 #3
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)
예제 #4
0
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None:
    """Generate a "__ne__" method from a "__eq__" method. """
    builder.enter_method(cls, '__ne__', object_rprimitive)
    rhs_arg = builder.add_argument('rhs', object_rprimitive)

    # If __eq__ returns NotImplemented, then __ne__ should also
    not_implemented_block, regular_block = BasicBlock(), BasicBlock()
    eqval = builder.add(MethodCall(builder.self(), '__eq__', [rhs_arg], line))
    not_implemented = builder.add(LoadAddress(not_implemented_op.type,
                                              not_implemented_op.src, line))
    builder.add(Branch(
        builder.translate_is_op(eqval, not_implemented, 'is', line),
        not_implemented_block,
        regular_block,
        Branch.BOOL))

    builder.activate_block(regular_block)
    retval = builder.coerce(
        builder.unary_op(eqval, 'not', line), object_rprimitive, line
    )
    builder.add(Return(retval))

    builder.activate_block(not_implemented_block)
    builder.add(Return(not_implemented))

    builder.leave_method()
예제 #5
0
def try_finally_body(
        builder: IRBuilder, finally_block: BasicBlock, finally_body: GenFunc,
        ret_reg: Optional[Value],
        old_exc: Value) -> Tuple[BasicBlock, FinallyNonlocalControl]:
    cleanup_block = BasicBlock()
    # Compile the finally block with the nonlocal control flow overridden to restore exc_info
    builder.builder.push_error_handler(cleanup_block)
    finally_control = FinallyNonlocalControl(builder.nonlocal_control[-1],
                                             ret_reg, old_exc)
    builder.nonlocal_control.append(finally_control)
    builder.activate_block(finally_block)
    finally_body()
    builder.nonlocal_control.pop()

    return cleanup_block, finally_control
예제 #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())
예제 #7
0
파일: specialize.py 프로젝트: alanhdu/mypy
def translate_next_call(builder: IRBuilder, expr: CallExpr,
                        callee: RefExpr) -> Optional[Value]:
    """Special case for calling next() on a generator expression, an
    idiom that shows up some in mypy.

    For example, next(x for x in l if x.id == 12, None) will
    generate code that searches l for an element where x.id == 12
    and produce the first such object, or None if no such element
    exists.
    """
    if not (expr.arg_kinds in ([ARG_POS], [ARG_POS, ARG_POS])
            and isinstance(expr.args[0], GeneratorExpr)):
        return None

    gen = expr.args[0]

    retval = Register(builder.node_type(expr))
    default_val = None
    if len(expr.args) > 1:
        default_val = builder.accept(expr.args[1])

    exit_block = BasicBlock()

    def gen_inner_stmts() -> None:
        # next takes the first element of the generator, so if
        # something gets produced, we are done.
        builder.assign(retval, builder.accept(gen.left_expr),
                       gen.left_expr.line)
        builder.goto(exit_block)

    loop_params = list(zip(gen.indices, gen.sequences, gen.condlists))
    comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line)

    # Now we need the case for when nothing got hit. If there was
    # a default value, we produce it, and otherwise we raise
    # StopIteration.
    if default_val:
        builder.assign(retval, default_val, gen.left_expr.line)
        builder.goto(exit_block)
    else:
        builder.add(
            RaiseStandardError(RaiseStandardError.STOP_ITERATION, None,
                               expr.line))
        builder.add(Unreachable())

    builder.activate_block(exit_block)
    return retval
예제 #8
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))
예제 #9
0
파일: function.py 프로젝트: wesleyks/mypy
def emit_yield(builder: IRBuilder, val: Value, line: int) -> Value:
    retval = builder.coerce(val, builder.ret_types[-1], line)

    cls = builder.fn_info.generator_class
    # Create a new block for the instructions immediately following the yield expression, and
    # set the next label so that the next time '__next__' is called on the generator object,
    # the function continues at the new block.
    next_block = BasicBlock()
    next_label = len(cls.continuation_blocks)
    cls.continuation_blocks.append(next_block)
    builder.assign(cls.next_label_target, builder.add(LoadInt(next_label)), line)
    builder.add(Return(retval))
    builder.activate_block(next_block)

    add_raise_exception_blocks_to_generator_class(builder, line)

    assert cls.send_arg_reg is not None
    return cls.send_arg_reg
예제 #10
0
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR:
    """Generate a "__ne__" method from a "__eq__" method. """
    builder.enter()

    rt_args = (RuntimeArg("self", RInstance(cls)), RuntimeArg("rhs", object_rprimitive))

    # The environment operates on Vars, so we make some up
    fake_vars = [(Var(arg.name), arg.type) for arg in rt_args]
    args = [
        builder.read(
            builder.environment.add_local_reg(
                var, type, is_arg=True
            ),
            line
        )
        for var, type in fake_vars
    ]  # type: List[Value]
    builder.ret_types[-1] = object_rprimitive

    # If __eq__ returns NotImplemented, then __ne__ should also
    not_implemented_block, regular_block = BasicBlock(), BasicBlock()
    eqval = builder.add(MethodCall(args[0], '__eq__', [args[1]], line))
    not_implemented = builder.add(LoadAddress(not_implemented_op.type,
                                              not_implemented_op.src, line))
    builder.add(Branch(
        builder.translate_is_op(eqval, not_implemented, 'is', line),
        not_implemented_block,
        regular_block,
        Branch.BOOL_EXPR))

    builder.activate_block(regular_block)
    retval = builder.coerce(
        builder.unary_op(eqval, 'not', line), object_rprimitive, line
    )
    builder.add(Return(retval))

    builder.activate_block(not_implemented_block)
    builder.add(Return(not_implemented))

    blocks, env, ret_type, _ = builder.leave()
    return FuncIR(
        FuncDecl('__ne__', cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type)),
        blocks, env)
예제 #11
0
def for_loop_helper_with_index(builder: IRBuilder, index: Lvalue,
                               expr: Expression, body_insts: Callable[[Value],
                                                                      None],
                               line: int) -> None:
    """Generate IR for a sequence iteration.

    This function only works for sequence type. Compared to for_loop_helper,
    it would feed iteration index to body_insts.

    Args:
        index: the loop index Lvalue
        expr: the expression to iterate over
        body_insts: a function that generates the body of the loop.
                    It needs a index as parameter.
    """
    expr_reg = builder.accept(expr)
    assert is_sequence_rprimitive(expr_reg.type)
    target_type = builder.get_sequence_type(expr)

    body_block = BasicBlock()
    step_block = BasicBlock()
    exit_block = BasicBlock()
    condition_block = BasicBlock()

    for_gen = ForSequence(builder, index, body_block, exit_block, line, False)
    for_gen.init(expr_reg, target_type, reverse=False)

    builder.push_loop_stack(step_block, exit_block)

    builder.goto_and_activate(condition_block)
    for_gen.gen_condition()

    builder.activate_block(body_block)
    for_gen.begin_body()
    body_insts(builder.read(for_gen.index_target))

    builder.goto_and_activate(step_block)
    for_gen.gen_step()
    builder.goto(condition_block)

    for_gen.add_cleanup(exit_block)
    builder.pop_loop_stack()

    builder.activate_block(exit_block)
예제 #12
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)
예제 #13
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
예제 #14
0
def try_finally_resolve_control(builder: IRBuilder,
                                cleanup_block: BasicBlock,
                                finally_control: FinallyNonlocalControl,
                                old_exc: Value,
                                ret_reg: Optional[Value]) -> BasicBlock:
    """Resolve the control flow out of a finally block.

    This means returning if there was a return, propagating
    exceptions, break/continue (soon), or just continuing on.
    """
    reraise, rest = BasicBlock(), BasicBlock()
    builder.add(Branch(old_exc, rest, reraise, Branch.IS_ERROR))

    # Reraise the exception if there was one
    builder.activate_block(reraise)
    builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())
    builder.builder.pop_error_handler()

    # If there was a return, keep returning
    if ret_reg:
        builder.activate_block(rest)
        return_block, rest = BasicBlock(), BasicBlock()
        builder.add(Branch(ret_reg, rest, return_block, Branch.IS_ERROR))

        builder.activate_block(return_block)
        builder.nonlocal_control[-1].gen_return(builder, ret_reg, -1)

    # TODO: handle break/continue
    builder.activate_block(rest)
    out_block = BasicBlock()
    builder.goto(out_block)

    # If there was an exception, restore again
    builder.activate_block(cleanup_block)
    finally_control.gen_cleanup(builder, -1)
    builder.call_c(keep_propagating_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())

    return out_block
예제 #15
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
예제 #16
0
def add_close_to_generator_class(builder: IRBuilder,
                                 fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    with builder.enter_method(fn_info.generator_class.ir, 'close',
                              object_rprimitive, fn_info):
        except_block, else_block = BasicBlock(), BasicBlock()
        builder.builder.push_error_handler(except_block)
        builder.goto_and_activate(BasicBlock())
        generator_exit = builder.load_module_attr_by_fullname(
            'builtins.GeneratorExit', fn_info.fitem.line)
        builder.add(
            MethodCall(
                builder.self(), 'throw',
                [generator_exit,
                 builder.none_object(),
                 builder.none_object()]))
        builder.goto(else_block)
        builder.builder.pop_error_handler()

        builder.activate_block(except_block)
        old_exc = builder.call_c(error_catch_op, [], fn_info.fitem.line)
        builder.nonlocal_control.append(
            ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc))
        stop_iteration = builder.load_module_attr_by_fullname(
            'builtins.StopIteration', fn_info.fitem.line)
        exceptions = builder.add(
            TupleSet([generator_exit, stop_iteration], fn_info.fitem.line))
        matches = builder.call_c(exc_matches_op, [exceptions],
                                 fn_info.fitem.line)

        match_block, non_match_block = BasicBlock(), BasicBlock()
        builder.add(Branch(matches, match_block, non_match_block, Branch.BOOL))

        builder.activate_block(match_block)
        builder.call_c(restore_exc_info_op, [builder.read(old_exc)],
                       fn_info.fitem.line)
        builder.add(Return(builder.none_object()))

        builder.activate_block(non_match_block)
        builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())

        builder.nonlocal_control.pop()

        builder.activate_block(else_block)
        builder.add(
            RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                               'generator ignored GeneratorExit',
                               fn_info.fitem.line))
        builder.add(Unreachable())
예제 #17
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)
예제 #18
0
def try_finally_entry_blocks(builder: IRBuilder, err_handler: BasicBlock,
                             return_entry: BasicBlock, main_entry: BasicBlock,
                             finally_block: BasicBlock,
                             ret_reg: Optional[Register]) -> Value:
    old_exc = builder.alloc_temp(exc_rtuple)

    # Entry block for non-exceptional flow
    builder.activate_block(main_entry)
    if ret_reg:
        builder.add(
            Assign(ret_reg,
                   builder.add(LoadErrorValue(builder.ret_types[-1]))))
    builder.goto(return_entry)

    builder.activate_block(return_entry)
    builder.add(Assign(old_exc, builder.add(LoadErrorValue(exc_rtuple))))
    builder.goto(finally_block)

    # Entry block for errors
    builder.activate_block(err_handler)
    if ret_reg:
        builder.add(
            Assign(ret_reg,
                   builder.add(LoadErrorValue(builder.ret_types[-1]))))
    builder.add(Assign(old_exc, builder.call_c(error_catch_op, [], -1)))
    builder.goto(finally_block)

    return old_exc
예제 #19
0
파일: for_helpers.py 프로젝트: xw0329/mypy
def for_loop_helper(builder: IRBuilder, index: Lvalue, expr: Expression,
                    body_insts: GenFunc, else_insts: Optional[GenFunc],
                    line: int) -> None:
    """Generate IR for a loop.

    Args:
        index: the loop index Lvalue
        expr: the expression to iterate over
        body_insts: a function that generates the body of the loop
        else_insts: a function that generates the else block instructions
    """
    # Body of the loop
    body_block = BasicBlock()
    # Block that steps to the next item
    step_block = BasicBlock()
    # Block for the else clause, if we need it
    else_block = BasicBlock()
    # Block executed after the loop
    exit_block = BasicBlock()

    # Determine where we want to exit, if our condition check fails.
    normal_loop_exit = else_block if else_insts is not None else exit_block

    for_gen = make_for_loop_generator(builder, index, expr, body_block,
                                      normal_loop_exit, line)

    builder.push_loop_stack(step_block, exit_block)
    condition_block = BasicBlock()
    builder.goto_and_activate(condition_block)

    # Add loop condition check.
    for_gen.gen_condition()

    # Generate loop body.
    builder.activate_block(body_block)
    for_gen.begin_body()
    body_insts()

    # We generate a separate step block (which might be empty).
    builder.goto_and_activate(step_block)
    for_gen.gen_step()
    # Go back to loop condition.
    builder.goto(condition_block)

    for_gen.add_cleanup(normal_loop_exit)
    builder.pop_loop_stack()

    if else_insts is not None:
        builder.activate_block(else_block)
        else_insts()
        builder.goto(exit_block)

    builder.activate_block(exit_block)
예제 #20
0
def transform_if_stmt(builder: IRBuilder, stmt: IfStmt) -> None:
    if_body, next = BasicBlock(), BasicBlock()
    else_body = BasicBlock() if stmt.else_body else next

    # If statements are normalized
    assert len(stmt.expr) == 1

    builder.process_conditional(stmt.expr[0], if_body, else_body)
    builder.activate_block(if_body)
    builder.accept(stmt.body[0])
    builder.goto(next)
    if stmt.else_body:
        builder.activate_block(else_body)
        builder.accept(stmt.else_body)
        builder.goto(next)
    builder.activate_block(next)
예제 #21
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)
예제 #22
0
def transform_while_stmt(builder: IRBuilder, s: WhileStmt) -> None:
    body, next, top, else_block = BasicBlock(), BasicBlock(), BasicBlock(), BasicBlock()
    normal_loop_exit = else_block if s.else_body is not None else next

    builder.push_loop_stack(top, next)

    # Split block so that we get a handle to the top of the loop.
    builder.goto_and_activate(top)
    process_conditional(builder, s.expr, body, normal_loop_exit)

    builder.activate_block(body)
    builder.accept(s.body)
    # Add branch to the top at the end of the body.
    builder.goto(top)

    builder.pop_loop_stack()

    if s.else_body is not None:
        builder.activate_block(else_block)
        builder.accept(s.else_body)
        builder.goto(next)

    builder.activate_block(next)
예제 #23
0
def transform_conditional_expr(builder: IRBuilder, expr: ConditionalExpr) -> Value:
    if_body, else_body, next_block = BasicBlock(), BasicBlock(), BasicBlock()

    builder.process_conditional(expr.cond, if_body, else_body)
    expr_type = builder.node_type(expr)
    # Having actual Phi nodes would be really nice here!
    target = Register(expr_type)

    builder.activate_block(if_body)
    true_value = builder.accept(expr.if_expr)
    true_value = builder.coerce(true_value, expr_type, expr.line)
    builder.add(Assign(target, true_value))
    builder.goto(next_block)

    builder.activate_block(else_body)
    false_value = builder.accept(expr.else_expr)
    false_value = builder.coerce(false_value, expr_type, expr.line)
    builder.add(Assign(target, false_value))
    builder.goto(next_block)

    builder.activate_block(next_block)

    return target
예제 #24
0
파일: function.py 프로젝트: wesleyks/mypy
def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, AwaitExpr]) -> Value:
    # This is basically an implementation of the code in PEP 380.

    # TODO: do we want to use the right types here?
    result = builder.alloc_temp(object_rprimitive)
    to_yield_reg = builder.alloc_temp(object_rprimitive)
    received_reg = builder.alloc_temp(object_rprimitive)

    if isinstance(o, YieldFromExpr):
        iter_val = builder.primitive_op(iter_op, [builder.accept(o.expr)], o.line)
    else:
        iter_val = builder.primitive_op(coro_op, [builder.accept(o.expr)], o.line)

    iter_reg = builder.maybe_spill_assignable(iter_val)

    stop_block, main_block, done_block = BasicBlock(), BasicBlock(), BasicBlock()
    _y_init = builder.primitive_op(next_raw_op, [builder.read(iter_reg)], o.line)
    builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR))

    # Try extracting a return value from a StopIteration and return it.
    # If it wasn't, this reraises the exception.
    builder.activate_block(stop_block)
    builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line)
    builder.goto(done_block)

    builder.activate_block(main_block)
    builder.assign(to_yield_reg, _y_init, o.line)

    # OK Now the main loop!
    loop_block = BasicBlock()
    builder.goto_and_activate(loop_block)

    def try_body() -> None:
        builder.assign(
            received_reg, emit_yield(builder, builder.read(to_yield_reg), o.line), o.line
        )

    def except_body() -> None:
        # The body of the except is all implemented in a C function to
        # reduce how much code we need to generate. It returns a value
        # indicating whether to break or yield (or raise an exception).
        res = builder.primitive_op(yield_from_except_op, [builder.read(iter_reg)], o.line)
        to_stop = builder.add(TupleGet(res, 0, o.line))
        val = builder.add(TupleGet(res, 1, o.line))

        ok, stop = BasicBlock(), BasicBlock()
        builder.add(Branch(to_stop, stop, ok, Branch.BOOL_EXPR))

        # The exception got swallowed. Continue, yielding the returned value
        builder.activate_block(ok)
        builder.assign(to_yield_reg, val, o.line)
        builder.nonlocal_control[-1].gen_continue(builder, o.line)

        # The exception was a StopIteration. Stop iterating.
        builder.activate_block(stop)
        builder.assign(result, val, o.line)
        builder.nonlocal_control[-1].gen_break(builder, o.line)

    def else_body() -> None:
        # Do a next() or a .send(). It will return NULL on exception
        # but it won't automatically propagate.
        _y = builder.primitive_op(
            send_op, [builder.read(iter_reg), builder.read(received_reg)], o.line
        )
        ok, stop = BasicBlock(), BasicBlock()
        builder.add(Branch(_y, stop, ok, Branch.IS_ERROR))

        # Everything's fine. Yield it.
        builder.activate_block(ok)
        builder.assign(to_yield_reg, _y, o.line)
        builder.nonlocal_control[-1].gen_continue(builder, o.line)

        # Try extracting a return value from a StopIteration and return it.
        # If it wasn't, this rereaises the exception.
        builder.activate_block(stop)
        builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line)
        builder.nonlocal_control[-1].gen_break(builder, o.line)

    builder.push_loop_stack(loop_block, done_block)
    transform_try_except(
        builder, try_body, [(None, None, except_body)], else_body, o.line
    )
    builder.pop_loop_stack()

    builder.goto_and_activate(done_block)
    return builder.read(result)
예제 #25
0
파일: function.py 프로젝트: alanhdu/mypy
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)
예제 #26
0
def create_switch_for_generator_class(builder: IRBuilder) -> None:
    builder.add(Goto(builder.fn_info.generator_class.switch_block))
    block = BasicBlock()
    builder.fn_info.generator_class.continuation_blocks.append(block)
    builder.activate_block(block)
예제 #27
0
def transform_try_except(builder: IRBuilder, body: GenFunc,
                         handlers: Sequence[Tuple[Optional[Expression],
                                                  Optional[Expression],
                                                  GenFunc]],
                         else_body: Optional[GenFunc], line: int) -> None:
    """Generalized try/except/else handling that takes functions to gen the bodies.

    The point of this is to also be able to support with."""
    assert handlers, "try needs except"

    except_entry, exit_block, cleanup_block = BasicBlock(), BasicBlock(
    ), BasicBlock()
    double_except_block = BasicBlock()
    # If there is an else block, jump there after the try, otherwise just leave
    else_block = BasicBlock() if else_body else exit_block

    # Compile the try block with an error handler
    builder.builder.push_error_handler(except_entry)
    builder.goto_and_activate(BasicBlock())
    body()
    builder.goto(else_block)
    builder.builder.pop_error_handler()

    # The error handler catches the error and then checks it
    # against the except clauses. We compile the error handler
    # itself with an error handler so that it can properly restore
    # the *old* exc_info if an exception occurs.
    # The exception chaining will be done automatically when the
    # exception is raised, based on the exception in exc_info.
    builder.builder.push_error_handler(double_except_block)
    builder.activate_block(except_entry)
    old_exc = builder.maybe_spill(builder.call_c(error_catch_op, [], line))
    # Compile the except blocks with the nonlocal control flow overridden to clear exc_info
    builder.nonlocal_control.append(
        ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc))

    # Process the bodies
    for type, var, handler_body in handlers:
        next_block = None
        if type:
            next_block, body_block = BasicBlock(), BasicBlock()
            matches = builder.call_c(exc_matches_op, [builder.accept(type)],
                                     type.line)
            builder.add(Branch(matches, body_block, next_block, Branch.BOOL))
            builder.activate_block(body_block)
        if var:
            target = builder.get_assignment_target(var)
            builder.assign(target,
                           builder.call_c(get_exc_value_op, [], var.line),
                           var.line)
        handler_body()
        builder.goto(cleanup_block)
        if next_block:
            builder.activate_block(next_block)

    # Reraise the exception if needed
    if next_block:
        builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())

    builder.nonlocal_control.pop()
    builder.builder.pop_error_handler()

    # Cleanup for if we leave except through normal control flow:
    # restore the saved exc_info information and continue propagating
    # the exception if it exists.
    builder.activate_block(cleanup_block)
    builder.call_c(restore_exc_info_op, [builder.read(old_exc)], line)
    builder.goto(exit_block)

    # Cleanup for if we leave except through a raised exception:
    # restore the saved exc_info information and continue propagating
    # the exception.
    builder.activate_block(double_except_block)
    builder.call_c(restore_exc_info_op, [builder.read(old_exc)], line)
    builder.call_c(keep_propagating_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())

    # If present, compile the else body in the obvious way
    if else_body:
        builder.activate_block(else_block)
        else_body()
        builder.goto(exit_block)

    builder.activate_block(exit_block)