gh-134043: use stackrefs in vectorcalling methods (#134044)

Adds `_PyObject_GetMethodStackRef` which uses stackrefs and takes advantage of deferred reference counting in free-threading while calling method objects in vectorcall.
This commit is contained in:
Kumar Aditya 2025-05-27 22:28:27 +05:30 committed by GitHub
parent 3f9eb55e09
commit a380d57873
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 148 additions and 13 deletions

View file

@ -1664,6 +1664,116 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
return 0;
}
int
_PyObject_GetMethodStackRef(PyThreadState *ts, PyObject *obj,
PyObject *name, _PyStackRef *method)
{
int meth_found = 0;
assert(PyStackRef_IsNull(*method));
PyTypeObject *tp = Py_TYPE(obj);
if (!_PyType_IsReady(tp)) {
if (PyType_Ready(tp) < 0) {
return 0;
}
}
if (tp->tp_getattro != PyObject_GenericGetAttr || !PyUnicode_CheckExact(name)) {
PyObject *res = PyObject_GetAttr(obj, name);
if (res != NULL) {
*method = PyStackRef_FromPyObjectSteal(res);
}
return 0;
}
_PyType_LookupStackRefAndVersion(tp, name, method);
PyObject *descr = PyStackRef_AsPyObjectBorrow(*method);
descrgetfunc f = NULL;
if (descr != NULL) {
if (_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
meth_found = 1;
}
else {
f = Py_TYPE(descr)->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj));
PyStackRef_CLEAR(*method);
if (value != NULL) {
*method = PyStackRef_FromPyObjectSteal(value);
}
return 0;
}
}
}
PyObject *dict, *attr;
if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
_PyObject_TryGetInstanceAttribute(obj, name, &attr)) {
if (attr != NULL) {
PyStackRef_CLEAR(*method);
*method = PyStackRef_FromPyObjectSteal(attr);
return 0;
}
dict = NULL;
}
else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
dict = (PyObject *)_PyObject_GetManagedDict(obj);
}
else {
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr != NULL) {
dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*dictptr);
}
else {
dict = NULL;
}
}
if (dict != NULL) {
// TODO: use _Py_dict_lookup_threadsafe_stackref
Py_INCREF(dict);
PyObject *value;
if (PyDict_GetItemRef(dict, name, &value) != 0) {
// found or error
Py_DECREF(dict);
PyStackRef_CLEAR(*method);
if (value != NULL) {
*method = PyStackRef_FromPyObjectSteal(value);
}
return 0;
}
// not found
Py_DECREF(dict);
}
if (meth_found) {
assert(!PyStackRef_IsNull(*method));
return 1;
}
if (f != NULL) {
PyObject *value = f(descr, obj, (PyObject *)Py_TYPE(obj));
PyStackRef_CLEAR(*method);
if (value) {
*method = PyStackRef_FromPyObjectSteal(value);
}
return 0;
}
if (descr != NULL) {
assert(!PyStackRef_IsNull(*method));
return 0;
}
PyErr_Format(PyExc_AttributeError,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
_PyObject_SetAttributeErrorContext(obj, name);
assert(PyStackRef_IsNull(*method));
return 0;
}
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
PyObject *