ext/Internal/capi-trampolines.cpp (681 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) #include "capi-trampolines.h" #include "api-handle.h" #include "capi.h" #include "dict-builtins.h" #include "runtime.h" #include "thread.h" namespace py { static const word kMaxStackArguments = 6; // method no args static RawObject callMethNoArgs(Thread* thread, const Function& function, const Object& self) { HandleScope scope(thread); Int address(&scope, function.code()); binaryfunc method = bit_cast<binaryfunc>(address.asCPtr()); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(thread->runtime(), *self); PyObject* pyresult = (*method)(self_obj, nullptr); Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult)); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } return *result; } static RawObject raiseTypeErrorMustBeBound(Thread* thread, const Function& function) { HandleScope scope(thread); Str function_name(&scope, function.name()); return thread->raiseWithFmt( LayoutId::kTypeError, "'%S' must be bound to an object", &function_name); } static RawObject raiseTypeErrorNoArguments(Thread* thread, const Function& function, word nargs) { HandleScope scope(thread); Str function_name(&scope, function.name()); if (nargs == 0) { return raiseTypeErrorMustBeBound(thread, function); } return thread->raiseWithFmt(LayoutId::kTypeError, "'%S' takes no arguments (%w given)", &function_name, nargs - 1); } static RawObject raiseTypeErrorNoKeywordArguments(Thread* thread, const Function& function) { HandleScope scope(thread); Str function_name(&scope, function.name()); return thread->raiseWithFmt( LayoutId::kTypeError, "'%S' takes no keyword arguments", &function_name); } RawObject methodTrampolineNoArgs(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs)); if (nargs != 1) { RawObject result = raiseTypeErrorNoArguments(thread, function, nargs); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(0)); RawObject result = callMethNoArgs(thread, function, self); thread->stackDrop(nargs + 1); return result; } RawObject methodTrampolineNoArgsKw(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs + 1)); Tuple kw_names(&scope, thread->stackPeek(0)); if (kw_names.length() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(nargs + 2); return result; } if (nargs != 1) { RawObject result = raiseTypeErrorNoArguments(thread, function, nargs); thread->stackDrop(nargs + 2); return result; } Object self(&scope, thread->stackPeek(1)); RawObject result = callMethNoArgs(thread, function, self); thread->stackDrop(nargs + 2); return result; } RawObject methodTrampolineNoArgsEx(Thread* thread, word flags) { HandleScope scope(thread); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; Function function(&scope, thread->stackPeek(has_varkeywords + 1)); Tuple args(&scope, thread->stackPeek(has_varkeywords)); if (has_varkeywords) { Object kw_args(&scope, thread->stackTop()); if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs"); if (Dict::cast(*kw_args).numItems() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } } word args_length = args.length(); if (args_length != 1) { RawObject result = raiseTypeErrorNoArguments(thread, function, args_length); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, args.at(0)); RawObject result = callMethNoArgs(thread, function, self); thread->stackDrop(has_varkeywords + 2); return result; } // method one arg static RawObject raiseTypeErrorOneArgument(Thread* thread, const Function& function, word nargs) { HandleScope scope(thread); Str function_name(&scope, function.name()); if (nargs == 0) { return raiseTypeErrorMustBeBound(thread, function); } return thread->raiseWithFmt(LayoutId::kTypeError, "'%S' takes exactly one argument (%w given)", &function_name, nargs - 1); } static RawObject callMethOneArg(Thread* thread, const Function& function, const Object& self, const Object& arg) { HandleScope scope(thread); Int address(&scope, function.code()); binaryfunc method = bit_cast<binaryfunc>(address.asCPtr()); Runtime* runtime = thread->runtime(); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self); PyObject* arg_obj = ApiHandle::newReference(runtime, *arg); PyObject* pyresult = (*method)(self_obj, arg_obj); Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult)); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } ApiHandle::fromPyObject(arg_obj)->decref(); return *result; } RawObject methodTrampolineOneArg(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs)); if (nargs != 2) { RawObject result = raiseTypeErrorOneArgument(thread, function, nargs); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(1)); Object arg(&scope, thread->stackPeek(0)); RawObject result = callMethOneArg(thread, function, self, arg); thread->stackDrop(nargs + 1); return result; } RawObject methodTrampolineOneArgKw(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs + 1)); Tuple kw_names(&scope, thread->stackPeek(0)); if (kw_names.length() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(nargs + 2); return result; } if (nargs != 2) { RawObject result = raiseTypeErrorOneArgument(thread, function, nargs); thread->stackDrop(nargs + 2); return result; } Object self(&scope, thread->stackPeek(2)); Object arg(&scope, thread->stackPeek(1)); RawObject result = callMethOneArg(thread, function, self, arg); thread->stackDrop(nargs + 2); return result; } RawObject methodTrampolineOneArgEx(Thread* thread, word flags) { HandleScope scope(thread); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; Function function(&scope, thread->stackPeek(has_varkeywords + 1)); if (has_varkeywords) { Object kw_args(&scope, thread->stackTop()); if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs"); if (Dict::cast(*kw_args).numItems() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } } Tuple varargs(&scope, thread->stackPeek(has_varkeywords)); if (varargs.length() != 2) { RawObject result = raiseTypeErrorOneArgument(thread, function, varargs.length()); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, varargs.at(0)); Object arg(&scope, varargs.at(1)); RawObject result = callMethOneArg(thread, function, self, arg); thread->stackDrop(has_varkeywords + 2); return result; } // callMethVarArgs static RawObject callMethVarArgs(Thread* thread, const Function& function, const Object& self, const Object& varargs) { HandleScope scope(thread); Int address(&scope, function.code()); binaryfunc method = bit_cast<binaryfunc>(address.asCPtr()); Runtime* runtime = thread->runtime(); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self); PyObject* varargs_obj = ApiHandle::newReference(runtime, *varargs); PyObject* pyresult = (*method)(self_obj, varargs_obj); Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult)); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } ApiHandle::fromPyObject(varargs_obj)->decref(); return *result; } RawObject methodTrampolineVarArgs(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(nargs - 1)); Object varargs_obj(&scope, NoneType::object()); word num_varargs = nargs - 1; if (num_varargs > 0) { MutableTuple varargs(&scope, thread->runtime()->newMutableTuple(num_varargs)); for (word i = 0; i < num_varargs; i++) { varargs.atPut(num_varargs - i - 1, thread->stackPeek(i)); } varargs_obj = varargs.becomeImmutable(); } else { varargs_obj = thread->runtime()->emptyTuple(); } RawObject result = callMethVarArgs(thread, function, self, varargs_obj); thread->stackDrop(nargs + 1); return result; } RawObject methodTrampolineVarArgsKw(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs + 1)); Tuple kw_names(&scope, thread->stackPeek(0)); if (kw_names.length() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(nargs + 2); return result; } if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 2); return result; } Object self(&scope, thread->stackPeek(nargs)); Object varargs_obj(&scope, NoneType::object()); word num_varargs = nargs - 1; if (num_varargs > 0) { MutableTuple varargs(&scope, thread->runtime()->newMutableTuple(num_varargs)); for (word i = 0; i < num_varargs; i++) { varargs.atPut(i, thread->stackPeek(num_varargs - i)); } varargs_obj = varargs.becomeImmutable(); } else { varargs_obj = thread->runtime()->emptyTuple(); } RawObject result = callMethVarArgs(thread, function, self, varargs_obj); thread->stackDrop(nargs + 2); return result; } RawObject methodTrampolineVarArgsEx(Thread* thread, word flags) { HandleScope scope(thread); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; Function function(&scope, thread->stackPeek(has_varkeywords + 1)); if (has_varkeywords) { Object kw_args(&scope, thread->stackTop()); if (!kw_args.isDict()) UNIMPLEMENTED("mapping kwargs"); if (Dict::cast(*kw_args).numItems() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } } Tuple args(&scope, thread->stackPeek(has_varkeywords)); if (args.length() == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, args.at(0)); Object varargs(&scope, thread->runtime()->tupleSubseq(thread, args, 1, args.length() - 1)); RawObject result = callMethVarArgs(thread, function, self, varargs); thread->stackDrop(has_varkeywords + 2); return result; } // callMethKeywordArgs static RawObject callMethKeywords(Thread* thread, const Function& function, const Object& self, const Object& args, const Object& kwargs) { HandleScope scope(thread); Int address(&scope, function.code()); ternaryfunc method = bit_cast<ternaryfunc>(address.asCPtr()); Runtime* runtime = thread->runtime(); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self); PyObject* args_obj = ApiHandle::newReference(runtime, *args); PyObject* kwargs_obj = nullptr; if (*kwargs != NoneType::object()) { kwargs_obj = ApiHandle::newReference(runtime, *kwargs); } PyObject* pyresult = (*method)(self_obj, args_obj, kwargs_obj); Object result(&scope, ApiHandle::checkFunctionResult(thread, pyresult)); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } ApiHandle::fromPyObject(args_obj)->decref(); if (kwargs_obj != nullptr) { ApiHandle::fromPyObject(kwargs_obj)->decref(); } return *result; } RawObject methodTrampolineKeywords(Thread* thread, word nargs) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Function function(&scope, thread->stackPeek(nargs)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(nargs - 1)); Object varargs_obj(&scope, NoneType::object()); word num_varargs = nargs - 1; if (num_varargs > 0) { MutableTuple varargs(&scope, runtime->newMutableTuple(num_varargs)); for (word i = 0; i < num_varargs; i++) { varargs.atPut(num_varargs - i - 1, thread->stackPeek(i)); } varargs_obj = varargs.becomeImmutable(); } else { varargs_obj = runtime->emptyTuple(); } Object keywords(&scope, NoneType::object()); RawObject result = callMethKeywords(thread, function, self, varargs_obj, keywords); thread->stackDrop(nargs + 1); return result; } RawObject methodTrampolineKeywordsKw(Thread* thread, word nargs) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); Tuple kw_names(&scope, thread->stackPeek(0)); Object kwargs(&scope, NoneType::object()); word num_keywords = kw_names.length(); if (num_keywords != 0) { Dict dict(&scope, runtime->newDict()); for (word i = 0; i < num_keywords; i++) { Str name(&scope, kw_names.at(i)); Object value(&scope, thread->stackPeek(num_keywords - i)); dictAtPutByStr(thread, dict, name, value); } kwargs = *dict; } Function function(&scope, thread->stackPeek(nargs + 1)); if (nargs - num_keywords == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 2); return result; } word num_positional = nargs - num_keywords - 1; Object args_obj(&scope, NoneType::object()); if (num_positional > 0) { MutableTuple args(&scope, runtime->newMutableTuple(num_positional)); for (word i = 0; i < num_positional; i++) { args.atPut(i, thread->stackPeek(nargs - i - 1)); } args_obj = args.becomeImmutable(); } else { args_obj = runtime->emptyTuple(); } Object self(&scope, thread->stackPeek(nargs)); RawObject result = callMethKeywords(thread, function, self, args_obj, kwargs); thread->stackDrop(nargs + 2); return result; } RawObject methodTrampolineKeywordsEx(Thread* thread, word flags) { HandleScope scope(thread); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; Tuple varargs(&scope, thread->stackPeek(has_varkeywords)); Object kwargs(&scope, NoneType::object()); if (has_varkeywords) { kwargs = thread->stackTop(); if (!kwargs.isDict()) UNIMPLEMENTED("mapping kwargs"); } Function function(&scope, thread->stackPeek(has_varkeywords + 1)); if (varargs.length() == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, varargs.at(0)); Object args(&scope, thread->runtime()->tupleSubseq(thread, varargs, 1, varargs.length() - 1)); RawObject result = callMethKeywords(thread, function, self, args, kwargs); thread->stackDrop(has_varkeywords + 2); return result; } static RawObject callMethFast(Thread* thread, const Function& function, const Object& self, PyObject* const* args, word num_args) { _PyCFunctionFast method = bit_cast<_PyCFunctionFast>(Int::cast(function.code()).asCPtr()); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(thread->runtime(), *self); PyObject* pyresult = (*method)(self_obj, args, num_args); RawObject result = ApiHandle::checkFunctionResult(thread, pyresult); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } return result; } RawObject methodTrampolineFast(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(nargs - 1)); word num_positional = nargs - 1; PyObject* small_array[kMaxStackArguments]; PyObject** args; if (num_positional <= kMaxStackArguments) { args = small_array; } else { args = reinterpret_cast<PyObject**>( std::malloc(num_positional * sizeof(args[0]))); } Runtime* runtime = thread->runtime(); for (word i = 0; i < num_positional; i++) { args[nargs - i - 2] = ApiHandle::newReference(runtime, thread->stackPeek(i)); } Object result(&scope, callMethFast(thread, function, self, args, num_positional)); for (word i = 0; i < num_positional; i++) { ApiHandle::fromPyObject(args[nargs - i - 2])->decref(); } thread->stackDrop(nargs + 1); if (args != small_array) { std::free(args); } return *result; } RawObject methodTrampolineFastKw(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs + 1)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 2); return result; } Tuple kw_names(&scope, thread->stackPeek(0)); if (kw_names.length() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(nargs + 2); return result; } Object self(&scope, thread->stackPeek(nargs)); word num_positional = nargs - 1; PyObject* small_array[kMaxStackArguments]; PyObject** args; if (num_positional <= kMaxStackArguments) { args = small_array; } else { args = reinterpret_cast<PyObject**>( std::malloc(num_positional * sizeof(args[0]))); } Runtime* runtime = thread->runtime(); for (word i = 0; i < num_positional; i++) { args[i] = ApiHandle::newReference(runtime, thread->stackPeek(nargs - i - 1)); } Object result(&scope, callMethFast(thread, function, self, args, nargs - 1)); for (word i = 0; i < num_positional; i++) { ApiHandle::fromPyObject(args[i])->decref(); } thread->stackDrop(nargs + 2); if (args != small_array) { std::free(args); } return *result; } RawObject methodTrampolineFastEx(Thread* thread, word flags) { HandleScope scope(thread); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; Function function(&scope, thread->stackPeek(has_varkeywords + 1)); // Get the keyword arguments if (has_varkeywords) { Object kw_args_obj(&scope, thread->stackTop()); if (!kw_args_obj.isDict()) UNIMPLEMENTED("mapping kwargs"); Dict kw_args(&scope, *kw_args_obj); if (kw_args.numItems() != 0) { RawObject result = raiseTypeErrorNoKeywordArguments(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } } Tuple args_tuple(&scope, thread->stackPeek(has_varkeywords)); word args_length = args_tuple.length(); if (args_length == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, args_tuple.at(0)); word num_positional = args_length - 1; PyObject* small_array[kMaxStackArguments]; PyObject** args; if (num_positional <= kMaxStackArguments) { args = small_array; } else { args = reinterpret_cast<PyObject**>( std::malloc(num_positional * sizeof(args[0]))); } // Set the positional arguments Runtime* runtime = thread->runtime(); for (word i = 0; i < num_positional; i++) { args[i] = ApiHandle::newReference(runtime, args_tuple.at(i + 1)); } Object result(&scope, callMethFast(thread, function, self, args, num_positional)); for (word i = 0; i < num_positional; i++) { ApiHandle::fromPyObject(args[i])->decref(); } thread->stackDrop(has_varkeywords + 2); if (args != small_array) { std::free(args); } return *result; } static RawObject callMethFastWithKeywordsWithKwargs( Thread* thread, const Function& function, const Object& self, PyObject* const* args, word num_args, const Object& kw_names) { _PyCFunctionFastWithKeywords method = bit_cast<_PyCFunctionFastWithKeywords>( Int::cast(function.code()).asCPtr()); Runtime* runtime = thread->runtime(); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(runtime, *self); ApiHandle* kw_names_obj = ApiHandle::newReference(runtime, *kw_names); PyObject* pyresult = (*method)(self_obj, args, num_args, kw_names_obj); RawObject result = ApiHandle::checkFunctionResult(thread, pyresult); kw_names_obj->decref(); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } return result; } static RawObject callMethFastWithKeywords(Thread* thread, const Function& function, const Object& self, PyObject* const* args, word num_args) { _PyCFunctionFastWithKeywords method = bit_cast<_PyCFunctionFastWithKeywords>( Int::cast(function.code()).asCPtr()); PyObject* self_obj = self.isUnbound() ? nullptr : ApiHandle::newReference(thread->runtime(), *self); PyObject* pyresult = (*method)(self_obj, args, num_args, nullptr); RawObject result = ApiHandle::checkFunctionResult(thread, pyresult); if (self_obj != nullptr) { ApiHandle::fromPyObject(self_obj)->decref(); } return result; } RawObject methodTrampolineFastWithKeywords(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 1); return result; } Object self(&scope, thread->stackPeek(nargs - 1)); std::unique_ptr<PyObject*[]> fastcall_args(new PyObject*[nargs - 1]); Runtime* runtime = thread->runtime(); for (word i = 0; i < nargs - 1; i++) { fastcall_args[nargs - i - 2] = ApiHandle::newReference(runtime, thread->stackPeek(i)); } word num_positional = nargs - 1; Object result(&scope, callMethFastWithKeywords(thread, function, self, fastcall_args.get(), num_positional)); for (word i = 0; i < nargs - 1; i++) { ApiHandle::fromPyObject(fastcall_args[nargs - i - 2])->decref(); } thread->stackDrop(nargs + 1); return *result; } RawObject methodTrampolineFastWithKeywordsKw(Thread* thread, word nargs) { HandleScope scope(thread); Function function(&scope, thread->stackPeek(nargs + 1)); if (nargs == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(nargs + 2); return result; } Object self(&scope, thread->stackPeek(nargs)); std::unique_ptr<PyObject*[]> fastcall_args(new PyObject*[nargs - 1]); Runtime* runtime = thread->runtime(); for (word i = 0; i < nargs - 1; i++) { fastcall_args[i] = ApiHandle::newReference(runtime, thread->stackPeek(nargs - i - 1)); } Tuple kw_names(&scope, thread->stackPeek(0)); word num_positional = nargs - kw_names.length() - 1; Object result(&scope, callMethFastWithKeywordsWithKwargs( thread, function, self, fastcall_args.get(), num_positional, kw_names)); for (word i = 0; i < nargs - 1; i++) { ApiHandle::fromPyObject(fastcall_args[i])->decref(); } thread->stackDrop(nargs + 2); return *result; } RawObject methodTrampolineFastWithKeywordsEx(Thread* thread, word flags) { HandleScope scope(thread); Runtime* runtime = thread->runtime(); bool has_varkeywords = flags & CallFunctionExFlag::VAR_KEYWORDS; word num_keywords = 0; // Get the keyword arguments if (has_varkeywords) { Object kw_args_obj(&scope, thread->stackTop()); if (!kw_args_obj.isDict()) UNIMPLEMENTED("mapping kwargs"); Dict kw_args(&scope, *kw_args_obj); num_keywords = kw_args.numItems(); } Function function(&scope, thread->stackPeek(has_varkeywords + 1)); Tuple args(&scope, thread->stackPeek(has_varkeywords)); word args_length = args.length(); if (args_length == 0) { RawObject result = raiseTypeErrorMustBeBound(thread, function); thread->stackDrop(has_varkeywords + 2); return result; } Object self(&scope, args.at(0)); word num_positional = args_length - 1; std::unique_ptr<PyObject*[]> fastcall_args( new PyObject*[num_positional + num_keywords]); // Set the positional arguments for (word i = 0; i < num_positional; i++) { fastcall_args[i] = ApiHandle::newReference(runtime, args.at(i + 1)); } // Set the keyword arguments Tuple kw_names(&scope, runtime->emptyTuple()); if (has_varkeywords) { Dict kw_args(&scope, thread->stackTop()); if (num_keywords > 0) { Object key(&scope, NoneType::object()); Object value(&scope, NoneType::object()); kw_names = runtime->newMutableTuple(num_keywords); for (word dict_i = 0, arg_i = 0; dictNextItem(kw_args, &dict_i, &key, &value); arg_i++) { MutableTuple::cast(*kw_names).atPut(arg_i, *key); fastcall_args[num_positional + arg_i] = ApiHandle::newReference(runtime, *value); } MutableTuple::cast(*kw_names).becomeImmutable(); } } Object result(&scope, NoneType::object()); if (!has_varkeywords) { result = callMethFastWithKeywords(thread, function, self, fastcall_args.get(), num_positional); } else { result = callMethFastWithKeywordsWithKwargs( thread, function, self, fastcall_args.get(), num_positional, kw_names); } for (word i = 0; i < num_positional + num_keywords; i++) { ApiHandle::fromPyObject(fastcall_args[i])->decref(); } thread->stackDrop(has_varkeywords + 2); return *result; } } // namespace py