Skip to content

Commit ec0a590

Browse files
committed
Fall through to slow path if slot is NULL
1 parent e334e53 commit ec0a590

File tree

1 file changed

+40
-43
lines changed

1 file changed

+40
-43
lines changed

Python/ceval.c

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3170,11 +3170,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
31703170
if (co_opcache != NULL && PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
31713171
{
31723172
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.
31743174
la = &co_opcache->u.la;
31753175
if (la->type == type && la->tp_version_tag == type->tp_version_tag)
31763176
{
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.
31783178
if (la->hint < 0) {
31793179
/* Even faster path -- slot hint */
31803180
Py_ssize_t offset = ~la->hint;
@@ -3187,55 +3187,52 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
31873187
Py_DECREF(owner);
31883188
DISPATCH();
31893189
}
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);
31993204

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+
}
32073214

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();
32123220
} 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);
32163225
}
3217-
3218-
Py_INCREF(res);
3219-
SET_TOP(res);
3220-
Py_DECREF(owner);
3221-
Py_DECREF(dict);
3222-
DISPATCH();
32233226
} 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
32263228
OPCACHE_DEOPT_LOAD_ATTR();
3227-
Py_DECREF(dict);
32283229
}
3229-
} else {
3230-
// There is no dict, or __dict__ doesn't satisfy PyDict_CheckExact
3231-
OPCACHE_DEOPT_LOAD_ATTR();
32323230
}
32333231
} else {
32343232
// The type of the object has either been updated,
32353233
// or is different. Maybe it will stabilize?
32363234
OPCACHE_MAYBE_DEOPT_LOAD_ATTR();
32373235
}
3238-
32393236
OPCACHE_STAT_ATTR_MISS();
32403237
}
32413238

@@ -3251,7 +3248,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
32513248
}
32523249
PyObject *descr = _PyType_Lookup(type, name);
32533250
if (descr != NULL) {
3254-
// We found an attribute with a data-like descriptor
3251+
// We found an attribute with a data-like descriptor.
32553252
PyTypeObject *dtype = Py_TYPE(descr);
32563253
if (dtype == &PyMemberDescr_Type) { // It's a slot
32573254
PyMemberDescrObject *member = (PyMemberDescrObject *)descr;
@@ -3280,14 +3277,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
32803277

32813278
DISPATCH();
32823279
}
3283-
else {
3284-
Py_DECREF(owner);
3285-
goto error;
3286-
}
3280+
// Else slot is NULL. Fall through to slow path to raise AttributeError(name).
32873281
}
3282+
// Else it's a slot of a different type. We don't handle those.
32883283
}
3284+
// Else it's some other kind of descriptor that we don't handle.
32893285
OPCACHE_DEOPT_LOAD_ATTR();
32903286
} else if (type->tp_dictoffset > 0) {
3287+
// We found an instance with a __dict__.
32913288
dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
32923289
dict = *dictptr;
32933290

0 commit comments

Comments
 (0)