@@ -3170,11 +3170,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
3170
3170
if (co_opcache != NULL && PyType_HasFeature (type , Py_TPFLAGS_VALID_VERSION_TAG ))
3171
3171
{
3172
3172
if (co_opcache -> optimized > 0 ) {
3173
- /* Fast path -- cache hit makes LOAD_ATTR ~30% faster */
3173
+ // Fast path -- cache hit makes LOAD_ATTR ~30% faster.
3174
3174
la = & co_opcache -> u .la ;
3175
3175
if (la -> type == type && la -> tp_version_tag == type -> tp_version_tag )
3176
3176
{
3177
- // Hint >= 0 is a dict index; hint < 0 is an inverted slot index
3177
+ // Hint >= 0 is a dict index; hint < 0 is an inverted slot index.
3178
3178
if (la -> hint < 0 ) {
3179
3179
/* Even faster path -- slot hint */
3180
3180
Py_ssize_t offset = ~la -> hint ;
@@ -3187,55 +3187,52 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
3187
3187
Py_DECREF (owner );
3188
3188
DISPATCH ();
3189
3189
}
3190
- else {
3191
- Py_DECREF (owner );
3192
- goto error ;
3193
- }
3194
- }
3195
-
3196
- /* Fast path for dict */
3197
- assert (type -> tp_dict != NULL );
3198
- assert (type -> tp_dictoffset > 0 );
3190
+ // Else slot is NULL. Fall through to slow path to raise AttributeError(name).
3191
+ // Don't DEOPT, since the slot is still there.
3192
+ } else {
3193
+ // Fast path for dict
3194
+ assert (type -> tp_dict != NULL );
3195
+ assert (type -> tp_dictoffset > 0 );
3196
+
3197
+ dictptr = (PyObject * * ) ((char * )owner + type -> tp_dictoffset );
3198
+ dict = * dictptr ;
3199
+ if (dict != NULL && PyDict_CheckExact (dict )) {
3200
+ Py_ssize_t hint = la -> hint ;
3201
+ Py_INCREF (dict );
3202
+ res = NULL ;
3203
+ la -> hint = _PyDict_GetItemHint ((PyDictObject * )dict , name , hint , & res );
3199
3204
3200
- dictptr = (PyObject * * ) ((char * )owner + type -> tp_dictoffset );
3201
- dict = * dictptr ;
3202
- if (dict != NULL && PyDict_CheckExact (dict )) {
3203
- Py_ssize_t hint = la -> hint ;
3204
- Py_INCREF (dict );
3205
- res = NULL ;
3206
- la -> hint = _PyDict_GetItemHint ((PyDictObject * )dict , name , hint , & res );
3205
+ if (res != NULL ) {
3206
+ if (la -> hint == hint && hint >= 0 ) {
3207
+ /* Our hint has helped -- cache hit. */
3208
+ OPCACHE_STAT_ATTR_HIT ();
3209
+ } else {
3210
+ /* The hint we provided didn't work.
3211
+ Maybe next time? */
3212
+ OPCACHE_MAYBE_DEOPT_LOAD_ATTR ();
3213
+ }
3207
3214
3208
- if (res != NULL ) {
3209
- if (la -> hint == hint && hint >= 0 ) {
3210
- /* Our hint has helped -- cache hit. */
3211
- OPCACHE_STAT_ATTR_HIT ();
3215
+ Py_INCREF (res );
3216
+ SET_TOP (res );
3217
+ Py_DECREF (owner );
3218
+ Py_DECREF (dict );
3219
+ DISPATCH ();
3212
3220
} else {
3213
- /* The hint we provided didn't work.
3214
- Maybe next time? */
3215
- OPCACHE_MAYBE_DEOPT_LOAD_ATTR ();
3221
+ // This attribute can be missing sometimes -- we
3222
+ // don't want to optimize this lookup.
3223
+ OPCACHE_DEOPT_LOAD_ATTR ();
3224
+ Py_DECREF (dict );
3216
3225
}
3217
-
3218
- Py_INCREF (res );
3219
- SET_TOP (res );
3220
- Py_DECREF (owner );
3221
- Py_DECREF (dict );
3222
- DISPATCH ();
3223
3226
} else {
3224
- // This attribute can be missing sometimes -- we
3225
- // don't want to optimize this lookup.
3227
+ // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact
3226
3228
OPCACHE_DEOPT_LOAD_ATTR ();
3227
- Py_DECREF (dict );
3228
3229
}
3229
- } else {
3230
- // There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact
3231
- OPCACHE_DEOPT_LOAD_ATTR ();
3232
3230
}
3233
3231
} else {
3234
3232
// The type of the object has either been updated,
3235
3233
// or is different. Maybe it will stabilize?
3236
3234
OPCACHE_MAYBE_DEOPT_LOAD_ATTR ();
3237
3235
}
3238
-
3239
3236
OPCACHE_STAT_ATTR_MISS ();
3240
3237
}
3241
3238
@@ -3251,7 +3248,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
3251
3248
}
3252
3249
PyObject * descr = _PyType_Lookup (type , name );
3253
3250
if (descr != NULL ) {
3254
- // We found an attribute with a data-like descriptor
3251
+ // We found an attribute with a data-like descriptor.
3255
3252
PyTypeObject * dtype = Py_TYPE (descr );
3256
3253
if (dtype == & PyMemberDescr_Type ) { // It's a slot
3257
3254
PyMemberDescrObject * member = (PyMemberDescrObject * )descr ;
@@ -3280,14 +3277,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
3280
3277
3281
3278
DISPATCH ();
3282
3279
}
3283
- else {
3284
- Py_DECREF (owner );
3285
- goto error ;
3286
- }
3280
+ // Else slot is NULL. Fall through to slow path to raise AttributeError(name).
3287
3281
}
3282
+ // Else it's a slot of a different type. We don't handle those.
3288
3283
}
3284
+ // Else it's some other kind of descriptor that we don't handle.
3289
3285
OPCACHE_DEOPT_LOAD_ATTR ();
3290
3286
} else if (type -> tp_dictoffset > 0 ) {
3287
+ // We found an instance with a __dict__.
3291
3288
dictptr = (PyObject * * ) ((char * )owner + type -> tp_dictoffset );
3292
3289
dict = * dictptr ;
3293
3290
0 commit comments