(self, op: GetAttr)
| 395 | return f"({cast}{obj})->{self.emitter.attr(op.attr)}" |
| 396 | |
| 397 | def visit_get_attr(self, op: GetAttr) -> None: |
| 398 | if op.allow_error_value: |
| 399 | self.get_attr_with_allow_error_value(op) |
| 400 | return |
| 401 | dest = self.reg(op) |
| 402 | obj = self.reg(op.obj) |
| 403 | rtype = op.class_type |
| 404 | cl = rtype.class_ir |
| 405 | attr_rtype, decl_cl = cl.attr_details(op.attr) |
| 406 | prefer_method = cl.is_trait and attr_rtype.error_overlap |
| 407 | if cl.get_method(op.attr, prefer_method=prefer_method): |
| 408 | # Properties are essentially methods, so use vtable access for them |
| 409 | # (except for non-ext classes, which have no vtable — see emit_method_call). |
| 410 | if not cl.is_ext_class or cl.is_method_final(op.attr): |
| 411 | self.emit_method_call(f"{dest} = ", op.obj, op.attr, []) |
| 412 | else: |
| 413 | version = "_TRAIT" if cl.is_trait else "" |
| 414 | self.emit_line( |
| 415 | "%s = CPY_GET_ATTR%s(%s, %s, %d, %s, %s); /* %s */" |
| 416 | % ( |
| 417 | dest, |
| 418 | version, |
| 419 | obj, |
| 420 | self.emitter.type_struct_name(rtype.class_ir), |
| 421 | rtype.getter_index(op.attr), |
| 422 | rtype.struct_name(self.names), |
| 423 | self.ctype(rtype.attr_type(op.attr)), |
| 424 | op.attr, |
| 425 | ) |
| 426 | ) |
| 427 | else: |
| 428 | # Otherwise, use direct or offset struct access. |
| 429 | attr_expr = self.get_attr_expr(obj, op, decl_cl) |
| 430 | self.emitter.emit_line(f"{dest} = {attr_expr};") |
| 431 | always_defined = cl.is_always_defined(op.attr) |
| 432 | merged_branch = None |
| 433 | if not always_defined: |
| 434 | self.emitter.emit_undefined_attr_check( |
| 435 | attr_rtype, dest, "==", obj, op.attr, cl, unlikely=True |
| 436 | ) |
| 437 | branch = self.next_branch() |
| 438 | if branch is not None: |
| 439 | if ( |
| 440 | branch.value is op |
| 441 | and branch.op == Branch.IS_ERROR |
| 442 | and branch.traceback_entry is not None |
| 443 | and not branch.negated |
| 444 | ): |
| 445 | # Generate code for the following branch here to avoid |
| 446 | # redundant branches in the generated code. |
| 447 | self.emit_attribute_error(branch, cl.name, op.attr) |
| 448 | self.emit_line("goto %s;" % self.label(branch.true)) |
| 449 | merged_branch = branch |
| 450 | self.emitter.emit_line("}") |
| 451 | if not merged_branch: |
| 452 | exc_class = "PyExc_AttributeError" |
| 453 | self.emitter.emit_line( |
| 454 | 'PyErr_SetString({}, "attribute {} of {} undefined");'.format( |
nothing calls this directly
no test coverage detected