예제 #1
0
파일: function.py 프로젝트: wesleyks/mypy
def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
    func_ir, func_reg = gen_func_item(
        builder,
        dec.func,
        dec.func.name,
        builder.mapper.fdef_to_sig(dec.func)
    )

    if dec.func in builder.nested_fitems:
        assert func_reg is not None
        decorated_func = load_decorated_func(builder, dec.func, func_reg)
        builder.assign(get_func_target(builder, dec.func), decorated_func, dec.func.line)
        func_reg = decorated_func
    else:
        # Obtain the the function name in order to construct the name of the helper function.
        name = dec.func.fullname.split('.')[-1]
        helper_name = decorator_helper_name(name)

        # Load the callable object representing the non-decorated function, and decorate it.
        orig_func = builder.load_global_str(helper_name, dec.line)
        decorated_func = load_decorated_func(builder, dec.func, orig_func)

        # Set the callable object representing the decorated function as a global.
        builder.call_c(dict_set_item_op,
                    [builder.load_globals_dict(),
                    builder.load_static_unicode(dec.func.name), decorated_func],
                    decorated_func.line)

    builder.functions.append(func_ir)
예제 #2
0
파일: function.py 프로젝트: alanhdu/mypy
def transform_decorator(builder: IRBuilder, dec: Decorator) -> None:
    func_ir, func_reg = gen_func_item(
        builder,
        dec.func,
        dec.func.name,
        builder.mapper.fdef_to_sig(dec.func)
    )

    if dec.func in builder.nested_fitems:
        assert func_reg is not None
        decorated_func = load_decorated_func(builder, dec.func, func_reg)
        builder.assign(get_func_target(builder, dec.func), decorated_func, dec.func.line)
        func_reg = decorated_func
    # If the prebuild pass didn't put this function in the function to decorators map (for example
    # if this is a registered singledispatch implementation with no other decorators), we should
    # treat this function as a regular function, not a decorated function
    elif dec.func in builder.fdefs_to_decorators:
        # Obtain the the function name in order to construct the name of the helper function.
        name = dec.func.fullname.split('.')[-1]
        helper_name = decorator_helper_name(name)

        # Load the callable object representing the non-decorated function, and decorate it.
        orig_func = builder.load_global_str(helper_name, dec.line)
        decorated_func = load_decorated_func(builder, dec.func, orig_func)

        # Set the callable object representing the decorated function as a global.
        builder.call_c(dict_set_item_op,
                    [builder.load_globals_dict(),
                    builder.load_str(dec.func.name), decorated_func],
                    decorated_func.line)

    builder.functions.append(func_ir)
예제 #3
0
 def __init__(self,
              fitem: FuncItem = INVALID_FUNC_DEF,
              name: str = '',
              class_name: Optional[str] = None,
              namespace: str = '',
              is_nested: bool = False,
              contains_nested: bool = False,
              is_decorated: bool = False,
              in_non_ext: bool = False) -> None:
     self.fitem = fitem
     self.name = name if not is_decorated else decorator_helper_name(name)
     self.class_name = class_name
     self.ns = namespace
     # Callable classes implement the '__call__' method, and are used to represent functions
     # that are nested inside of other functions.
     self._callable_class = None  # type: Optional[ImplicitClass]
     # Environment classes are ClassIR instances that contain attributes representing the
     # variables in the environment of the function they correspond to. Environment classes are
     # generated for functions that contain nested functions.
     self._env_class = None  # type: Optional[ClassIR]
     # Generator classes implement the '__next__' method, and are used to represent generators
     # returned by generator functions.
     self._generator_class = None  # type: Optional[GeneratorClass]
     # Environment class registers are the local registers associated with instances of an
     # environment class, used for getting and setting attributes. curr_env_reg is the register
     # associated with the current environment.
     self._curr_env_reg = None  # type: Optional[Value]
     # These are flags denoting whether a given function is nested, contains a nested function,
     # is decorated, or is within a non-extension class.
     self.is_nested = is_nested
     self.contains_nested = contains_nested
     self.is_decorated = is_decorated
     self.in_non_ext = in_non_ext
