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)) self_target = builder.add_self_to_env(cls) arg = builder.read(self_target, 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)) args, _, blocks, return_type, _ = builder.leave() return FuncIR( FuncDecl(target.name + '__' + base.name + '_glue', cls.name, builder.module_name, FuncSignature([rt_arg], return_type)), args, blocks)
def setup_env_for_generator_class(builder: IRBuilder) -> None: """Populates the environment for a generator class.""" fitem = builder.fn_info.fitem cls = builder.fn_info.generator_class self_target = builder.add_self_to_env(cls.ir) # Add the type, value, and traceback variables to the environment. exc_type = builder.add_local(Var('type'), object_rprimitive, is_arg=True) exc_val = builder.add_local(Var('value'), object_rprimitive, is_arg=True) exc_tb = builder.add_local(Var('traceback'), object_rprimitive, is_arg=True) # TODO: Use the right type here instead of object? exc_arg = builder.add_local(Var('arg'), object_rprimitive, is_arg=True) cls.exc_regs = (exc_type, exc_val, exc_tb) cls.send_arg_reg = exc_arg cls.self_reg = builder.read(self_target, fitem.line) cls.curr_env_reg = load_outer_env(builder, cls.self_reg, builder.symtables[-1]) # Define a variable representing the label to go to the next time # the '__next__' function of the generator is called, and add it # as an attribute to the environment class. cls.next_label_target = builder.add_var_to_env_class( Var(NEXT_LABEL_ATTR_NAME), int_rprimitive, cls, reassign=False) # Add arguments from the original generator function to the # environment of the generator class. add_args_to_env(builder, local=False, base=cls, reassign=False) # Set the next label register for the generator class. cls.next_label_reg = builder.read(cls.next_label_target, fitem.line)
def setup_callable_class(builder: IRBuilder) -> None: """Generate an (incomplete) callable class representing function. This can be a nested function or a function within a non-extension class. Also set 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 which points to another ClassIR representing the environment class where some of its variables can be accessed. Note that some methods, such as '__call__', are not yet created here. Use additional functions, such as add_call_to_callable_class(), to add them. Return 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 = f'{builder.fn_info.namespaced_name()}_obj' 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 environment of the callable class, # and store that variable in a register to be accessed later. self_target = builder.add_self_to_env(callable_class_ir) builder.fn_info.callable_class.self_reg = builder.read( self_target, builder.fn_info.fitem.line)