Generate an (incomplete) callable class representing a 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
(builder: IRBuilder)
| 26 | |
| 27 | |
| 28 | def setup_callable_class(builder: IRBuilder) -> None: |
| 29 | """Generate an (incomplete) callable class representing a function. |
| 30 | |
| 31 | This can be a nested function or a function within a non-extension |
| 32 | class. Also set up the 'self' variable for that class. |
| 33 | |
| 34 | This takes the most recently visited function and returns a |
| 35 | ClassIR to represent that function. Each callable class contains |
| 36 | an environment attribute which points to another ClassIR |
| 37 | representing the environment class where some of its variables can |
| 38 | be accessed. |
| 39 | |
| 40 | Note that some methods, such as '__call__', are not yet |
| 41 | created here. Use additional functions, such as |
| 42 | add_call_to_callable_class(), to add them. |
| 43 | |
| 44 | Return a newly constructed ClassIR representing the callable |
| 45 | class for the nested function. |
| 46 | """ |
| 47 | # Check to see that the name has not already been taken. If so, |
| 48 | # rename the class. We allow multiple uses of the same function |
| 49 | # name because this is valid in if-else blocks. Example: |
| 50 | # |
| 51 | # if True: |
| 52 | # def foo(): ----> foo_obj() |
| 53 | # return True |
| 54 | # else: |
| 55 | # def foo(): ----> foo_obj_0() |
| 56 | # return False |
| 57 | name = base_name = f"{builder.fn_info.namespaced_name()}_obj" |
| 58 | count = 0 |
| 59 | while name in builder.callable_class_names: |
| 60 | name = base_name + "_" + str(count) |
| 61 | count += 1 |
| 62 | builder.callable_class_names.add(name) |
| 63 | |
| 64 | # Define the actual callable class ClassIR, and set its |
| 65 | # environment to point at the previously defined environment |
| 66 | # class. |
| 67 | callable_class_ir = ClassIR(name, builder.module_name, is_generated=True, is_final_class=True) |
| 68 | callable_class_ir.reuse_freed_instance = True |
| 69 | |
| 70 | # The functools @wraps decorator attempts to call setattr on |
| 71 | # nested functions, so we create a dict for these nested |
| 72 | # functions. |
| 73 | # https://github.com/python/cpython/blob/3.7/Lib/functools.py#L58 |
| 74 | if builder.fn_info.is_nested: |
| 75 | callable_class_ir.has_dict = True |
| 76 | |
| 77 | # If the enclosing class doesn't contain nested (which will happen if |
| 78 | # this is a toplevel lambda), don't set up an environment. |
| 79 | if builder.fn_infos[-2].contains_nested: |
| 80 | callable_class_ir.attributes[ENV_ATTR_NAME] = RInstance(builder.fn_infos[-2].env_class) |
| 81 | callable_class_ir.mro = [callable_class_ir] |
| 82 | builder.fn_info.callable_class = ImplicitClass(callable_class_ir) |
| 83 | builder.classes.append(callable_class_ir) |
| 84 | |
| 85 | # Add a 'self' variable to the environment of the callable class, |
no test coverage detected
searching dependent graphs…