示例#1
0
def prepare_func_def(module_name: str, class_name: Optional[str],
                     fdef: FuncDef, mapper: Mapper) -> FuncDecl:
    kind = FUNC_STATICMETHOD if fdef.is_static else (
        FUNC_CLASSMETHOD if fdef.is_class else FUNC_NORMAL)
    decl = FuncDecl(fdef.name, class_name, module_name, mapper.fdef_to_sig(fdef), kind)
    mapper.func_to_decl[fdef] = decl
    return decl
示例#2
0
def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo,
                                fn_decl: FuncDecl, sig: FuncSignature) -> None:
    """Generates the 'send' method for a generator class."""
    # FIXME: this is basically the same as add_next...
    builder.enter(fn_info)
    self_reg = builder.read(
        add_self_to_env(builder.environment, fn_info.generator_class.ir))
    arg = builder.environment.add_local_reg(Var('arg'), object_rprimitive,
                                            True)
    none_reg = builder.none_object()

    # Call the helper function with error flags set to Py_None, and return that result.
    result = builder.add(
        Call(fn_decl,
             [self_reg, none_reg, none_reg, none_reg,
              builder.read(arg)], fn_info.fitem.line))
    builder.add(Return(result))
    blocks, env, _, fn_info = builder.leave()

    sig = FuncSignature((
        RuntimeArg(SELF_NAME, object_rprimitive),
        RuntimeArg('arg', object_rprimitive),
    ), sig.ret_type)
    next_fn_decl = FuncDecl('send', fn_info.generator_class.ir.name,
                            builder.module_name, sig)
    next_fn_ir = FuncIR(next_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['send'] = next_fn_ir
    builder.functions.append(next_fn_ir)
示例#3
0
 def test_register(self) -> None:
     op = LoadInt(5)
     self.block.ops.append(op)
     fn = FuncIR(
         FuncDecl('myfunc', None, 'mod',
                  FuncSignature([self.arg], list_rprimitive)), [self.reg],
         [self.block])
     value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])),
                       value_names)
     generate_native_function(fn,
                              emitter,
                              'prog.py',
                              'prog',
                              optimize_int=False)
     result = emitter.fragments
     assert_string_arrays_equal([
         'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
         '    CPyTagged cpy_r_i0;\n',
         'CPyL0: ;\n',
         '    cpy_r_i0 = 10;\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
示例#4
0
 def test_call_two_args(self) -> None:
     decl = FuncDecl('myfn', None, 'mod',
                     FuncSignature([RuntimeArg('m', int_rprimitive),
                                    RuntimeArg('n', int_rprimitive)],
                                   int_rprimitive))
     self.assert_emit(Call(decl, [self.m, self.k], 55),
                      "cpy_r_r0 = CPyDef_myfn(cpy_r_m, cpy_r_k);")
示例#5
0
文件: function.py 项目: wesleyks/mypy
def gen_func_ir(builder: IRBuilder,
                blocks: List[BasicBlock],
                sig: FuncSignature,
                env: Environment,
                fn_info: FuncInfo,
                cdef: Optional[ClassDef]) -> Tuple[FuncIR, Optional[Value]]:
    """Generate the FuncIR for a function.

    This takes the basic blocks, environment, and function info of a
    particular function and returns the IR. If the function is nested,
    also returns the register containing the instance of the
    corresponding callable class.
    """
    func_reg = None  # type: Optional[Value]
    if fn_info.is_nested or fn_info.in_non_ext:
        func_ir = add_call_to_callable_class(builder, blocks, sig, env, fn_info)
        add_get_to_callable_class(builder, fn_info)
        func_reg = instantiate_callable_class(builder, fn_info)
    else:
        assert isinstance(fn_info.fitem, FuncDef)
        func_decl = builder.mapper.func_to_decl[fn_info.fitem]
        if fn_info.is_decorated:
            class_name = None if cdef is None else cdef.name
            func_decl = FuncDecl(fn_info.name, class_name, builder.module_name, sig,
                                 func_decl.kind,
                                 func_decl.is_prop_getter, func_decl.is_prop_setter)
            func_ir = FuncIR(func_decl, blocks, env, fn_info.fitem.line,
                             traceback_name=fn_info.fitem.name)
        else:
            func_ir = FuncIR(func_decl, blocks, env,
                             fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    return (func_ir, func_reg)
示例#6
0
文件: main.py 项目: xuefeng115/mypy
def transform_mypy_file(builder: IRBuilder, mypyfile: MypyFile) -> None:
    if mypyfile.fullname in ('typing', 'abc'):
        # These module are special; their contents are currently all
        # built-in primitives.
        return

    builder.set_module(mypyfile.fullname, mypyfile.path)

    classes = [node for node in mypyfile.defs if isinstance(node, ClassDef)]

    # Collect all classes.
    for cls in classes:
        ir = builder.mapper.type_to_ir[cls.info]
        builder.classes.append(ir)

    builder.enter('<top level>')

    # Make sure we have a builtins import
    builder.gen_import('builtins', -1)

    # Generate ops.
    for node in mypyfile.defs:
        builder.accept(node)
    builder.maybe_add_implicit_return()

    # Generate special function representing module top level.
    blocks, env, ret_type, _ = builder.leave()
    sig = FuncSignature([], none_rprimitive)
    func_ir = FuncIR(FuncDecl(TOP_LEVEL_NAME, None, builder.module_name, sig),
                     blocks,
                     env,
                     traceback_name="<module>")
    builder.functions.append(func_ir)
示例#7
0
文件: function.py 项目: wesleyks/mypy
def gen_glue_property(builder: IRBuilder,
                      sig: FuncSignature,
                      target: FuncIR,
                      cls: ClassIR,
                      base: ClassIR,
                      line: int,
                      do_pygetattr: bool) -> FuncIR:
    """Generate glue methods for properties that mediate between different subclass types.

    Similarly to methods, properties of derived types can be covariantly subtyped. Thus,
    properties also require glue. However, this only requires the return type to change.
    Further, instead of a method call, an attribute get is performed.

    If do_pygetattr is True, then get the attribute using the Python C
    API instead of a native call.
    """
    builder.enter()

    rt_arg = RuntimeArg(SELF_NAME, RInstance(cls))
    arg = builder.read(add_self_to_env(builder.environment, cls), line)
    builder.ret_types[-1] = sig.ret_type
    if do_pygetattr:
        retval = builder.py_get_attr(arg, target.name, line)
    else:
        retval = builder.add(GetAttr(arg, target.name, line))
    retbox = builder.coerce(retval, sig.ret_type, line)
    builder.add(Return(retbox))

    blocks, env, return_type, _ = builder.leave()
    return FuncIR(
        FuncDecl(target.name + '__' + base.name + '_glue',
                 cls.name, builder.module_name, FuncSignature([rt_arg], return_type)),
        blocks, env)
示例#8
0
def gen_glue_method(builder: IRBuilder, sig: FuncSignature, target: FuncIR,
                    cls: ClassIR, base: ClassIR, line: int,
                    do_pycall: bool,
                    ) -> FuncIR:
    """Generate glue methods that mediate between different method types in subclasses.

    For example, if we have:

    class A:
        def f(builder: IRBuilder, x: int) -> object: ...

    then it is totally permissible to have a subclass

    class B(A):
        def f(builder: IRBuilder, x: object) -> int: ...

    since '(object) -> int' is a subtype of '(int) -> object' by the usual
    contra/co-variant function subtyping rules.

    The trickiness here is that int and object have different
    runtime representations in mypyc, so A.f and B.f have
    different signatures at the native C level. To deal with this,
    we need to generate glue methods that mediate between the
    different versions by coercing the arguments and return
    values.

    If do_pycall is True, then make the call using the C API
    instead of a native call.
    """
    builder.enter()
    builder.ret_types[-1] = sig.ret_type

    rt_args = list(sig.args)
    if target.decl.kind == FUNC_NORMAL:
        rt_args[0] = RuntimeArg(sig.args[0].name, RInstance(cls))

    # 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.add_local_reg(var, type, is_arg=True), line)
            for var, type in fake_vars]
    arg_names = [arg.name if arg.kind in (ARG_NAMED, ARG_NAMED_OPT) else None
                 for arg in rt_args]
    arg_kinds = [concrete_arg_kind(arg.kind) for arg in rt_args]

    if do_pycall:
        retval = builder.builder.py_method_call(
            args[0], target.name, args[1:], line, arg_kinds[1:], arg_names[1:])
    else:
        retval = builder.builder.call(target.decl, args, arg_kinds, arg_names, line)
    retval = builder.coerce(retval, sig.ret_type, line)
    builder.add(Return(retval))

    arg_regs, _, blocks, ret_type, _ = builder.leave()
    return FuncIR(
        FuncDecl(target.name + '__' + base.name + '_glue',
                 cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type),
                 target.decl.kind),
        arg_regs, blocks)
示例#9
0
 def func_decl(self, name: str) -> FuncDecl:
     return FuncDecl(name=name,
                     class_name=None,
                     module_name="module",
                     sig=FuncSignature(
                         args=[],
                         ret_type=none_rprimitive,
                     ))
示例#10
0
 def leave_method(self) -> None:
     """Finish the generation of IR for a method."""
     arg_regs, args, blocks, ret_type, fn_info = self.leave()
     sig = FuncSignature(args, ret_type)
     name = self.function_name_stack.pop()
     class_ir = self.class_ir_stack.pop()
     decl = FuncDecl(name, class_ir.name, self.module_name, sig)
     ir = FuncIR(decl, arg_regs, blocks)
     class_ir.methods[name] = ir
     class_ir.method_decls[name] = ir.decl
     self.functions.append(ir)
示例#11
0
 def func_decl(self,
               name: str,
               ret_type: Optional[RType] = None) -> FuncDecl:
     if ret_type is None:
         ret_type = none_rprimitive
     return FuncDecl(
         name=name,
         class_name=None,
         module_name="module",
         sig=FuncSignature(
             args=[],
             ret_type=ret_type,
         ),
     )
示例#12
0
 def test_simple(self) -> None:
     self.block.ops.append(Return(self.reg))
     fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])))
     generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False)
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             'CPyL0: ;\n',
             '    return cpy_r_arg;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
