bpo-36974: implement PEP 590 (GH-13185) · python/cpython@aacc77f (original) (raw)
`@@ -47,7 +47,7 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
`
47
47
`/* Suggested size (number of positional arguments) for arrays of PyObject*
`
48
48
` allocated on a C stack to avoid allocating memory on the heap memory. Such
`
49
49
` array is used to pass positional arguments to call functions of the
`
50
``
`-
_PyObject_FastCall() family.
`
``
50
`+
_PyObject_Vectorcall() family.
`
51
51
``
52
52
` The size is chosen to not abuse the C stack and so limit the risk of stack
`
53
53
` overflow. The size is also chosen to allow using the small stack for most
`
`@@ -56,50 +56,103 @@ PyAPI_FUNC(int) _PyStack_UnpackDict(
`
56
56
`#define _PY_FASTCALL_SMALL_STACK 5
`
57
57
``
58
58
`/* Return 1 if callable supports FASTCALL calling convention for positional
`
59
``
`-
arguments: see _PyObject_FastCallDict() and _PyObject_FastCallKeywords() */
`
``
59
`+
arguments: see _PyObject_Vectorcall() and _PyObject_FastCallDict() */
`
60
60
`PyAPI_FUNC(int) _PyObject_HasFastCall(PyObject *callable);
`
61
61
``
62
``
`-
/* Call the callable object 'callable' with the "fast call" calling convention:
`
63
``
`-
args is a C array for positional arguments (nargs is the number of
`
64
``
`-
positional arguments), kwargs is a dictionary for keyword arguments.
`
``
62
`+
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
`
``
63
`+
PyObject *result,
`
``
64
`+
const char *where);
`
65
65
``
66
``
`-
If nargs is equal to zero, args can be NULL. kwargs can be NULL.
`
67
``
`-
nargs must be greater or equal to zero.
`
``
66
`+
/* === Vectorcall protocol (PEP 590) ============================= */
`
68
67
``
69
``
`-
Return the result on success. Raise an exception and return NULL on
`
70
``
`-
error. */
`
71
``
`-
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
`
``
68
`+
/* Call callable using tp_call. Arguments are like _PyObject_Vectorcall()
`
``
69
`+
or _PyObject_FastCallDict() (both forms are supported),
`
``
70
`+
except that nargs is plainly the number of arguments without flags. */
`
``
71
`+
PyAPI_FUNC(PyObject *) _PyObject_MakeTpCall(
`
72
72
`PyObject *callable,
`
73
``
`-
PyObject *const *args,
`
74
``
`-
Py_ssize_t nargs,
`
75
``
`-
PyObject *kwargs);
`
``
73
`+
PyObject *const *args, Py_ssize_t nargs,
`
``
74
`+
PyObject *keywords);
`
76
75
``
77
``
`-
/* Call the callable object 'callable' with the "fast call" calling convention:
`
78
``
`-
args is a C array for positional arguments followed by values of
`
79
``
`-
keyword arguments. Keys of keyword arguments are stored as a tuple
`
80
``
`-
of strings in kwnames. nargs is the number of positional parameters at
`
81
``
`-
the beginning of stack. The size of kwnames gives the number of keyword
`
82
``
`-
values in the stack after positional arguments.
`
``
76
`+
#define PY_VECTORCALL_ARGUMENTS_OFFSET ((size_t)1 << (8 * sizeof(size_t) - 1))
`
83
77
``
84
``
`-
kwnames must only contains str strings, no subclass, and all keys must
`
85
``
`-
be unique.
`
``
78
`+
static inline Py_ssize_t
`
``
79
`+
PyVectorcall_NARGS(size_t n)
`
``
80
`+
{
`
``
81
`+
return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET;
`
``
82
`+
}
`
``
83
+
``
84
`+
static inline vectorcallfunc
`
``
85
`+
_PyVectorcall_Function(PyObject *callable)
`
``
86
`+
{
`
``
87
`+
PyTypeObject *tp = Py_TYPE(callable);
`
``
88
`+
if (!PyType_HasFeature(tp, _Py_TPFLAGS_HAVE_VECTORCALL)) {
`
``
89
`+
return NULL;
`
``
90
`+
}
`
``
91
`+
assert(PyCallable_Check(callable));
`
``
92
`+
Py_ssize_t offset = tp->tp_vectorcall_offset;
`
``
93
`+
assert(offset > 0);
`
``
94
`+
vectorcallfunc *ptr = (vectorcallfunc *)(((char *)callable) + offset);
`
``
95
`+
return *ptr;
`
``
96
`+
}
`
``
97
+
``
98
`+
/* Call the callable object 'callable' with the "vectorcall" calling
`
``
99
`+
convention.
`
``
100
+
``
101
`+
args is a C array for positional arguments.
`
``
102
+
``
103
`+
nargsf is the number of positional arguments plus optionally the flag
`
``
104
`+
PY_VECTORCALL_ARGUMENTS_OFFSET which means that the caller is allowed to
`
``
105
`+
modify args[-1].
`
86
106
``
87
``
`-
If nargs is equal to zero and there is no keyword argument (kwnames is
`
88
``
`-
NULL or its size is zero), args can be NULL.
`
``
107
`+
kwnames is a tuple of keyword names. The values of the keyword arguments
`
``
108
`+
are stored in "args" after the positional arguments (note that the number
`
``
109
`+
of keyword arguments does not change nargsf). kwnames can also be NULL if
`
``
110
`+
there are no keyword arguments.
`
``
111
+
``
112
`+
keywords must only contains str strings (no subclass), and all keys must
`
``
113
`+
be unique.
`
89
114
``
90
115
` Return the result on success. Raise an exception and return NULL on
`
91
116
` error. */
`
92
``
`-
PyAPI_FUNC(PyObject *) _PyObject_FastCallKeywords(
`
``
117
`+
static inline PyObject *
`
``
118
`+
_PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
`
``
119
`+
size_t nargsf, PyObject *kwnames)
`
``
120
`+
{
`
``
121
`+
assert(kwnames == NULL || PyTuple_Check(kwnames));
`
``
122
`+
assert(args != NULL || PyVectorcall_NARGS(nargsf) == 0);
`
``
123
`+
vectorcallfunc func = _PyVectorcall_Function(callable);
`
``
124
`+
if (func == NULL) {
`
``
125
`+
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
`
``
126
`+
return _PyObject_MakeTpCall(callable, args, nargs, kwnames);
`
``
127
`+
}
`
``
128
`+
PyObject *res = func(callable, args, nargsf, kwnames);
`
``
129
`+
return _Py_CheckFunctionResult(callable, res, NULL);
`
``
130
`+
}
`
``
131
+
``
132
`+
/* Same as _PyObject_Vectorcall except that keyword arguments are passed as
`
``
133
`+
dict, which may be NULL if there are no keyword arguments. */
`
``
134
`+
PyAPI_FUNC(PyObject *) _PyObject_FastCallDict(
`
93
135
`PyObject *callable,
`
94
136
`PyObject *const *args,
`
95
``
`-
Py_ssize_t nargs,
`
96
``
`-
PyObject *kwnames);
`
``
137
`+
size_t nargsf,
`
``
138
`+
PyObject *kwargs);
`
97
139
``
98
``
`-
#define _PyObject_FastCall(func, args, nargs) \
`
99
``
`-
_PyObject_FastCallDict((func), (args), (nargs), NULL)
`
``
140
`+
/* Call "callable" (which must support vectorcall) with positional arguments
`
``
141
`+
"tuple" and keyword arguments "dict". "dict" may also be NULL */
`
``
142
`+
PyAPI_FUNC(PyObject *) PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *dict);
`
100
143
``
101
``
`-
#define _PyObject_CallNoArg(func) \
`
102
``
`-
_PyObject_FastCallDict((func), NULL, 0, NULL)
`
``
144
`+
/* Same as _PyObject_Vectorcall except without keyword arguments */
`
``
145
`+
static inline PyObject *
`
``
146
`+
_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
`
``
147
`+
{
`
``
148
`+
return _PyObject_Vectorcall(func, args, (size_t)nargs, NULL);
`
``
149
`+
}
`
``
150
+
``
151
`+
/* Call a callable without any arguments */
`
``
152
`+
static inline PyObject *
`
``
153
`+
_PyObject_CallNoArg(PyObject *func) {
`
``
154
`+
return _PyObject_Vectorcall(func, NULL, 0, NULL);
`
``
155
`+
}
`
103
156
``
104
157
`PyAPI_FUNC(PyObject *) _PyObject_Call_Prepend(
`
105
158
`PyObject *callable,
`
`@@ -113,10 +166,6 @@ PyAPI_FUNC(PyObject *) _PyObject_FastCall_Prepend(
`
113
166
`PyObject *const *args,
`
114
167
`Py_ssize_t nargs);
`
115
168
``
116
``
`-
PyAPI_FUNC(PyObject *) _Py_CheckFunctionResult(PyObject *callable,
`
117
``
`-
PyObject *result,
`
118
``
`-
const char *where);
`
119
``
-
120
169
`/* Like PyObject_CallMethod(), but expect a _Py_Identifier*
`
121
170
` as the method name. */
`
122
171
`PyAPI_FUNC(PyObject *) _PyObject_CallMethodId(PyObject *obj,
`