this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "capi-trampolines.h"
3
4#include "api-handle.h"
5#include "capi.h"
6#include "dict-builtins.h"
7#include "runtime.h"
8#include "thread.h"
9
10namespace py {
11
12static const word kMaxStackArguments = 6;
13
14// method no args
15
16static RawObject callMethNoArgs(Thread* thread, const Function& function,
17 const Object& self) {
18 HandleScope scope(thread);
19 Int address(&scope, function.code());
20 binaryfunc method = bit_cast<binaryfunc>(address.asCPtr());
21 PyObject* self_obj = self.isUnbound()
22 ? nullptr
23 : ApiHandle::newReference(thread->runtime(), *self);
24 PyObject* pyresult = (*method)(self_obj, nullptr);
25 Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult));
26 if (self_obj != nullptr) {
27 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
28 }
29 return *result;
30}
31
32static RawObject raiseTypeErrorMustBeBound(Thread* thread,
33 const Function& function) {
34 HandleScope scope(thread);
35 Str function_name(&scope, function.name());
36 return thread->raiseWithFmt(
37 LayoutId::kTypeError, "'%S' must be bound to an object", &function_name);
38}
39
40static RawObject raiseTypeErrorNoArguments(Thread* thread,
41 const Function& function,
42 word nargs) {
43 HandleScope scope(thread);
44 Str function_name(&scope, function.name());
45 if (nargs == 0) {
46 return raiseTypeErrorMustBeBound(thread, function);
47 }
48 return thread->raiseWithFmt(LayoutId::kTypeError,
49 "'%S' takes no arguments (%w given)",
50 &function_name, nargs - 1);
51}
52
53static RawObject raiseTypeErrorNoKeywordArguments(Thread* thread,
54 const Function& function) {
55 HandleScope scope(thread);
56 Str function_name(&scope, function.name());
57 return thread->raiseWithFmt(
58 LayoutId::kTypeError, "'%S' takes no keyword arguments", &function_name);
59}
60
61RawObject methodTrampolineNoArgs(Thread* thread, word nargs) {
62 HandleScope scope(thread);
63 Function function(&scope, thread->stackPeek(nargs));
64 if (nargs != 1) {
65 RawObject result = raiseTypeErrorNoArguments(thread, function, nargs);
66 thread->stackDrop(nargs + 1);
67 return result;
68 }
69 Object self(&scope, thread->stackPeek(0));
70 RawObject result = callMethNoArgs(thread, function, self);
71 thread->stackDrop(nargs + 1);
72 return result;
73}
74
75RawObject methodTrampolineNoArgsKw(Thread* thread, word nargs) {
76 HandleScope scope(thread);
77 Function function(&scope, thread->stackPeek(nargs + 1));
78 Tuple kw_names(&scope, thread->stackPeek(0));
79 if (kw_names.length() != 0) {
80 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
81 thread->stackDrop(nargs + 2);
82 return result;
83 }
84 if (nargs != 1) {
85 RawObject result = raiseTypeErrorNoArguments(thread, function, nargs);
86 thread->stackDrop(nargs + 2);
87 return result;
88 }
89 Object self(&scope, thread->stackPeek(1));
90 RawObject result = callMethNoArgs(thread, function, self);
91 thread->stackDrop(nargs + 2);
92 return result;
93}
94
95RawObject methodTrampolineNoArgsEx(Thread* thread, word flags) {
96 HandleScope scope(thread);
97 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
98 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
99 Tuple args(&scope, thread->stackPeek(has_varkeywords));
100 if (has_varkeywords) {
101 Object kw_args(&scope, thread->stackTop());
102 if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs");
103 if (Dict::cast(*kw_args).numItems() != 0) {
104 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
105 thread->stackDrop(has_varkeywords + 2);
106 return result;
107 }
108 }
109 word args_length = args.length();
110 if (args_length != 1) {
111 RawObject result = raiseTypeErrorNoArguments(thread, function, args_length);
112 thread->stackDrop(has_varkeywords + 2);
113 return result;
114 }
115 Object self(&scope, args.at(0));
116 RawObject result = callMethNoArgs(thread, function, self);
117 thread->stackDrop(has_varkeywords + 2);
118 return result;
119}
120
121// method one arg
122
123static RawObject raiseTypeErrorOneArgument(Thread* thread,
124 const Function& function,
125 word nargs) {
126 HandleScope scope(thread);
127 Str function_name(&scope, function.name());
128 if (nargs == 0) {
129 return raiseTypeErrorMustBeBound(thread, function);
130 }
131 return thread->raiseWithFmt(LayoutId::kTypeError,
132 "'%S' takes exactly one argument (%w given)",
133 &function_name, nargs - 1);
134}
135
136static RawObject callMethOneArg(Thread* thread, const Function& function,
137 const Object& self, const Object& arg) {
138 HandleScope scope(thread);
139 Int address(&scope, function.code());
140 binaryfunc method = bit_cast<binaryfunc>(address.asCPtr());
141 Runtime* runtime = thread->runtime();
142 PyObject* self_obj =
143 self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self);
144 PyObject* arg_obj = ApiHandle::newReference(runtime, *arg);
145 PyObject* pyresult = (*method)(self_obj, arg_obj);
146 Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult));
147 if (self_obj != nullptr) {
148 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
149 }
150 ApiHandle::decref(ApiHandle::fromPyObject(arg_obj));
151 return *result;
152}
153
154RawObject methodTrampolineOneArg(Thread* thread, word nargs) {
155 HandleScope scope(thread);
156 Function function(&scope, thread->stackPeek(nargs));
157 if (nargs != 2) {
158 RawObject result = raiseTypeErrorOneArgument(thread, function, nargs);
159 thread->stackDrop(nargs + 1);
160 return result;
161 }
162 Object self(&scope, thread->stackPeek(1));
163 Object arg(&scope, thread->stackPeek(0));
164 RawObject result = callMethOneArg(thread, function, self, arg);
165 thread->stackDrop(nargs + 1);
166 return result;
167}
168
169RawObject methodTrampolineOneArgKw(Thread* thread, word nargs) {
170 HandleScope scope(thread);
171 Function function(&scope, thread->stackPeek(nargs + 1));
172 Tuple kw_names(&scope, thread->stackPeek(0));
173 if (kw_names.length() != 0) {
174 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
175 thread->stackDrop(nargs + 2);
176 return result;
177 }
178 if (nargs != 2) {
179 RawObject result = raiseTypeErrorOneArgument(thread, function, nargs);
180 thread->stackDrop(nargs + 2);
181 return result;
182 }
183 Object self(&scope, thread->stackPeek(2));
184 Object arg(&scope, thread->stackPeek(1));
185 RawObject result = callMethOneArg(thread, function, self, arg);
186 thread->stackDrop(nargs + 2);
187 return result;
188}
189
190RawObject methodTrampolineOneArgEx(Thread* thread, word flags) {
191 HandleScope scope(thread);
192 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
193 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
194 if (has_varkeywords) {
195 Object kw_args(&scope, thread->stackTop());
196 if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs");
197 if (Dict::cast(*kw_args).numItems() != 0) {
198 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
199 thread->stackDrop(has_varkeywords + 2);
200 return result;
201 }
202 }
203 Tuple varargs(&scope, thread->stackPeek(has_varkeywords));
204 if (varargs.length() != 2) {
205 RawObject result =
206 raiseTypeErrorOneArgument(thread, function, varargs.length());
207 thread->stackDrop(has_varkeywords + 2);
208 return result;
209 }
210 Object self(&scope, varargs.at(0));
211 Object arg(&scope, varargs.at(1));
212 RawObject result = callMethOneArg(thread, function, self, arg);
213 thread->stackDrop(has_varkeywords + 2);
214 return result;
215}
216
217// callMethVarArgs
218
219static RawObject callMethVarArgs(Thread* thread, const Function& function,
220 const Object& self, const Object& varargs) {
221 HandleScope scope(thread);
222 Int address(&scope, function.code());
223 binaryfunc method = bit_cast<binaryfunc>(address.asCPtr());
224 Runtime* runtime = thread->runtime();
225 PyObject* self_obj =
226 self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self);
227 PyObject* varargs_obj = ApiHandle::newReference(runtime, *varargs);
228 PyObject* pyresult = (*method)(self_obj, varargs_obj);
229 Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult));
230 if (self_obj != nullptr) {
231 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
232 }
233 ApiHandle::decref(ApiHandle::fromPyObject(varargs_obj));
234 return *result;
235}
236
237RawObject methodTrampolineVarArgs(Thread* thread, word nargs) {
238 HandleScope scope(thread);
239 Function function(&scope, thread->stackPeek(nargs));
240 if (nargs == 0) {
241 RawObject result = raiseTypeErrorMustBeBound(thread, function);
242 thread->stackDrop(nargs + 1);
243 return result;
244 }
245 Object self(&scope, thread->stackPeek(nargs - 1));
246 Object varargs_obj(&scope, NoneType::object());
247 word num_varargs = nargs - 1;
248 if (num_varargs > 0) {
249 MutableTuple varargs(&scope,
250 thread->runtime()->newMutableTuple(num_varargs));
251 for (word i = 0; i < num_varargs; i++) {
252 varargs.atPut(num_varargs - i - 1, thread->stackPeek(i));
253 }
254 varargs_obj = varargs.becomeImmutable();
255 } else {
256 varargs_obj = thread->runtime()->emptyTuple();
257 }
258 RawObject result = callMethVarArgs(thread, function, self, varargs_obj);
259 thread->stackDrop(nargs + 1);
260 return result;
261}
262
263RawObject methodTrampolineVarArgsKw(Thread* thread, word nargs) {
264 HandleScope scope(thread);
265 Function function(&scope, thread->stackPeek(nargs + 1));
266 Tuple kw_names(&scope, thread->stackPeek(0));
267 if (kw_names.length() != 0) {
268 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
269 thread->stackDrop(nargs + 2);
270 return result;
271 }
272 if (nargs == 0) {
273 RawObject result = raiseTypeErrorMustBeBound(thread, function);
274 thread->stackDrop(nargs + 2);
275 return result;
276 }
277 Object self(&scope, thread->stackPeek(nargs));
278 Object varargs_obj(&scope, NoneType::object());
279 word num_varargs = nargs - 1;
280 if (num_varargs > 0) {
281 MutableTuple varargs(&scope,
282 thread->runtime()->newMutableTuple(num_varargs));
283 for (word i = 0; i < num_varargs; i++) {
284 varargs.atPut(i, thread->stackPeek(num_varargs - i));
285 }
286 varargs_obj = varargs.becomeImmutable();
287 } else {
288 varargs_obj = thread->runtime()->emptyTuple();
289 }
290 RawObject result = callMethVarArgs(thread, function, self, varargs_obj);
291 thread->stackDrop(nargs + 2);
292 return result;
293}
294
295RawObject methodTrampolineVarArgsEx(Thread* thread, word flags) {
296 HandleScope scope(thread);
297 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
298 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
299 if (has_varkeywords) {
300 Object kw_args(&scope, thread->stackTop());
301 if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs");
302 if (Dict::cast(*kw_args).numItems() != 0) {
303 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
304 thread->stackDrop(has_varkeywords + 2);
305 return result;
306 }
307 }
308 Tuple args(&scope, thread->stackPeek(has_varkeywords));
309 if (args.length() == 0) {
310 RawObject result = raiseTypeErrorMustBeBound(thread, function);
311 thread->stackDrop(has_varkeywords + 2);
312 return result;
313 }
314 Object self(&scope, args.at(0));
315 Object varargs(&scope, thread->runtime()->tupleSubseq(thread, args, 1,
316 args.length() - 1));
317 RawObject result = callMethVarArgs(thread, function, self, varargs);
318 thread->stackDrop(has_varkeywords + 2);
319 return result;
320}
321
322// callMethKeywordArgs
323
324static RawObject callMethKeywords(Thread* thread, const Function& function,
325 const Object& self, const Object& args,
326 const Object& kwargs) {
327 HandleScope scope(thread);
328 Int address(&scope, function.code());
329 ternaryfunc method = bit_cast<ternaryfunc>(address.asCPtr());
330 Runtime* runtime = thread->runtime();
331 PyObject* self_obj =
332 self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self);
333 PyObject* args_obj = ApiHandle::newReference(runtime, *args);
334 PyObject* kwargs_obj = nullptr;
335 if (*kwargs != NoneType::object()) {
336 kwargs_obj = ApiHandle::newReference(runtime, *kwargs);
337 }
338 PyObject* pyresult = (*method)(self_obj, args_obj, kwargs_obj);
339 Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult));
340 if (self_obj != nullptr) {
341 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
342 }
343 ApiHandle::decref(ApiHandle::fromPyObject(args_obj));
344 if (kwargs_obj != nullptr) {
345 ApiHandle::decref(ApiHandle::fromPyObject(kwargs_obj));
346 }
347 return *result;
348}
349
350RawObject methodTrampolineKeywords(Thread* thread, word nargs) {
351 HandleScope scope(thread);
352 Runtime* runtime = thread->runtime();
353 Function function(&scope, thread->stackPeek(nargs));
354 if (nargs == 0) {
355 RawObject result = raiseTypeErrorMustBeBound(thread, function);
356 thread->stackDrop(nargs + 1);
357 return result;
358 }
359 Object self(&scope, thread->stackPeek(nargs - 1));
360 Object varargs_obj(&scope, NoneType::object());
361 word num_varargs = nargs - 1;
362 if (num_varargs > 0) {
363 MutableTuple varargs(&scope, runtime->newMutableTuple(num_varargs));
364 for (word i = 0; i < num_varargs; i++) {
365 varargs.atPut(num_varargs - i - 1, thread->stackPeek(i));
366 }
367 varargs_obj = varargs.becomeImmutable();
368 } else {
369 varargs_obj = runtime->emptyTuple();
370 }
371 Object keywords(&scope, NoneType::object());
372 RawObject result =
373 callMethKeywords(thread, function, self, varargs_obj, keywords);
374 thread->stackDrop(nargs + 1);
375 return result;
376}
377
378RawObject methodTrampolineKeywordsKw(Thread* thread, word nargs) {
379 HandleScope scope(thread);
380 Runtime* runtime = thread->runtime();
381 Tuple kw_names(&scope, thread->stackPeek(0));
382 Object kwargs(&scope, NoneType::object());
383 word num_keywords = kw_names.length();
384 if (num_keywords != 0) {
385 Dict dict(&scope, runtime->newDict());
386 for (word i = 0; i < num_keywords; i++) {
387 Str name(&scope, kw_names.at(i));
388 Object value(&scope, thread->stackPeek(num_keywords - i));
389 dictAtPutByStr(thread, dict, name, value);
390 }
391 kwargs = *dict;
392 }
393 Function function(&scope, thread->stackPeek(nargs + 1));
394 if (nargs - num_keywords == 0) {
395 RawObject result = raiseTypeErrorMustBeBound(thread, function);
396 thread->stackDrop(nargs + 2);
397 return result;
398 }
399 word num_positional = nargs - num_keywords - 1;
400 Object args_obj(&scope, NoneType::object());
401 if (num_positional > 0) {
402 MutableTuple args(&scope, runtime->newMutableTuple(num_positional));
403 for (word i = 0; i < num_positional; i++) {
404 args.atPut(i, thread->stackPeek(nargs - i - 1));
405 }
406 args_obj = args.becomeImmutable();
407 } else {
408 args_obj = runtime->emptyTuple();
409 }
410 Object self(&scope, thread->stackPeek(nargs));
411 RawObject result = callMethKeywords(thread, function, self, args_obj, kwargs);
412 thread->stackDrop(nargs + 2);
413 return result;
414}
415
416RawObject methodTrampolineKeywordsEx(Thread* thread, word flags) {
417 HandleScope scope(thread);
418 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
419 Tuple varargs(&scope, thread->stackPeek(has_varkeywords));
420 Object kwargs(&scope, NoneType::object());
421 if (has_varkeywords) {
422 kwargs = thread->stackTop();
423 if (!kwargs.isDict()) UNIMPLEMENTED("mapping kwargs");
424 }
425 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
426 if (varargs.length() == 0) {
427 RawObject result = raiseTypeErrorMustBeBound(thread, function);
428 thread->stackDrop(has_varkeywords + 2);
429 return result;
430 }
431 Object self(&scope, varargs.at(0));
432 Object args(&scope, thread->runtime()->tupleSubseq(thread, varargs, 1,
433 varargs.length() - 1));
434 RawObject result = callMethKeywords(thread, function, self, args, kwargs);
435 thread->stackDrop(has_varkeywords + 2);
436 return result;
437}
438
439static RawObject callMethFast(Thread* thread, const Function& function,
440 const Object& self, PyObject* const* args,
441 word num_args) {
442 _PyCFunctionFast method =
443 bit_cast<_PyCFunctionFast>(Int::cast(function.code()).asCPtr());
444 PyObject* self_obj = self.isUnbound()
445 ? nullptr
446 : ApiHandle::newReference(thread->runtime(), *self);
447 PyObject* pyresult = (*method)(self_obj, args, num_args);
448 RawObject result = ApiHandle::checkFunctionResult(thread, pyresult);
449 if (self_obj != nullptr) {
450 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
451 }
452 return result;
453}
454
455RawObject methodTrampolineFast(Thread* thread, word nargs) {
456 HandleScope scope(thread);
457 Function function(&scope, thread->stackPeek(nargs));
458 if (nargs == 0) {
459 RawObject result = raiseTypeErrorMustBeBound(thread, function);
460 thread->stackDrop(nargs + 1);
461 return result;
462 }
463 Object self(&scope, thread->stackPeek(nargs - 1));
464 word num_positional = nargs - 1;
465
466 PyObject* small_array[kMaxStackArguments] = {};
467 PyObject** args;
468 if (num_positional <= kMaxStackArguments) {
469 args = small_array;
470 } else {
471 args = reinterpret_cast<PyObject**>(
472 std::malloc(num_positional * sizeof(args[0])));
473 }
474 Runtime* runtime = thread->runtime();
475 for (word i = 0; i < num_positional; i++) {
476 args[nargs - i - 2] =
477 ApiHandle::newReference(runtime, thread->stackPeek(i));
478 }
479 Object result(&scope,
480 callMethFast(thread, function, self, args, num_positional));
481 for (word i = 0; i < num_positional; i++) {
482 ApiHandle::decref(ApiHandle::fromPyObject(args[nargs - i - 2]));
483 }
484 thread->stackDrop(nargs + 1);
485 if (args != small_array) {
486 std::free(args);
487 }
488 return *result;
489}
490
491RawObject methodTrampolineFastKw(Thread* thread, word nargs) {
492 HandleScope scope(thread);
493 Function function(&scope, thread->stackPeek(nargs + 1));
494 if (nargs == 0) {
495 RawObject result = raiseTypeErrorMustBeBound(thread, function);
496 thread->stackDrop(nargs + 2);
497 return result;
498 }
499 Tuple kw_names(&scope, thread->stackPeek(0));
500 if (kw_names.length() != 0) {
501 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
502 thread->stackDrop(nargs + 2);
503 return result;
504 }
505 Object self(&scope, thread->stackPeek(nargs));
506 word num_positional = nargs - 1;
507
508 PyObject* small_array[kMaxStackArguments] = {};
509 PyObject** args;
510 if (num_positional <= kMaxStackArguments) {
511 args = small_array;
512 } else {
513 args = reinterpret_cast<PyObject**>(
514 std::malloc(num_positional * sizeof(args[0])));
515 }
516 Runtime* runtime = thread->runtime();
517 for (word i = 0; i < num_positional; i++) {
518 args[i] =
519 ApiHandle::newReference(runtime, thread->stackPeek(nargs - i - 1));
520 }
521 Object result(&scope, callMethFast(thread, function, self, args, nargs - 1));
522 for (word i = 0; i < num_positional; i++) {
523 ApiHandle::decref(ApiHandle::fromPyObject(args[i]));
524 }
525 thread->stackDrop(nargs + 2);
526 if (args != small_array) {
527 std::free(args);
528 }
529 return *result;
530}
531
532RawObject methodTrampolineFastEx(Thread* thread, word flags) {
533 HandleScope scope(thread);
534 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
535 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
536
537 // Get the keyword arguments
538 if (has_varkeywords) {
539 Object kw_args_obj(&scope, thread->stackTop());
540 if (!kw_args_obj.isDict()) UNIMPLEMENTED("mapping kwargs");
541 Dict kw_args(&scope, *kw_args_obj);
542 if (kw_args.numItems() != 0) {
543 RawObject result = raiseTypeErrorNoKeywordArguments(thread, function);
544 thread->stackDrop(has_varkeywords + 2);
545 return result;
546 }
547 }
548
549 Tuple args_tuple(&scope, thread->stackPeek(has_varkeywords));
550 word args_length = args_tuple.length();
551 if (args_length == 0) {
552 RawObject result = raiseTypeErrorMustBeBound(thread, function);
553 thread->stackDrop(has_varkeywords + 2);
554 return result;
555 }
556 Object self(&scope, args_tuple.at(0));
557 word num_positional = args_length - 1;
558
559 PyObject* small_array[kMaxStackArguments] = {};
560 PyObject** args;
561 if (num_positional <= kMaxStackArguments) {
562 args = small_array;
563 } else {
564 args = reinterpret_cast<PyObject**>(
565 std::malloc(num_positional * sizeof(args[0])));
566 }
567
568 // Set the positional arguments
569 Runtime* runtime = thread->runtime();
570 for (word i = 0; i < num_positional; i++) {
571 args[i] = ApiHandle::newReference(runtime, args_tuple.at(i + 1));
572 }
573
574 Object result(&scope,
575 callMethFast(thread, function, self, args, num_positional));
576 for (word i = 0; i < num_positional; i++) {
577 ApiHandle::decref(ApiHandle::fromPyObject(args[i]));
578 }
579 thread->stackDrop(has_varkeywords + 2);
580 if (args != small_array) {
581 std::free(args);
582 }
583 return *result;
584}
585
586static RawObject callMethFastWithKeywordsWithKwargs(
587 Thread* thread, const Function& function, const Object& self,
588 PyObject* const* args, word num_args, const Object& kw_names) {
589 _PyCFunctionFastWithKeywords method = bit_cast<_PyCFunctionFastWithKeywords>(
590 Int::cast(function.code()).asCPtr());
591 Runtime* runtime = thread->runtime();
592 PyObject* self_obj =
593 self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self);
594 ApiHandle* kw_names_obj = ApiHandle::newReference(runtime, *kw_names);
595 PyObject* pyresult = (*method)(self_obj, args, num_args, kw_names_obj);
596 RawObject result = ApiHandle::checkFunctionResult(thread, pyresult);
597 ApiHandle::decref(kw_names_obj);
598 if (self_obj != nullptr) {
599 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
600 }
601 return result;
602}
603
604static RawObject callMethFastWithKeywords(Thread* thread,
605 const Function& function,
606 const Object& self,
607 PyObject* const* args,
608 word num_args) {
609 _PyCFunctionFastWithKeywords method = bit_cast<_PyCFunctionFastWithKeywords>(
610 Int::cast(function.code()).asCPtr());
611 PyObject* self_obj = self.isUnbound()
612 ? nullptr
613 : ApiHandle::newReference(thread->runtime(), *self);
614 PyObject* pyresult = (*method)(self_obj, args, num_args, nullptr);
615 RawObject result = ApiHandle::checkFunctionResult(thread, pyresult);
616 if (self_obj != nullptr) {
617 ApiHandle::decref(ApiHandle::fromPyObject(self_obj));
618 }
619 return result;
620}
621
622RawObject methodTrampolineFastWithKeywords(Thread* thread, word nargs) {
623 HandleScope scope(thread);
624 Function function(&scope, thread->stackPeek(nargs));
625 if (nargs == 0) {
626 RawObject result = raiseTypeErrorMustBeBound(thread, function);
627 thread->stackDrop(nargs + 1);
628 return result;
629 }
630 Object self(&scope, thread->stackPeek(nargs - 1));
631
632 std::unique_ptr<PyObject*[]> fastcall_args(new PyObject*[nargs - 1]);
633 Runtime* runtime = thread->runtime();
634 for (word i = 0; i < nargs - 1; i++) {
635 fastcall_args[nargs - i - 2] =
636 ApiHandle::newReference(runtime, thread->stackPeek(i));
637 }
638 word num_positional = nargs - 1;
639 Object result(&scope,
640 callMethFastWithKeywords(thread, function, self,
641 fastcall_args.get(), num_positional));
642 for (word i = 0; i < nargs - 1; i++) {
643 ApiHandle::decref(ApiHandle::fromPyObject(fastcall_args[nargs - i - 2]));
644 }
645 thread->stackDrop(nargs + 1);
646 return *result;
647}
648
649RawObject methodTrampolineFastWithKeywordsKw(Thread* thread, word nargs) {
650 HandleScope scope(thread);
651 Function function(&scope, thread->stackPeek(nargs + 1));
652 if (nargs == 0) {
653 RawObject result = raiseTypeErrorMustBeBound(thread, function);
654 thread->stackDrop(nargs + 2);
655 return result;
656 }
657 Object self(&scope, thread->stackPeek(nargs));
658
659 std::unique_ptr<PyObject*[]> fastcall_args(new PyObject*[nargs - 1]);
660 Runtime* runtime = thread->runtime();
661 for (word i = 0; i < nargs - 1; i++) {
662 fastcall_args[i] =
663 ApiHandle::newReference(runtime, thread->stackPeek(nargs - i - 1));
664 }
665 Tuple kw_names(&scope, thread->stackPeek(0));
666 word num_positional = nargs - kw_names.length() - 1;
667 Object result(&scope, callMethFastWithKeywordsWithKwargs(
668 thread, function, self, fastcall_args.get(),
669 num_positional, kw_names));
670 for (word i = 0; i < nargs - 1; i++) {
671 ApiHandle::decref(ApiHandle::fromPyObject(fastcall_args[i]));
672 }
673 thread->stackDrop(nargs + 2);
674 return *result;
675}
676
677RawObject methodTrampolineFastWithKeywordsEx(Thread* thread, word flags) {
678 HandleScope scope(thread);
679 Runtime* runtime = thread->runtime();
680 bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS;
681 word num_keywords = 0;
682
683 // Get the keyword arguments
684 if (has_varkeywords) {
685 Object kw_args_obj(&scope, thread->stackTop());
686 if (!kw_args_obj.isDict()) UNIMPLEMENTED("mapping kwargs");
687 Dict kw_args(&scope, *kw_args_obj);
688 num_keywords = kw_args.numItems();
689 }
690
691 Function function(&scope, thread->stackPeek(has_varkeywords + 1));
692 Tuple args(&scope, thread->stackPeek(has_varkeywords));
693 word args_length = args.length();
694 if (args_length == 0) {
695 RawObject result = raiseTypeErrorMustBeBound(thread, function);
696 thread->stackDrop(has_varkeywords + 2);
697 return result;
698 }
699 Object self(&scope, args.at(0));
700 word num_positional = args_length - 1;
701 std::unique_ptr<PyObject*[]> fastcall_args(
702 new PyObject*[num_positional + num_keywords]);
703
704 // Set the positional arguments
705 for (word i = 0; i < num_positional; i++) {
706 fastcall_args[i] = ApiHandle::newReference(runtime, args.at(i + 1));
707 }
708
709 // Set the keyword arguments
710 Tuple kw_names(&scope, runtime->emptyTuple());
711 if (has_varkeywords) {
712 Dict kw_args(&scope, thread->stackTop());
713
714 if (num_keywords > 0) {
715 Object key(&scope, NoneType::object());
716 Object value(&scope, NoneType::object());
717 kw_names = runtime->newMutableTuple(num_keywords);
718 for (word dict_i = 0, arg_i = 0;
719 dictNextItem(kw_args, &dict_i, &key, &value); arg_i++) {
720 MutableTuple::cast(*kw_names).atPut(arg_i, *key);
721 fastcall_args[num_positional + arg_i] =
722 ApiHandle::newReference(runtime, *value);
723 }
724 MutableTuple::cast(*kw_names).becomeImmutable();
725 }
726 }
727
728 Object result(&scope, NoneType::object());
729 if (!has_varkeywords) {
730 result = callMethFastWithKeywords(thread, function, self,
731 fastcall_args.get(), num_positional);
732 } else {
733 result = callMethFastWithKeywordsWithKwargs(
734 thread, function, self, fastcall_args.get(), num_positional, kw_names);
735 }
736 for (word i = 0; i < num_positional + num_keywords; i++) {
737 ApiHandle::decref(ApiHandle::fromPyObject(fastcall_args[i]));
738 }
739 thread->stackDrop(has_varkeywords + 2);
740 return *result;
741}
742
743} // namespace py