示例#13
0
def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo,
                                 fn_decl: FuncDecl,
                                 sig: FuncSignature) -> None:
    """Generates the 'throw' method for a generator class."""
    builder.enter(fn_info)
    self_reg = builder.read(
        add_self_to_env(builder.environment, fn_info.generator_class.ir))

    # Add the type, value, and traceback variables to the environment.
    typ = builder.environment.add_local_reg(Var('type'), object_rprimitive,
                                            True)
    val = builder.environment.add_local_reg(Var('value'), object_rprimitive,
                                            True)
    tb = builder.environment.add_local_reg(Var('traceback'), object_rprimitive,
                                           True)

    # Because the value and traceback arguments are optional and hence
    # can be NULL if not passed in, we have to assign them Py_None if
    # they are not passed in.
    none_reg = builder.none_object()
    builder.assign_if_null(val, lambda: none_reg, builder.fn_info.fitem.line)
    builder.assign_if_null(tb, lambda: none_reg, builder.fn_info.fitem.line)

    # Call the helper function using the arguments passed in, and return that result.
    result = builder.add(
        Call(fn_decl, [
            self_reg,
            builder.read(typ),
            builder.read(val),
            builder.read(tb), none_reg
        ], fn_info.fitem.line))
    builder.add(Return(result))
    blocks, env, _, fn_info = builder.leave()

    # Create the FuncSignature for the throw function. Note that the
    # value and traceback fields are optional, and are assigned to if
    # they are not passed in inside the body of the throw function.
    sig = FuncSignature((RuntimeArg(
        SELF_NAME, object_rprimitive), RuntimeArg('type', object_rprimitive),
                         RuntimeArg('value', object_rprimitive, ARG_OPT),
                         RuntimeArg('traceback', object_rprimitive, ARG_OPT)),
                        sig.ret_type)

    throw_fn_decl = FuncDecl('throw', fn_info.generator_class.ir.name,
                             builder.module_name, sig)
    throw_fn_ir = FuncIR(throw_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['throw'] = throw_fn_ir
    builder.functions.append(throw_fn_ir)
示例#14
0
def add_iter_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
    """Generates the '__iter__' method for a generator class."""
    builder.enter(fn_info)
    self_target = add_self_to_env(builder.environment,
                                  fn_info.generator_class.ir)
    builder.add(Return(builder.read(self_target, fn_info.fitem.line)))
    blocks, env, _, fn_info = builder.leave()

    # Next, add the actual function as a method of the generator class.
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), ),
                        object_rprimitive)
    iter_fn_decl = FuncDecl('__iter__', fn_info.generator_class.ir.name,
                            builder.module_name, sig)
    iter_fn_ir = FuncIR(iter_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['__iter__'] = iter_fn_ir
    builder.functions.append(iter_fn_ir)
示例#15
0
文件: function.py 项目: alanhdu/mypy
def gen_dispatch_func_ir(
    builder: IRBuilder,
    fitem: FuncDef,
    main_func_name: str,
    dispatch_name: str,
    sig: FuncSignature,
) -> FuncIR:
    """Create a dispatch function (a function that checks the first argument type and dispatches
    to the correct implementation)
    """
    builder.enter()
    generate_singledispatch_dispatch_function(builder, main_func_name, fitem)
    args, _, blocks, _, fn_info = builder.leave()
    func_decl = FuncDecl(dispatch_name, None, builder.module_name, sig)
    dispatch_func_ir = FuncIR(func_decl, args, blocks)
    return dispatch_func_ir
示例#16
0
def add_call_to_callable_class(builder: IRBuilder,
                               blocks: List[BasicBlock],
                               sig: FuncSignature,
                               env: Environment,
                               fn_info: FuncInfo) -> FuncIR:
    """Generates a '__call__' method for a callable class representing a nested function.

    This takes the blocks, signature, and environment associated with a function definition and
    uses those to build the '__call__' method of a given callable class, used to represent that
    function. Note that a 'self' parameter is added to its list of arguments, as the nested
    function becomes a class method.
    """
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type)
    call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig)
    call_fn_ir = FuncIR(call_fn_decl, blocks, env,
                        fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.callable_class.ir.methods['__call__'] = call_fn_ir
    return call_fn_ir
示例#17
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)
示例#18
0
def add_call_to_callable_class(builder: IRBuilder,
                               args: List[Register],
                               blocks: List[BasicBlock],
                               sig: FuncSignature,
                               fn_info: FuncInfo) -> FuncIR:
    """Generate a '__call__' method for a callable class representing a nested function.

    This takes the blocks and signature associated with a function
    definition and uses those to build the '__call__' method of a
    given callable class, used to represent that function.
    """
    # Since we create a method, we also add a 'self' parameter.
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type)
    call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig)
    call_fn_ir = FuncIR(call_fn_decl, args, blocks,
                        fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.callable_class.ir.methods['__call__'] = call_fn_ir
    fn_info.callable_class.ir.method_decls['__call__'] = call_fn_decl
    return call_fn_ir
示例#19
0
def add_helper_to_generator_class(builder: IRBuilder,
                                  arg_regs: List[Register],
                                  blocks: List[BasicBlock],
                                  sig: FuncSignature,
                                  fn_info: FuncInfo) -> FuncDecl:
    """Generates a helper method for a generator class, called by '__next__' and 'throw'."""
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),
                         RuntimeArg('type', object_rprimitive),
                         RuntimeArg('value', object_rprimitive),
                         RuntimeArg('traceback', object_rprimitive),
                         RuntimeArg('arg', object_rprimitive)
                         ), sig.ret_type)
    helper_fn_decl = FuncDecl('__mypyc_generator_helper__', fn_info.generator_class.ir.name,
                              builder.module_name, sig)
    helper_fn_ir = FuncIR(helper_fn_decl, arg_regs, blocks,
                          fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.generator_class.ir.methods['__mypyc_generator_helper__'] = helper_fn_ir
    builder.functions.append(helper_fn_ir)
    return helper_fn_decl
示例#20
0
 def test_register(self) -> None:
     self.env.temp_index = 0
     op = LoadInt(5)
     self.block.ops.append(op)
     self.env.add_op(op)
     fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)),
                 [self.block], self.env)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])))
     generate_native_function(fn, emitter, 'prog.py', 'prog', False)
     result = emitter.fragments
     assert_string_arrays_equal(
         [
             'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
             '    CPyTagged cpy_r_i0;\n',
             'CPyL0: ;\n',
             '    cpy_r_i0 = 10;\n',
             '}\n',
         ],
         result, msg='Generated code invalid')
