(
cl: ClassIR,
dealloc_func_name: str,
clear_func_name: str,
has_tp_finalize: bool,
emitter: Emitter,
)
| 920 | |
| 921 | |
| 922 | def generate_dealloc_for_class( |
| 923 | cl: ClassIR, |
| 924 | dealloc_func_name: str, |
| 925 | clear_func_name: str, |
| 926 | has_tp_finalize: bool, |
| 927 | emitter: Emitter, |
| 928 | ) -> None: |
| 929 | emitter.emit_line("static void") |
| 930 | emitter.emit_line(f"{dealloc_func_name}({cl.struct_name(emitter.names)} *self)") |
| 931 | emitter.emit_line("{") |
| 932 | if has_tp_finalize: |
| 933 | emitter.emit_line("PyObject *type, *value, *traceback;") |
| 934 | emitter.emit_line("PyErr_Fetch(&type, &value, &traceback);") |
| 935 | emitter.emit_line("int res = PyObject_CallFinalizerFromDealloc((PyObject *)self);") |
| 936 | # CPython interpreter uses PyErr_WriteUnraisable: https://docs.python.org/3/c-api/exceptions.html#c.PyErr_WriteUnraisable |
| 937 | # However, the message is slightly different due to the way mypyc compiles classes. |
| 938 | # CPython interpreter prints: Exception ignored in: <function F.__del__ at 0x100aed940> |
| 939 | # mypyc prints: Exception ignored in: <slot wrapper '__del__' of 'F' objects> |
| 940 | emitter.emit_line("if (PyErr_Occurred() != NULL) {") |
| 941 | # Don't untrack instance if error occurred |
| 942 | emitter.emit_line("PyErr_WriteUnraisable((PyObject *)self);") |
| 943 | emitter.emit_line("res = -1;") |
| 944 | emitter.emit_line("}") |
| 945 | emitter.emit_line("PyErr_Restore(type, value, traceback);") |
| 946 | emitter.emit_line("if (res < 0) {") |
| 947 | emitter.emit_line("goto done;") |
| 948 | emitter.emit_line("}") |
| 949 | if not cl.is_acyclic: |
| 950 | emitter.emit_line("PyObject_GC_UnTrack(self);") |
| 951 | if cl.builtin_base: |
| 952 | emitter.emit_line(f"{clear_func_name}(self);") |
| 953 | # For native subclasses of builtins such as dict, the base deallocator |
| 954 | # is responsible for tearing down base-owned storage and freeing memory. |
| 955 | # Re-track self if base is GC-aware to match cpython's subtype_dealloc. |
| 956 | base = f"{emitter.type_struct_name(cl)}->tp_base" |
| 957 | base_arg = "(PyObject *)self" |
| 958 | emitter.emit_line(f"if (PyType_IS_GC({base})) PyObject_GC_Track({base_arg});") |
| 959 | emitter.emit_base_tp_function_call(cl, "tp_dealloc", base_arg) |
| 960 | emitter.emit_line("done: ;") |
| 961 | emitter.emit_line("}") |
| 962 | return |
| 963 | if cl.reuse_freed_instance: |
| 964 | emit_reuse_dealloc(cl, emitter) |
| 965 | # The trashcan is needed to handle deep recursive deallocations |
| 966 | emitter.emit_line(f"CPy_TRASHCAN_BEGIN(self, {dealloc_func_name})") |
| 967 | emitter.emit_line(f"{clear_func_name}(self);") |
| 968 | emitter.emit_line("Py_TYPE(self)->tp_free((PyObject *)self);") |
| 969 | emitter.emit_line("CPy_TRASHCAN_END(self)") |
| 970 | emitter.emit_line("done: ;") |
| 971 | emitter.emit_line("}") |
| 972 | |
| 973 | |
| 974 | def emit_reuse_dealloc(cl: ClassIR, emitter: Emitter) -> None: |
no test coverage detected
searching dependent graphs…