(self, dest: str, op_obj: Value, name: str, op_args: list[Value])
| 597 | self.emit_method_call(dest, op.obj, op.method, op.args) |
| 598 | |
| 599 | def emit_method_call(self, dest: str, op_obj: Value, name: str, op_args: list[Value]) -> None: |
| 600 | obj = self.reg(op_obj) |
| 601 | rtype = op_obj.type |
| 602 | assert isinstance(rtype, RInstance), rtype |
| 603 | class_ir = rtype.class_ir |
| 604 | # Use method_decl (not get_method) because under separate compilation the |
| 605 | # FuncIR body may live in a different group — only its declaration is |
| 606 | # visible here, and a decl is all we need to emit a direct C call |
| 607 | # (the symbol resolves through that group's exports table). |
| 608 | method_decl = rtype.class_ir.method_decl(name) |
| 609 | |
| 610 | # Can we call the method directly, bypassing vtable? Non-extension classes |
| 611 | # don't have a vtable (compute_vtable is skipped for them), so the only |
| 612 | # way to dispatch is a direct C call. |
| 613 | is_direct = not class_ir.is_ext_class or class_ir.is_method_final(name) |
| 614 | |
| 615 | # The first argument gets omitted for static methods and |
| 616 | # turned into the class for class methods |
| 617 | obj_args = ( |
| 618 | [] |
| 619 | if method_decl.kind == FUNC_STATICMETHOD |
| 620 | else [f"(PyObject *)Py_TYPE({obj})"] if method_decl.kind == FUNC_CLASSMETHOD else [obj] |
| 621 | ) |
| 622 | args = ", ".join(obj_args + [self.reg(arg) for arg in op_args]) |
| 623 | mtype = native_function_type_from_decl(method_decl, self.emitter) |
| 624 | version = "_TRAIT" if rtype.class_ir.is_trait else "" |
| 625 | if is_direct: |
| 626 | # Directly call method, without going through the vtable. |
| 627 | self.emit_line(f"{dest}{self.emitter.native_function_call(method_decl)}({args});") |
| 628 | else: |
| 629 | # Call using vtable. |
| 630 | method_idx = rtype.method_index(name) |
| 631 | self.emit_line( |
| 632 | "{}CPY_GET_METHOD{}({}, {}, {}, {}, {})({}); /* {} */".format( |
| 633 | dest, |
| 634 | version, |
| 635 | obj, |
| 636 | self.emitter.type_struct_name(rtype.class_ir), |
| 637 | method_idx, |
| 638 | rtype.struct_name(self.names), |
| 639 | mtype, |
| 640 | args, |
| 641 | name, |
| 642 | ) |
| 643 | ) |
| 644 | |
| 645 | def visit_inc_ref(self, op: IncRef) -> None: |
| 646 | if ( |
no test coverage detected