示例#21
0
def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
    # OK AND NOW THE FUN PART
    base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs
    if base_exprs:
        bases = [builder.accept(x) for x in base_exprs]
        tp_bases = builder.new_tuple(bases, cdef.line)
    else:
        tp_bases = builder.add(
            LoadErrorValue(object_rprimitive, is_borrowed=True))
    modname = builder.load_static_unicode(builder.module_name)
    template = builder.add(
        LoadStatic(object_rprimitive, cdef.name + "_template",
                   builder.module_name, NAMESPACE_TYPE))
    # Create the class
    tp = builder.call_c(pytype_from_template_op, [template, tp_bases, modname],
                        cdef.line)
    # Immediately fix up the trait vtables, before doing anything with the class.
    ir = builder.mapper.type_to_ir[cdef.info]
    if not ir.is_trait and not ir.builtin_base:
        builder.add(
            Call(
                FuncDecl(cdef.name + '_trait_vtable_setup',
                         None, builder.module_name,
                         FuncSignature([], bool_rprimitive)), [], -1))
    # Populate a '__mypyc_attrs__' field containing the list of attrs
    builder.call_c(py_setattr_op, [
        tp,
        builder.load_static_unicode('__mypyc_attrs__'),
        create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info],
                                 cdef.line)
    ], cdef.line)

    # Save the class
    builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE))

    # Add it to the dict
    builder.call_c(dict_set_item_op, [
        builder.load_globals_dict(),
        builder.load_static_unicode(cdef.name),
        tp,
    ], cdef.line)

    return tp
