@@ -362,6 +362,8 @@ miss_counter_start(void) {
362
362
#define SPEC_FAIL_OUT_OF_RANGE 4
363
363
#define SPEC_FAIL_EXPECTED_ERROR 5
364
364
#define SPEC_FAIL_WRONG_NUMBER_ARGUMENTS 6
365
+ #define SPEC_FAIL_NOT_PY_FUNCTION 7
366
+
365
367
366
368
#define SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT 18
367
369
@@ -387,6 +389,7 @@ miss_counter_start(void) {
387
389
#define SPEC_FAIL_ATTR_HAS_MANAGED_DICT 25
388
390
#define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26
389
391
#define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27
392
+ #define SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION 28
390
393
391
394
/* Binary subscr and store subscr */
392
395
@@ -498,6 +501,9 @@ miss_counter_start(void) {
498
501
#define SPEC_FAIL_UNPACK_SEQUENCE_ITERATOR 8
499
502
#define SPEC_FAIL_UNPACK_SEQUENCE_SEQUENCE 9
500
503
504
+ static int function_kind (PyCodeObject * code );
505
+ static bool function_check_args (PyObject * o , int expected_argcount , int opcode );
506
+ static uint32_t function_get_version (PyObject * o , int opcode );
501
507
502
508
static int
503
509
specialize_module_load_attr (PyObject * owner , _Py_CODEUNIT * instr ,
@@ -557,21 +563,58 @@ typedef enum {
557
563
MUTABLE , /* Instance of a mutable class; might, or might not, be a descriptor */
558
564
ABSENT , /* Attribute is not present on the class */
559
565
DUNDER_CLASS , /* __class__ attribute */
560
- GETSET_OVERRIDDEN /* __getattribute__ or __setattr__ has been overridden */
566
+ GETSET_OVERRIDDEN , /* __getattribute__ or __setattr__ has been overridden */
567
+ GETATTRIBUTE_IS_PYTHON_FUNCTION /* Descriptor requires calling a Python __getattribute__ */
561
568
} DescriptorClassification ;
562
569
563
570
564
571
static DescriptorClassification
565
572
analyze_descriptor (PyTypeObject * type , PyObject * name , PyObject * * descr , int store )
566
573
{
574
+ bool has_getattr = false;
567
575
if (store ) {
568
576
if (type -> tp_setattro != PyObject_GenericSetAttr ) {
569
577
* descr = NULL ;
570
578
return GETSET_OVERRIDDEN ;
571
579
}
572
580
}
573
581
else {
574
- if (type -> tp_getattro != PyObject_GenericGetAttr ) {
582
+ getattrofunc getattro_slot = type -> tp_getattro ;
583
+ if (getattro_slot == PyObject_GenericGetAttr ) {
584
+ /* Normal attribute lookup; */
585
+ has_getattr = false;
586
+ }
587
+ else if (getattro_slot == _Py_slot_tp_getattr_hook ||
588
+ getattro_slot == _Py_slot_tp_getattro ) {
589
+ /* One or both of __getattribute__ or __getattr__ may have been
590
+ overridden See typeobject.c for why these functions are special. */
591
+ PyObject * getattribute = _PyType_Lookup (type ,
592
+ & _Py_ID (__getattribute__ ));
593
+ PyInterpreterState * interp = _PyInterpreterState_GET ();
594
+ bool has_custom_getattribute = getattribute != NULL &&
595
+ getattribute != interp -> callable_cache .object__getattribute__ ;
596
+ has_getattr = _PyType_Lookup (type , & _Py_ID (__getattr__ )) != NULL ;
597
+ if (has_custom_getattribute ) {
598
+ if (getattro_slot == _Py_slot_tp_getattro &&
599
+ !has_getattr &&
600
+ Py_IS_TYPE (getattribute , & PyFunction_Type )) {
601
+ * descr = getattribute ;
602
+ return GETATTRIBUTE_IS_PYTHON_FUNCTION ;
603
+ }
604
+ /* Potentially both __getattr__ and __getattribute__ are set.
605
+ Too complicated */
606
+ * descr = NULL ;
607
+ return GETSET_OVERRIDDEN ;
608
+ }
609
+ /* Potentially has __getattr__ but no custom __getattribute__.
610
+ Fall through to usual descriptor analysis.
611
+ Usual attribute lookup should only be allowed at runtime
612
+ if we can guarantee that there is no way an exception can be
613
+ raised. This means some specializations, e.g. specializing
614
+ for property() isn't safe.
615
+ */
616
+ }
617
+ else {
575
618
* descr = NULL ;
576
619
return GETSET_OVERRIDDEN ;
577
620
}
@@ -595,7 +638,10 @@ analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int sto
595
638
return OTHER_SLOT ;
596
639
}
597
640
if (desc_cls == & PyProperty_Type ) {
598
- return PROPERTY ;
641
+ /* We can't detect at runtime whether an attribute exists
642
+ with property. So that means we may have to call
643
+ __getattr__. */
644
+ return has_getattr ? GETSET_OVERRIDDEN : PROPERTY ;
599
645
}
600
646
if (PyUnicode_CompareWithASCIIString (name , "__class__" ) == 0 ) {
601
647
if (descriptor == _PyType_Lookup (& PyBaseObject_Type , name )) {
@@ -674,7 +720,6 @@ specialize_dict_access(
674
720
static int specialize_attr_loadmethod (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name ,
675
721
PyObject * descr , DescriptorClassification kind );
676
722
static int specialize_class_load_attr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name );
677
- static int function_kind (PyCodeObject * code );
678
723
679
724
int
680
725
_Py_Specialize_LoadAttr (PyObject * owner , _Py_CODEUNIT * instr , PyObject * name )
@@ -729,24 +774,13 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
729
774
SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_EXPECTED_ERROR );
730
775
goto fail ;
731
776
}
732
- if (Py_TYPE (fget ) != & PyFunction_Type ) {
733
- SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_ATTR_PROPERTY );
777
+ if (! Py_IS_TYPE (fget , & PyFunction_Type ) ) {
778
+ SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION );
734
779
goto fail ;
735
780
}
736
- PyFunctionObject * func = (PyFunctionObject * )fget ;
737
- PyCodeObject * fcode = (PyCodeObject * )func -> func_code ;
738
- int kind = function_kind (fcode );
739
- if (kind != SIMPLE_FUNCTION ) {
740
- SPECIALIZATION_FAIL (LOAD_ATTR , kind );
741
- goto fail ;
742
- }
743
- if (fcode -> co_argcount != 1 ) {
744
- SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_WRONG_NUMBER_ARGUMENTS );
745
- goto fail ;
746
- }
747
- int version = _PyFunction_GetVersionForCurrentState (func );
781
+ uint32_t version = function_check_args (fget , 1 , LOAD_ATTR ) &&
782
+ function_get_version (fget , LOAD_ATTR );
748
783
if (version == 0 ) {
749
- SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_OUT_OF_VERSIONS );
750
784
goto fail ;
751
785
}
752
786
write_u32 (lm_cache -> keys_version , version );
@@ -795,6 +829,22 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
795
829
case GETSET_OVERRIDDEN :
796
830
SPECIALIZATION_FAIL (LOAD_ATTR , SPEC_FAIL_OVERRIDDEN );
797
831
goto fail ;
832
+ case GETATTRIBUTE_IS_PYTHON_FUNCTION :
833
+ {
834
+ assert (type -> tp_getattro == _Py_slot_tp_getattro );
835
+ assert (Py_IS_TYPE (descr , & PyFunction_Type ));
836
+ _PyLoadMethodCache * lm_cache = (_PyLoadMethodCache * )(instr + 1 );
837
+ uint32_t func_version = function_check_args (descr , 2 , LOAD_ATTR ) &&
838
+ function_get_version (descr , LOAD_ATTR );
839
+ if (func_version == 0 ) {
840
+ goto fail ;
841
+ }
842
+ /* borrowed */
843
+ write_obj (lm_cache -> descr , descr );
844
+ write_u32 (lm_cache -> type_version , type -> tp_version_tag );
845
+ _Py_SET_OPCODE (* instr , LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN );
846
+ goto success ;
847
+ }
798
848
case BUILTIN_CLASSMETHOD :
799
849
case PYTHON_CLASSMETHOD :
800
850
case NON_OVERRIDING :
@@ -873,6 +923,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
873
923
case MUTABLE :
874
924
SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_ATTR_MUTABLE_CLASS );
875
925
goto fail ;
926
+ case GETATTRIBUTE_IS_PYTHON_FUNCTION :
876
927
case GETSET_OVERRIDDEN :
877
928
SPECIALIZATION_FAIL (STORE_ATTR , SPEC_FAIL_OVERRIDDEN );
878
929
goto fail ;
@@ -1215,6 +1266,39 @@ function_kind(PyCodeObject *code) {
1215
1266
return SIMPLE_FUNCTION ;
1216
1267
}
1217
1268
1269
+ /* Returning false indicates a failure. */
1270
+ static bool
1271
+ function_check_args (PyObject * o , int expected_argcount , int opcode )
1272
+ {
1273
+ assert (Py_IS_TYPE (o , & PyFunction_Type ));
1274
+ PyFunctionObject * func = (PyFunctionObject * )o ;
1275
+ PyCodeObject * fcode = (PyCodeObject * )func -> func_code ;
1276
+ int kind = function_kind (fcode );
1277
+ if (kind != SIMPLE_FUNCTION ) {
1278
+ SPECIALIZATION_FAIL (opcode , kind );
1279
+ return false;
1280
+ }
1281
+ if (fcode -> co_argcount != expected_argcount ) {
1282
+ SPECIALIZATION_FAIL (opcode , SPEC_FAIL_WRONG_NUMBER_ARGUMENTS );
1283
+ return false;
1284
+ }
1285
+ return true;
1286
+ }
1287
+
1288
+ /* Returning 0 indicates a failure. */
1289
+ static uint32_t
1290
+ function_get_version (PyObject * o , int opcode )
1291
+ {
1292
+ assert (Py_IS_TYPE (o , & PyFunction_Type ));
1293
+ PyFunctionObject * func = (PyFunctionObject * )o ;
1294
+ uint32_t version = _PyFunction_GetVersionForCurrentState (func );
1295
+ if (version == 0 ) {
1296
+ SPECIALIZATION_FAIL (opcode , SPEC_FAIL_OUT_OF_VERSIONS );
1297
+ return 0 ;
1298
+ }
1299
+ return version ;
1300
+ }
1301
+
1218
1302
int
1219
1303
_Py_Specialize_BinarySubscr (
1220
1304
PyObject * container , PyObject * sub , _Py_CODEUNIT * instr )
0 commit comments