예제 #4
0
파일: function.py 프로젝트: wesleyks/mypy
def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None:
    # Perform the function of visit_method for methods inside extension classes.
    name = fdef.name
    class_ir = builder.mapper.type_to_ir[cdef.info]
    func_ir, func_reg = gen_func_item(builder, fdef, name, builder.mapper.fdef_to_sig(fdef), cdef)
    builder.functions.append(func_ir)

    if is_decorated(builder, fdef):
        # Obtain the the function name in order to construct the name of the helper function.
        _, _, name = fdef.fullname.rpartition('.')
        helper_name = decorator_helper_name(name)
        # Read the PyTypeObject representing the class, get the callable object
        # representing the non-decorated method
        typ = builder.load_native_type_object(cdef.fullname)
        orig_func = builder.py_get_attr(typ, helper_name, fdef.line)

        # Decorate the non-decorated method
        decorated_func = load_decorated_func(builder, fdef, orig_func)

        # Set the callable object representing the decorated method as an attribute of the
        # extension class.
        builder.primitive_op(py_setattr_op,
                          [
                              typ,
                              builder.load_static_unicode(name),
                              decorated_func
                          ],
                          fdef.line)

    if fdef.is_property:
        # If there is a property setter, it will be processed after the getter,
        # We populate the optional setter field with none for now.
        assert name not in class_ir.properties
        class_ir.properties[name] = (func_ir, None)

    elif fdef in builder.prop_setters:
        # The respective property getter must have been processed already
        assert name in class_ir.properties
        getter_ir, _ = class_ir.properties[name]
        class_ir.properties[name] = (getter_ir, func_ir)

    class_ir.methods[func_ir.decl.name] = func_ir

    # If this overrides a parent class method with a different type, we need
    # to generate a glue method to mediate between them.
    for base in class_ir.mro[1:]:
        if (name in base.method_decls and name != '__init__'
                and not is_same_method_signature(class_ir.method_decls[name].sig,
                                                 base.method_decls[name].sig)):

            # TODO: Support contravariant subtyping in the input argument for
            # property setters. Need to make a special glue method for handling this,
            # similar to gen_glue_property.

            f = gen_glue(builder, base.method_decls[name].sig, func_ir, class_ir, base, fdef)
            class_ir.glue_methods[(base, name)] = f
            builder.functions.append(f)

    # If the class allows interpreted children, create glue
    # methods that dispatch via the Python API. These will go in a
    # "shadow vtable" that will be assigned to interpreted
    # children.
    if class_ir.allow_interpreted_subclasses:
        f = gen_glue(builder, func_ir.sig, func_ir, class_ir, class_ir, fdef, do_py_ops=True)
        class_ir.glue_methods[(class_ir, name)] = f
        builder.functions.append(f)