示例#22
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)
示例#23
0
def add_close_to_generator_class(builder: IRBuilder,
                                 fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    # TODO: Currently this method just triggers a runtime error,
    # we should fill this out eventually.
    builder.enter(fn_info)
    add_self_to_env(builder.environment, fn_info.generator_class.ir)
    builder.add(
        RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                           'close method on generator classes uimplemented',
                           fn_info.fitem.line))
    builder.add(Unreachable())
    blocks, env, _, fn_info = builder.leave()

    # Next, add the actual function as a method of the generator class.
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), ),
                        object_rprimitive)
    close_fn_decl = FuncDecl('close', fn_info.generator_class.ir.name,
                             builder.module_name, sig)
    close_fn_ir = FuncIR(close_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['close'] = close_fn_ir
    builder.functions.append(close_fn_ir)
示例#24
0
def prepare_class_def(path: str, module_name: str, cdef: ClassDef,
                      errors: Errors, mapper: Mapper) -> None:

    ir = mapper.type_to_ir[cdef.info]
    info = cdef.info

    attrs = get_mypyc_attrs(cdef)
    if attrs.get("allow_interpreted_subclasses") is True:
        ir.allow_interpreted_subclasses = True
    if attrs.get("serializable") is True:
        # Supports copy.copy and pickle (including subclasses)
        ir._serializable = True

    # We sort the table for determinism here on Python 3.5
    for name, node in sorted(info.names.items()):
        # Currently all plugin generated methods are dummies and not included.
        if node.plugin_generated:
            continue

        if isinstance(node.node, Var):
            assert node.node.type, "Class member %s missing type" % name
            if not node.node.is_classvar and name not in ('__slots__', '__deletable__'):
                ir.attributes[name] = mapper.type_to_rtype(node.node.type)
        elif isinstance(node.node, (FuncDef, Decorator)):
            prepare_method_def(ir, module_name, cdef, mapper, node.node)
        elif isinstance(node.node, OverloadedFuncDef):
            # Handle case for property with both a getter and a setter
            if node.node.is_property:
                if is_valid_multipart_property_def(node.node):
                    for item in node.node.items:
                        prepare_method_def(ir, module_name, cdef, mapper, item)
                else:
                    errors.error("Unsupported property decorator semantics", path, cdef.line)

            # Handle case for regular function overload
            else:
                assert node.node.impl
                prepare_method_def(ir, module_name, cdef, mapper, node.node.impl)

    # Check for subclassing from builtin types
    for cls in info.mro:
        # Special case exceptions and dicts
        # XXX: How do we handle *other* things??
        if cls.fullname == 'builtins.BaseException':
            ir.builtin_base = 'PyBaseExceptionObject'
        elif cls.fullname == 'builtins.dict':
            ir.builtin_base = 'PyDictObject'
        elif cls.fullname.startswith('builtins.'):
            if not can_subclass_builtin(cls.fullname):
                # Note that if we try to subclass a C extension class that
                # isn't in builtins, bad things will happen and we won't
                # catch it here! But this should catch a lot of the most
                # common pitfalls.
                errors.error("Inheriting from most builtin types is unimplemented",
                             path, cdef.line)

    if ir.builtin_base:
        ir.attributes.clear()

    # Set up a constructor decl
    init_node = cdef.info['__init__'].node
    if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef):
        init_sig = mapper.fdef_to_sig(init_node)

        defining_ir = mapper.type_to_ir.get(init_node.info)
        # If there is a nontrivial __init__ that wasn't defined in an
        # extension class, we need to make the constructor take *args,
        # **kwargs so it can call tp_init.
        if ((defining_ir is None or not defining_ir.is_ext_class
             or cdef.info['__init__'].plugin_generated)
                and init_node.info.fullname != 'builtins.object'):
            init_sig = FuncSignature(
                [init_sig.args[0],
                 RuntimeArg("args", tuple_rprimitive, ARG_STAR),
                 RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2)],
                init_sig.ret_type)

        ctor_sig = FuncSignature(init_sig.args[1:], RInstance(ir))
        ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig)
        mapper.func_to_decl[cdef.info] = ir.ctor

    # Set up the parent class
    bases = [mapper.type_to_ir[base.type] for base in info.bases
             if base.type in mapper.type_to_ir]
    if not all(c.is_trait for c in bases[1:]):
        errors.error("Non-trait bases must appear first in parent list", path, cdef.line)
    ir.traits = [c for c in bases if c.is_trait]

    mro = []
    base_mro = []
    for cls in info.mro:
        if cls not in mapper.type_to_ir:
            if cls.fullname != 'builtins.object':
                ir.inherits_python = True
            continue
        base_ir = mapper.type_to_ir[cls]
        if not base_ir.is_trait:
            base_mro.append(base_ir)
        mro.append(base_ir)

        if cls.defn.removed_base_type_exprs or not base_ir.is_ext_class:
            ir.inherits_python = True

    base_idx = 1 if not ir.is_trait else 0
    if len(base_mro) > base_idx:
        ir.base = base_mro[base_idx]
    ir.mro = mro
    ir.base_mro = base_mro

    for base in bases:
        if base.children is not None:
            base.children.append(ir)

    if is_dataclass(cdef):
        ir.is_augmented = True
