def setup_callable_class(builder: IRBuilder) -> None: """Generates a callable class representing a nested function or a function within a non-extension class and sets up the 'self' variable for that class. This takes the most recently visited function and returns a ClassIR to represent that function. Each callable class contains an environment attribute with points to another ClassIR representing the environment class where some of its variables can be accessed. Note that its '__call__' method is not yet implemented, and is implemented in the add_call_to_callable_class function. Returns a newly constructed ClassIR representing the callable class for the nested function. """ # Check to see that the name has not already been taken. If so, rename the class. We allow # multiple uses of the same function name because this is valid in if-else blocks. Example: # if True: # def foo(): ----> foo_obj() # return True # else: # def foo(): ----> foo_obj_0() # return False name = base_name = '{}_obj'.format(builder.fn_info.namespaced_name()) count = 0 while name in builder.callable_class_names: name = base_name + '_' + str(count) count += 1 builder.callable_class_names.add(name) # Define the actual callable class ClassIR, and set its environment to point at the # previously defined environment class. callable_class_ir = ClassIR(name, builder.module_name, is_generated=True) # The functools @wraps decorator attempts to call setattr on nested functions, so # we create a dict for these nested functions. # https://github.com/python/cpython/blob/3.7/Lib/functools.py#L58 if builder.fn_info.is_nested: callable_class_ir.has_dict = True # If the enclosing class doesn't contain nested (which will happen if # this is a toplevel lambda), don't set up an environment. if builder.fn_infos[-2].contains_nested: callable_class_ir.attributes[ENV_ATTR_NAME] = RInstance( builder.fn_infos[-2].env_class ) callable_class_ir.mro = [callable_class_ir] builder.fn_info.callable_class = ImplicitClass(callable_class_ir) builder.classes.append(callable_class_ir) # Add a 'self' variable to the callable class' environment, and store that variable in a # register to be accessed later. self_target = add_self_to_env(builder.environment, callable_class_ir) builder.fn_info.callable_class.self_reg = builder.read(self_target, builder.fn_info.fitem.line)
def load_outer_envs(builder: IRBuilder, base: ImplicitClass) -> None: index = len(builder.builders) - 2 # Load the first outer environment. This one is special because it gets saved in the # FuncInfo instance's prev_env_reg field. if index > 1: # outer_env = builder.fn_infos[index].environment outer_env = builder.builders[index].environment if isinstance(base, GeneratorClass): base.prev_env_reg = load_outer_env(builder, base.curr_env_reg, outer_env) else: base.prev_env_reg = load_outer_env(builder, base.self_reg, outer_env) env_reg = base.prev_env_reg index -= 1 # Load the remaining outer environments into registers. while index > 1: # outer_env = builder.fn_infos[index].environment outer_env = builder.builders[index].environment env_reg = load_outer_env(builder, env_reg, outer_env) index -= 1