@@ -6375,102 +6375,153 @@ _PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long
63756375
63766376 */
63776377PyObject *
6378- _Py_type_getattro_impl (PyTypeObject * type , PyObject * name , int * suppress_missing_attribute )
6378+ _Py_type_getattro_impl (PyTypeObject * type , PyObject * name , int * suppress_missing_attribute )
6379+ {
6380+ _PyStackRef ref = _Py_type_getattro_stackref (type , name , suppress_missing_attribute );
6381+ if (PyStackRef_IsNull (ref )) {
6382+ return NULL ;
6383+ }
6384+ return PyStackRef_AsPyObjectSteal (ref );
6385+ }
6386+
6387+ /* This is similar to PyObject_GenericGetAttr(),
6388+ but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */
6389+ PyObject *
6390+ _Py_type_getattro (PyObject * tp , PyObject * name )
6391+ {
6392+ PyTypeObject * type = PyTypeObject_CAST (tp );
6393+ return _Py_type_getattro_impl (type , name , NULL );
6394+ }
6395+
6396+ /* Like _Py_type_getattro but returns a _PyStackRef.
6397+ This can return a deferred reference in the free-threaded build
6398+ when the attribute is found without going through a descriptor.
6399+
6400+ suppress_missing_attribute (optional):
6401+ * NULL: do not suppress the exception
6402+ * Non-zero pointer: suppress the PyExc_AttributeError and
6403+ set *suppress_missing_attribute to 1 to signal we are returning NULL while
6404+ having suppressed the exception (other exceptions are not suppressed)
6405+ */
6406+ _PyStackRef
6407+ _Py_type_getattro_stackref (PyTypeObject * type , PyObject * name ,
6408+ int * suppress_missing_attribute )
63796409{
63806410 PyTypeObject * metatype = Py_TYPE (type );
6381- PyObject * meta_attribute , * attribute ;
6382- descrgetfunc meta_get ;
6383- PyObject * res ;
6411+ descrgetfunc meta_get = NULL ;
63846412
63856413 if (!PyUnicode_Check (name )) {
63866414 PyErr_Format (PyExc_TypeError ,
63876415 "attribute name must be string, not '%.200s'" ,
63886416 Py_TYPE (name )-> tp_name );
6389- return NULL ;
6417+ return PyStackRef_NULL ;
63906418 }
63916419
63926420 /* Initialize this type (we'll assume the metatype is initialized) */
63936421 if (!_PyType_IsReady (type )) {
63946422 if (PyType_Ready (type ) < 0 )
6395- return NULL ;
6423+ return PyStackRef_NULL ;
63966424 }
63976425
6398- /* No readable descriptor found yet */
6399- meta_get = NULL ;
6426+ /* Set up GC-visible stack refs */
6427+ _PyCStackRef result_ref , meta_attribute_ref , attribute_ref ;
6428+ PyThreadState * tstate = _PyThreadState_GET ();
6429+ _PyThreadState_PushCStackRef (tstate , & result_ref );
6430+ _PyThreadState_PushCStackRef (tstate , & meta_attribute_ref );
6431+ _PyThreadState_PushCStackRef (tstate , & attribute_ref );
64006432
64016433 /* Look for the attribute in the metatype */
6402- meta_attribute = _PyType_LookupRef (metatype , name );
6434+ _PyType_LookupStackRefAndVersion (metatype , name , & meta_attribute_ref . ref );
64036435
6404- if (meta_attribute != NULL ) {
6405- meta_get = Py_TYPE (meta_attribute )-> tp_descr_get ;
6436+ if (!PyStackRef_IsNull (meta_attribute_ref .ref )) {
6437+ PyObject * meta_attr_obj = PyStackRef_AsPyObjectBorrow (meta_attribute_ref .ref );
6438+ meta_get = Py_TYPE (meta_attr_obj )-> tp_descr_get ;
64066439
6407- if (meta_get != NULL && PyDescr_IsData (meta_attribute )) {
6440+ if (meta_get != NULL && PyDescr_IsData (meta_attr_obj )) {
64086441 /* Data descriptors implement tp_descr_set to intercept
64096442 * writes. Assume the attribute is not overridden in
64106443 * type's tp_dict (and bases): call the descriptor now.
64116444 */
6412- res = meta_get (meta_attribute , (PyObject * )type ,
6413- (PyObject * )metatype );
6414- Py_DECREF (meta_attribute );
6415- return res ;
6445+ PyObject * res = meta_get (meta_attr_obj , (PyObject * )type ,
6446+ (PyObject * )metatype );
6447+ if (res != NULL ) {
6448+ result_ref .ref = PyStackRef_FromPyObjectSteal (res );
6449+ }
6450+ goto done ;
64166451 }
64176452 }
64186453
64196454 /* No data descriptor found on metatype. Look in tp_dict of this
64206455 * type and its bases */
6421- attribute = _PyType_LookupRef (type , name );
6422- if (attribute != NULL ) {
6456+ _PyType_LookupStackRefAndVersion (type , name , & attribute_ref . ref );
6457+ if (! PyStackRef_IsNull ( attribute_ref . ref ) ) {
64236458 /* Implement descriptor functionality, if any */
6424- descrgetfunc local_get = Py_TYPE (attribute )-> tp_descr_get ;
6459+ PyObject * attr_obj = PyStackRef_AsPyObjectBorrow (attribute_ref .ref );
6460+ descrgetfunc local_get = Py_TYPE (attr_obj )-> tp_descr_get ;
64256461
6426- Py_XDECREF (meta_attribute );
6462+ /* Release meta_attribute early since we found in local dict */
6463+ PyStackRef_CLEAR (meta_attribute_ref .ref );
64276464
64286465 if (local_get != NULL ) {
6466+ /* Special case staticmethod to avoid descriptor call overhead.
6467+ * staticmethod.__get__ just returns the wrapped callable. */
6468+ if (Py_TYPE (attr_obj ) == & PyStaticMethod_Type ) {
6469+ PyObject * callable = _PyStaticMethod_GetFunc (attr_obj );
6470+ if (callable ) {
6471+ result_ref .ref = PyStackRef_FromPyObjectNew (callable );
6472+ goto done ;
6473+ }
6474+ }
64296475 /* NULL 2nd argument indicates the descriptor was
64306476 * found on the target object itself (or a base) */
6431- res = local_get (attribute , (PyObject * )NULL ,
6432- (PyObject * )type );
6433- Py_DECREF (attribute );
6434- return res ;
6477+ PyObject * res = local_get (attr_obj , (PyObject * )NULL ,
6478+ (PyObject * )type );
6479+ if (res != NULL ) {
6480+ result_ref .ref = PyStackRef_FromPyObjectSteal (res );
6481+ }
6482+ goto done ;
64356483 }
64366484
6437- return attribute ;
6485+ /* No descriptor, return the attribute directly */
6486+ result_ref .ref = attribute_ref .ref ;
6487+ attribute_ref .ref = PyStackRef_NULL ;
6488+ goto done ;
64386489 }
64396490
64406491 /* No attribute found in local __dict__ (or bases): use the
64416492 * descriptor from the metatype, if any */
64426493 if (meta_get != NULL ) {
6443- PyObject * res ;
6444- res = meta_get (meta_attribute , (PyObject * )type ,
6445- (PyObject * )metatype );
6446- Py_DECREF (meta_attribute );
6447- return res ;
6494+ PyObject * meta_attr_obj = PyStackRef_AsPyObjectBorrow (meta_attribute_ref .ref );
6495+ PyObject * res = meta_get (meta_attr_obj , (PyObject * )type ,
6496+ (PyObject * )metatype );
6497+ if (res != NULL ) {
6498+ result_ref .ref = PyStackRef_FromPyObjectSteal (res );
6499+ }
6500+ goto done ;
64486501 }
64496502
64506503 /* If an ordinary attribute was found on the metatype, return it now */
6451- if (meta_attribute != NULL ) {
6452- return meta_attribute ;
6504+ if (!PyStackRef_IsNull (meta_attribute_ref .ref )) {
6505+ result_ref .ref = meta_attribute_ref .ref ;
6506+ meta_attribute_ref .ref = PyStackRef_NULL ;
6507+ goto done ;
64536508 }
64546509
64556510 /* Give up */
64566511 if (suppress_missing_attribute == NULL ) {
64576512 PyErr_Format (PyExc_AttributeError ,
6458- "type object '%.100s' has no attribute '%U'" ,
6459- type -> tp_name , name );
6460- } else {
6513+ "type object '%.100s' has no attribute '%U'" ,
6514+ type -> tp_name , name );
6515+ }
6516+ else {
64616517 // signal the caller we have not set an PyExc_AttributeError and gave up
64626518 * suppress_missing_attribute = 1 ;
64636519 }
6464- return NULL ;
6465- }
64666520
6467- /* This is similar to PyObject_GenericGetAttr(),
6468- but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */
6469- PyObject *
6470- _Py_type_getattro (PyObject * tp , PyObject * name )
6471- {
6472- PyTypeObject * type = PyTypeObject_CAST (tp );
6473- return _Py_type_getattro_impl (type , name , NULL );
6521+ done :
6522+ _PyThreadState_PopCStackRef (tstate , & attribute_ref );
6523+ _PyThreadState_PopCStackRef (tstate , & meta_attribute_ref );
6524+ return _PyThreadState_PopCStackRefSteal (tstate , & result_ref );
64746525}
64756526
64766527// Called by type_setattro(). Updates both the type dict and
@@ -10937,15 +10988,19 @@ static PyObject *
1093710988slot_tp_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
1093810989{
1093910990 PyThreadState * tstate = _PyThreadState_GET ();
10940- PyObject * func , * result ;
10991+ PyObject * result ;
1094110992
10942- func = PyObject_GetAttr ((PyObject * )type , & _Py_ID (__new__ ));
10943- if (func == NULL ) {
10993+ _PyCStackRef func_ref ;
10994+ _PyThreadState_PushCStackRef (tstate , & func_ref );
10995+ func_ref .ref = _PyObject_GetAttrStackRef ((PyObject * )type , & _Py_ID (__new__ ));
10996+ if (PyStackRef_IsNull (func_ref .ref )) {
10997+ _PyThreadState_PopCStackRef (tstate , & func_ref );
1094410998 return NULL ;
1094510999 }
1094611000
11001+ PyObject * func = PyStackRef_AsPyObjectBorrow (func_ref .ref );
1094711002 result = _PyObject_Call_Prepend (tstate , func , (PyObject * )type , args , kwds );
10948- Py_DECREF ( func );
11003+ _PyThreadState_PopCStackRef ( tstate , & func_ref );
1094911004 return result ;
1095011005}
1095111006
0 commit comments