示例#25
0
def gen_glue_method(
    builder: IRBuilder,
    sig: FuncSignature,
    target: FuncIR,
    cls: ClassIR,
    base: ClassIR,
    line: int,
    do_pycall: bool,
) -> FuncIR:
    """Generate glue methods that mediate between different method types in subclasses.

    For example, if we have:

    class A:
        def f(builder: IRBuilder, x: int) -> object: ...

    then it is totally permissible to have a subclass

    class B(A):
        def f(builder: IRBuilder, x: object) -> int: ...

    since '(object) -> int' is a subtype of '(int) -> object' by the usual
    contra/co-variant function subtyping rules.

    The trickiness here is that int and object have different
    runtime representations in mypyc, so A.f and B.f have
    different signatures at the native C level. To deal with this,
    we need to generate glue methods that mediate between the
    different versions by coercing the arguments and return
    values.

    If do_pycall is True, then make the call using the C API
    instead of a native call.
    """
    builder.enter()
    builder.ret_types[-1] = sig.ret_type

    rt_args = list(sig.args)
    if target.decl.kind == FUNC_NORMAL:
        rt_args[0] = RuntimeArg(sig.args[0].name, RInstance(cls))

    arg_info = get_args(builder, rt_args, line)
    args, arg_kinds, arg_names = arg_info.args, arg_info.arg_kinds, arg_info.arg_names

    # We can do a passthrough *args/**kwargs with a native call, but if the
    # args need to get distributed out to arguments, we just let python handle it
    if (any(kind.is_star() for kind in arg_kinds)
            and any(not arg.kind.is_star() for arg in target.decl.sig.args)):
        do_pycall = True

    if do_pycall:
        if target.decl.kind == FUNC_STATICMETHOD:
            # FIXME: this won't work if we can do interpreted subclasses
            first = builder.builder.get_native_type(cls)
            st = 0
        else:
            first = args[0]
            st = 1
        retval = builder.builder.py_method_call(first, target.name, args[st:],
                                                line, arg_kinds[st:],
                                                arg_names[st:])
    else:
        retval = builder.builder.call(target.decl, args, arg_kinds, arg_names,
                                      line)
    retval = builder.coerce(retval, sig.ret_type, line)
    builder.add(Return(retval))

    arg_regs, _, blocks, ret_type, _ = builder.leave()
    return FuncIR(
        FuncDecl(target.name + '__' + base.name + '_glue', cls.name,
                 builder.module_name, FuncSignature(rt_args, ret_type),
                 target.decl.kind), arg_regs, blocks)
