this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include "trampolines.h"
3
4#include "dict-builtins.h"
5#include "frame.h"
6#include "globals.h"
7#include "handles.h"
8#include "interpreter.h"
9#include "objects.h"
10#include "runtime.h"
11#include "str-builtins.h"
12#include "thread.h"
13#include "tuple-builtins.h"
14
15namespace py {
16
17// Populate the free variable and cell variable arguments.
18void processFreevarsAndCellvars(Thread* thread, Frame* frame) {
19 // initialize cell variables
20 HandleScope scope(thread);
21 Function function(&scope, frame->function());
22 DCHECK(function.hasFreevarsOrCellvars(),
23 "no free variables or cell variables");
24 Code code(&scope, function.code());
25 Runtime* runtime = thread->runtime();
26 word num_locals = code.nlocals();
27 word num_cellvars = code.numCellvars();
28 for (word i = 0; i < code.numCellvars(); i++) {
29 Cell cell(&scope, runtime->newCell());
30
31 // Allocate a cell for a local variable if cell2arg is not preset
32 if (code.cell2arg().isNoneType()) {
33 frame->setLocal(num_locals + i, *cell);
34 continue;
35 }
36
37 // Allocate a cell for a local variable if cell2arg is present but
38 // the cell does not match any argument
39 Object arg_index(&scope, Tuple::cast(code.cell2arg()).at(i));
40 if (arg_index.isNoneType()) {
41 frame->setLocal(num_locals + i, *cell);
42 continue;
43 }
44
45 // Allocate a cell for an argument
46 word local_idx = Int::cast(*arg_index).asWord();
47 cell.setValue(frame->local(local_idx));
48 frame->setLocal(local_idx, NoneType::object());
49 frame->setLocal(num_locals + i, *cell);
50 }
51
52 // initialize free variables
53 DCHECK(code.numFreevars() == 0 ||
54 code.numFreevars() == Tuple::cast(function.closure()).length(),
55 "Number of freevars is different than the closure.");
56 for (word i = 0; i < code.numFreevars(); i++) {
57 frame->setLocal(num_locals + num_cellvars + i,
58 Tuple::cast(function.closure()).at(i));
59 }
60}
61
62RawObject raiseMissingArgumentsError(Thread* thread, word nargs,
63 RawFunction function) {
64 HandleScope scope(thread);
65 Function function_obj(&scope, function);
66 Object defaults(&scope, function_obj.defaults());
67 word n_defaults = defaults.isNoneType() ? 0 : Tuple::cast(*defaults).length();
68 return thread->raiseWithFmt(
69 LayoutId::kTypeError,
70 "'%F' takes min %w positional arguments but %w given", &function_obj,
71 function_obj.argcount() - n_defaults, nargs);
72}
73
74RawObject processDefaultArguments(Thread* thread, word nargs,
75 RawFunction function_raw) {
76 word argcount = function_raw.argcount();
77 word n_missing_args = argcount - nargs;
78 if (n_missing_args > 0) {
79 RawObject result =
80 addDefaultArguments(thread, nargs, function_raw, n_missing_args);
81 if (result.isErrorException()) return result;
82 function_raw = Function::cast(result);
83 nargs += n_missing_args;
84 if (function_raw.hasSimpleCall()) {
85 DCHECK(function_raw.totalArgs() == nargs, "argument count mismatch");
86 return function_raw;
87 }
88 }
89
90 HandleScope scope(thread);
91 Runtime* runtime = thread->runtime();
92 Function function(&scope, function_raw);
93 Object varargs_param(&scope, runtime->emptyTuple());
94 if (n_missing_args < 0) {
95 // We have too many arguments.
96 if (!function.hasVarargs()) {
97 thread->stackDrop(nargs + 1);
98 return thread->raiseWithFmt(
99 LayoutId::kTypeError,
100 "'%F' takes max %w positional arguments but %w given", &function,
101 argcount, nargs);
102 }
103 // Put extra positional args into the varargs tuple.
104 word len = -n_missing_args;
105 MutableTuple tuple(&scope, runtime->newMutableTuple(len));
106 for (word i = (len - 1); i >= 0; i--) {
107 tuple.atPut(i, thread->stackPop());
108 }
109 nargs -= len;
110 varargs_param = tuple.becomeImmutable();
111 }
112
113 // If there are any keyword-only args, there must be defaults for them
114 // because we arrived here via CALL_FUNCTION (and thus, no keywords were
115 // supplied at the call site).
116 Code code(&scope, function.code());
117 word kwonlyargcount = code.kwonlyargcount();
118 if (kwonlyargcount > 0) {
119 if (function.kwDefaults().isNoneType()) {
120 thread->stackDrop(nargs + 1);
121 return thread->raiseWithFmt(LayoutId::kTypeError,
122 "missing keyword-only argument");
123 }
124 Dict kw_defaults(&scope, function.kwDefaults());
125 Tuple formal_names(&scope, code.varnames());
126 word first_kw = argcount;
127 Object name(&scope, NoneType::object());
128 for (word i = 0; i < kwonlyargcount; i++) {
129 name = formal_names.at(first_kw + i);
130 RawObject value = dictAtByStr(thread, kw_defaults, name);
131 if (value.isErrorNotFound()) {
132 thread->stackDrop(nargs + i + 1);
133 return thread->raiseWithFmt(LayoutId::kTypeError,
134 "missing keyword-only argument");
135 }
136 thread->stackPush(value);
137 }
138 nargs += kwonlyargcount;
139 }
140
141 if (function.hasVarargs()) {
142 thread->stackPush(*varargs_param);
143 nargs++;
144 }
145 if (function.hasVarkeyargs()) {
146 // VARKEYARGS - because we arrived via CALL_FUNCTION, no keyword arguments
147 // provided. Just add an empty dict.
148 thread->stackPush(runtime->newDict());
149 nargs++;
150 }
151 DCHECK(function.totalArgs() == nargs, "argument count mismatch");
152 return *function;
153}
154
155// Verify correct number and order of arguments. If order is wrong, try to
156// fix it. If argument is missing (denoted by Error::object()), try to supply
157// it with a default. This routine expects the number of args on the stack
158// and number of names in the actual_names tuple to match. Caller must pad
159// prior to calling to ensure this.
160// Return None::object() if successful, error object if not.
161static RawObject checkArgs(Thread* thread, const Function& function,
162 RawObject* kw_arg_base, const Tuple& actual_names,
163 const Tuple& formal_names, word start) {
164 word posonlyargcount = RawCode::cast(function.code()).posonlyargcount();
165 word num_actuals = actual_names.length();
166 // Helper function to swap actual arguments and names
167 auto swap = [&kw_arg_base](RawMutableTuple ordered_names, word arg_pos1,
168 word arg_pos2) -> void {
169 RawObject tmp = *(kw_arg_base - arg_pos1);
170 *(kw_arg_base - arg_pos1) = *(kw_arg_base - arg_pos2);
171 *(kw_arg_base - arg_pos2) = tmp;
172 tmp = ordered_names.at(arg_pos1);
173 ordered_names.atPut(arg_pos1, ordered_names.at(arg_pos2));
174 ordered_names.atPut(arg_pos2, tmp);
175 };
176 // Helper function to retrieve argument
177 auto arg_at = [&kw_arg_base](word idx) -> RawObject& {
178 return *(kw_arg_base - idx);
179 };
180 HandleScope scope(thread);
181 // In case the order of the parameters in the call does not match the
182 // declaration order, create a copy of `actual_names` to adjust it to match
183 // `formal_names`.
184 Tuple ordered_names(&scope, *actual_names);
185 Object formal_name(&scope, NoneType::object());
186 for (word arg_pos = 0; arg_pos < num_actuals; arg_pos++) {
187 word formal_pos = arg_pos + start;
188 formal_name = formal_names.at(formal_pos);
189 RawObject result =
190 Runtime::objectEquals(thread, ordered_names.at(arg_pos), *formal_name);
191 if (result.isErrorException()) return result;
192 if (result == Bool::trueObj()) {
193 if (formal_pos >= posonlyargcount) {
194 // We're good here: actual & formal arg names match. Check the next
195 // one.
196 continue;
197 }
198 // A matching keyword arg but for a positional-only parameter.
199 return Thread::current()->raiseWithFmt(
200 LayoutId::kTypeError,
201 "keyword argument specified for positional-only argument '%S'",
202 &formal_name);
203 }
204 // Mismatch. Try to fix it. Note: args grow down.
205 // TODO(T66307914): Avoid heap allocation here.
206 // In case `actual_names` needs to be adjusted, create a copy to avoid
207 // modifying `actual_names`.
208 if (ordered_names == actual_names) {
209 word actual_names_length = actual_names.length();
210 ordered_names = thread->runtime()->newMutableTuple(actual_names_length);
211 for (word i = 0; i < actual_names_length; ++i) {
212 ordered_names.atPut(i, actual_names.at(i));
213 }
214 }
215 DCHECK(ordered_names.isMutableTuple(), "MutableTuple is expected");
216 bool swapped = false;
217 // Look for expected Formal name in Actuals tuple.
218 for (word i = arg_pos + 1; i < num_actuals; i++) {
219 result = Runtime::objectEquals(thread, ordered_names.at(i), *formal_name);
220 if (result.isErrorException()) return result;
221 if (result == Bool::trueObj()) {
222 // Found it. Swap both the stack and the ordered_names tuple.
223 swap(MutableTuple::cast(*ordered_names), arg_pos, i);
224 swapped = true;
225 break;
226 }
227 }
228 if (swapped) {
229 // We managed to fix it. Check the next one.
230 continue;
231 }
232 // Can't find an Actual for this Formal.
233 // If we have a real actual in current slot, move it somewhere safe.
234 if (!arg_at(arg_pos).isError()) {
235 for (word i = arg_pos + 1; i < num_actuals; i++) {
236 if (arg_at(i).isError()) {
237 // Found an uninitialized slot. Use it to save current actual.
238 swap(MutableTuple::cast(*ordered_names), arg_pos, i);
239 break;
240 }
241 }
242 // If we were unable to find a slot to swap into, TypeError
243 if (!arg_at(arg_pos).isError()) {
244 Object param_name(&scope, swapped ? formal_names.at(arg_pos)
245 : ordered_names.at(arg_pos));
246 return thread->raiseWithFmt(
247 LayoutId::kTypeError,
248 "%F() got an unexpected keyword argument '%S'", &function,
249 ¶m_name);
250 }
251 }
252 // Now, can we fill that slot with a default argument?
253 word absolute_pos = arg_pos + start;
254 word argcount = function.argcount();
255 if (absolute_pos < argcount) {
256 word defaults_size = function.hasDefaults()
257 ? Tuple::cast(function.defaults()).length()
258 : 0;
259 word defaults_start = argcount - defaults_size;
260 if (absolute_pos >= (defaults_start)) {
261 // Set the default value
262 Tuple default_args(&scope, function.defaults());
263 *(kw_arg_base - arg_pos) =
264 default_args.at(absolute_pos - defaults_start);
265 continue; // Got it, move on to the next
266 }
267 } else if (!function.kwDefaults().isNoneType()) {
268 // How about a kwonly default?
269 Dict kw_defaults(&scope, function.kwDefaults());
270 Str name(&scope, formal_names.at(arg_pos + start));
271 RawObject val = dictAtByStr(thread, kw_defaults, name);
272 if (!val.isErrorNotFound()) {
273 *(kw_arg_base - arg_pos) = val;
274 continue; // Got it, move on to the next
275 }
276 }
277 return thread->raiseWithFmt(LayoutId::kTypeError, "missing argument");
278 }
279 return NoneType::object();
280}
281
282static word findName(Thread* thread, word posonlyargcount, const Object& name,
283 const Tuple& names) {
284 word len = names.length();
285 for (word i = posonlyargcount; i < len; i++) {
286 RawObject result = Runtime::objectEquals(thread, *name, names.at(i));
287 if (result.isErrorException()) return -1;
288 if (result == Bool::trueObj()) {
289 return i;
290 }
291 }
292 return len;
293}
294
295// Converts the outgoing arguments of a keyword call into positional arguments
296// and processes default arguments, rearranging everything into a form expected
297// by the callee.
298RawObject prepareKeywordCall(Thread* thread, word nargs,
299 RawFunction function_raw) {
300 HandleScope scope(thread);
301 Function function(&scope, function_raw);
302 // Pop the tuple of kwarg names
303 Tuple keywords(&scope, thread->stackPop());
304 Code code(&scope, function.code());
305 word expected_args = function.argcount() + code.kwonlyargcount();
306 word num_keyword_args = keywords.length();
307 word num_positional_args = nargs - num_keyword_args;
308 Tuple varnames(&scope, code.varnames());
309 Object tmp_varargs(&scope, NoneType::object());
310 Object tmp_dict(&scope, NoneType::object());
311
312 // We expect use of keyword argument calls to be uncommon, but when used
313 // we anticipate mostly use of simple forms. General scheme here is to
314 // normalize the odd forms into standard form and then handle them all
315 // in the same place.
316 if (function.hasVarargsOrVarkeyargs()) {
317 Runtime* runtime = thread->runtime();
318 if (function.hasVarargs()) {
319 // If we have more positional than expected, add the remainder to a tuple,
320 // remove from the stack and close up the hole.
321 word excess = num_positional_args - function.argcount();
322 if (excess > 0) {
323 MutableTuple varargs(&scope, runtime->newMutableTuple(excess));
324 // Point to the leftmost excess argument
325 RawObject* p = (thread->stackPointer() + num_keyword_args + excess) - 1;
326 // Copy the excess to the * tuple
327 for (word i = 0; i < excess; i++) {
328 varargs.atPut(i, *(p - i));
329 }
330 // Fill in the hole
331 for (word i = 0; i < num_keyword_args; i++) {
332 *p = *(p - excess);
333 p--;
334 }
335 // Adjust the counts
336 thread->stackDrop(excess);
337 nargs -= excess;
338 num_positional_args -= excess;
339 tmp_varargs = varargs.becomeImmutable();
340 } else {
341 tmp_varargs = runtime->emptyTuple();
342 }
343 }
344 if (function.hasVarkeyargs()) {
345 // Too many positional args passed?
346 if (num_positional_args > function.argcount()) {
347 thread->stackDrop(nargs + 1);
348 return thread->raiseWithFmt(LayoutId::kTypeError,
349 "Too many positional arguments");
350 }
351 // If we have keyword arguments that don't appear in the formal parameter
352 // list, add them to a keyword dict.
353 Dict dict(&scope, runtime->newDict());
354 List saved_keyword_list(&scope, runtime->newList());
355 List saved_values(&scope, runtime->newList());
356 DCHECK(varnames.length() >= expected_args,
357 "varnames must be greater than or equal to positional args");
358 RawObject* p = thread->stackPointer() + (num_keyword_args - 1);
359 word posonlyargcount = code.posonlyargcount();
360 for (word i = 0; i < num_keyword_args; i++) {
361 Object key(&scope, keywords.at(i));
362 Object value(&scope, *(p - i));
363 word result = findName(thread, posonlyargcount, key, varnames);
364 if (result < 0) {
365 thread->stackDrop(nargs + 1);
366 return Error::exception();
367 }
368 if (result < expected_args) {
369 // Got a match, stash pair for future restoration on the stack
370 runtime->listAdd(thread, saved_keyword_list, key);
371 runtime->listAdd(thread, saved_values, value);
372 } else {
373 // New, add it and associated value to the varkeyargs dict
374 Object hash_obj(&scope, Interpreter::hash(thread, key));
375 if (hash_obj.isErrorException()) {
376 thread->stackDrop(nargs + 1);
377 return *hash_obj;
378 }
379 word hash = SmallInt::cast(*hash_obj).value();
380 Object dict_result(&scope, dictAtPut(thread, dict, key, hash, value));
381 if (dict_result.isErrorException()) {
382 thread->stackDrop(nargs + 1);
383 return *dict_result;
384 }
385 nargs--;
386 }
387 }
388 // Now, restore the stashed values to the stack and build a new
389 // keywords name list.
390 thread->stackDrop(num_keyword_args); // Pop all of the old keyword values
391 num_keyword_args = saved_keyword_list.numItems();
392 // Replace the old keywords list with a new one.
393 if (num_keyword_args > 0) {
394 MutableTuple new_keywords(&scope,
395 runtime->newMutableTuple(num_keyword_args));
396 for (word i = 0; i < num_keyword_args; i++) {
397 thread->stackPush(saved_values.at(i));
398 new_keywords.atPut(i, saved_keyword_list.at(i));
399 }
400 keywords = new_keywords.becomeImmutable();
401 } else {
402 keywords = runtime->emptyTuple();
403 }
404 tmp_dict = *dict;
405 }
406 }
407 // At this point, all vararg forms have been normalized
408 RawObject* kw_arg_base = (thread->stackPointer() + num_keyword_args) -
409 1; // pointer to first non-positional arg
410 if (UNLIKELY(nargs > expected_args)) {
411 thread->stackDrop(nargs + 1);
412 return thread->raiseWithFmt(LayoutId::kTypeError, "Too many arguments");
413 }
414 if (UNLIKELY(nargs < expected_args)) {
415 // Too few args passed. Can we supply default args to make it work?
416 // First, normalize & pad keywords and stack arguments
417 word name_tuple_size = expected_args - num_positional_args;
418 MutableTuple padded_keywords(
419 &scope, thread->runtime()->newMutableTuple(name_tuple_size));
420 padded_keywords.replaceFromWith(0, *keywords, num_keyword_args);
421 // Fill in missing spots w/ Error code
422 for (word i = num_keyword_args; i < name_tuple_size; i++) {
423 thread->stackPush(Error::error());
424 nargs++;
425 padded_keywords.atPut(i, Error::error());
426 }
427 keywords = padded_keywords.becomeImmutable();
428 }
429 // Now we've got the right number. Do they match up?
430 RawObject res = checkArgs(thread, function, kw_arg_base, keywords, varnames,
431 num_positional_args);
432 if (res.isErrorException()) {
433 thread->stackDrop(nargs + 1);
434 return res; // TypeError created by checkArgs.
435 }
436 CHECK(res.isNoneType(), "checkArgs should return an Error or None");
437 // If we're a vararg form, need to push the tuple/dict.
438 if (function.hasVarargs()) {
439 thread->stackPush(*tmp_varargs);
440 nargs++;
441 }
442 if (function.hasVarkeyargs()) {
443 thread->stackPush(*tmp_dict);
444 nargs++;
445 }
446 DCHECK(function.totalArgs() == nargs, "argument count mismatch");
447 return *function;
448}
449
450// Converts explode arguments into positional arguments.
451//
452// Returns the new number of positional arguments as a SmallInt, or Error if an
453// exception was raised (most likely due to a non-string keyword name).
454static RawObject processExplodeArguments(Thread* thread, word flags) {
455 HandleScope scope(thread);
456 Object kw_mapping(&scope, NoneType::object());
457 if (flags & CallFunctionExFlag::VAR_KEYWORDS) {
458 kw_mapping = thread->stackPop();
459 }
460 Tuple positional_args(&scope, thread->stackPop());
461 word length = positional_args.length();
462 for (word i = 0; i < length; i++) {
463 thread->stackPush(positional_args.at(i));
464 }
465 word nargs = length;
466 Runtime* runtime = thread->runtime();
467 if (flags & CallFunctionExFlag::VAR_KEYWORDS) {
468 if (!kw_mapping.isDict()) {
469 DCHECK(runtime->isMapping(thread, kw_mapping),
470 "kw_mapping must have __getitem__");
471 Dict dict(&scope, runtime->newDict());
472 Object result(&scope, dictMergeIgnore(thread, dict, kw_mapping));
473 if (result.isErrorException()) {
474 thread->stackDrop(nargs + 1);
475 if (thread->pendingExceptionType() ==
476 runtime->typeAt(LayoutId::kAttributeError)) {
477 thread->clearPendingException();
478 return thread->raiseWithFmt(LayoutId::kTypeError,
479 "argument must be a mapping, not %T\n",
480 &kw_mapping);
481 }
482 return *result;
483 }
484 kw_mapping = *dict;
485 }
486 Dict dict(&scope, *kw_mapping);
487 word len = dict.numItems();
488 if (len == 0) {
489 thread->stackPush(runtime->emptyTuple());
490 return SmallInt::fromWord(nargs);
491 }
492 MutableTuple keys(&scope, runtime->newMutableTuple(len));
493 Object key(&scope, NoneType::object());
494 Object value(&scope, NoneType::object());
495 for (word i = 0, j = 0; dictNextItem(dict, &i, &key, &value); j++) {
496 if (!runtime->isInstanceOfStr(*key)) {
497 thread->stackDrop(nargs + 1);
498 return thread->raiseWithFmt(LayoutId::kTypeError,
499 "keywords must be strings");
500 }
501 keys.atPut(j, *key);
502 thread->stackPush(*value);
503 nargs++;
504 }
505 thread->stackPush(keys.becomeImmutable());
506 }
507 return SmallInt::fromWord(nargs);
508}
509
510// Takes the outgoing arguments of an explode argument call and rearranges them
511// into the form expected by the callee.
512RawObject prepareExplodeCall(Thread* thread, word flags,
513 RawFunction function_raw) {
514 HandleScope scope(thread);
515 Function function(&scope, function_raw);
516
517 RawObject arg_obj = processExplodeArguments(thread, flags);
518 if (arg_obj.isErrorException()) return arg_obj;
519 word new_argc = SmallInt::cast(arg_obj).value();
520
521 if (flags & CallFunctionExFlag::VAR_KEYWORDS) {
522 RawObject result = prepareKeywordCall(thread, new_argc, *function);
523 if (result.isErrorException()) {
524 return result;
525 }
526 } else {
527 // Are we one of the less common cases?
528 if (new_argc != function.argcount() || !(function.hasSimpleCall())) {
529 RawObject result = processDefaultArguments(thread, new_argc, *function);
530 if (result.isErrorException()) {
531 return result;
532 }
533 }
534 }
535 return *function;
536}
537
538static RawObject createGeneratorObject(Thread* thread,
539 const Function& function) {
540 Runtime* runtime = thread->runtime();
541 if (function.isGenerator()) return runtime->newGenerator();
542 if (function.isCoroutine()) return runtime->newCoroutine();
543 DCHECK(function.isAsyncGenerator(), "unexpected type");
544 HandleScope scope(thread);
545 Layout async_gen_layout(&scope, runtime->layoutAt(LayoutId::kAsyncGenerator));
546 AsyncGenerator async_gen(&scope, runtime->newInstance(async_gen_layout));
547 async_gen.setFinalizer(NoneType::object());
548 async_gen.setHooksInited(false);
549 return *async_gen;
550}
551
552static RawObject createGenerator(Thread* thread, const Function& function) {
553 Runtime* runtime = thread->runtime();
554 HandleScope scope(thread);
555 GeneratorFrame generator_frame(&scope, runtime->newGeneratorFrame(function));
556 thread->currentFrame()->addReturnMode(Frame::kExitRecursiveInterpreter);
557 thread->popFrameToGeneratorFrame(generator_frame);
558 GeneratorBase gen_base(&scope, createGeneratorObject(thread, function));
559 gen_base.setGeneratorFrame(*generator_frame);
560 gen_base.setExceptionState(runtime->newExceptionState());
561 gen_base.setQualname(function.qualname());
562 gen_base.setName(function.name());
563 return *gen_base;
564}
565
566RawObject generatorTrampoline(Thread* thread, word nargs) {
567 HandleScope scope(thread);
568 Function function(&scope, thread->stackPeek(nargs));
569 RawObject error = preparePositionalCall(thread, nargs, *function);
570 if (error.isErrorException()) {
571 return error;
572 }
573 Frame* callee_frame = thread->pushCallFrame(*function);
574 if (UNLIKELY(callee_frame == nullptr)) {
575 thread->stackDrop(nargs + 1);
576 return Error::exception();
577 }
578 if (function.hasFreevarsOrCellvars()) {
579 processFreevarsAndCellvars(thread, callee_frame);
580 }
581 return createGenerator(thread, function);
582}
583
584RawObject generatorTrampolineKw(Thread* thread, word nargs) {
585 HandleScope scope(thread);
586 // The argument does not include the hidden keyword dictionary argument. Add
587 // one to skip over the keyword dictionary to read the function object.
588 Function function(&scope, thread->stackPeek(nargs + 1));
589 RawObject error = prepareKeywordCall(thread, nargs, *function);
590 if (error.isErrorException()) {
591 return error;
592 }
593 Frame* callee_frame = thread->pushCallFrame(*function);
594 if (UNLIKELY(callee_frame == nullptr)) {
595 thread->stackDrop(nargs + 1);
596 return Error::exception();
597 }
598 if (function.hasFreevarsOrCellvars()) {
599 processFreevarsAndCellvars(thread, callee_frame);
600 }
601 return createGenerator(thread, function);
602}
603
604RawObject generatorTrampolineEx(Thread* thread, word flags) {
605 HandleScope scope(thread);
606 // The argument is either zero when there is one argument and one when there
607 // are two arguments. Skip over these arguments to read the function object.
608 word function_offset = (flags & CallFunctionExFlag::VAR_KEYWORDS) ? 2 : 1;
609 Function function(&scope, thread->stackPeek(function_offset));
610 RawObject error = prepareExplodeCall(thread, flags, *function);
611 if (error.isErrorException()) {
612 return error;
613 }
614 Frame* callee_frame = thread->pushCallFrame(*function);
615 if (UNLIKELY(callee_frame == nullptr)) {
616 thread->stackDrop(function_offset + 1);
617 return Error::exception();
618 }
619 if (function.hasFreevarsOrCellvars()) {
620 processFreevarsAndCellvars(thread, callee_frame);
621 }
622 return createGenerator(thread, function);
623}
624
625RawObject interpreterTrampoline(Thread* thread, word nargs) {
626 HandleScope scope(thread);
627 Function function(&scope, thread->stackPeek(nargs));
628 RawObject error = preparePositionalCall(thread, nargs, *function);
629 if (error.isErrorException()) {
630 return error;
631 }
632 Frame* callee_frame = thread->pushCallFrame(*function);
633 if (UNLIKELY(callee_frame == nullptr)) {
634 thread->stackDrop(nargs + 1);
635 return Error::exception();
636 }
637 if (function.hasFreevarsOrCellvars()) {
638 processFreevarsAndCellvars(thread, callee_frame);
639 }
640 return Interpreter::execute(thread);
641}
642
643RawObject interpreterTrampolineKw(Thread* thread, word nargs) {
644 HandleScope scope(thread);
645 // The argument does not include the hidden keyword dictionary argument. Add
646 // one to skip the keyword dictionary to get to the function object.
647 Function function(&scope, thread->stackPeek(nargs + 1));
648 RawObject error = prepareKeywordCall(thread, nargs, *function);
649 if (error.isErrorException()) {
650 return error;
651 }
652 Frame* callee_frame = thread->pushCallFrame(*function);
653 if (UNLIKELY(callee_frame == nullptr)) {
654 thread->stackDrop(nargs + 2);
655 return Error::exception();
656 }
657 if (function.hasFreevarsOrCellvars()) {
658 processFreevarsAndCellvars(thread, callee_frame);
659 }
660 return Interpreter::execute(thread);
661}
662
663RawObject interpreterTrampolineEx(Thread* thread, word flags) {
664 HandleScope scope(thread);
665 // The argument is either zero when there is one argument and one when there
666 // are two arguments. Skip over these arguments to read the function object.
667 word function_offset = (flags & CallFunctionExFlag::VAR_KEYWORDS) ? 2 : 1;
668 Function function(&scope, thread->stackPeek(function_offset));
669 RawObject error = prepareExplodeCall(thread, flags, *function);
670 if (error.isErrorException()) {
671 return error;
672 }
673 Frame* callee_frame = thread->pushCallFrame(*function);
674 if (UNLIKELY(callee_frame == nullptr)) {
675 thread->stackDrop(function_offset + 1);
676 return Error::exception();
677 }
678 if (function.hasFreevarsOrCellvars()) {
679 processFreevarsAndCellvars(thread, callee_frame);
680 }
681 return Interpreter::execute(thread);
682}
683
684RawObject unimplementedTrampoline(Thread*, word) {
685 UNIMPLEMENTED("Trampoline");
686}
687
688static inline RawObject builtinTrampolineImpl(Thread* thread, word arg,
689 word function_idx,
690 PrepareCallFunc prepare_call) {
691 // Warning: This code is using `RawXXX` variables for performance! This is
692 // despite the fact that we call functions that do potentially perform memory
693 // allocations. This is legal here because we always rely on the functions
694 // returning an up-to-date address and we make sure to never access any value
695 // produce before a call after that call. Be careful not to break this
696 // invariant if you change the code!
697
698 RawObject prepare_result = prepare_call(
699 thread, arg, Function::cast(thread->stackPeek(function_idx)));
700 if (prepare_result.isErrorException()) {
701 return prepare_result;
702 }
703 RawFunction function_obj = Function::cast(prepare_result);
704
705 RawObject result = NoneType::object();
706 {
707 BuiltinFunction function = bit_cast<BuiltinFunction>(
708 SmallInt::cast(function_obj.stacksizeOrBuiltin()).asAlignedCPtr());
709
710 word nargs = function_obj.totalArgs();
711 Frame* callee_frame = thread->pushNativeFrame(nargs);
712 if (UNLIKELY(callee_frame == nullptr)) {
713 thread->stackDrop(nargs + 1);
714 return Error::exception();
715 }
716 result = (*function)(thread, Arguments(callee_frame));
717 // End scope so people do not accidentally use raw variables after the call
718 // which could have triggered a GC.
719 }
720 DCHECK(thread->isErrorValueOk(result), "error/exception mismatch");
721 thread->popFrame();
722 return result;
723}
724
725RawObject builtinTrampoline(Thread* thread, word nargs) {
726 return builtinTrampolineImpl(thread, nargs, /*function_idx=*/nargs,
727 preparePositionalCall);
728}
729
730RawObject builtinTrampolineKw(Thread* thread, word nargs) {
731 return builtinTrampolineImpl(thread, nargs, /*function_idx=*/nargs + 1,
732 prepareKeywordCall);
733}
734
735RawObject builtinTrampolineEx(Thread* thread, word flags) {
736 return builtinTrampolineImpl(
737 thread, flags,
738 /*function_idx=*/(flags & CallFunctionExFlag::VAR_KEYWORDS) ? 2 : 1,
739 prepareExplodeCall);
740}
741
742} // namespace py