Skip to content

Commit a4620e9

Browse files
Keep the method stackref visible to the GC during special method lookup
1 parent 6da6044 commit a4620e9

7 files changed

Lines changed: 31 additions & 24 deletions

File tree

‎Include/internal/pycore_object.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,8 @@ extern int _PyObject_IsInstanceDictEmpty(PyObject *);
957957

958958
// Export for 'math' shared extension
959959
PyAPI_FUNC(PyObject*) _PyObject_LookupSpecial(PyObject *, PyObject *);
960-
PyAPI_FUNC(int) _PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method_and_self);
960+
PyAPI_FUNC(int) _PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method,
961+
_PyStackRef *self);
961962

962963
// Calls the method named `attr` on `self`, but does not set an exception if
963964
// the attribute does not exist.

‎Modules/_testinternalcapi/test_cases.c.h‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Objects/abstract.c‎

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2642,16 +2642,13 @@ call_special_method(PyThreadState *tstate, PyObject *cls, PyObject *name,
26422642
{
26432643
_PyCStackRef cref;
26442644
_PyThreadState_PushCStackRef(tstate, &cref);
2645-
_PyStackRef method_and_self[2] = {
2646-
PyStackRef_NULL, PyStackRef_FromPyObjectBorrow(cls)
2647-
};
2648-
int found = _PyObject_LookupSpecialMethod(name, method_and_self);
2649-
cref.ref = method_and_self[0];
2645+
_PyStackRef self = PyStackRef_FromPyObjectBorrow(cls);
2646+
int found = _PyObject_LookupSpecialMethod(name, &cref.ref, &self);
26502647
if (found > 0) {
26512648
*res = NULL;
26522649
if (!_Py_EnterRecursiveCallTstate(tstate, where)) {
26532650
PyObject *method = PyStackRef_AsPyObjectBorrow(cref.ref);
2654-
PyObject *args[2] = {PyStackRef_AsPyObjectBorrow(method_and_self[1]), arg};
2651+
PyObject *args[2] = {PyStackRef_AsPyObjectBorrow(self), arg};
26552652
if (args[0] != NULL) {
26562653
/* Unbound method: prepend self. */
26572654
*res = PyObject_Vectorcall(method, args, 2, NULL);
@@ -2665,7 +2662,7 @@ call_special_method(PyThreadState *tstate, PyObject *cls, PyObject *name,
26652662
found = -1;
26662663
}
26672664
}
2668-
PyStackRef_XCLOSE(method_and_self[1]);
2665+
PyStackRef_XCLOSE(self);
26692666
_PyThreadState_PopCStackRef(tstate, &cref);
26702667
return found;
26712668
}

‎Objects/typeobject.c‎

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2931,17 +2931,22 @@ _PyObject_LookupSpecial(PyObject *self, PyObject *attr)
29312931
return res;
29322932
}
29332933

2934-
// Lookup the method name `attr` on `self`. On entry, `method_and_self[0]`
2935-
// is null and `method_and_self[1]` is `self`. On exit, `method_and_self[0]`
2936-
// is the method object and `method_and_self[1]` is `self` if the method is
2937-
// not bound.
2934+
// Lookup the method name `attr` on `*self`. On entry, `*method` is null.
2935+
// On exit, `*method` is the method object and `*self` is cleared if the
2936+
// method is bound.
29382937
// Return 1 on success, -1 on error, and 0 if the method is missing.
2938+
//
2939+
// `method` must point to a location that the garbage collector can see,
2940+
// such as the `ref` field of a `_PyCStackRef` or a slot on the interpreter
2941+
// stack. A descriptor may be invoked while `*method` holds the only
2942+
// reference to the method object, and that can trigger a collection.
29392943
int
2940-
_PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method_and_self)
2944+
_PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method,
2945+
_PyStackRef *self)
29412946
{
2942-
PyObject *self = PyStackRef_AsPyObjectBorrow(method_and_self[1]);
2943-
_PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, &method_and_self[0]);
2944-
PyObject *method_o = PyStackRef_AsPyObjectBorrow(method_and_self[0]);
2947+
PyObject *self_o = PyStackRef_AsPyObjectBorrow(*self);
2948+
_PyType_LookupStackRefAndVersion(Py_TYPE(self_o), attr, method);
2949+
PyObject *method_o = PyStackRef_AsPyObjectBorrow(*method);
29452950
if (method_o == NULL) {
29462951
return 0;
29472952
}
@@ -2953,14 +2958,14 @@ _PyObject_LookupSpecialMethod(PyObject *attr, _PyStackRef *method_and_self)
29532958

29542959
descrgetfunc f = Py_TYPE(method_o)->tp_descr_get;
29552960
if (f != NULL) {
2956-
PyObject *func = f(method_o, self, (PyObject *)(Py_TYPE(self)));
2961+
PyObject *func = f(method_o, self_o, (PyObject *)(Py_TYPE(self_o)));
29572962
if (func == NULL) {
29582963
return -1;
29592964
}
2960-
PyStackRef_CLEAR(method_and_self[0]); // clear method
2961-
method_and_self[0] = PyStackRef_FromPyObjectSteal(func);
2965+
PyStackRef_CLEAR(*method); // clear method
2966+
*method = PyStackRef_FromPyObjectSteal(func);
29622967
}
2963-
PyStackRef_CLEAR(method_and_self[1]); // clear self
2968+
PyStackRef_CLEAR(*self); // clear self
29642969
return 1;
29652970
}
29662971

‎Python/bytecodes.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4141,7 +4141,8 @@ dummy_func(
41414141

41424142
op(_LOAD_SPECIAL, (method_and_self[2] -- method_and_self[2])) {
41434143
PyObject *name = _Py_SpecialMethods[oparg].name;
4144-
int err = _PyObject_LookupSpecialMethod(name, method_and_self);
4144+
int err = _PyObject_LookupSpecialMethod(name, &method_and_self[0],
4145+
&method_and_self[1]);
41454146
if (err <= 0) {
41464147
if (err == 0) {
41474148
PyObject *owner = PyStackRef_AsPyObjectBorrow(method_and_self[1]);

‎Python/executor_cases.c.h‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Python/generated_cases.c.h‎

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)