示例#26
0
    def __init__(self,
                 name: str,
                 module_name: str,
                 is_trait: bool = False,
                 is_generated: bool = False,
                 is_abstract: bool = False,
                 is_ext_class: bool = True) -> None:
        self.name = name
        self.module_name = module_name
        self.is_trait = is_trait
        self.is_generated = is_generated
        self.is_abstract = is_abstract
        self.is_ext_class = is_ext_class
        # An augmented class has additional methods separate from what mypyc generates.
        # Right now the only one is dataclasses.
        self.is_augmented = False
        # Does this inherit from a Python class?
        self.inherits_python = False
        # Do instances of this class have __dict__?
        self.has_dict = False
        # Do we allow interpreted subclasses? Derived from a mypyc_attr.
        self.allow_interpreted_subclasses = False
        # Does this class need getseters to be generated for its attributes? (getseters are also
        # added if is_generated is False)
        self.needs_getseters = False
        # Is this class declared as serializable (supports copy.copy
        # and pickle) using @mypyc_attr(serializable=True)?
        #
        # Additionally, any class with this attribute False but with
        # an __init__ that can be called without any arguments is
        # *implicitly serializable*. In this case __init__ will be
        # called during deserialization without arguments. If this is
        # True, we match Python semantics and __init__ won't be called
        # during deserialization.
        #
        # This impacts also all subclasses. Use is_serializable() to
        # also consider base classes.
        self._serializable = False
        # If this a subclass of some built-in python class, the name
        # of the object for that class. We currently only support this
        # in a few ad-hoc cases.
        self.builtin_base: Optional[str] = None
        # Default empty constructor
        self.ctor = FuncDecl(name, None, module_name,
                             FuncSignature([], RInstance(self)))

        self.attributes: OrderedDict[str, RType] = OrderedDict()
        # Deletable attributes
        self.deletable: List[str] = []
        # We populate method_types with the signatures of every method before
        # we generate methods, and we rely on this information being present.
        self.method_decls: OrderedDict[str, FuncDecl] = OrderedDict()
        # Map of methods that are actually present in an extension class
        self.methods: OrderedDict[str, FuncIR] = OrderedDict()
        # Glue methods for boxing/unboxing when a class changes the type
        # while overriding a method. Maps from (parent class overridden, method)
        # to IR of glue method.
        self.glue_methods: Dict[Tuple[ClassIR, str], FuncIR] = OrderedDict()

        # Properties are accessed like attributes, but have behavior like method calls.
        # They don't belong in the methods dictionary, since we don't want to expose them to
        # Python's method API. But we want to put them into our own vtable as methods, so that
        # they are properly handled and overridden. The property dictionary values are a tuple
        # containing a property getter and an optional property setter.
        self.properties: OrderedDict[str,
                                     Tuple[FuncIR,
                                           Optional[FuncIR]]] = OrderedDict()
        # We generate these in prepare_class_def so that we have access to them when generating
        # other methods and properties that rely on these types.
        self.property_types: OrderedDict[str, RType] = OrderedDict()

        self.vtable: Optional[Dict[str, int]] = None
        self.vtable_entries: VTableEntries = []
        self.trait_vtables: OrderedDict[ClassIR, VTableEntries] = OrderedDict()
        # N.B: base might not actually quite be the direct base.
        # It is the nearest concrete base, but we allow a trait in between.
        self.base: Optional[ClassIR] = None
        self.traits: List[ClassIR] = []
        # Supply a working mro for most generated classes. Real classes will need to
        # fix it up.
        self.mro: List[ClassIR] = [self]
        # base_mro is the chain of concrete (non-trait) ancestors
        self.base_mro: List[ClassIR] = [self]

        # Direct subclasses of this class (use subclasses() to also include non-direct ones)
        # None if separate compilation prevents this from working
        self.children: Optional[List[ClassIR]] = []

        # Instance attributes that are initialized in the class body.
        self.attrs_with_defaults: Set[str] = set()

        # Attributes that are always initialized in __init__ or class body
        # (inferred in mypyc.analysis.attrdefined using interprocedural analysis)
        self._always_initialized_attrs: Set[str] = set()

        # Attributes that are sometimes initialized in __init__
        self._sometimes_initialized_attrs: Set[str] = set()

        # If True, __init__ can make 'self' visible to unanalyzed/arbitrary code
        self.init_self_leak = False
