Skip to content

Commit 70bed6f

Browse files
bpo-45107: Make LOAD_METHOD_CLASS safer and faster, clean up comments (GH-28177)
* Improve comments * Check cls is a type, remove dict calculation
1 parent b0a6ede commit 70bed6f

File tree

2 files changed

+9
-15
lines changed

2 files changed

+9
-15
lines changed

Python/ceval.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4496,6 +4496,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
44964496
}
44974497

44984498
TARGET(LOAD_METHOD_MODULE): {
4499+
/* LOAD_METHOD, for module methods */
44994500
assert(cframe.use_tracing == 0);
45004501
PyObject *owner = TOP();
45014502
PyObject *res;
@@ -4515,15 +4516,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
45154516
_PyObjectCache *cache2 = &caches[-2].obj;
45164517

45174518
PyObject *cls = TOP();
4518-
PyTypeObject *cls_type = Py_TYPE(cls);
4519-
assert(cls_type->tp_dictoffset > 0);
4520-
PyObject *dict = *(PyObject **) ((char *)cls + cls_type->tp_dictoffset);
4521-
// Don't care if no dict -- tp_version_tag should catch anything wrong.
4522-
DEOPT_IF(dict != NULL && ((PyDictObject *)dict)->ma_keys->dk_version !=
4523-
cache1->dk_version_or_hint, LOAD_METHOD);
4519+
DEOPT_IF(!PyType_Check(cls), LOAD_METHOD);
45244520
DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version,
45254521
LOAD_METHOD);
4526-
assert(cache1->dk_version_or_hint != 0);
45274522
assert(cache1->tp_version != 0);
45284523

45294524
STAT_INC(LOAD_METHOD, hit);

Python/specialize.c

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -974,20 +974,19 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name,
974974
// Fall through.
975975
} // Else owner is maybe a builtin with no dict, or __slots__. Doesn't matter.
976976

977-
/* `descr` is borrowed. Just check tp_version_tag before accessing in case
978-
* it's deleted. This is safe for methods (even inherited ones from super
979-
* classes!) as long as tp_version_tag is validated for two main reasons:
977+
/* `descr` is borrowed. This is safe for methods (even inherited ones from
978+
* super classes!) as long as tp_version_tag is validated for two main reasons:
980979
*
981980
* 1. The class will always hold a reference to the method so it will
982981
* usually not be GC-ed. Should it be deleted in Python, e.g.
983982
* `del obj.meth`, tp_version_tag will be invalidated, because of reason 2.
984983
*
985984
* 2. The pre-existing type method cache (MCACHE) uses the same principles
986-
* of caching a borrowed descriptor. It does all the heavy lifting for us.
987-
* E.g. it invalidates on any MRO modification, on any type object
988-
* change along said MRO, etc. (see PyType_Modified usages in typeobject.c).
989-
* The type method cache has been working since Python 2.6 and it's
990-
* battle-tested.
985+
* of caching a borrowed descriptor. The MCACHE infrastructure does all the
986+
* heavy lifting for us. E.g. it invalidates tp_version_tag on any MRO
987+
* modification, on any type object change along said MRO, etc. (see
988+
* PyType_Modified usages in typeobject.c). The MCACHE has been
989+
* working since Python 2.6 and it's battle-tested.
991990
*/
992991
cache2->obj = descr;
993992
cache1->dk_version_or_hint = keys_version;

0 commit comments

Comments
 (0)