@@ -612,27 +612,36 @@ _PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...)
612612}
613613
614614
615- /* Resolve 'name' on 'obj' with _PyObject_GetMethod and call it directly,
616- avoiding the bound-method object that PyObject_GetAttr()+call would allocate. */
615+ /* Resolve 'name' on 'obj' with _PyObject_GetMethodStackRef and call it
616+ directly, avoiding the bound-method object that PyObject_GetAttr()+call
617+ would allocate. Using the StackRef variant keeps method resolution
618+ reference-count-free on the fast path so it scales in free-threading. */
617619static PyObject *
618620callmethod_va (PyObject * obj , PyObject * name ,
619621 const char * format , va_list va )
620622{
621623 PyThreadState * tstate = _PyThreadState_GET ();
624+ PyObject * result = NULL ;
622625
623- PyObject * method = NULL ;
624- /* unbound: 1 -> 'method' is an unbound function, call method(obj, *args);
625- 0 -> 'method' is the resolved attribute, call method(*args). */
626- int unbound = _PyObject_GetMethod (obj , name , & method );
627- if (method == NULL ) {
628- return NULL ;
626+ _PyCStackRef self , method ;
627+ _PyThreadState_PushCStackRef (tstate , & self );
628+ _PyThreadState_PushCStackRef (tstate , & method );
629+ self .ref = PyStackRef_FromPyObjectBorrow (obj );
630+ /* On return, self.ref is non-NULL -> call method(self, *args) (unbound
631+ method or classmethod), NULL -> call method(*args). */
632+ int res = _PyObject_GetMethodStackRef (tstate , & self .ref , name , & method .ref );
633+ if (res < 0 ) {
634+ goto pop_return ;
629635 }
630- if (!PyCallable_Check (method )) {
636+
637+ PyObject * callable = PyStackRef_AsPyObjectBorrow (method .ref );
638+ PyObject * self_obj = PyStackRef_AsPyObjectBorrow (self .ref );
639+
640+ if (!PyCallable_Check (callable )) {
631641 _PyErr_Format (tstate , PyExc_TypeError ,
632642 "attribute of type '%.200s' is not callable" ,
633- Py_TYPE (method )-> tp_name );
634- Py_DECREF (method );
635- return NULL ;
643+ Py_TYPE (callable )-> tp_name );
644+ goto pop_return ;
636645 }
637646
638647 /* Build the positional arguments from the format string. */
@@ -643,8 +652,7 @@ callmethod_va(PyObject *obj, PyObject *name,
643652 built = _Py_VaBuildStack (small_stack , _PY_FASTCALL_SMALL_STACK ,
644653 format , va , & nargs );
645654 if (built == NULL ) {
646- Py_DECREF (method );
647- return NULL ;
655+ goto pop_return ;
648656 }
649657 }
650658
@@ -656,12 +664,11 @@ callmethod_va(PyObject *obj, PyObject *name,
656664 n = PyTuple_GET_SIZE (built [0 ]);
657665 }
658666
659- PyObject * result ;
660- if (unbound ) {
661- result = _PyObject_VectorcallPrepend (tstate , method , obj , args , n , NULL );
667+ if (self_obj != NULL ) {
668+ result = _PyObject_VectorcallPrepend (tstate , callable , self_obj , args , n , NULL );
662669 }
663670 else {
664- result = _PyObject_VectorcallTstate (tstate , method , args , n , NULL );
671+ result = _PyObject_VectorcallTstate (tstate , callable , args , n , NULL );
665672 }
666673
667674 for (Py_ssize_t i = 0 ; i < nargs ; i ++ ) {
@@ -670,7 +677,10 @@ callmethod_va(PyObject *obj, PyObject *name,
670677 if (built != NULL && built != small_stack ) {
671678 PyMem_Free (built );
672679 }
673- Py_DECREF (method );
680+
681+ pop_return :
682+ _PyThreadState_PopCStackRef (tstate , & method );
683+ _PyThreadState_PopCStackRef (tstate , & self );
674684 return result ;
675685}
676686
0 commit comments