示例#27
0
    def __init__(self, name: str, module_name: str, is_trait: bool = False,
                 is_generated: bool = False, is_abstract: bool = False,
                 is_ext_class: bool = True) -> None:
        self.name = name
        self.module_name = module_name
        self.is_trait = is_trait
        self.is_generated = is_generated
        self.is_abstract = is_abstract
        self.is_ext_class = is_ext_class
        # An augmented class has additional methods separate from what mypyc generates.
        # Right now the only one is dataclasses.
        self.is_augmented = False
        # Does this inherit from a Python class?
        self.inherits_python = False
        # Do instances of this class have __dict__?
        self.has_dict = False
        # Do we allow interpreted subclasses? Derived from a mypyc_attr.
        self.allow_interpreted_subclasses = False
        # If this a subclass of some built-in python class, the name
        # of the object for that class. We currently only support this
        # in a few ad-hoc cases.
        self.builtin_base = None  # type: Optional[str]
        # Default empty constructor
        self.ctor = FuncDecl(name, None, module_name, FuncSignature([], RInstance(self)))

        self.attributes = OrderedDict()  # type: OrderedDict[str, RType]
        # We populate method_types with the signatures of every method before
        # we generate methods, and we rely on this information being present.
        self.method_decls = OrderedDict()  # type: OrderedDict[str, FuncDecl]
        # Map of methods that are actually present in an extension class
        self.methods = OrderedDict()  # type: OrderedDict[str, FuncIR]
        # Glue methods for boxing/unboxing when a class changes the type
        # while overriding a method. Maps from (parent class overrided, method)
        # to IR of glue method.
        self.glue_methods = OrderedDict()  # type: Dict[Tuple[ClassIR, str], FuncIR]

        # Properties are accessed like attributes, but have behavior like method calls.
        # They don't belong in the methods dictionary, since we don't want to expose them to
        # Python's method API. But we want to put them into our own vtable as methods, so that
        # they are properly handled and overridden. The property dictionary values are a tuple
        # containing a property getter and an optional property setter.
        self.properties = OrderedDict()  # type: OrderedDict[str, Tuple[FuncIR, Optional[FuncIR]]]
        # We generate these in prepare_class_def so that we have access to them when generating
        # other methods and properties that rely on these types.
        self.property_types = OrderedDict()  # type: OrderedDict[str, RType]

        self.vtable = None  # type: Optional[Dict[str, int]]
        self.vtable_entries = []  # type: VTableEntries
        self.trait_vtables = OrderedDict()  # type: OrderedDict[ClassIR, VTableEntries]
        # N.B: base might not actually quite be the direct base.
        # It is the nearest concrete base, but we allow a trait in between.
        self.base = None  # type: Optional[ClassIR]
        self.traits = []  # type: List[ClassIR]
        # Supply a working mro for most generated classes. Real classes will need to
        # fix it up.
        self.mro = [self]  # type: List[ClassIR]
        # base_mro is the chain of concrete (non-trait) ancestors
        self.base_mro = [self]  # type: List[ClassIR]

        # Direct subclasses of this class (use subclasses() to also incude non-direct ones)
        # None if separate compilation prevents this from working
        self.children = []  # type: Optional[List[ClassIR]]