예제 #5
0
파일: function.py 프로젝트: alanhdu/mypy
def gen_func_item(builder: IRBuilder,
                  fitem: FuncItem,
                  name: str,
                  sig: FuncSignature,
                  cdef: Optional[ClassDef] = None,
                  ) -> Tuple[FuncIR, Optional[Value]]:
    """Generate and return the FuncIR for a given FuncDef.

    If the given FuncItem is a nested function, then we generate a
    callable class representing the function and use that instead of
    the actual function. if the given FuncItem contains a nested
    function, then we generate an environment class so that inner
    nested functions can access the environment of the given FuncDef.

    Consider the following nested function:

        def a() -> None:
            def b() -> None:
                def c() -> None:
                    return None
                return None
            return None

    The classes generated would look something like the following.

                has pointer to        +-------+
        +-------------------------->  | a_env |
        |                             +-------+
        |                                 ^
        |                                 | has pointer to
    +-------+     associated with     +-------+
    | b_obj |   ------------------->  | b_env |
    +-------+                         +-------+
                                          ^
                                          |
    +-------+         has pointer to      |
    | c_obj |   --------------------------+
    +-------+
    """

    # TODO: do something about abstract methods.

    func_reg: Optional[Value] = None

    # We treat lambdas as always being nested because we always generate
    # a class for lambdas, no matter where they are. (It would probably also
    # work to special case toplevel lambdas and generate a non-class function.)
    is_nested = fitem in builder.nested_fitems or isinstance(fitem, LambdaExpr)
    contains_nested = fitem in builder.encapsulating_funcs.keys()
    is_decorated = fitem in builder.fdefs_to_decorators
    is_singledispatch = fitem in builder.singledispatch_impls
    in_non_ext = False
    class_name = None
    if cdef:
        ir = builder.mapper.type_to_ir[cdef.info]
        in_non_ext = not ir.is_ext_class
        class_name = cdef.name

    if is_singledispatch:
        func_name = '__mypyc_singledispatch_main_function_{}__'.format(name)
    else:
        func_name = name
    builder.enter(FuncInfo(fitem, func_name, class_name, gen_func_ns(builder),
                           is_nested, contains_nested, is_decorated, in_non_ext))

    # Functions that contain nested functions need an environment class to store variables that
    # are free in their nested functions. Generator functions need an environment class to
    # store a variable denoting the next instruction to be executed when the __next__ function
    # is called, along with all the variables inside the function itself.
    if builder.fn_info.contains_nested or builder.fn_info.is_generator:
        setup_env_class(builder)

    if builder.fn_info.is_nested or builder.fn_info.in_non_ext:
        setup_callable_class(builder)

    if builder.fn_info.is_generator:
        # Do a first-pass and generate a function that just returns a generator object.
        gen_generator_func(builder)
        args, _, blocks, ret_type, fn_info = builder.leave()
        func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, fn_info, cdef)

        # Re-enter the FuncItem and visit the body of the function this time.
        builder.enter(fn_info)
        setup_env_for_generator_class(builder)
        load_outer_envs(builder, builder.fn_info.generator_class)
        if builder.fn_info.is_nested and isinstance(fitem, FuncDef):
            setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class)
        create_switch_for_generator_class(builder)
        add_raise_exception_blocks_to_generator_class(builder, fitem.line)
    else:
        load_env_registers(builder)
        gen_arg_defaults(builder)

    if builder.fn_info.contains_nested and not builder.fn_info.is_generator:
        finalize_env_class(builder)

    builder.ret_types[-1] = sig.ret_type

    # Add all variables and functions that are declared/defined within this
    # function and are referenced in functions nested within this one to this
    # function's environment class so the nested functions can reference
    # them even if they are declared after the nested function's definition.
    # Note that this is done before visiting the body of this function.

    env_for_func: Union[FuncInfo, ImplicitClass] = builder.fn_info
    if builder.fn_info.is_generator:
        env_for_func = builder.fn_info.generator_class
    elif builder.fn_info.is_nested or builder.fn_info.in_non_ext:
        env_for_func = builder.fn_info.callable_class

    if builder.fn_info.fitem in builder.free_variables:
        # Sort the variables to keep things deterministic
        for var in sorted(builder.free_variables[builder.fn_info.fitem],
                          key=lambda x: x.name):
            if isinstance(var, Var):
                rtype = builder.type_to_rtype(var.type)
                builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False)

    if builder.fn_info.fitem in builder.encapsulating_funcs:
        for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
            if isinstance(nested_fn, FuncDef):
                # The return type is 'object' instead of an RInstance of the
                # callable class because differently defined functions with
                # the same name and signature across conditional blocks
                # will generate different callable classes, so the callable
                # class that gets instantiated must be generic.
                builder.add_var_to_env_class(
                    nested_fn, object_rprimitive, env_for_func, reassign=False
                )

    builder.accept(fitem.body)
    builder.maybe_add_implicit_return()

    if builder.fn_info.is_generator:
        populate_switch_for_generator_class(builder)

    # Hang on to the local symbol table for a while, since we use it
    # to calculate argument defaults below.
    symtable = builder.symtables[-1]

    args, _, blocks, ret_type, fn_info = builder.leave()

    if fn_info.is_generator:
        add_methods_to_generator_class(
            builder, fn_info, sig, args, blocks, fitem.is_coroutine)
    else:
        func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, fn_info, cdef)

    # Evaluate argument defaults in the surrounding scope, since we
    # calculate them *once* when the function definition is evaluated.
    calculate_arg_defaults(builder, fn_info, func_reg, symtable)

    if is_singledispatch:
        # add the generated main singledispatch function
        builder.functions.append(func_ir)
        # create the dispatch function
        assert isinstance(fitem, FuncDef)
        dispatch_name = decorator_helper_name(name) if is_decorated else name
        dispatch_func_ir = gen_dispatch_func_ir(builder, fitem, fn_info.name, dispatch_name, sig)
        return dispatch_func_ir, None

    return (func_ir, func_reg)