From 6da6044da94f770956cffc9c7f11c34105daa713 Mon Sep 17 00:00:00 2001 From: Kumar Aditya Date: Thu, 17 Sep 2026 14:51:36 +0530 Subject: [PATCH] Call __instancecheck__ and __subclasscheck__ without creating a bound method --- Objects/abstract.c | 79 +++++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 32 deletions(-) diff --git a/Objects/abstract.c b/Objects/abstract.c index 28f751965f36b9..502807c0ac13f0 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -9,6 +9,7 @@ #include "pycore_list.h" // _PyList_AppendTakeRef() #include "pycore_long.h" // _PyLong_IsNegative() #include "pycore_object.h" // _Py_CheckSlotResult() +#include "pycore_stackref.h" // _PyStackRef #include "pycore_pybuffer.h" // _PyBuffer_ReleaseInInterpreterAndRawFree() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() @@ -2635,6 +2636,40 @@ object_isinstance(PyObject *inst, PyObject *cls) return retval; } +static int +call_special_method(PyThreadState *tstate, PyObject *cls, PyObject *name, + const char *where, PyObject *arg, PyObject **res) +{ + _PyCStackRef cref; + _PyThreadState_PushCStackRef(tstate, &cref); + _PyStackRef method_and_self[2] = { + PyStackRef_NULL, PyStackRef_FromPyObjectBorrow(cls) + }; + int found = _PyObject_LookupSpecialMethod(name, method_and_self); + cref.ref = method_and_self[0]; + if (found > 0) { + *res = NULL; + if (!_Py_EnterRecursiveCallTstate(tstate, where)) { + PyObject *method = PyStackRef_AsPyObjectBorrow(cref.ref); + PyObject *args[2] = {PyStackRef_AsPyObjectBorrow(method_and_self[1]), arg}; + if (args[0] != NULL) { + /* Unbound method: prepend self. */ + *res = PyObject_Vectorcall(method, args, 2, NULL); + } + else { + *res = PyObject_Vectorcall(method, args + 1, 1, NULL); + } + _Py_LeaveRecursiveCallTstate(tstate); + } + if (*res == NULL) { + found = -1; + } + } + PyStackRef_XCLOSE(method_and_self[1]); + _PyThreadState_PopCStackRef(tstate, &cref); + return found; +} + static int object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls) { @@ -2672,26 +2707,16 @@ object_recursive_isinstance(PyThreadState *tstate, PyObject *inst, PyObject *cls return r; } - PyObject *checker = _PyObject_LookupSpecial(cls, &_Py_ID(__instancecheck__)); - if (checker != NULL) { - if (_Py_EnterRecursiveCallTstate(tstate, " in __instancecheck__")) { - Py_DECREF(checker); - return -1; - } - - PyObject *res = PyObject_CallOneArg(checker, inst); - _Py_LeaveRecursiveCallTstate(tstate); - Py_DECREF(checker); - - if (res == NULL) { - return -1; - } + PyObject *res; + int found = call_special_method(tstate, cls, &_Py_ID(__instancecheck__), + " in __instancecheck__", inst, &res); + if (found > 0) { int ok = PyObject_IsTrue(res); Py_DECREF(res); return ok; } - else if (_PyErr_Occurred(tstate)) { + else if (found < 0) { return -1; } @@ -2731,8 +2756,6 @@ recursive_issubclass(PyObject *derived, PyObject *cls) static int object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) { - PyObject *checker; - /* We know what type's __subclasscheck__ does. */ if (PyType_CheckExact(cls)) { /* Quick test for an exact match */ @@ -2763,23 +2786,15 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls) return r; } - checker = _PyObject_LookupSpecial(cls, &_Py_ID(__subclasscheck__)); - if (checker != NULL) { - int ok = -1; - if (_Py_EnterRecursiveCallTstate(tstate, " in __subclasscheck__")) { - Py_DECREF(checker); - return ok; - } - PyObject *res = PyObject_CallOneArg(checker, derived); - _Py_LeaveRecursiveCallTstate(tstate); - Py_DECREF(checker); - if (res != NULL) { - ok = PyObject_IsTrue(res); - Py_DECREF(res); - } + PyObject *res; + int found = call_special_method(tstate, cls, &_Py_ID(__subclasscheck__), + " in __subclasscheck__", derived, &res); + if (found > 0) { + int ok = PyObject_IsTrue(res); + Py_DECREF(res); return ok; } - else if (_PyErr_Occurred(tstate)) { + else if (found < 0) { return -1; }