示例#28
0
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
    """Generate an initialization method for default attr values (from class vars)."""
    cls = builder.mapper.type_to_ir[cdef.info]
    if cls.builtin_base:
        return

    # Pull out all assignments in classes in the mro so we can initialize them
    # TODO: Support nested statements
    default_assignments = []
    for info in reversed(cdef.info.mro):
        if info not in builder.mapper.type_to_ir:
            continue
        for stmt in info.defn.defs.body:
            if (isinstance(stmt, AssignmentStmt)
                    and isinstance(stmt.lvalues[0], NameExpr)
                    and not is_class_var(stmt.lvalues[0])
                    and not isinstance(stmt.rvalue, TempNode)):
                if stmt.lvalues[0].name == '__slots__':
                    continue

                # Skip type annotated assignments in dataclasses
                if is_dataclass(cdef) and stmt.type:
                    continue

                default_assignments.append(stmt)

    if not default_assignments:
        return

    builder.enter()
    builder.ret_types[-1] = bool_rprimitive

    rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)), )
    self_var = builder.read(add_self_to_env(builder.environment, cls), -1)

    for stmt in default_assignments:
        lvalue = stmt.lvalues[0]
        assert isinstance(lvalue, NameExpr)
        if not stmt.is_final_def and not is_constant(stmt.rvalue):
            builder.warning('Unsupported default attribute value',
                            stmt.rvalue.line)

        # If the attribute is initialized to None and type isn't optional,
        # don't initialize it to anything.
        attr_type = cls.attr_type(lvalue.name)
        if isinstance(stmt.rvalue,
                      RefExpr) and stmt.rvalue.fullname == 'builtins.None':
            if (not is_optional_type(attr_type)
                    and not is_object_rprimitive(attr_type)
                    and not is_none_rprimitive(attr_type)):
                continue
        val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line)
        builder.add(SetAttr(self_var, lvalue.name, val, -1))

    builder.add(Return(builder.true()))

    blocks, env, ret_type, _ = builder.leave()
    ir = FuncIR(
        FuncDecl('__mypyc_defaults_setup', cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type)), blocks, env)
    builder.functions.append(ir)
    cls.methods[ir.name] = ir