mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-131586: Avoid refcount contention in some "special" calls (#131588)
In the free threaded build, the `_PyObject_LookupSpecial()` call can lead to reference count contention on the returned function object becuase it doesn't use stackrefs. Refactor some of the callers to use `_PyObject_MaybeCallSpecialNoArgs`, which uses stackrefs internally. This fixes the scaling bottleneck in the "lookup_special" microbenchmark in `ftscalingbench.py`. However, the are still some uses of `_PyObject_LookupSpecial()` that need to be addressed in future PRs.
This commit is contained in:
parent
3d4ac1a2c2
commit
67fbfb42bd
16 changed files with 450 additions and 374 deletions
|
@ -598,6 +598,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
|||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__bytes__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__call__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__cantrace__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__ceil__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__class_getitem__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__classcell__));
|
||||
|
@ -622,6 +623,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
|||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__file__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__firstlineno__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__float__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floor__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__floordiv__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__format__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__fspath__));
|
||||
|
@ -727,6 +729,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
|||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasscheck__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__subclasshook__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__truediv__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__trunc__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__type_params__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_is_unpacked_typevartuple__));
|
||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(__typing_prepare_subst__));
|
||||
|
|
|
@ -89,6 +89,7 @@ struct _Py_global_strings {
|
|||
STRUCT_FOR_ID(__bytes__)
|
||||
STRUCT_FOR_ID(__call__)
|
||||
STRUCT_FOR_ID(__cantrace__)
|
||||
STRUCT_FOR_ID(__ceil__)
|
||||
STRUCT_FOR_ID(__class__)
|
||||
STRUCT_FOR_ID(__class_getitem__)
|
||||
STRUCT_FOR_ID(__classcell__)
|
||||
|
@ -113,6 +114,7 @@ struct _Py_global_strings {
|
|||
STRUCT_FOR_ID(__file__)
|
||||
STRUCT_FOR_ID(__firstlineno__)
|
||||
STRUCT_FOR_ID(__float__)
|
||||
STRUCT_FOR_ID(__floor__)
|
||||
STRUCT_FOR_ID(__floordiv__)
|
||||
STRUCT_FOR_ID(__format__)
|
||||
STRUCT_FOR_ID(__fspath__)
|
||||
|
@ -218,6 +220,7 @@ struct _Py_global_strings {
|
|||
STRUCT_FOR_ID(__subclasscheck__)
|
||||
STRUCT_FOR_ID(__subclasshook__)
|
||||
STRUCT_FOR_ID(__truediv__)
|
||||
STRUCT_FOR_ID(__trunc__)
|
||||
STRUCT_FOR_ID(__type_params__)
|
||||
STRUCT_FOR_ID(__typing_is_unpacked_typevartuple__)
|
||||
STRUCT_FOR_ID(__typing_prepare_subst__)
|
||||
|
|
|
@ -891,6 +891,12 @@ extern bool _PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name,
|
|||
extern PyObject *_PyType_LookupRefAndVersion(PyTypeObject *, PyObject *,
|
||||
unsigned int *);
|
||||
|
||||
// Internal API to look for a name through the MRO.
|
||||
// This stores a stack reference in out and returns the value of
|
||||
// type->tp_version or zero if name is missing. It doesn't set an exception!
|
||||
extern unsigned int
|
||||
_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out);
|
||||
|
||||
// Cache the provided init method in the specialization cache of type if the
|
||||
// provided type version matches the current version of the type.
|
||||
//
|
||||
|
@ -946,6 +952,14 @@ extern int _PyObject_IsInstanceDictEmpty(PyObject *);
|
|||
PyAPI_FUNC(PyObject*) _PyObject_LookupSpecial(PyObject *, PyObject *);
|
||||
PyAPI_FUNC(PyObject*) _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or_null);
|
||||
|
||||
// Calls the method named `attr` on `self`, but does not set an exception if
|
||||
// the attribute does not exist.
|
||||
PyAPI_FUNC(PyObject *)
|
||||
_PyObject_MaybeCallSpecialNoArgs(PyObject *self, PyObject *attr);
|
||||
|
||||
PyAPI_FUNC(PyObject *)
|
||||
_PyObject_MaybeCallSpecialOneArg(PyObject *self, PyObject *attr, PyObject *arg);
|
||||
|
||||
extern int _PyObject_IsAbstract(PyObject *);
|
||||
|
||||
PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);
|
||||
|
|
3
Include/internal/pycore_runtime_init_generated.h
generated
3
Include/internal/pycore_runtime_init_generated.h
generated
|
@ -596,6 +596,7 @@ extern "C" {
|
|||
INIT_ID(__bytes__), \
|
||||
INIT_ID(__call__), \
|
||||
INIT_ID(__cantrace__), \
|
||||
INIT_ID(__ceil__), \
|
||||
INIT_ID(__class__), \
|
||||
INIT_ID(__class_getitem__), \
|
||||
INIT_ID(__classcell__), \
|
||||
|
@ -620,6 +621,7 @@ extern "C" {
|
|||
INIT_ID(__file__), \
|
||||
INIT_ID(__firstlineno__), \
|
||||
INIT_ID(__float__), \
|
||||
INIT_ID(__floor__), \
|
||||
INIT_ID(__floordiv__), \
|
||||
INIT_ID(__format__), \
|
||||
INIT_ID(__fspath__), \
|
||||
|
@ -725,6 +727,7 @@ extern "C" {
|
|||
INIT_ID(__subclasscheck__), \
|
||||
INIT_ID(__subclasshook__), \
|
||||
INIT_ID(__truediv__), \
|
||||
INIT_ID(__trunc__), \
|
||||
INIT_ID(__type_params__), \
|
||||
INIT_ID(__typing_is_unpacked_typevartuple__), \
|
||||
INIT_ID(__typing_prepare_subst__), \
|
||||
|
|
|
@ -592,7 +592,7 @@ PyStackRef_XCLOSE(_PyStackRef ref)
|
|||
|
||||
// Note: this is a macro because MSVC (Windows) has trouble inlining it.
|
||||
|
||||
#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_REFCNT)) == ((b).bits & (~Py_TAG_REFCNT)))
|
||||
#define PyStackRef_Is(a, b) (((a).bits & (~Py_TAG_BITS)) == ((b).bits & (~Py_TAG_BITS)))
|
||||
|
||||
#endif // !defined(Py_GIL_DISABLED) && defined(Py_STACKREF_DEBUG)
|
||||
|
||||
|
@ -640,6 +640,28 @@ PyStackRef_FunctionCheck(_PyStackRef stackref)
|
|||
return PyFunction_Check(PyStackRef_AsPyObjectBorrow(stackref));
|
||||
}
|
||||
|
||||
static inline void
|
||||
_PyThreadState_PushCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
|
||||
{
|
||||
#ifdef Py_GIL_DISABLED
|
||||
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
|
||||
ref->next = tstate_impl->c_stack_refs;
|
||||
tstate_impl->c_stack_refs = ref;
|
||||
#endif
|
||||
ref->ref = PyStackRef_NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_PyThreadState_PopCStackRef(PyThreadState *tstate, _PyCStackRef *ref)
|
||||
{
|
||||
#ifdef Py_GIL_DISABLED
|
||||
_PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
|
||||
assert(tstate_impl->c_stack_refs == ref);
|
||||
tstate_impl->c_stack_refs = ref->next;
|
||||
#endif
|
||||
PyStackRef_XCLOSE(ref->ref);
|
||||
}
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
|
||||
static inline int
|
||||
|
@ -656,6 +678,17 @@ _Py_TryIncrefCompareStackRef(PyObject **src, PyObject *op, _PyStackRef *out)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
_Py_TryXGetStackRef(PyObject **src, _PyStackRef *out)
|
||||
{
|
||||
PyObject *op = _Py_atomic_load_ptr_relaxed(src);
|
||||
if (op == NULL) {
|
||||
*out = PyStackRef_NULL;
|
||||
return 1;
|
||||
}
|
||||
return _Py_TryIncrefCompareStackRef(src, op, out);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Like Py_VISIT but for _PyStackRef fields
|
||||
|
|
|
@ -65,6 +65,16 @@ typedef union _PyStackRef {
|
|||
#endif
|
||||
} _PyStackRef;
|
||||
|
||||
// A stackref that can be stored in a regular C local variable and be visible
|
||||
// to the GC in the free threading build.
|
||||
// Used in combination with _PyThreadState_PushCStackRef().
|
||||
typedef struct _PyCStackRef {
|
||||
_PyStackRef ref;
|
||||
#ifdef Py_GIL_DISABLED
|
||||
struct _PyCStackRef *next;
|
||||
#endif
|
||||
} _PyCStackRef;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -47,8 +47,9 @@ typedef struct _PyThreadStateImpl {
|
|||
struct _qsbr_thread_state *qsbr; // only used by free-threaded build
|
||||
struct llist_node mem_free_queue; // delayed free queue
|
||||
|
||||
|
||||
#ifdef Py_GIL_DISABLED
|
||||
// Stack references for the current thread that exist on the C stack
|
||||
struct _PyCStackRef *c_stack_refs;
|
||||
struct _gc_thread_state gc;
|
||||
struct _mimalloc_thread_state mimalloc;
|
||||
struct _Py_freelists freelists;
|
||||
|
|
12
Include/internal/pycore_unicodeobject_generated.h
generated
12
Include/internal/pycore_unicodeobject_generated.h
generated
|
@ -144,6 +144,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
|||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__ceil__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__class__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
|
@ -240,6 +244,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
|||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__floor__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__floordiv__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
|
@ -660,6 +668,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
|||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__trunc__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
assert(PyUnicode_GET_LENGTH(string) != 1);
|
||||
string = &_Py_ID(__type_params__);
|
||||
_PyUnicode_InternStatic(interp, &string);
|
||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||
|
|
|
@ -383,6 +383,10 @@ class BoolTest(unittest.TestCase):
|
|||
__bool__ = None
|
||||
self.assertRaises(TypeError, bool, B())
|
||||
|
||||
class C:
|
||||
__len__ = None
|
||||
self.assertRaises(TypeError, bool, C())
|
||||
|
||||
def test_real_and_imag(self):
|
||||
self.assertEqual(True.real, 1)
|
||||
self.assertEqual(True.imag, 0)
|
||||
|
|
|
@ -1746,6 +1746,11 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
|
|||
a[0] = a
|
||||
self.assertEqual(repr(a), '{0: {...}}')
|
||||
|
||||
def test_repr_blocked(self):
|
||||
class C:
|
||||
__repr__ = None
|
||||
self.assertRaises(TypeError, repr, C())
|
||||
|
||||
def test_round(self):
|
||||
self.assertEqual(round(0.0), 0.0)
|
||||
self.assertEqual(type(round(0.0)), int)
|
||||
|
|
|
@ -573,6 +573,8 @@ class MathTests(unittest.TestCase):
|
|||
#self.assertEqual(math.ceil(NINF), NINF)
|
||||
#self.assertTrue(math.isnan(math.floor(NAN)))
|
||||
|
||||
class TestFloorIsNone(float):
|
||||
__floor__ = None
|
||||
class TestFloor:
|
||||
def __floor__(self):
|
||||
return 42
|
||||
|
@ -588,6 +590,7 @@ class MathTests(unittest.TestCase):
|
|||
self.assertEqual(math.floor(FloatLike(41.9)), 41)
|
||||
self.assertRaises(TypeError, math.floor, TestNoFloor())
|
||||
self.assertRaises(ValueError, math.floor, TestBadFloor())
|
||||
self.assertRaises(TypeError, math.floor, TestFloorIsNone(3.5))
|
||||
|
||||
t = TestNoFloor()
|
||||
t.__floor__ = lambda *args: args
|
||||
|
|
|
@ -78,19 +78,6 @@ module math
|
|||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=76bc7002685dd942]*/
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject *str___ceil__;
|
||||
PyObject *str___floor__;
|
||||
PyObject *str___trunc__;
|
||||
} math_module_state;
|
||||
|
||||
static inline math_module_state*
|
||||
get_math_module_state(PyObject *module)
|
||||
{
|
||||
void *state = _PyModule_GetState(module);
|
||||
assert(state != NULL);
|
||||
return (math_module_state *)state;
|
||||
}
|
||||
|
||||
/*
|
||||
Double and triple length extended precision algorithms from:
|
||||
|
@ -1140,18 +1127,17 @@ math_ceil(PyObject *module, PyObject *number)
|
|||
x = PyFloat_AS_DOUBLE(number);
|
||||
}
|
||||
else {
|
||||
math_module_state *state = get_math_module_state(module);
|
||||
PyObject *method = _PyObject_LookupSpecial(number, state->str___ceil__);
|
||||
if (method != NULL) {
|
||||
PyObject *result = _PyObject_CallNoArgs(method);
|
||||
Py_DECREF(method);
|
||||
PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__ceil__));
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
if (PyErr_Occurred())
|
||||
else if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
x = PyFloat_AsDouble(number);
|
||||
if (x == -1.0 && PyErr_Occurred())
|
||||
if (x == -1.0 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return PyLong_FromDouble(ceil(x));
|
||||
}
|
||||
|
@ -1209,18 +1195,17 @@ math_floor(PyObject *module, PyObject *number)
|
|||
x = PyFloat_AS_DOUBLE(number);
|
||||
}
|
||||
else {
|
||||
math_module_state *state = get_math_module_state(module);
|
||||
PyObject *method = _PyObject_LookupSpecial(number, state->str___floor__);
|
||||
if (method != NULL) {
|
||||
PyObject *result = _PyObject_CallNoArgs(method);
|
||||
Py_DECREF(method);
|
||||
PyObject *result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__floor__));
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
if (PyErr_Occurred())
|
||||
else if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
x = PyFloat_AsDouble(number);
|
||||
if (x == -1.0 && PyErr_Occurred())
|
||||
if (x == -1.0 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return PyLong_FromDouble(floor(x));
|
||||
}
|
||||
|
@ -2074,24 +2059,20 @@ static PyObject *
|
|||
math_trunc(PyObject *module, PyObject *x)
|
||||
/*[clinic end generated code: output=34b9697b707e1031 input=2168b34e0a09134d]*/
|
||||
{
|
||||
PyObject *trunc, *result;
|
||||
|
||||
if (PyFloat_CheckExact(x)) {
|
||||
return PyFloat_Type.tp_as_number->nb_int(x);
|
||||
}
|
||||
|
||||
math_module_state *state = get_math_module_state(module);
|
||||
trunc = _PyObject_LookupSpecial(x, state->str___trunc__);
|
||||
if (trunc == NULL) {
|
||||
if (!PyErr_Occurred())
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"type %.100s doesn't define __trunc__ method",
|
||||
Py_TYPE(x)->tp_name);
|
||||
return NULL;
|
||||
PyObject *result = _PyObject_MaybeCallSpecialNoArgs(x, &_Py_ID(__trunc__));
|
||||
if (result != NULL) {
|
||||
return result;
|
||||
}
|
||||
result = _PyObject_CallNoArgs(trunc);
|
||||
Py_DECREF(trunc);
|
||||
return result;
|
||||
else if (!PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"type %.100s doesn't define __trunc__ method",
|
||||
Py_TYPE(x)->tp_name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -4084,19 +4065,6 @@ static int
|
|||
math_exec(PyObject *module)
|
||||
{
|
||||
|
||||
math_module_state *state = get_math_module_state(module);
|
||||
state->str___ceil__ = PyUnicode_InternFromString("__ceil__");
|
||||
if (state->str___ceil__ == NULL) {
|
||||
return -1;
|
||||
}
|
||||
state->str___floor__ = PyUnicode_InternFromString("__floor__");
|
||||
if (state->str___floor__ == NULL) {
|
||||
return -1;
|
||||
}
|
||||
state->str___trunc__ = PyUnicode_InternFromString("__trunc__");
|
||||
if (state->str___trunc__ == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (PyModule_Add(module, "pi", PyFloat_FromDouble(Py_MATH_PI)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -4116,22 +4084,6 @@ math_exec(PyObject *module)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
math_clear(PyObject *module)
|
||||
{
|
||||
math_module_state *state = get_math_module_state(module);
|
||||
Py_CLEAR(state->str___ceil__);
|
||||
Py_CLEAR(state->str___floor__);
|
||||
Py_CLEAR(state->str___trunc__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
math_free(void *module)
|
||||
{
|
||||
math_clear((PyObject *)module);
|
||||
}
|
||||
|
||||
static PyMethodDef math_methods[] = {
|
||||
{"acos", math_acos, METH_O, math_acos_doc},
|
||||
{"acosh", math_acosh, METH_O, math_acosh_doc},
|
||||
|
@ -4208,11 +4160,9 @@ static struct PyModuleDef mathmodule = {
|
|||
PyModuleDef_HEAD_INIT,
|
||||
.m_name = "math",
|
||||
.m_doc = module_doc,
|
||||
.m_size = sizeof(math_module_state),
|
||||
.m_size = 0,
|
||||
.m_methods = math_methods,
|
||||
.m_slots = math_slots,
|
||||
.m_clear = math_clear,
|
||||
.m_free = math_free,
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
|
|
|
@ -1680,14 +1680,20 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
Py_TYPE(name)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
Py_INCREF(name);
|
||||
|
||||
if (!_PyType_IsReady(tp)) {
|
||||
if (PyType_Ready(tp) < 0)
|
||||
goto done;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
descr = _PyType_LookupRef(tp, name);
|
||||
Py_INCREF(name);
|
||||
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
|
||||
_PyType_LookupStackRefAndVersion(tp, name, &cref.ref);
|
||||
descr = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
|
||||
f = NULL;
|
||||
if (descr != NULL) {
|
||||
|
@ -1758,8 +1764,8 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
}
|
||||
|
||||
if (descr != NULL) {
|
||||
res = descr;
|
||||
descr = NULL;
|
||||
res = PyStackRef_AsPyObjectSteal(cref.ref);
|
||||
cref.ref = PyStackRef_NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -1771,7 +1777,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
_PyObject_SetAttributeErrorContext(obj, name);
|
||||
}
|
||||
done:
|
||||
Py_XDECREF(descr);
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
Py_DECREF(name);
|
||||
return res;
|
||||
}
|
||||
|
@ -1805,7 +1811,13 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
|
||||
Py_INCREF(name);
|
||||
Py_INCREF(tp);
|
||||
descr = _PyType_LookupRef(tp, name);
|
||||
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
|
||||
_PyType_LookupStackRefAndVersion(tp, name, &cref.ref);
|
||||
descr = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
|
||||
if (descr != NULL) {
|
||||
f = Py_TYPE(descr)->tp_descr_set;
|
||||
|
@ -1872,7 +1884,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
|
|||
_PyObject_SetAttributeErrorContext(obj, name);
|
||||
}
|
||||
done:
|
||||
Py_XDECREF(descr);
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
Py_DECREF(tp);
|
||||
Py_DECREF(name);
|
||||
return res;
|
||||
|
|
|
@ -103,13 +103,9 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer);
|
|||
static PyObject *
|
||||
slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
|
||||
|
||||
static PyObject *
|
||||
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
|
||||
|
||||
static int
|
||||
slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
|
||||
|
||||
|
||||
static inline PyTypeObject *
|
||||
type_from_ref(PyObject *ref)
|
||||
{
|
||||
|
@ -1146,8 +1142,29 @@ PyType_Modified(PyTypeObject *type)
|
|||
static int
|
||||
is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b);
|
||||
|
||||
// Check if the `mro` method on `type` is overridden, i.e.,
|
||||
// `type(tp).mro is not type.mro`.
|
||||
static int
|
||||
has_custom_mro(PyTypeObject *tp)
|
||||
{
|
||||
_PyCStackRef c_ref1, c_ref2;
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyThreadState_PushCStackRef(tstate, &c_ref1);
|
||||
_PyThreadState_PushCStackRef(tstate, &c_ref2);
|
||||
|
||||
_PyType_LookupStackRefAndVersion(Py_TYPE(tp), &_Py_ID(mro), &c_ref1.ref);
|
||||
_PyType_LookupStackRefAndVersion(&PyType_Type, &_Py_ID(mro), &c_ref2.ref);
|
||||
|
||||
int custom = !PyStackRef_Is(c_ref1.ref, c_ref2.ref);
|
||||
|
||||
_PyThreadState_PopCStackRef(tstate, &c_ref2);
|
||||
_PyThreadState_PopCStackRef(tstate, &c_ref1);
|
||||
return custom;
|
||||
}
|
||||
|
||||
static void
|
||||
type_mro_modified(PyTypeObject *type, PyObject *bases) {
|
||||
type_mro_modified(PyTypeObject *type, PyObject *bases)
|
||||
{
|
||||
/*
|
||||
Check that all base classes or elements of the MRO of type are
|
||||
able to be cached. This function is called after the base
|
||||
|
@ -1161,29 +1178,10 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
|
|||
each subclass when their mro is recursively updated.
|
||||
*/
|
||||
Py_ssize_t i, n;
|
||||
int custom = !Py_IS_TYPE(type, &PyType_Type);
|
||||
int unbound;
|
||||
|
||||
ASSERT_TYPE_LOCK_HELD();
|
||||
if (custom) {
|
||||
PyObject *mro_meth, *type_mro_meth;
|
||||
mro_meth = lookup_maybe_method(
|
||||
(PyObject *)type, &_Py_ID(mro), &unbound);
|
||||
if (mro_meth == NULL) {
|
||||
goto clear;
|
||||
}
|
||||
type_mro_meth = lookup_maybe_method(
|
||||
(PyObject *)&PyType_Type, &_Py_ID(mro), &unbound);
|
||||
if (type_mro_meth == NULL) {
|
||||
Py_DECREF(mro_meth);
|
||||
goto clear;
|
||||
}
|
||||
int custom_mro = (mro_meth != type_mro_meth);
|
||||
Py_DECREF(mro_meth);
|
||||
Py_DECREF(type_mro_meth);
|
||||
if (custom_mro) {
|
||||
goto clear;
|
||||
}
|
||||
if (!Py_IS_TYPE(type, &PyType_Type) && has_custom_mro(type)) {
|
||||
goto clear;
|
||||
}
|
||||
n = PyTuple_GET_SIZE(bases);
|
||||
for (i = 0; i < n; i++) {
|
||||
|
@ -1224,7 +1222,6 @@ This is similar to func_version_cache.
|
|||
void
|
||||
_PyType_SetVersion(PyTypeObject *tp, unsigned int version)
|
||||
{
|
||||
|
||||
BEGIN_TYPE_LOCK();
|
||||
set_version_unlocked(tp, version);
|
||||
END_TYPE_LOCK();
|
||||
|
@ -2805,36 +2802,51 @@ _PyObject_LookupSpecialMethod(PyObject *self, PyObject *attr, PyObject **self_or
|
|||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
|
||||
static int
|
||||
lookup_method_ex(PyObject *self, PyObject *attr, _PyStackRef *out,
|
||||
int raise_attribute_error)
|
||||
{
|
||||
PyObject *res = _PyType_LookupRef(Py_TYPE(self), attr);
|
||||
if (res == NULL) {
|
||||
return NULL;
|
||||
_PyType_LookupStackRefAndVersion(Py_TYPE(self), attr, out);
|
||||
if (PyStackRef_IsNull(*out)) {
|
||||
if (raise_attribute_error) {
|
||||
PyErr_SetObject(PyExc_AttributeError, attr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
|
||||
PyObject *value = PyStackRef_AsPyObjectBorrow(*out);
|
||||
if (_PyType_HasFeature(Py_TYPE(value), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
|
||||
/* Avoid temporary PyMethodObject */
|
||||
*unbound = 1;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
*unbound = 0;
|
||||
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
|
||||
if (f != NULL) {
|
||||
Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
|
||||
|
||||
descrgetfunc f = Py_TYPE(value)->tp_descr_get;
|
||||
if (f != NULL) {
|
||||
value = f(value, self, (PyObject *)(Py_TYPE(self)));
|
||||
PyStackRef_CLEAR(*out);
|
||||
if (value == NULL) {
|
||||
if (!raise_attribute_error &&
|
||||
PyErr_ExceptionMatches(PyExc_AttributeError))
|
||||
{
|
||||
PyErr_Clear();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
*out = PyStackRef_FromPyObjectSteal(value);
|
||||
}
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
lookup_method(PyObject *self, PyObject *attr, int *unbound)
|
||||
static int
|
||||
lookup_maybe_method(PyObject *self, PyObject *attr, _PyStackRef *out)
|
||||
{
|
||||
PyObject *res = lookup_maybe_method(self, attr, unbound);
|
||||
if (res == NULL && !PyErr_Occurred()) {
|
||||
PyErr_SetObject(PyExc_AttributeError, attr);
|
||||
}
|
||||
return res;
|
||||
return lookup_method_ex(self, attr, out, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
lookup_method(PyObject *self, PyObject *attr, _PyStackRef *out)
|
||||
{
|
||||
return lookup_method_ex(self, attr, out, 1);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2864,6 +2876,45 @@ call_unbound_noarg(int unbound, PyObject *func, PyObject *self)
|
|||
}
|
||||
}
|
||||
|
||||
// Call the method with the name `attr` on `self`. Returns NULL with an
|
||||
// exception set if the method is missing or an error occurs.
|
||||
static PyObject *
|
||||
call_method_noarg(PyObject *self, PyObject *attr)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
PyObject *res = NULL;
|
||||
int unbound = lookup_method(self, attr, &cref.ref);
|
||||
if (unbound >= 0) {
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
}
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
call_method(PyObject *self, PyObject *attr, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
PyObject *res = NULL;
|
||||
int unbound = lookup_method(self, attr, &cref.ref);
|
||||
if (unbound >= 0) {
|
||||
PyObject *meth = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
if (unbound) {
|
||||
res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
|
||||
}
|
||||
else {
|
||||
res = _PyObject_Call(tstate, meth, args, kwds);
|
||||
}
|
||||
}
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* A variation of PyObject_CallMethod* that uses lookup_method()
|
||||
instead of PyObject_GetAttrString().
|
||||
|
||||
|
@ -2875,14 +2926,16 @@ vectorcall_method(PyObject *name, PyObject *const *args, Py_ssize_t nargs)
|
|||
assert(nargs >= 1);
|
||||
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
int unbound;
|
||||
PyObject *retval = NULL;
|
||||
PyObject *self = args[0];
|
||||
PyObject *func = lookup_method(self, name, &unbound);
|
||||
if (func == NULL) {
|
||||
return NULL;
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
int unbound = lookup_method(self, name, &cref.ref);
|
||||
if (unbound >= 0) {
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
|
||||
}
|
||||
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
|
||||
Py_DECREF(func);
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -2894,19 +2947,81 @@ vectorcall_maybe(PyThreadState *tstate, PyObject *name,
|
|||
{
|
||||
assert(nargs >= 1);
|
||||
|
||||
int unbound;
|
||||
PyObject *self = args[0];
|
||||
PyObject *func = lookup_maybe_method(self, name, &unbound);
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
int unbound = lookup_maybe_method(self, name, &cref.ref);
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
if (func == NULL) {
|
||||
if (!PyErr_Occurred())
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
if (!PyErr_Occurred()) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
PyObject *retval = vectorcall_unbound(tstate, unbound, func, args, nargs);
|
||||
Py_DECREF(func);
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Call the method with the name `attr` on `self`. Returns NULL if the
|
||||
method is missing or an error occurs. No exception is set if
|
||||
the method is missing. If attr_is_none is not NULL, it is set to 1 if
|
||||
the attribute was found and was None, or 0 if it was not found. */
|
||||
static PyObject *
|
||||
maybe_call_special_no_args(PyObject *self, PyObject *attr, int *attr_is_none)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
|
||||
PyObject *res = NULL;
|
||||
int unbound = lookup_maybe_method(self, attr, &cref.ref);
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
if (attr_is_none != NULL) {
|
||||
*attr_is_none = (func == Py_None);
|
||||
}
|
||||
if (func != NULL && (func != Py_None || attr_is_none == NULL)) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
}
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
maybe_call_special_one_arg(PyObject *self, PyObject *attr, PyObject *arg,
|
||||
int *attr_is_none)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
|
||||
PyObject *res = NULL;
|
||||
int unbound = lookup_maybe_method(self, attr, &cref.ref);
|
||||
PyObject *func = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
if (attr_is_none != NULL) {
|
||||
*attr_is_none = (func == Py_None);
|
||||
}
|
||||
if (func != NULL && (func != Py_None || attr_is_none == NULL)) {
|
||||
PyObject *args[] = { self, arg };
|
||||
res = vectorcall_unbound(tstate, unbound, func, args, 2);
|
||||
}
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
return res;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyObject_MaybeCallSpecialNoArgs(PyObject *self, PyObject *attr)
|
||||
{
|
||||
return maybe_call_special_no_args(self, attr, NULL);
|
||||
}
|
||||
|
||||
PyObject *
|
||||
_PyObject_MaybeCallSpecialOneArg(PyObject *self, PyObject *attr, PyObject *arg)
|
||||
{
|
||||
return maybe_call_special_one_arg(self, attr, arg, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
Method resolution order algorithm C3 described in
|
||||
"A Monotonic Superclass Linearization for Dylan",
|
||||
|
@ -3288,13 +3403,7 @@ mro_invoke(PyTypeObject *type)
|
|||
const int custom = !Py_IS_TYPE(type, &PyType_Type);
|
||||
|
||||
if (custom) {
|
||||
int unbound;
|
||||
PyObject *mro_meth = lookup_method(
|
||||
(PyObject *)type, &_Py_ID(mro), &unbound);
|
||||
if (mro_meth == NULL)
|
||||
return NULL;
|
||||
mro_result = call_unbound_noarg(unbound, mro_meth, (PyObject *)type);
|
||||
Py_DECREF(mro_meth);
|
||||
mro_result = call_method_noarg((PyObject *)type, &_Py_ID(mro));
|
||||
}
|
||||
else {
|
||||
mro_result = mro_implementation_unlocked(type);
|
||||
|
@ -5611,10 +5720,20 @@ _PyTypes_AfterFork(void)
|
|||
PyObject *
|
||||
_PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *version)
|
||||
{
|
||||
PyObject *res;
|
||||
int error;
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
_PyStackRef out;
|
||||
unsigned int ver = _PyType_LookupStackRefAndVersion(type, name, &out);
|
||||
if (version) {
|
||||
*version = ver;
|
||||
}
|
||||
if (PyStackRef_IsNull(out)) {
|
||||
return NULL;
|
||||
}
|
||||
return PyStackRef_AsPyObjectSteal(out);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
_PyType_LookupStackRefAndVersion(PyTypeObject *type, PyObject *name, _PyStackRef *out)
|
||||
{
|
||||
unsigned int h = MCACHE_HASH_METHOD(type, name);
|
||||
struct type_cache *cache = get_type_cache();
|
||||
struct type_cache_entry *entry = &cache->hashtable[h];
|
||||
|
@ -5628,16 +5747,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
|
|||
_Py_atomic_load_ptr_relaxed(&entry->name) == name) {
|
||||
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
|
||||
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
|
||||
PyObject *value = _Py_atomic_load_ptr_relaxed(&entry->value);
|
||||
// If the sequence is still valid then we're done
|
||||
if (value == NULL || _Py_TryIncref(value)) {
|
||||
if (_Py_TryXGetStackRef(&entry->value, out)) {
|
||||
// If the sequence is still valid then we're done
|
||||
if (_PySeqLock_EndRead(&entry->sequence, sequence)) {
|
||||
if (version != NULL) {
|
||||
*version = entry_version;
|
||||
}
|
||||
return value;
|
||||
return entry_version;
|
||||
}
|
||||
Py_XDECREF(value);
|
||||
PyStackRef_XCLOSE(*out);
|
||||
}
|
||||
else {
|
||||
// If we can't incref the object we need to fallback to locking
|
||||
|
@ -5650,16 +5765,12 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
|
|||
}
|
||||
}
|
||||
#else
|
||||
if (entry->version == type->tp_version_tag &&
|
||||
entry->name == name) {
|
||||
if (entry->version == type->tp_version_tag && entry->name == name) {
|
||||
assert(type->tp_version_tag);
|
||||
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
|
||||
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
|
||||
Py_XINCREF(entry->value);
|
||||
if (version != NULL) {
|
||||
*version = entry->version;
|
||||
}
|
||||
return entry->value;
|
||||
*out = entry->value ? PyStackRef_FromPyObjectNew(entry->value) : PyStackRef_NULL;
|
||||
return entry->version;
|
||||
}
|
||||
#endif
|
||||
OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name));
|
||||
|
@ -5671,6 +5782,9 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
|
|||
// We need to atomically do the lookup and capture the version before
|
||||
// anyone else can modify our mro or mutate the type.
|
||||
|
||||
PyObject *res;
|
||||
int error;
|
||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||
int has_version = 0;
|
||||
unsigned int assigned_version = 0;
|
||||
BEGIN_TYPE_LOCK();
|
||||
|
@ -5694,11 +5808,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
|
|||
if (error == -1) {
|
||||
PyErr_Clear();
|
||||
}
|
||||
if (version != NULL) {
|
||||
// 0 is not a valid version
|
||||
*version = 0;
|
||||
}
|
||||
return NULL;
|
||||
*out = PyStackRef_NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (has_version) {
|
||||
|
@ -5709,11 +5820,8 @@ _PyType_LookupRefAndVersion(PyTypeObject *type, PyObject *name, unsigned int *ve
|
|||
Py_DECREF(old_value);
|
||||
#endif
|
||||
}
|
||||
if (version != NULL) {
|
||||
// 0 is not a valid version
|
||||
*version = has_version ? assigned_version : 0;
|
||||
}
|
||||
return res;
|
||||
*out = res ? PyStackRef_FromPyObjectSteal(res) : PyStackRef_NULL;
|
||||
return has_version ? assigned_version : 0;
|
||||
}
|
||||
|
||||
/* Internal API to look for a name through the MRO.
|
||||
|
@ -9802,32 +9910,23 @@ slot_sq_ass_item(PyObject *self, Py_ssize_t index, PyObject *value)
|
|||
static int
|
||||
slot_sq_contains(PyObject *self, PyObject *value)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyObject *func, *res;
|
||||
int result = -1, unbound;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__contains__), &unbound);
|
||||
if (func == Py_None) {
|
||||
Py_DECREF(func);
|
||||
int attr_is_none = 0;
|
||||
PyObject *res = maybe_call_special_one_arg(self, &_Py_ID(__contains__), value,
|
||||
&attr_is_none);
|
||||
if (attr_is_none) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not a container",
|
||||
Py_TYPE(self)->tp_name);
|
||||
"'%.200s' object is not a container",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
if (func != NULL) {
|
||||
PyObject *args[2] = {self, value};
|
||||
res = vectorcall_unbound(tstate, unbound, func, args, 2);
|
||||
Py_DECREF(func);
|
||||
if (res != NULL) {
|
||||
result = PyObject_IsTrue(res);
|
||||
Py_DECREF(res);
|
||||
}
|
||||
else if (res == NULL && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
else if (! PyErr_Occurred()) {
|
||||
/* Possible results: -1 and 1 */
|
||||
result = (int)_PySequence_IterSearch(self, value,
|
||||
PY_ITERSEARCH_CONTAINS);
|
||||
else if (res == NULL) {
|
||||
return (int)_PySequence_IterSearch(self, value, PY_ITERSEARCH_CONTAINS);
|
||||
}
|
||||
int result = PyObject_IsTrue(res);
|
||||
Py_DECREF(res);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -9891,31 +9990,29 @@ SLOT0(slot_nb_absolute, __abs__)
|
|||
static int
|
||||
slot_nb_bool(PyObject *self)
|
||||
{
|
||||
PyObject *func, *value;
|
||||
int result, unbound;
|
||||
int using_len = 0;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__bool__), &unbound);
|
||||
if (func == NULL) {
|
||||
if (PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__len__), &unbound);
|
||||
if (func == NULL) {
|
||||
if (PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
int attr_is_none = 0;
|
||||
PyObject *value = maybe_call_special_no_args(self, &_Py_ID(__bool__),
|
||||
&attr_is_none);
|
||||
if (attr_is_none) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' cannot be interpreted as a boolean",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
else if (value == NULL && !PyErr_Occurred()) {
|
||||
value = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__len__));
|
||||
if (value == NULL && !PyErr_Occurred()) {
|
||||
return 1;
|
||||
}
|
||||
using_len = 1;
|
||||
}
|
||||
|
||||
value = call_unbound_noarg(unbound, func, self);
|
||||
if (value == NULL) {
|
||||
goto error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result;
|
||||
if (using_len) {
|
||||
/* bool type enforced by slot_nb_len */
|
||||
result = PyObject_IsTrue(value);
|
||||
|
@ -9930,14 +10027,8 @@ slot_nb_bool(PyObject *self)
|
|||
Py_TYPE(value)->tp_name);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
Py_DECREF(value);
|
||||
Py_DECREF(func);
|
||||
return result;
|
||||
|
||||
error:
|
||||
Py_DECREF(func);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -9984,18 +10075,15 @@ SLOT1(slot_nb_inplace_true_divide, __itruediv__, PyObject *)
|
|||
static PyObject *
|
||||
slot_tp_repr(PyObject *self)
|
||||
{
|
||||
PyObject *func, *res;
|
||||
int unbound;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__repr__), &unbound);
|
||||
if (func != NULL) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
PyObject *res = _PyObject_MaybeCallSpecialNoArgs(self, &_Py_ID(__repr__));
|
||||
if (res != NULL) {
|
||||
return res;
|
||||
}
|
||||
PyErr_Clear();
|
||||
else if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
return PyUnicode_FromFormat("<%s object at %p>",
|
||||
Py_TYPE(self)->tp_name, self);
|
||||
Py_TYPE(self)->tp_name, self);
|
||||
}
|
||||
|
||||
SLOT0(slot_tp_str, __str__)
|
||||
|
@ -10003,25 +10091,15 @@ SLOT0(slot_tp_str, __str__)
|
|||
static Py_hash_t
|
||||
slot_tp_hash(PyObject *self)
|
||||
{
|
||||
PyObject *func, *res;
|
||||
Py_ssize_t h;
|
||||
int unbound;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__hash__), &unbound);
|
||||
|
||||
if (func == Py_None) {
|
||||
Py_SETREF(func, NULL);
|
||||
}
|
||||
|
||||
if (func == NULL) {
|
||||
PyObject *res;
|
||||
int attr_is_none = 0;
|
||||
res = maybe_call_special_no_args(self, &_Py_ID(__hash__), &attr_is_none);
|
||||
if (attr_is_none || res == NULL) {
|
||||
if (PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
return PyObject_HashNotImplemented(self);
|
||||
}
|
||||
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
if (res == NULL)
|
||||
return -1;
|
||||
|
||||
if (!PyLong_Check(res)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__hash__ method should return an integer");
|
||||
|
@ -10032,7 +10110,7 @@ slot_tp_hash(PyObject *self)
|
|||
Py_hash_t. Therefore our transformation must preserve values that
|
||||
already lie within this range, to ensure that if x.__hash__() returns
|
||||
hash(y) then hash(x) == hash(y). */
|
||||
h = PyLong_AsSsize_t(res);
|
||||
Py_ssize_t h = PyLong_AsSsize_t(res);
|
||||
if (h == -1 && PyErr_Occurred()) {
|
||||
/* res was not within the range of a Py_hash_t, so we're free to
|
||||
use any sufficiently bit-mixing transformation;
|
||||
|
@ -10050,24 +10128,7 @@ slot_tp_hash(PyObject *self)
|
|||
static PyObject *
|
||||
slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
int unbound;
|
||||
|
||||
PyObject *meth = lookup_method(self, &_Py_ID(__call__), &unbound);
|
||||
if (meth == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *res;
|
||||
if (unbound) {
|
||||
res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
|
||||
}
|
||||
else {
|
||||
res = _PyObject_Call(tstate, meth, args, kwds);
|
||||
}
|
||||
|
||||
Py_DECREF(meth);
|
||||
return res;
|
||||
return call_method(self, &_Py_ID(__call__), args, kwds);
|
||||
}
|
||||
|
||||
/* There are two slot dispatch functions for tp_getattro.
|
||||
|
@ -10194,51 +10255,46 @@ static PyObject *name_op[] = {
|
|||
static PyObject *
|
||||
slot_tp_richcompare(PyObject *self, PyObject *other, int op)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
|
||||
int unbound;
|
||||
PyObject *func = lookup_maybe_method(self, name_op[op], &unbound);
|
||||
if (func == NULL) {
|
||||
PyErr_Clear();
|
||||
PyObject *res = _PyObject_MaybeCallSpecialOneArg(self, name_op[op], other);
|
||||
if (res == NULL) {
|
||||
if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
PyObject *stack[2] = {self, other};
|
||||
PyObject *res = vectorcall_unbound(tstate, unbound, func, stack, 2);
|
||||
Py_DECREF(func);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
has_dunder_getitem(PyObject *self)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
_PyCStackRef c_ref;
|
||||
_PyThreadState_PushCStackRef(tstate, &c_ref);
|
||||
lookup_maybe_method(self, &_Py_ID(__getitem__), &c_ref.ref);
|
||||
int has_dunder_getitem = !PyStackRef_IsNull(c_ref.ref);
|
||||
_PyThreadState_PopCStackRef(tstate, &c_ref);
|
||||
return has_dunder_getitem;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
slot_tp_iter(PyObject *self)
|
||||
{
|
||||
int unbound;
|
||||
PyObject *func, *res;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__iter__), &unbound);
|
||||
if (func == Py_None) {
|
||||
Py_DECREF(func);
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not iterable",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (func != NULL) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
int attr_is_none = 0;
|
||||
PyObject *res = maybe_call_special_no_args(self, &_Py_ID(__iter__),
|
||||
&attr_is_none);
|
||||
if (res != NULL) {
|
||||
return res;
|
||||
}
|
||||
|
||||
PyErr_Clear();
|
||||
func = lookup_maybe_method(self, &_Py_ID(__getitem__), &unbound);
|
||||
if (func == NULL) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not iterable",
|
||||
Py_TYPE(self)->tp_name);
|
||||
else if (PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
else if (attr_is_none || !has_dunder_getitem(self)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%.200s' object is not iterable",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return NULL;
|
||||
}
|
||||
Py_DECREF(func);
|
||||
return PySeqIter_New(self);
|
||||
}
|
||||
|
||||
|
@ -10296,22 +10352,7 @@ slot_tp_descr_set(PyObject *self, PyObject *target, PyObject *value)
|
|||
static int
|
||||
slot_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
|
||||
int unbound;
|
||||
PyObject *meth = lookup_method(self, &_Py_ID(__init__), &unbound);
|
||||
if (meth == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *res;
|
||||
if (unbound) {
|
||||
res = _PyObject_Call_Prepend(tstate, meth, self, args, kwds);
|
||||
}
|
||||
else {
|
||||
res = _PyObject_Call(tstate, meth, args, kwds);
|
||||
}
|
||||
Py_DECREF(meth);
|
||||
PyObject *res = call_method(self, &_Py_ID(__init__), args, kwds);
|
||||
if (res == NULL)
|
||||
return -1;
|
||||
if (res != Py_None) {
|
||||
|
@ -10344,16 +10385,18 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|||
static void
|
||||
slot_tp_finalize(PyObject *self)
|
||||
{
|
||||
int unbound;
|
||||
PyObject *del, *res;
|
||||
|
||||
/* Save the current exception, if any. */
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyObject *exc = _PyErr_GetRaisedException(tstate);
|
||||
|
||||
_PyCStackRef cref;
|
||||
_PyThreadState_PushCStackRef(tstate, &cref);
|
||||
|
||||
/* Execute __del__ method, if any. */
|
||||
del = lookup_maybe_method(self, &_Py_ID(__del__), &unbound);
|
||||
if (del != NULL) {
|
||||
res = call_unbound_noarg(unbound, del, self);
|
||||
int unbound = lookup_maybe_method(self, &_Py_ID(__del__), &cref.ref);
|
||||
if (unbound >= 0) {
|
||||
PyObject *del = PyStackRef_AsPyObjectBorrow(cref.ref);
|
||||
PyObject *res = call_unbound_noarg(unbound, del, self);
|
||||
if (res == NULL) {
|
||||
PyErr_FormatUnraisable("Exception ignored while "
|
||||
"calling deallocator %R", del);
|
||||
|
@ -10361,11 +10404,12 @@ slot_tp_finalize(PyObject *self)
|
|||
else {
|
||||
Py_DECREF(res);
|
||||
}
|
||||
Py_DECREF(del);
|
||||
}
|
||||
|
||||
_PyThreadState_PopCStackRef(tstate, &cref);
|
||||
|
||||
/* Restore the saved exception. */
|
||||
PyErr_SetRaisedException(exc);
|
||||
_PyErr_SetRaisedException(tstate, exc);
|
||||
}
|
||||
|
||||
typedef struct _PyBufferWrapper {
|
||||
|
@ -10613,58 +10657,34 @@ slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
|
|||
releasebuffer_maybe_call_super(self, buffer);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
slot_am_generic(PyObject *self, PyObject *name)
|
||||
{
|
||||
PyObject *res = _PyObject_MaybeCallSpecialNoArgs(self, name);
|
||||
if (res == NULL && !PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"object %.50s does not have %U method",
|
||||
Py_TYPE(self)->tp_name, name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
slot_am_await(PyObject *self)
|
||||
{
|
||||
int unbound;
|
||||
PyObject *func, *res;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__await__), &unbound);
|
||||
if (func != NULL) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
return res;
|
||||
}
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"object %.50s does not have __await__ method",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return NULL;
|
||||
return slot_am_generic(self, &_Py_ID(__await__));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
slot_am_aiter(PyObject *self)
|
||||
{
|
||||
int unbound;
|
||||
PyObject *func, *res;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__aiter__), &unbound);
|
||||
if (func != NULL) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
return res;
|
||||
}
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"object %.50s does not have __aiter__ method",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return NULL;
|
||||
return slot_am_generic(self, &_Py_ID(__aiter__));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
slot_am_anext(PyObject *self)
|
||||
{
|
||||
int unbound;
|
||||
PyObject *func, *res;
|
||||
|
||||
func = lookup_maybe_method(self, &_Py_ID(__anext__), &unbound);
|
||||
if (func != NULL) {
|
||||
res = call_unbound_noarg(unbound, func, self);
|
||||
Py_DECREF(func);
|
||||
return res;
|
||||
}
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"object %.50s does not have __anext__ method",
|
||||
Py_TYPE(self)->tp_name);
|
||||
return NULL;
|
||||
return slot_am_generic(self, &_Py_ID(__anext__));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -2537,22 +2537,19 @@ static PyObject *
|
|||
builtin_round_impl(PyObject *module, PyObject *number, PyObject *ndigits)
|
||||
/*[clinic end generated code: output=ff0d9dd176c02ede input=275678471d7aca15]*/
|
||||
{
|
||||
PyObject *round, *result;
|
||||
|
||||
round = _PyObject_LookupSpecial(number, &_Py_ID(__round__));
|
||||
if (round == NULL) {
|
||||
if (!PyErr_Occurred())
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"type %.100s doesn't define __round__ method",
|
||||
Py_TYPE(number)->tp_name);
|
||||
return NULL;
|
||||
PyObject *result;
|
||||
if (ndigits == Py_None) {
|
||||
result = _PyObject_MaybeCallSpecialNoArgs(number, &_Py_ID(__round__));
|
||||
}
|
||||
else {
|
||||
result = _PyObject_MaybeCallSpecialOneArg(number, &_Py_ID(__round__),
|
||||
ndigits);
|
||||
}
|
||||
if (result == NULL && !PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"type %.100s doesn't define __round__ method",
|
||||
Py_TYPE(number)->tp_name);
|
||||
}
|
||||
|
||||
if (ndigits == Py_None)
|
||||
result = _PyObject_CallNoArgs(round);
|
||||
else
|
||||
result = PyObject_CallOneArg(round, ndigits);
|
||||
Py_DECREF(round);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -433,6 +433,12 @@ static void
|
|||
gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *state)
|
||||
{
|
||||
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
|
||||
_PyCStackRef *c_ref = ((_PyThreadStateImpl *)p)->c_stack_refs;
|
||||
while (c_ref != NULL) {
|
||||
gc_visit_stackref(c_ref->ref);
|
||||
c_ref = c_ref->next;
|
||||
}
|
||||
|
||||
for (_PyInterpreterFrame *f = p->current_frame; f != NULL; f = f->previous) {
|
||||
if (f->owner >= FRAME_OWNED_BY_INTERPRETER) {
|
||||
continue;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue