|
31 | 31 | #include "pycore_tuple.h" // _PyTuple_DebugMallocStats() |
32 | 32 | #include "pycore_typeobject.h" // _PyBufferWrapper_Type |
33 | 33 | #include "pycore_typevarobject.h" // _PyTypeAlias_Type |
| 34 | +#include "pycore_stackref.h" // PyStackRef_FromPyObjectSteal |
34 | 35 | #include "pycore_unionobject.h" // _PyUnion_Type |
35 | 36 |
|
36 | 37 |
|
@@ -1334,6 +1335,54 @@ PyObject_GetAttr(PyObject *v, PyObject *name) |
1334 | 1335 | return result; |
1335 | 1336 | } |
1336 | 1337 |
|
| 1338 | +/* Like PyObject_GetAttr but returns a _PyStackRef. |
| 1339 | + For types (tp_getattro == _Py_type_getattro), this can return |
| 1340 | + a deferred reference to reduce reference count contention. */ |
| 1341 | +_PyStackRef |
| 1342 | +PyObject_GetAttrStackRef(PyObject *v, PyObject *name) |
| 1343 | +{ |
| 1344 | + PyTypeObject *tp = Py_TYPE(v); |
| 1345 | + if (!PyUnicode_Check(name)) { |
| 1346 | + PyErr_Format(PyExc_TypeError, |
| 1347 | + "attribute name must be string, not '%.200s'", |
| 1348 | + Py_TYPE(name)->tp_name); |
| 1349 | + return PyStackRef_NULL; |
| 1350 | + } |
| 1351 | + |
| 1352 | + /* Fast path for types - can return deferred references */ |
| 1353 | + if (tp->tp_getattro == _Py_type_getattro) { |
| 1354 | + _PyStackRef result = _Py_type_getattro_stackref((PyTypeObject *)v, name, NULL); |
| 1355 | + if (PyStackRef_IsNull(result)) { |
| 1356 | + _PyObject_SetAttributeErrorContext(v, name); |
| 1357 | + } |
| 1358 | + return result; |
| 1359 | + } |
| 1360 | + |
| 1361 | + /* Fall back to regular PyObject_GetAttr and convert to stackref */ |
| 1362 | + PyObject *result = NULL; |
| 1363 | + if (tp->tp_getattro != NULL) { |
| 1364 | + result = (*tp->tp_getattro)(v, name); |
| 1365 | + } |
| 1366 | + else if (tp->tp_getattr != NULL) { |
| 1367 | + const char *name_str = PyUnicode_AsUTF8(name); |
| 1368 | + if (name_str == NULL) { |
| 1369 | + return PyStackRef_NULL; |
| 1370 | + } |
| 1371 | + result = (*tp->tp_getattr)(v, (char *)name_str); |
| 1372 | + } |
| 1373 | + else { |
| 1374 | + PyErr_Format(PyExc_AttributeError, |
| 1375 | + "'%.100s' object has no attribute '%U'", |
| 1376 | + tp->tp_name, name); |
| 1377 | + } |
| 1378 | + |
| 1379 | + if (result == NULL) { |
| 1380 | + _PyObject_SetAttributeErrorContext(v, name); |
| 1381 | + return PyStackRef_NULL; |
| 1382 | + } |
| 1383 | + return PyStackRef_FromPyObjectSteal(result); |
| 1384 | +} |
| 1385 | + |
1337 | 1386 | int |
1338 | 1387 | PyObject_GetOptionalAttr(PyObject *v, PyObject *name, PyObject **result) |
1339 | 1388 | { |
|
0 commit comments