diff options
author | AlexSm <alex@ydb.tech> | 2024-03-05 10:40:59 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-05 12:40:59 +0300 |
commit | 1ac13c847b5358faba44dbb638a828e24369467b (patch) | |
tree | 07672b4dd3604ad3dee540a02c6494cb7d10dc3d /contrib/tools/python3/Objects/methodobject.c | |
parent | ffcca3e7f7958ddc6487b91d3df8c01054bd0638 (diff) | |
download | ydb-1ac13c847b5358faba44dbb638a828e24369467b.tar.gz |
Library import 16 (#2433)
Co-authored-by: robot-piglet <robot-piglet@yandex-team.com>
Co-authored-by: deshevoy <deshevoy@yandex-team.com>
Co-authored-by: robot-contrib <robot-contrib@yandex-team.com>
Co-authored-by: thegeorg <thegeorg@yandex-team.com>
Co-authored-by: robot-ya-builder <robot-ya-builder@yandex-team.com>
Co-authored-by: svidyuk <svidyuk@yandex-team.com>
Co-authored-by: shadchin <shadchin@yandex-team.com>
Co-authored-by: robot-ratatosk <robot-ratatosk@yandex-team.com>
Co-authored-by: innokentii <innokentii@yandex-team.com>
Co-authored-by: arkady-e1ppa <arkady-e1ppa@yandex-team.com>
Co-authored-by: snermolaev <snermolaev@yandex-team.com>
Co-authored-by: dimdim11 <dimdim11@yandex-team.com>
Co-authored-by: kickbutt <kickbutt@yandex-team.com>
Co-authored-by: abdullinsaid <abdullinsaid@yandex-team.com>
Co-authored-by: korsunandrei <korsunandrei@yandex-team.com>
Co-authored-by: petrk <petrk@yandex-team.com>
Co-authored-by: miroslav2 <miroslav2@yandex-team.com>
Co-authored-by: serjflint <serjflint@yandex-team.com>
Co-authored-by: akhropov <akhropov@yandex-team.com>
Co-authored-by: prettyboy <prettyboy@yandex-team.com>
Co-authored-by: ilikepugs <ilikepugs@yandex-team.com>
Co-authored-by: hiddenpath <hiddenpath@yandex-team.com>
Co-authored-by: mikhnenko <mikhnenko@yandex-team.com>
Co-authored-by: spreis <spreis@yandex-team.com>
Co-authored-by: andreyshspb <andreyshspb@yandex-team.com>
Co-authored-by: dimaandreev <dimaandreev@yandex-team.com>
Co-authored-by: rashid <rashid@yandex-team.com>
Co-authored-by: robot-ydb-importer <robot-ydb-importer@yandex-team.com>
Co-authored-by: r-vetrov <r-vetrov@yandex-team.com>
Co-authored-by: ypodlesov <ypodlesov@yandex-team.com>
Co-authored-by: zaverden <zaverden@yandex-team.com>
Co-authored-by: vpozdyayev <vpozdyayev@yandex-team.com>
Co-authored-by: robot-cozmo <robot-cozmo@yandex-team.com>
Co-authored-by: v-korovin <v-korovin@yandex-team.com>
Co-authored-by: arikon <arikon@yandex-team.com>
Co-authored-by: khoden <khoden@yandex-team.com>
Co-authored-by: psydmm <psydmm@yandex-team.com>
Co-authored-by: robot-javacom <robot-javacom@yandex-team.com>
Co-authored-by: dtorilov <dtorilov@yandex-team.com>
Co-authored-by: sennikovmv <sennikovmv@yandex-team.com>
Co-authored-by: hcpp <hcpp@ydb.tech>
Diffstat (limited to 'contrib/tools/python3/Objects/methodobject.c')
-rw-r--r-- | contrib/tools/python3/Objects/methodobject.c | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/contrib/tools/python3/Objects/methodobject.c b/contrib/tools/python3/Objects/methodobject.c new file mode 100644 index 0000000000..4473504516 --- /dev/null +++ b/contrib/tools/python3/Objects/methodobject.c @@ -0,0 +1,559 @@ + +/* Method object implementation */ + +#include "Python.h" +#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() +#include "pycore_object.h" +#include "pycore_pyerrors.h" +#include "pycore_pystate.h" // _PyThreadState_GET() +#include "structmember.h" // PyMemberDef + +/* undefine macro trampoline to PyCFunction_NewEx */ +#undef PyCFunction_New +/* undefine macro trampoline to PyCMethod_New */ +#undef PyCFunction_NewEx + +/* Forward declarations */ +static PyObject * cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames); +static PyObject * cfunction_call( + PyObject *func, PyObject *args, PyObject *kwargs); + + +PyObject * +PyCFunction_New(PyMethodDef *ml, PyObject *self) +{ + return PyCFunction_NewEx(ml, self, NULL); +} + +PyObject * +PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) +{ + return PyCMethod_New(ml, self, module, NULL); +} + +PyObject * +PyCMethod_New(PyMethodDef *ml, PyObject *self, PyObject *module, PyTypeObject *cls) +{ + /* Figure out correct vectorcall function to use */ + vectorcallfunc vectorcall; + switch (ml->ml_flags & (METH_VARARGS | METH_FASTCALL | METH_NOARGS | + METH_O | METH_KEYWORDS | METH_METHOD)) + { + case METH_VARARGS: + case METH_VARARGS | METH_KEYWORDS: + /* For METH_VARARGS functions, it's more efficient to use tp_call + * instead of vectorcall. */ + vectorcall = NULL; + break; + case METH_FASTCALL: + vectorcall = cfunction_vectorcall_FASTCALL; + break; + case METH_FASTCALL | METH_KEYWORDS: + vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS; + break; + case METH_NOARGS: + vectorcall = cfunction_vectorcall_NOARGS; + break; + case METH_O: + vectorcall = cfunction_vectorcall_O; + break; + case METH_METHOD | METH_FASTCALL | METH_KEYWORDS: + vectorcall = cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD; + break; + default: + PyErr_Format(PyExc_SystemError, + "%s() method: bad call flags", ml->ml_name); + return NULL; + } + + PyCFunctionObject *op = NULL; + + if (ml->ml_flags & METH_METHOD) { + if (!cls) { + PyErr_SetString(PyExc_SystemError, + "attempting to create PyCMethod with a METH_METHOD " + "flag but no class"); + return NULL; + } + PyCMethodObject *om = PyObject_GC_New(PyCMethodObject, &PyCMethod_Type); + if (om == NULL) { + return NULL; + } + om->mm_class = (PyTypeObject*)Py_NewRef(cls); + op = (PyCFunctionObject *)om; + } else { + if (cls) { + PyErr_SetString(PyExc_SystemError, + "attempting to create PyCFunction with class " + "but no METH_METHOD flag"); + return NULL; + } + op = PyObject_GC_New(PyCFunctionObject, &PyCFunction_Type); + if (op == NULL) { + return NULL; + } + } + + op->m_weakreflist = NULL; + op->m_ml = ml; + op->m_self = Py_XNewRef(self); + op->m_module = Py_XNewRef(module); + op->vectorcall = vectorcall; + _PyObject_GC_TRACK(op); + return (PyObject *)op; +} + +PyCFunction +PyCFunction_GetFunction(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return PyCFunction_GET_FUNCTION(op); +} + +PyObject * +PyCFunction_GetSelf(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return PyCFunction_GET_SELF(op); +} + +int +PyCFunction_GetFlags(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return -1; + } + return PyCFunction_GET_FLAGS(op); +} + +PyTypeObject * +PyCMethod_GetClass(PyObject *op) +{ + if (!PyCFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return PyCFunction_GET_CLASS(op); +} + +/* Methods (the standard built-in methods, that is) */ + +static void +meth_dealloc(PyCFunctionObject *m) +{ + // The Py_TRASHCAN mechanism requires that we be able to + // call PyObject_GC_UnTrack twice on an object. + PyObject_GC_UnTrack(m); + Py_TRASHCAN_BEGIN(m, meth_dealloc); + if (m->m_weakreflist != NULL) { + PyObject_ClearWeakRefs((PyObject*) m); + } + // Dereference class before m_self: PyCFunction_GET_CLASS accesses + // PyMethodDef m_ml, which could be kept alive by m_self + Py_XDECREF(PyCFunction_GET_CLASS(m)); + Py_XDECREF(m->m_self); + Py_XDECREF(m->m_module); + PyObject_GC_Del(m); + Py_TRASHCAN_END; +} + +static PyObject * +meth_reduce(PyCFunctionObject *m, PyObject *Py_UNUSED(ignored)) +{ + if (m->m_self == NULL || PyModule_Check(m->m_self)) + return PyUnicode_FromString(m->m_ml->ml_name); + + return Py_BuildValue("N(Os)", _PyEval_GetBuiltin(&_Py_ID(getattr)), + m->m_self, m->m_ml->ml_name); +} + +static PyMethodDef meth_methods[] = { + {"__reduce__", (PyCFunction)meth_reduce, METH_NOARGS, NULL}, + {NULL, NULL} +}; + +static PyObject * +meth_get__text_signature__(PyCFunctionObject *m, void *closure) +{ + return _PyType_GetTextSignatureFromInternalDoc(m->m_ml->ml_name, m->m_ml->ml_doc); +} + +static PyObject * +meth_get__doc__(PyCFunctionObject *m, void *closure) +{ + return _PyType_GetDocFromInternalDoc(m->m_ml->ml_name, m->m_ml->ml_doc); +} + +static PyObject * +meth_get__name__(PyCFunctionObject *m, void *closure) +{ + return PyUnicode_FromString(m->m_ml->ml_name); +} + +static PyObject * +meth_get__qualname__(PyCFunctionObject *m, void *closure) +{ + /* If __self__ is a module or NULL, return m.__name__ + (e.g. len.__qualname__ == 'len') + + If __self__ is a type, return m.__self__.__qualname__ + '.' + m.__name__ + (e.g. dict.fromkeys.__qualname__ == 'dict.fromkeys') + + Otherwise return type(m.__self__).__qualname__ + '.' + m.__name__ + (e.g. [].append.__qualname__ == 'list.append') */ + PyObject *type, *type_qualname, *res; + + if (m->m_self == NULL || PyModule_Check(m->m_self)) + return PyUnicode_FromString(m->m_ml->ml_name); + + type = PyType_Check(m->m_self) ? m->m_self : (PyObject*)Py_TYPE(m->m_self); + + type_qualname = PyObject_GetAttr(type, &_Py_ID(__qualname__)); + if (type_qualname == NULL) + return NULL; + + if (!PyUnicode_Check(type_qualname)) { + PyErr_SetString(PyExc_TypeError, "<method>.__class__." + "__qualname__ is not a unicode object"); + Py_XDECREF(type_qualname); + return NULL; + } + + res = PyUnicode_FromFormat("%S.%s", type_qualname, m->m_ml->ml_name); + Py_DECREF(type_qualname); + return res; +} + +static int +meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg) +{ + Py_VISIT(PyCFunction_GET_CLASS(m)); + Py_VISIT(m->m_self); + Py_VISIT(m->m_module); + return 0; +} + +static PyObject * +meth_get__self__(PyCFunctionObject *m, void *closure) +{ + PyObject *self; + + self = PyCFunction_GET_SELF(m); + if (self == NULL) + self = Py_None; + return Py_NewRef(self); +} + +static PyGetSetDef meth_getsets [] = { + {"__doc__", (getter)meth_get__doc__, NULL, NULL}, + {"__name__", (getter)meth_get__name__, NULL, NULL}, + {"__qualname__", (getter)meth_get__qualname__, NULL, NULL}, + {"__self__", (getter)meth_get__self__, NULL, NULL}, + {"__text_signature__", (getter)meth_get__text_signature__, NULL, NULL}, + {0} +}; + +#define OFF(x) offsetof(PyCFunctionObject, x) + +static PyMemberDef meth_members[] = { + {"__module__", T_OBJECT, OFF(m_module), 0}, + {NULL} +}; + +static PyObject * +meth_repr(PyCFunctionObject *m) +{ + if (m->m_self == NULL || PyModule_Check(m->m_self)) + return PyUnicode_FromFormat("<built-in function %s>", + m->m_ml->ml_name); + return PyUnicode_FromFormat("<built-in method %s of %s object at %p>", + m->m_ml->ml_name, + Py_TYPE(m->m_self)->tp_name, + m->m_self); +} + +static PyObject * +meth_richcompare(PyObject *self, PyObject *other, int op) +{ + PyCFunctionObject *a, *b; + PyObject *res; + int eq; + + if ((op != Py_EQ && op != Py_NE) || + !PyCFunction_Check(self) || + !PyCFunction_Check(other)) + { + Py_RETURN_NOTIMPLEMENTED; + } + a = (PyCFunctionObject *)self; + b = (PyCFunctionObject *)other; + eq = a->m_self == b->m_self; + if (eq) + eq = a->m_ml->ml_meth == b->m_ml->ml_meth; + if (op == Py_EQ) + res = eq ? Py_True : Py_False; + else + res = eq ? Py_False : Py_True; + return Py_NewRef(res); +} + +static Py_hash_t +meth_hash(PyCFunctionObject *a) +{ + Py_hash_t x, y; + x = _Py_HashPointer(a->m_self); + y = _Py_HashPointer((void*)(a->m_ml->ml_meth)); + x ^= y; + if (x == -1) + x = -2; + return x; +} + + +PyTypeObject PyCFunction_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + "builtin_function_or_method", + sizeof(PyCFunctionObject), + 0, + (destructor)meth_dealloc, /* tp_dealloc */ + offsetof(PyCFunctionObject, vectorcall), /* tp_vectorcall_offset */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + (reprfunc)meth_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)meth_hash, /* tp_hash */ + cfunction_call, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_VECTORCALL, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)meth_traverse, /* tp_traverse */ + 0, /* tp_clear */ + meth_richcompare, /* tp_richcompare */ + offsetof(PyCFunctionObject, m_weakreflist), /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + meth_methods, /* tp_methods */ + meth_members, /* tp_members */ + meth_getsets, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ +}; + +PyTypeObject PyCMethod_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "builtin_method", + .tp_basicsize = sizeof(PyCMethodObject), + .tp_base = &PyCFunction_Type, +}; + +/* Vectorcall functions for each of the PyCFunction calling conventions, + * except for METH_VARARGS (possibly combined with METH_KEYWORDS) which + * doesn't use vectorcall. + * + * First, common helpers + */ + +static inline int +cfunction_check_kwargs(PyThreadState *tstate, PyObject *func, PyObject *kwnames) +{ + assert(!_PyErr_Occurred(tstate)); + assert(PyCFunction_Check(func)); + if (kwnames && PyTuple_GET_SIZE(kwnames)) { + PyObject *funcstr = _PyObject_FunctionStr(func); + if (funcstr != NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "%U takes no keyword arguments", funcstr); + Py_DECREF(funcstr); + } + return -1; + } + return 0; +} + +typedef void (*funcptr)(void); + +static inline funcptr +cfunction_enter_call(PyThreadState *tstate, PyObject *func) +{ + if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { + return NULL; + } + return (funcptr)PyCFunction_GET_FUNCTION(func); +} + +/* Now the actual vectorcall functions */ +static PyObject * +cfunction_vectorcall_FASTCALL( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFast meth = (_PyCFunctionFast) + cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +} + +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = _PyThreadState_GET(); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords) + cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), args, nargs, kwnames); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +} + +static PyObject * +cfunction_vectorcall_FASTCALL_KEYWORDS_METHOD( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = _PyThreadState_GET(); + PyTypeObject *cls = PyCFunction_GET_CLASS(func); + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + PyCMethod meth = (PyCMethod)cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } + PyObject *result = meth(PyCFunction_GET_SELF(func), cls, args, nargs, kwnames); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +} + +static PyObject * +cfunction_vectorcall_NOARGS( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 0) { + PyObject *funcstr = _PyObject_FunctionStr(func); + if (funcstr != NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "%U takes no arguments (%zd given)", funcstr, nargs); + Py_DECREF(funcstr); + } + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } + PyObject *result = _PyCFunction_TrampolineCall( + meth, PyCFunction_GET_SELF(func), NULL); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +} + +static PyObject * +cfunction_vectorcall_O( + PyObject *func, PyObject *const *args, size_t nargsf, PyObject *kwnames) +{ + PyThreadState *tstate = _PyThreadState_GET(); + if (cfunction_check_kwargs(tstate, func, kwnames)) { + return NULL; + } + Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); + if (nargs != 1) { + PyObject *funcstr = _PyObject_FunctionStr(func); + if (funcstr != NULL) { + _PyErr_Format(tstate, PyExc_TypeError, + "%U takes exactly one argument (%zd given)", funcstr, nargs); + Py_DECREF(funcstr); + } + return NULL; + } + PyCFunction meth = (PyCFunction)cfunction_enter_call(tstate, func); + if (meth == NULL) { + return NULL; + } + PyObject *result = _PyCFunction_TrampolineCall( + meth, PyCFunction_GET_SELF(func), args[0]); + _Py_LeaveRecursiveCallTstate(tstate); + return result; +} + + +static PyObject * +cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs) +{ + assert(kwargs == NULL || PyDict_Check(kwargs)); + + PyThreadState *tstate = _PyThreadState_GET(); + assert(!_PyErr_Occurred(tstate)); + + int flags = PyCFunction_GET_FLAGS(func); + if (!(flags & METH_VARARGS)) { + /* If this is not a METH_VARARGS function, delegate to vectorcall */ + return PyVectorcall_Call(func, args, kwargs); + } + + /* For METH_VARARGS, we cannot use vectorcall as the vectorcall pointer + * is NULL. This is intentional, since vectorcall would be slower. */ + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + + PyObject *result; + if (flags & METH_KEYWORDS) { + result = _PyCFunctionWithKeywords_TrampolineCall( + (*(PyCFunctionWithKeywords)(void(*)(void))meth), + self, args, kwargs); + } + else { + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + _PyErr_Format(tstate, PyExc_TypeError, + "%.200s() takes no keyword arguments", + ((PyCFunctionObject*)func)->m_ml->ml_name); + return NULL; + } + result = _PyCFunction_TrampolineCall(meth, self, args); + } + return _Py_CheckFunctionResult(tstate, func, result, NULL); +} + +#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE) +#error #include <emscripten.h> + +EM_JS(PyObject*, _PyCFunctionWithKeywords_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *self, PyObject *args, PyObject *kw), { + return wasmTable.get(func)(self, args, kw); +}); +#endif |