"""Helpers that store information about functions and the related classes.""" from typing import List, Optional, Tuple from mypy.nodes import FuncItem from mypyc.ir.ops import Value, BasicBlock from mypyc.ir.func_ir import INVALID_FUNC_DEF from mypyc.ir.class_ir import ClassIR from mypyc.irbuild.targets import AssignmentTarget class FuncInfo: """Contains information about functions as they are generated.""" 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 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: Optional[ImplicitClass] = None # 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: Optional[ClassIR] = None # Generator classes implement the '__next__' method, and are used to represent generators # returned by generator functions. self._generator_class: Optional[GeneratorClass] = None # 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: Optional[Value] = None # 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 # TODO: add field for ret_type: RType = none_rprimitive def namespaced_name(self) -> str: return '_'.join(x for x in [self.name, self.class_name, self.ns] if x) @property def is_generator(self) -> bool: return self.fitem.is_generator or self.fitem.is_coroutine @property def is_coroutine(self) -> bool: return self.fitem.is_coroutine @property def callable_class(self) -> 'ImplicitClass': assert self._callable_class is not None return self._callable_class @callable_class.setter def callable_class(self, cls: 'ImplicitClass') -> None: self._callable_class = cls @property def env_class(self) -> ClassIR: assert self._env_class is not None return self._env_class @env_class.setter def env_class(self, ir: ClassIR) -> None: self._env_class = ir @property def generator_class(self) -> 'GeneratorClass': assert self._generator_class is not None return self._generator_class @generator_class.setter def generator_class(self, cls: 'GeneratorClass') -> None: self._generator_class = cls @property def curr_env_reg(self) -> Value: assert self._curr_env_reg is not None return self._curr_env_reg class ImplicitClass: """Contains information regarding implicitly generated classes. Implicit classes are generated for nested functions and generator functions. They are not explicitly defined in the source code. NOTE: This is both a concrete class and used as a base class. """ def __init__(self, ir: ClassIR) -> None: # The ClassIR instance associated with this class. self.ir = ir # The register associated with the 'self' instance for this generator class. self._self_reg: Optional[Value] = None # 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. prev_env_reg is the self.__mypyc_env__ field # associated with the previous environment. self._curr_env_reg: Optional[Value] = None self._prev_env_reg: Optional[Value] = None @property def self_reg(self) -> Value: assert self._self_reg is not None return self._self_reg @self_reg.setter def self_reg(self, reg: Value) -> None: self._self_reg = reg @property def curr_env_reg(self) -> Value: assert self._curr_env_reg is not None return self._curr_env_reg @curr_env_reg.setter def curr_env_reg(self, reg: Value) -> None: self._curr_env_reg = reg @property def prev_env_reg(self) -> Value: assert self._prev_env_reg is not None return self._prev_env_reg @prev_env_reg.setter def prev_env_reg(self, reg: Value) -> None: self._prev_env_reg = reg class GeneratorClass(ImplicitClass): """Contains information about implicit generator function classes.""" def __init__(self, ir: ClassIR) -> None: super().__init__(ir) # This register holds the label number that the '__next__' function should go to the next # time it is called. self._next_label_reg: Optional[Value] = None self._next_label_target: Optional[AssignmentTarget] = None # These registers hold the error values for the generator object for the case that the # 'throw' function is called. self.exc_regs: Optional[Tuple[Value, Value, Value]] = None # Holds the arg passed to send self.send_arg_reg: Optional[Value] = None # The switch block is used to decide which instruction to go using the value held in the # next-label register. self.switch_block = BasicBlock() self.continuation_blocks: List[BasicBlock] = [] @property def next_label_reg(self) -> Value: assert self._next_label_reg is not None return self._next_label_reg @next_label_reg.setter def next_label_reg(self, reg: Value) -> None: self._next_label_reg = reg @property def next_label_target(self) -> AssignmentTarget: assert self._next_label_target is not None return self._next_label_target @next_label_target.setter def next_label_target(self, target: AssignmentTarget) -> None: self._next_label_target = target