@@ -547,6 +547,7 @@ _PyCode_Quicken(_Py_CODEUNIT *instructions, Py_ssize_t size, PyObject *consts,
547547#define SPEC_FAIL_ATTR_METACLASS_OVERRIDDEN 34
548548#define SPEC_FAIL_ATTR_SPLIT_DICT 35
549549#define SPEC_FAIL_ATTR_DESCR_NOT_DEFERRED 36
550+ #define SPEC_FAIL_ATTR_TYPE_CHANGED 37
550551
551552/* Binary subscr and store subscr */
552553
@@ -834,7 +835,8 @@ typedef enum {
834835 ABSENT , /* Attribute is not present on the class */
835836 DUNDER_CLASS , /* __class__ attribute */
836837 GETSET_OVERRIDDEN , /* __getattribute__ or __setattr__ has been overridden */
837- GETATTRIBUTE_IS_PYTHON_FUNCTION /* Descriptor requires calling a Python __getattribute__ */
838+ GETATTRIBUTE_IS_PYTHON_FUNCTION , /* Descriptor requires calling a Python __getattribute__ */
839+ TYPE_CHANGED , /* Error: the type changed during classification */
838840} DescriptorClassification ;
839841
840842
@@ -881,9 +883,11 @@ classify_descriptor(PyObject *descriptor, bool has_getattr)
881883}
882884
883885static DescriptorClassification
884- analyze_descriptor (PyTypeObject * type , PyObject * name , PyObject * * descr , int store )
886+ analyze_descriptor (PyTypeObject * type , PyObject * name , PyObject * * descr , unsigned int * tp_version , int store )
885887{
886888 bool has_getattr = false;
889+ bool have_getattribute_ver = false;
890+ unsigned int getattribute_ver ;
887891 if (store ) {
888892 if (type -> tp_setattro != PyObject_GenericSetAttr ) {
889893 * descr = NULL ;
@@ -900,22 +904,34 @@ analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int sto
900904 getattro_slot == _Py_slot_tp_getattro ) {
901905 /* One or both of __getattribute__ or __getattr__ may have been
902906 overridden See typeobject.c for why these functions are special. */
903- PyObject * getattribute = _PyType_Lookup (type ,
904- & _Py_ID (__getattribute__ ));
907+ PyObject * getattribute = _PyType_LookupRefAndVersion (type ,
908+ & _Py_ID (__getattribute__ ), & getattribute_ver );
909+ have_getattribute_ver = true;
905910 PyInterpreterState * interp = _PyInterpreterState_GET ();
906911 bool has_custom_getattribute = getattribute != NULL &&
907912 getattribute != interp -> callable_cache .object__getattribute__ ;
908- has_getattr = _PyType_Lookup (type , & _Py_ID (__getattr__ )) != NULL ;
913+ unsigned int getattr_ver ;
914+ PyObject * getattr = _PyType_LookupRefAndVersion (type , & _Py_ID (__getattr__ ), & getattr_ver );
915+ has_getattr = getattr != NULL ;
916+ if (getattr_ver != getattribute_ver ) {
917+ Py_XDECREF (getattribute );
918+ Py_XDECREF (getattr );
919+ return TYPE_CHANGED ;
920+ }
909921 if (has_custom_getattribute ) {
910922 if (getattro_slot == _Py_slot_tp_getattro &&
911923 !has_getattr &&
912924 Py_IS_TYPE (getattribute , & PyFunction_Type )) {
913925 * descr = getattribute ;
926+ assert (getattr == NULL );
927+ * tp_version = getattribute_ver ;
914928 return GETATTRIBUTE_IS_PYTHON_FUNCTION ;
915929 }
916930 /* Potentially both __getattr__ and __getattribute__ are set.
917931 Too complicated */
918932 * descr = NULL ;
933+ Py_XDECREF (getattribute );
934+ Py_XDECREF (getattr );
919935 return GETSET_OVERRIDDEN ;
920936 }
921937 /* Potentially has __getattr__ but no custom __getattribute__.
@@ -925,14 +941,22 @@ analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int sto
925941 raised. This means some specializations, e.g. specializing
926942 for property() isn't safe.
927943 */
944+ Py_XDECREF (getattr );
945+ Py_XDECREF (getattribute );
928946 }
929947 else {
930948 * descr = NULL ;
931949 return GETSET_OVERRIDDEN ;
932950 }
933951 }
934- PyObject * descriptor = _PyType_Lookup (type , name );
952+ unsigned int descr_version ;
953+ PyObject * descriptor = _PyType_LookupRefAndVersion (type , name , & descr_version );
954+ if (have_getattribute_ver && (descr_version != getattribute_ver )) {
955+ Py_XDECREF (descriptor );
956+ return TYPE_CHANGED ;
957+ }
935958 * descr = descriptor ;
959+ * tp_version = descr_version ;
936960 if (PyUnicode_CompareWithASCIIString (name , "__class__" ) == 0 ) {
937961 if (descriptor == _PyType_Lookup (& PyBaseObject_Type , name )) {
938962 return DUNDER_CLASS ;
@@ -1062,20 +1086,20 @@ instance_has_key(PyObject *obj, PyObject *name, uint32_t *shared_keys_version)
10621086#endif
10631087
10641088static int
1065- specialize_instance_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name )
1089+ do_specialize_instance_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name ,
1090+ bool shadow , uint32_t shared_keys_version ,
1091+ DescriptorClassification kind , PyObject * descr , unsigned int tp_version )
10661092{
10671093 _PyAttrCache * cache = (_PyAttrCache * )(instr + 1 );
10681094 PyTypeObject * type = Py_TYPE (owner );
1069- // 0 is not a valid version
1070- uint32_t shared_keys_version = 0 ;
1071- bool shadow = instance_has_key (owner , name , & shared_keys_version );
1072- PyObject * descr = NULL ;
1073- DescriptorClassification kind = analyze_descriptor (type , name , & descr , 0 );
1074- assert (descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN );
1075- if (type_get_version (type , LOAD_ATTR ) == 0 ) {
1095+ if (tp_version == 0 ) {
1096+ SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_OUT_OF_VERSIONS );
10761097 return -1 ;
10771098 }
10781099 switch (kind ) {
1100+ case TYPE_CHANGED :
1101+ SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_ATTR_TYPE_CHANGED );
1102+ return -1 ;
10791103 case OVERRIDING :
10801104 SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR );
10811105 return -1 ;
@@ -1245,6 +1269,21 @@ specialize_instance_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* na
12451269
12461270#undef FT_UNIMPLEMENTED
12471271
1272+ static int
1273+ specialize_instance_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name )
1274+ {
1275+ // 0 is not a valid version
1276+ uint32_t shared_keys_version = 0 ;
1277+ bool shadow = instance_has_key (owner , name , & shared_keys_version );
1278+ PyObject * descr = NULL ;
1279+ unsigned int tp_version ;
1280+ PyTypeObject * type = Py_TYPE (owner );
1281+ DescriptorClassification kind = analyze_descriptor (type , name , & descr , & tp_version , 0 );
1282+ int result = do_specialize_instance_load_attr (owner , instr , name , shadow , shared_keys_version , kind , descr , tp_version );
1283+ Py_XDECREF (descr );
1284+ return result ;
1285+ }
1286+
12481287void
12491288_Py_Specialize_LoadAttr (_PyStackRef owner_st , _Py_CODEUNIT * instr , PyObject * name )
12501289{
@@ -1290,6 +1329,7 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na
12901329 assert (_PyOpcode_Caches [STORE_ATTR ] == INLINE_CACHE_ENTRIES_STORE_ATTR );
12911330 _PyAttrCache * cache = (_PyAttrCache * )(instr + 1 );
12921331 PyTypeObject * type = Py_TYPE (owner );
1332+ PyObject * descr = NULL ;
12931333 if (!_PyType_IsReady (type )) {
12941334 // We *might* not really need this check, but we inherited it from
12951335 // PyObject_GenericSetAttr and friends... and this way we still do the
@@ -1301,12 +1341,15 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na
13011341 SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_OVERRIDDEN );
13021342 goto fail ;
13031343 }
1304- PyObject * descr ;
1305- DescriptorClassification kind = analyze_descriptor (type , name , & descr , 1 );
1344+ unsigned int tp_version ;
1345+ DescriptorClassification kind = analyze_descriptor (type , name , & descr , & tp_version , 1 );
13061346 if (type_get_version (type , STORE_ATTR ) == 0 ) {
13071347 goto fail ;
13081348 }
13091349 switch (kind ) {
1350+ case TYPE_CHANGED :
1351+ SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_ATTR_TYPE_CHANGED );
1352+ goto fail ;
13101353 case OVERRIDING :
13111354 SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR );
13121355 goto fail ;
@@ -1375,11 +1418,13 @@ _Py_Specialize_StoreAttr(_PyStackRef owner_st, _Py_CODEUNIT *instr, PyObject *na
13751418 assert (!PyErr_Occurred ());
13761419 instr -> op .code = STORE_ATTR ;
13771420 cache -> counter = adaptive_counter_backoff (cache -> counter );
1421+ Py_XDECREF (descr );
13781422 return ;
13791423success :
13801424 STAT_INC (STORE_ATTR , success );
13811425 assert (!PyErr_Occurred ());
13821426 cache -> counter = adaptive_counter_cooldown ();
1427+ Py_XDECREF (descr );
13831428}
13841429
13851430#ifndef Py_GIL_DISABLED
@@ -1448,18 +1493,25 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
14481493 }
14491494 PyObject * descr = NULL ;
14501495 DescriptorClassification kind = 0 ;
1451- kind = analyze_descriptor (cls , name , & descr , 0 );
1496+ unsigned int tp_version ;
1497+ kind = analyze_descriptor (cls , name , & descr , & tp_version , 0 );
14521498 if (type_get_version (cls , LOAD_ATTR ) == 0 ) {
1499+ Py_XDECREF (descr );
14531500 return -1 ;
14541501 }
14551502 bool metaclass_check = false;
14561503 if ((Py_TYPE (cls )-> tp_flags & Py_TPFLAGS_IMMUTABLETYPE ) == 0 ) {
14571504 metaclass_check = true;
14581505 if (type_get_version (Py_TYPE (cls ), LOAD_ATTR ) == 0 ) {
1506+ Py_XDECREF (descr );
14591507 return -1 ;
14601508 }
14611509 }
14621510 switch (kind ) {
1511+ case TYPE_CHANGED :
1512+ SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_ATTR_TYPE_CHANGED );
1513+ Py_XDECREF (descr );
1514+ return -1 ;
14631515 case METHOD :
14641516 case NON_DESCRIPTOR :
14651517 write_u32 (cache -> type_version , cls -> tp_version_tag );
@@ -1471,14 +1523,17 @@ specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
14711523 else {
14721524 specialize (instr , LOAD_ATTR_CLASS );
14731525 }
1526+ Py_XDECREF (descr );
14741527 return 0 ;
14751528#ifdef Py_STATS
14761529 case ABSENT :
14771530 SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_EXPECTED_ERROR );
1531+ Py_XDECREF (descr );
14781532 return -1 ;
14791533#endif
14801534 default :
14811535 SPECIALIZATION_FAIL (LOAD_ATTR , load_attr_fail_kind (kind ));
1536+ Py_XDECREF (descr );
14821537 return -1 ;
14831538 }
14841539}
0 commit comments