diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 76f1c351e159086..ff70c9f38dce445 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -95,6 +95,30 @@ def test_kwargs_order_preserved_in_c_functions(self): self.assertEqual(list(collections.OrderedDict(b=1, a=2, c=3)), ['b', 'a', 'c']) + def test_kwargs_unpacking_mutation_isolated(self): + # gh-86199: a callee mutating its **kwargs must never affect the + # caller's dict. + def fn(**kw): + kw['injected'] = None + d = {'a': 1} + fn(**d) + self.assertEqual(d, {'a': 1}) + e = {} + fn(**e) + self.assertEqual(e, {}) + + @cpython_only + @unittest.skipIf(_testcapi is None, "requires _testcapi") + def test_kwargs_unpacking_not_aliased_in_tp_call(self): + # gh-86199: a tp_call callee receives a private copy of the kwargs + # dict, never the caller's dict. + get_kwargs = _testcapi.get_kwargs # METH_VARARGS | METH_KEYWORDS + d = {'a': 1} + self.assertIsNot(get_kwargs(**d), d) + self.assertEqual(get_kwargs(**d), d) + e = {} + self.assertIsNot(get_kwargs(**e), e) + def test_frames_are_popped_after_failed_calls(self): # GH-93252: stuff blows up if we don't pop the new frame after # recovering from failed calls: diff --git a/Lib/test/test_free_threading/test_kwargs_unpack.py b/Lib/test/test_free_threading/test_kwargs_unpack.py new file mode 100644 index 000000000000000..eaa8103d7848586 --- /dev/null +++ b/Lib/test/test_free_threading/test_kwargs_unpack.py @@ -0,0 +1,45 @@ +import unittest +from unittest import TestCase + +from test.support import threading_helper, import_helper + +_testcapi = import_helper.import_module("_testcapi") + +threading_helper.requires_working_threading(module=True) + + +class TestKwargsUnpackRace(TestCase): + def test_mutate_kwargs_during_unpack(self): + # gh-86199: unpacking a shared kwargs dict must tolerate another + # thread resizing it. + num_mutators, num_callers = 2, 6 + iters = 1000 + min_keys, max_keys = 4, 3000 + + fastcalldict = _testcapi.pyobject_fastcalldict + + def target(**kwargs): + return len(kwargs) + + shared = {f"k{i}": i for i in range(min_keys)} + + def resize_kwargs(): + for _ in range(iters): + for i in range(min_keys, max_keys): + shared[f"k{i}"] = i + for i in range(max_keys - 1, min_keys - 1, -1): + shared.pop(f"k{i}", None) + + def call_target(): + for _ in range(iters): + try: + fastcalldict(target, (), shared) + except Exception: + pass + + threading_helper.run_concurrently( + [resize_kwargs] * num_mutators + [call_target] * num_callers) + + +if __name__ == "__main__": + unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-24-23-59-00.gh-issue-86199.pQk3vX.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-24-23-59-00.gh-issue-86199.pQk3vX.rst new file mode 100644 index 000000000000000..6236163b5b46582 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-24-23-59-00.gh-issue-86199.pQk3vX.rst @@ -0,0 +1,6 @@ +Speed up calls of the form ``f(**kwargs)`` by no longer always copying the +keyword arguments before the call; such calls are now up to 1.4x faster. +This also fixes :c:func:`PyObject_Call` to copy the keyword arguments before +passing them to a callee that would otherwise receive the caller's dict, so a +called object can no longer mutate the caller's dict (:gh:`86795`), and makes +unpacking a shared keyword-arguments dict safe on the free-threaded build. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 7a75e80298fcd82..de735eadc21ab11 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -2769,6 +2769,7 @@ _PyStackRef func_st; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef null; _PyStackRef callargs_st; _PyStackRef kwargs_st; @@ -2786,6 +2787,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = func_st; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -2812,10 +2814,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _CALL_FUNCTION_EX_NON_PY_GENERAL { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func; @@ -2882,6 +2924,7 @@ static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef callargs_st; _PyStackRef kwargs_st; @@ -2898,6 +2941,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = stack_pointer[-4]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -2924,6 +2968,46 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _CHECK_IS_PY_CALLABLE_EX { @@ -2942,7 +3026,7 @@ } // _PY_FRAME_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); @@ -3015,6 +3099,7 @@ opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef null; _PyStackRef callargs_st; @@ -3040,6 +3125,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (!PyTuple_CheckExact(callargs_o)) { @@ -3065,10 +3151,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _DO_CALL_FUNCTION_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func; @@ -7216,6 +7342,7 @@ opcode = INSTRUMENTED_CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef null; _PyStackRef callargs_st; @@ -7224,6 +7351,7 @@ /* Skip 1 cache entry */ // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = stack_pointer[-4]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -7250,10 +7378,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _DO_CALL_FUNCTION_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func; diff --git a/Objects/call.c b/Objects/call.c index 9718642473103cf..53c85e0f098dfd1 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -1,6 +1,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgsTstate() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() +#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION() #include "pycore_dict.h" // _PyDict_FromItems() #include "pycore_function.h" // _PyFunction_Vectorcall() definition #include "pycore_modsupport.h" // _Py_VaBuildStack() @@ -358,10 +359,23 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable, return NULL; } - result = (*call)(callable, args, kwargs); + /* Pass tp_call a copy of kwargs so that the callee cannot + mutate the caller's dict (gh-86199, gh-86795). */ + PyObject *kwcopy = NULL; + if (kwargs != NULL && PyDict_GET_SIZE(kwargs) != 0) { + kwcopy = PyDict_Copy(kwargs); + if (kwcopy == NULL) { + _Py_LeaveRecursiveCallTstate(tstate); + return NULL; + } + } + + result = (*call)(callable, args, kwcopy); _Py_LeaveRecursiveCallTstate(tstate); + Py_XDECREF(kwcopy); + return _Py_CheckFunctionResult(tstate, callable, result, NULL); } } @@ -1020,7 +1034,11 @@ _PyStack_UnpackDict(PyThreadState *tstate, assert(kwargs != NULL); assert(PyDict_Check(kwargs)); + PyObject **stack; + PyObject *kwnames; Py_ssize_t nkwargs = PyDict_GET_SIZE(kwargs); + +retry:; /* Check for overflow in the PyMem_Malloc() call below. The subtraction * in this check cannot overflow: both maxnargs and nkwargs are * non-negative signed integers, so their difference fits in the type. */ @@ -1031,13 +1049,13 @@ _PyStack_UnpackDict(PyThreadState *tstate, } /* Add 1 to support PY_VECTORCALL_ARGUMENTS_OFFSET */ - PyObject **stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0])); + stack = PyMem_Malloc((1 + nargs + nkwargs) * sizeof(args[0])); if (stack == NULL) { _PyErr_NoMemory(tstate); return NULL; } - PyObject *kwnames = PyTuple_New(nkwargs); + kwnames = PyTuple_New(nkwargs); if (kwnames == NULL) { PyMem_Free(stack); return NULL; @@ -1051,17 +1069,28 @@ _PyStack_UnpackDict(PyThreadState *tstate, } PyObject **kwstack = stack + nargs; - /* This loop doesn't support lookup function mutating the dictionary - to change its size. It's a deliberate choice for speed, this function is - called in the performance critical hot code. */ + /* Copy the items out under the dict's lock. The loop allocates + nothing, so the critical section is never suspended mid-loop. */ Py_ssize_t pos = 0, i = 0; PyObject *key, *value; unsigned long keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS; - while (PyDict_Next(kwargs, &pos, &key, &value)) { - keys_are_strings &= Py_TYPE(key)->tp_flags; - PyTuple_SET_ITEM(kwnames, i, Py_NewRef(key)); - kwstack[i] = Py_NewRef(value); - i++; + Py_BEGIN_CRITICAL_SECTION(kwargs); + if (PyDict_GET_SIZE(kwargs) == nkwargs) { + while (PyDict_Next(kwargs, &pos, &key, &value)) { + keys_are_strings &= Py_TYPE(key)->tp_flags; + PyTuple_SET_ITEM(kwnames, i, Py_NewRef(key)); + kwstack[i] = Py_NewRef(value); + i++; + } + } + Py_END_CRITICAL_SECTION(); + if (i != nkwargs) { + /* Resized before we locked it; redo with the new size. */ + assert(i == 0); + PyMem_Free(stack - 1); + Py_DECREF(kwnames); + nkwargs = PyDict_GET_SIZE(kwargs); + goto retry; } /* keys_are_strings has the value Py_TPFLAGS_UNICODE_SUBCLASS if that diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fb0cdf4d65e060d..0f938e081e9e659 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5628,13 +5628,33 @@ dummy_func( callargs = PyStackRef_FromPyObjectSteal(tuple_o); PyStackRef_CLOSE(temp); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + PyObject *dict_o = PyDict_New(); + if (dict_o == NULL) { + ERROR_NO_POP(); + } + PyObject *dupkey = NULL; + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + if (err < 0) { + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + Py_XDECREF(dupkey); + Py_DECREF(dict_o); + ERROR_NO_POP(); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + PyStackRef_CLOSE(temp); + } } op(_DO_CALL_FUNCTION_EX, (func_st, null, callargs_st, kwargs_st -- result)) { (void)null; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); - // DICT_MERGE is called before this opcode if there are kwargs. + // _MAKE_CALLARGS_A_TUPLE is called before this op. // It converts all dict subtypes in kwargs into regular dicts. EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func); PyObject *result_o; diff --git a/Python/codegen.c b/Python/codegen.c index bedf3b17c52ce44..3961c5cd7fffa08 100644 --- a/Python/codegen.c +++ b/Python/codegen.c @@ -4475,7 +4475,13 @@ codegen_call_helper_impl(compiler *c, location loc, BUILD_LIST, LIST_APPEND, LIST_EXTEND, 1)); } /* Then keyword arguments */ - if (nkwelts) { + if (nkwelts == 1 && + ((keyword_ty)asdl_seq_GET(keywords, 0))->arg == NULL) + { + /* A lone ** unpacking: CALL_FUNCTION_EX converts it if needed. */ + VISIT(c, expr, ((keyword_ty)asdl_seq_GET(keywords, 0))->value); + } + else if (nkwelts) { /* Has a new dict been pushed */ int have_dict = 0; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9aad9e003765cf8..ab367b01116d5c1 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -20040,18 +20040,20 @@ case _MAKE_CALLARGS_A_TUPLE_r33: { CHECK_CURRENT_CACHED_VALUES(3); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); + _PyStackRef kwargs; _PyStackRef callargs; _PyStackRef func; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; + kwargs = _stack_item_2; callargs = _stack_item_1; func = stack_pointer[-1]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (!PyTuple_CheckExact(callargs_o)) { stack_pointer[0] = _stack_item_0; stack_pointer[1] = callargs; - stack_pointer[2] = _stack_item_2; + stack_pointer[2] = kwargs; stack_pointer += 3; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -20079,7 +20081,54 @@ _PyFrame_StackPointerInvalidate(frame); stack_pointer += -3; } - _tos_cache2 = _stack_item_2; + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[0] = _stack_item_0; + stack_pointer[1] = callargs; + stack_pointer[2] = kwargs; + stack_pointer += 3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + SET_CURRENT_CACHED_VALUES(0); + JUMP_TO_ERROR(); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + stack_pointer += -3; + } + _tos_cache2 = kwargs; _tos_cache1 = callargs; _tos_cache0 = _stack_item_0; SET_CURRENT_CACHED_VALUES(3); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 77c18b3d61fefc7..5fcb104a269c906 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2769,6 +2769,7 @@ _PyStackRef func_st; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef null; _PyStackRef callargs_st; _PyStackRef kwargs_st; @@ -2786,6 +2787,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = func_st; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -2812,10 +2814,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _CALL_FUNCTION_EX_NON_PY_GENERAL { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func; @@ -2882,6 +2924,7 @@ static_assert(INLINE_CACHE_ENTRIES_CALL_FUNCTION_EX == 1, "incorrect cache size"); _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef callargs_st; _PyStackRef kwargs_st; @@ -2898,6 +2941,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = stack_pointer[-4]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -2924,6 +2968,46 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _CHECK_IS_PY_CALLABLE_EX { @@ -2942,7 +3026,7 @@ } // _PY_FRAME_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; PyObject *func = PyStackRef_AsPyObjectBorrow(func_st); PyObject *callargs = PyStackRef_AsPyObjectSteal(callargs_st); @@ -3015,6 +3099,7 @@ opcode = CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef null; _PyStackRef callargs_st; @@ -3040,6 +3125,7 @@ } // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); if (!PyTuple_CheckExact(callargs_o)) { @@ -3065,10 +3151,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _DO_CALL_FUNCTION_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func; @@ -7216,6 +7342,7 @@ opcode = INSTRUMENTED_CALL_FUNCTION_EX; _PyStackRef func; _PyStackRef callargs; + _PyStackRef kwargs; _PyStackRef func_st; _PyStackRef null; _PyStackRef callargs_st; @@ -7224,6 +7351,7 @@ /* Skip 1 cache entry */ // _MAKE_CALLARGS_A_TUPLE { + kwargs = stack_pointer[-1]; callargs = stack_pointer[-2]; func = stack_pointer[-4]; PyObject *callargs_o = PyStackRef_AsPyObjectBorrow(callargs); @@ -7250,10 +7378,50 @@ PyStackRef_CLOSE(temp); _PyFrame_StackPointerInvalidate(frame); } + PyObject *kwargs_o = PyStackRef_AsPyObjectBorrow(kwargs); + if (kwargs_o != NULL && !PyDict_CheckExact(kwargs_o)) { + stack_pointer[-2] = callargs; + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyFrame_StackPointerValidate(frame); + PyObject *dict_o = PyDict_New(); + _PyFrame_StackPointerInvalidate(frame); + if (dict_o == NULL) { + JUMP_TO_LABEL(error); + } + PyObject *dupkey = NULL; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + int err = _PyDict_MergeUniq(dict_o, kwargs_o, &dupkey); + _PyFrame_StackPointerInvalidate(frame); + if (err < 0) { + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + _PyEval_FormatKwargsError(tstate, + PyStackRef_AsPyObjectBorrow(func), kwargs_o, dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_XDECREF(dupkey); + _PyFrame_StackPointerInvalidate(frame); + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + Py_DECREF(dict_o); + _PyFrame_StackPointerInvalidate(frame); + JUMP_TO_LABEL(error); + } + assert(dupkey == NULL); + _PyStackRef temp = kwargs; + kwargs = PyStackRef_FromPyObjectSteal(dict_o); + stack_pointer[-1] = kwargs; + assert(stack_pointer == _PyFrame_GetStackPointer(frame)); + _PyFrame_StackPointerValidate(frame); + PyStackRef_CLOSE(temp); + _PyFrame_StackPointerInvalidate(frame); + } } // _DO_CALL_FUNCTION_EX { - kwargs_st = stack_pointer[-1]; + kwargs_st = kwargs; callargs_st = callargs; null = stack_pointer[-3]; func_st = func;