@@ -18,6 +18,7 @@ typedef struct {
18
18
PyObject * kw ;
19
19
PyObject * dict ;
20
20
PyObject * weakreflist ; /* List of weak references */
21
+ int use_fastcall ;
21
22
} partialobject ;
22
23
23
24
static PyTypeObject partial_type ;
@@ -110,6 +111,8 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
110
111
return NULL ;
111
112
}
112
113
114
+ pto -> use_fastcall = _PyObject_HasFastCall (func );
115
+
113
116
return (PyObject * )pto ;
114
117
}
115
118
@@ -127,69 +130,110 @@ partial_dealloc(partialobject *pto)
127
130
}
128
131
129
132
static PyObject *
130
- partial_call (partialobject * pto , PyObject * args , PyObject * kw )
133
+ partial_fastcall (partialobject * pto , PyObject * * args , Py_ssize_t nargs ,
134
+ PyObject * kwargs )
131
135
{
136
+ PyObject * small_stack [_PY_FASTCALL_SMALL_STACK ];
132
137
PyObject * ret ;
133
- PyObject * argappl , * kwappl ;
134
- PyObject * * stack ;
135
- Py_ssize_t nargs ;
138
+ PyObject * * stack , * * stack_buf = NULL ;
139
+ Py_ssize_t nargs2 , pto_nargs ;
136
140
137
- assert (PyCallable_Check (pto -> fn ));
138
- assert (PyTuple_Check (pto -> args ));
139
- assert (PyDict_Check (pto -> kw ));
141
+ pto_nargs = PyTuple_GET_SIZE (pto -> args );
142
+ nargs2 = pto_nargs + nargs ;
140
143
141
- if (PyTuple_GET_SIZE (pto -> args ) == 0 ) {
142
- stack = & PyTuple_GET_ITEM (args , 0 );
143
- nargs = PyTuple_GET_SIZE (args );
144
- argappl = NULL ;
144
+ if (pto_nargs == 0 ) {
145
+ stack = args ;
145
146
}
146
- else if (PyTuple_GET_SIZE ( args ) == 0 ) {
147
+ else if (nargs == 0 ) {
147
148
stack = & PyTuple_GET_ITEM (pto -> args , 0 );
148
- nargs = PyTuple_GET_SIZE (pto -> args );
149
- argappl = NULL ;
150
149
}
151
150
else {
152
- stack = NULL ;
153
- argappl = PySequence_Concat (pto -> args , args );
154
- if (argappl == NULL ) {
155
- return NULL ;
151
+ if (nargs2 <= (Py_ssize_t )Py_ARRAY_LENGTH (small_stack )) {
152
+ stack = small_stack ;
153
+ }
154
+ else {
155
+ stack_buf = PyMem_Malloc (nargs2 * sizeof (PyObject * ));
156
+ if (stack_buf == NULL ) {
157
+ PyErr_NoMemory ();
158
+ return NULL ;
159
+ }
160
+ stack = stack_buf ;
156
161
}
157
162
158
- assert (PyTuple_Check (argappl ));
163
+ /* use borrowed references */
164
+ memcpy (stack ,
165
+ & PyTuple_GET_ITEM (pto -> args , 0 ),
166
+ pto_nargs * sizeof (PyObject * ));
167
+ memcpy (& stack [pto_nargs ],
168
+ args ,
169
+ nargs * sizeof (PyObject * ));
170
+ }
171
+
172
+ ret = _PyObject_FastCallDict (pto -> fn , stack , nargs2 , kwargs );
173
+ PyMem_Free (stack_buf );
174
+ return ret ;
175
+ }
176
+
177
+ static PyObject *
178
+ partial_call_impl (partialobject * pto , PyObject * args , PyObject * kwargs )
179
+ {
180
+ PyObject * ret , * args2 ;
181
+
182
+ /* Note: tupleconcat() is optimized for empty tuples */
183
+ args2 = PySequence_Concat (pto -> args , args );
184
+ if (args2 == NULL ) {
185
+ return NULL ;
159
186
}
187
+ assert (PyTuple_Check (args2 ));
188
+
189
+ ret = PyObject_Call (pto -> fn , args2 , kwargs );
190
+ Py_DECREF (args2 );
191
+ return ret ;
192
+ }
193
+
194
+ static PyObject *
195
+ partial_call (partialobject * pto , PyObject * args , PyObject * kwargs )
196
+ {
197
+ PyObject * kwargs2 , * res ;
198
+
199
+ assert (PyCallable_Check (pto -> fn ));
200
+ assert (PyTuple_Check (pto -> args ));
201
+ assert (PyDict_Check (pto -> kw ));
160
202
161
203
if (PyDict_GET_SIZE (pto -> kw ) == 0 ) {
162
- kwappl = kw ;
163
- Py_XINCREF (kwappl );
204
+ /* kwargs can be NULL */
205
+ kwargs2 = kwargs ;
206
+ Py_XINCREF (kwargs2 );
164
207
}
165
208
else {
166
209
/* bpo-27840, bpo-29318: dictionary of keyword parameters must be
167
210
copied, because a function using "**kwargs" can modify the
168
211
dictionary. */
169
- kwappl = PyDict_Copy (pto -> kw );
170
- if (kwappl == NULL ) {
171
- Py_XDECREF (argappl );
212
+ kwargs2 = PyDict_Copy (pto -> kw );
213
+ if (kwargs2 == NULL ) {
172
214
return NULL ;
173
215
}
174
216
175
- if (kw != NULL ) {
176
- if (PyDict_Merge (kwappl , kw , 1 ) != 0 ) {
177
- Py_XDECREF (argappl );
178
- Py_DECREF (kwappl );
217
+ if (kwargs != NULL ) {
218
+ if (PyDict_Merge (kwargs2 , kwargs , 1 ) != 0 ) {
219
+ Py_DECREF (kwargs2 );
179
220
return NULL ;
180
221
}
181
222
}
182
223
}
183
224
184
- if (stack ) {
185
- ret = _PyObject_FastCallDict (pto -> fn , stack , nargs , kwappl );
225
+
226
+ if (pto -> use_fastcall ) {
227
+ res = partial_fastcall (pto ,
228
+ & PyTuple_GET_ITEM (args , 0 ),
229
+ PyTuple_GET_SIZE (args ),
230
+ kwargs2 );
186
231
}
187
232
else {
188
- ret = PyObject_Call (pto -> fn , argappl , kwappl );
189
- Py_DECREF (argappl );
233
+ res = partial_call_impl (pto , args , kwargs2 );
190
234
}
191
- Py_XDECREF (kwappl );
192
- return ret ;
235
+ Py_XDECREF (kwargs2 );
236
+ return res ;
193
237
}
194
238
195
239
static int
@@ -315,12 +359,13 @@ partial_setstate(partialobject *pto, PyObject *state)
315
359
return NULL ;
316
360
}
317
361
318
- Py_INCREF (fn );
319
362
if (dict == Py_None )
320
363
dict = NULL ;
321
364
else
322
365
Py_INCREF (dict );
323
366
367
+ Py_INCREF (fn );
368
+ pto -> use_fastcall = _PyObject_HasFastCall (fn );
324
369
Py_SETREF (pto -> fn , fn );
325
370
Py_SETREF (pto -> args , fnargs );
326
371
Py_SETREF (pto -> kw , kw );
0 commit comments