diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 9e1599a76485646..e8a985ca37a35ff 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -43,6 +43,7 @@ typedef struct { PyObject *func_annotations; /* Annotations, a dict or NULL */ PyObject *func_annotate; /* Callable to fill the annotations dictionary */ PyObject *func_typeparams; /* Tuple of active type variables or NULL */ + PyObject *func_old_codes; /* List of past code objects or NULL */ vectorcallfunc vectorcall; /* Version number for use by specializer. * Can set to non-zero when we want to specialize. diff --git a/Include/internal/pycore_interpframe.h b/Include/internal/pycore_interpframe.h index 9809cd292995f0b..3841f89317c1542 100644 --- a/Include/internal/pycore_interpframe.h +++ b/Include/internal/pycore_interpframe.h @@ -18,10 +18,9 @@ extern "C" { ((int)((IF)->instr_ptr - _PyFrame_GetBytecode((IF)))) static inline PyCodeObject *_PyFrame_GetCode(_PyInterpreterFrame *f) { - assert(!PyStackRef_IsNull(f->f_executable)); - PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); - assert(PyCode_Check(executable)); - return (PyCodeObject *)executable; + assert(f->f_executable != NULL); + assert(PyCode_Check(f->f_executable)); + return (PyCodeObject *)f->f_executable; } // Similar to _PyFrame_GetCode(), but return NULL if the frame is invalid or @@ -36,7 +35,7 @@ _PyFrame_SafeGetCode(_PyInterpreterFrame *f) return NULL; } - if (PyStackRef_IsNull(f->f_executable)) { + if (f->f_executable == NULL) { return NULL; } void *ptr; @@ -44,7 +43,7 @@ _PyFrame_SafeGetCode(_PyInterpreterFrame *f) if (_PyMem_IsPtrFreed(ptr)) { return NULL; } - PyObject *executable = PyStackRef_AsPyObjectBorrow(f->f_executable); + PyObject *executable = f->f_executable; if (_PyObject_IsFreed(executable)) { return NULL; } @@ -132,7 +131,7 @@ _PyFrame_NumSlotsForCodeObject(PyCodeObject *code) static inline void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) { - dest->f_executable = PyStackRef_MakeHeapSafe(src->f_executable); + dest->f_executable = src->f_executable; // Don't leave a dangling pointer to the old frame when creating generators // and coroutines: dest->previous = NULL; @@ -191,7 +190,7 @@ _PyFrame_Initialize( { frame->previous = previous; frame->f_funcobj = func; - frame->f_executable = PyStackRef_FromPyObjectNew(code); + frame->f_executable = (PyObject *)code; PyFunctionObject *func_obj = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(func); frame->f_builtins = func_obj->func_builtins; frame->f_globals = func_obj->func_globals; @@ -424,7 +423,7 @@ _PyFrame_PushTrampolineUnchecked(PyThreadState *tstate, PyCodeObject *code, int assert(tstate->datastack_top < tstate->datastack_limit); frame->previous = previous; frame->f_funcobj = PyStackRef_None; - frame->f_executable = PyStackRef_FromPyObjectNew(code); + frame->f_executable = (PyObject *)code; #ifdef Py_DEBUG frame->f_builtins = NULL; frame->f_globals = NULL; diff --git a/Include/internal/pycore_interpframe_structs.h b/Include/internal/pycore_interpframe_structs.h index 4d267e35504b94d..5487d68e7d0b8b7 100644 --- a/Include/internal/pycore_interpframe_structs.h +++ b/Include/internal/pycore_interpframe_structs.h @@ -27,7 +27,7 @@ enum _frameowner { }; struct _PyInterpreterFrame { - _PyStackRef f_executable; /* Deferred or strong reference (code object or None) */ + PyObject *f_executable; /* Borrowed reference (code object or None) */ struct _PyInterpreterFrame *previous; _PyStackRef f_funcobj; /* Deferred or strong reference. Only valid if not on C stack */ PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ diff --git a/Include/internal/pycore_stackref.h b/Include/internal/pycore_stackref.h index 9495ccc8ac38896..e1a78368ce32f47 100644 --- a/Include/internal/pycore_stackref.h +++ b/Include/internal/pycore_stackref.h @@ -770,7 +770,6 @@ PyStackRef_TYPE(_PyStackRef stackref) { STACKREF_CHECK_FUNC(Gen) STACKREF_CHECK_FUNC(Bool) STACKREF_CHECK_FUNC(ExceptionInstance) -STACKREF_CHECK_FUNC(Code) STACKREF_CHECK_FUNC(Function) static inline bool diff --git a/Lib/test/test_ctypes/test_functions.py b/Lib/test/test_ctypes/test_functions.py index 3454b83d43e1e76..2dff6d31d1e53fd 100644 --- a/Lib/test/test_ctypes/test_functions.py +++ b/Lib/test/test_ctypes/test_functions.py @@ -9,6 +9,8 @@ from test.support import import_helper _ctypes_test = import_helper.import_module("_ctypes_test") from _ctypes import _Pointer, _SimpleCData +import gc +import weakref try: @@ -463,6 +465,39 @@ def callback(*args): callback = proto(callback) self.assertRaises(ArgumentError, lambda: callback((1, 2, 3, 4), POINT())) + def test_reassign_code_while_running(self): + freevar1 = None + freevar2 = None + + def replacement(): + freevar1 + freevar2 + return "replacement" + + def original(): + original.__code__ = replacement.__code__ + return "original" + + self.assertEqual(original(), "original") + self.assertEqual(original(), "replacement") + + def test_function_code_object_no_leak(self): + def get_code(i): + ns = {} + exec(f"def f(): return {i}", ns) + return ns["f"].__code__ + + refs = [] + def f(): pass + for i in range(100): + f.__code__ = get_code(i) + refs.append(weakref.ref(f.__code__)) + self.assertEqual(f(), i) + + del f + gc.collect() + self.assertTrue(all(ref() is None for ref in refs)) + if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 1773633730ea001..fd1fa40b73e2149 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1703,7 +1703,7 @@ def func(): check(x, size('3PiccPPP' + INTERPRETER_FRAME + 'P')) # function def func(): pass - check(func, size('16Pi')) + check(func, size('17Pi')) class c(): @staticmethod def foo(): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst new file mode 100644 index 000000000000000..d89d2e278b63eee --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-04-17-04-03.gh-issue-152666.Vo5wJv.rst @@ -0,0 +1,2 @@ +Avoid reference counting of :class:`code` objects when creating and destroying +frames by having functions retain a list of all past :class:`code` objects. diff --git a/Modules/_testinternalcapi/interpreter.c b/Modules/_testinternalcapi/interpreter.c index 237635c5f2bcdba..1c8cb0c8613ed88 100644 --- a/Modules/_testinternalcapi/interpreter.c +++ b/Modules/_testinternalcapi/interpreter.c @@ -84,7 +84,7 @@ Test_EvalFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) entry.frame.f_globals = (PyObject*)0xaaa3; entry.frame.f_builtins = (PyObject*)0xaaa4; #endif - entry.frame.f_executable = PyStackRef_None; + entry.frame.f_executable = NULL; entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR + 1; entry.frame.stackpointer = entry.stack; entry.frame.owner = FRAME_OWNED_BY_INTERPRETER; diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 5f2b1ae5d978aab..f750a78881ddf71 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -8340,7 +8340,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -13245,7 +13245,7 @@ } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); @@ -13254,7 +13254,7 @@ } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. @@ -13707,7 +13707,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e7ac59379dcfbcc..36f64818a1de940 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1939,7 +1939,7 @@ frame_dealloc(PyObject *op) /* Kill all local variables including specials, if we own them */ if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { - PyStackRef_CLEAR(frame->f_executable); + frame->f_executable = NULL; PyStackRef_CLEAR(frame->f_funcobj); Py_CLEAR(frame->f_locals); _PyStackRef *locals = _PyFrame_GetLocalsArray(frame); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 0fffd36ad462dab..1d898ba71f42685 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -145,6 +145,7 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr) op->func_typeparams = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = FUNC_VERSION_UNSET; + op->func_old_codes = NULL; // NOTE: functions created via FrameConstructor do not use deferred // reference counting because they are typically not part of cycles // nor accessed by multiple threads. @@ -223,6 +224,7 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname op->func_typeparams = NULL; op->vectorcall = _PyFunction_Vectorcall; op->func_version = FUNC_VERSION_UNSET; + op->func_old_codes = NULL; if (((code_obj->co_flags & CO_NESTED) == 0) || (code_obj->co_flags & CO_METHOD)) { // Use deferred reference counting for top-level functions, but not @@ -686,6 +688,17 @@ func_set_code(PyObject *self, PyObject *value, void *Py_UNUSED(ignored)) handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value); _PyFunction_ClearVersion(op); + if (op->func_old_codes == NULL) { + op->func_old_codes = PyList_New(0); + if (op->func_old_codes == NULL) { + return -1; + } + } + + if (PyList_Append(op->func_old_codes, op->func_code) < 0) { + return -1; + } + Py_XSETREF(op->func_code, Py_NewRef(value)); return 0; } @@ -1114,6 +1127,7 @@ func_clear(PyObject *self) Py_CLEAR(op->func_annotations); Py_CLEAR(op->func_annotate); Py_CLEAR(op->func_typeparams); + Py_CLEAR(op->func_old_codes); // Don't Py_CLEAR(op->func_code), since code is always required // to be non-NULL. Similarly, name and qualname shouldn't be NULL. // However, name and qualname could be str subclasses, so they @@ -1169,6 +1183,7 @@ func_traverse(PyObject *self, visitproc visit, void *arg) Py_VISIT(f->func_annotate); Py_VISIT(f->func_typeparams); Py_VISIT(f->func_qualname); + Py_VISIT(f->func_old_codes); return 0; } diff --git a/Objects/genobject.c b/Objects/genobject.c index 3cdc06733363d3e..bdc427c1aac99f3 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -94,9 +94,9 @@ gen_traverse(PyObject *self, visitproc visit, void *arg) } } else { - // We still need to visit the code object when the frame is cleared to + // We still need to visit the function object when the frame is cleared to // ensure that it's kept alive if the reference is deferred. - _Py_VISIT_STACKREF(gen->gi_iframe.f_executable); + _Py_VISIT_STACKREF(gen->gi_iframe.f_funcobj); } /* No need to visit cr_origin, because it's just tuples/str/int, so can't participate in a reference cycle. */ @@ -231,7 +231,7 @@ gen_dealloc(PyObject *self) gen_clear_frame(gen); } assert(gen->gi_exc_state.exc_value == NULL); - PyStackRef_CLEAR(gen->gi_iframe.f_executable); + gen->gi_iframe.f_executable = NULL; Py_CLEAR(gen->gi_name); Py_CLEAR(gen->gi_qualname); @@ -1105,7 +1105,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.previous_item = NULL; - gen->gi_iframe.f_executable = PyStackRef_None; + gen->gi_iframe.f_executable = NULL; assert(func->func_name != NULL); gen->gi_name = Py_NewRef(func->func_name); assert(func->func_qualname != NULL); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8ac397081e27384..860d02039d35ef9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1870,7 +1870,7 @@ dummy_func( /* We don't know which of these is relevant here, so keep them equal */ assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -6318,7 +6318,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION__PUSH_FRAME, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { EXIT_IF(true); @@ -6326,7 +6326,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_YIELD_VALUE, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -6335,7 +6335,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_RETURN_VALUE, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -6344,7 +6344,7 @@ dummy_func( } tier2 op(_GUARD_CODE_VERSION_RETURN_GENERATOR, (version/2 -- )) { - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -6638,14 +6638,14 @@ dummy_func( } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { Py_SETREF(tracer->prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. diff --git a/Python/ceval.c b/Python/ceval.c index f3f03b28112137a..6e1d90a26a22227 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1272,7 +1272,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int entry.frame.f_globals = (PyObject*)0xaaa3; entry.frame.f_builtins = (PyObject*)0xaaa4; #endif - entry.frame.f_executable = PyStackRef_None; + entry.frame.f_executable = NULL; entry.frame.instr_ptr = (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS + 1; entry.frame.stackpointer = entry.stack; entry.frame.owner = FRAME_OWNED_BY_INTERPRETER; @@ -1982,7 +1982,7 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) tstate->datastack_top); assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); _PyFrame_ClearExceptCode(frame); - PyStackRef_CLEAR(frame->f_executable); + frame->f_executable = NULL; _PyThreadState_PopFrame(tstate, frame); } @@ -1997,6 +1997,9 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) gen->gi_exc_state.previous_item = NULL; assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); frame->previous = NULL; + if (PyGen_Check(gen)) { + frame->f_funcobj = PyStackRef_DUP(frame->f_funcobj); + } _PyFrame_ClearExceptCode(frame); _PyErr_ClearExcState(&gen->gi_exc_state); // gh-143939: There must not be any escaping calls between setting diff --git a/Python/ceval.h b/Python/ceval.h index 0437ab85c5a6682..74d7b456c375f6d 100644 --- a/Python/ceval.h +++ b/Python/ceval.h @@ -248,7 +248,7 @@ static void lltrace_resume_frame(_PyInterpreterFrame *frame) { PyObject *fobj = PyStackRef_AsPyObjectBorrow(frame->f_funcobj); - if (!PyStackRef_CodeCheck(frame->f_executable) || + if (!PyCode_Check(frame->f_executable) || fobj == NULL || !PyFunction_Check(fobj) ) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 96dea2d2a6d5bc1..79a023e8ed40659 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -9436,7 +9436,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -24053,7 +24053,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24072,7 +24072,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24094,7 +24094,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24119,7 +24119,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { if (true) { @@ -24143,7 +24143,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24163,7 +24163,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24186,7 +24186,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24212,7 +24212,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += 1 + INLINE_CACHE_ENTRIES_SEND; @@ -24237,7 +24237,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24257,7 +24257,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24280,7 +24280,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24306,7 +24306,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24331,7 +24331,7 @@ CHECK_CURRENT_CACHED_VALUES(0); ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24351,7 +24351,7 @@ ASSERT_WITHIN_STACK_BOUNDS_IGNORING_CACHE(__FILE__, __LINE__); _PyStackRef _stack_item_0 = _tos_cache0; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24374,7 +24374,7 @@ _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; @@ -24400,7 +24400,7 @@ _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; uint32_t version = (uint32_t)CURRENT_OPERAND0_32(); - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *code = frame->f_executable; assert(PyCode_Check(code)); if (((PyCodeObject *)code)->co_version != version) { frame->instr_ptr += frame->return_offset; diff --git a/Python/frame.c b/Python/frame.c index ff81eb0b3020c78..b7a4fcf7ac0f96b 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -13,7 +13,6 @@ _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg) Py_VISIT(frame->frame_obj); Py_VISIT(frame->f_locals); _Py_VISIT_STACKREF(frame->f_funcobj); - _Py_VISIT_STACKREF(frame->f_executable); return _PyGC_VisitFrameStack(frame, visit, arg); } @@ -52,9 +51,6 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); _PyInterpreterFrame *new_frame = (_PyInterpreterFrame *)f->_f_frame_data; _PyFrame_Copy(frame, new_frame); - // _PyFrame_Copy takes the reference to the executable, - // so we need to restore it. - new_frame->f_executable = PyStackRef_DUP(new_frame->f_executable); f->f_frame = new_frame; new_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; if (_PyFrame_IsIncomplete(new_frame)) { @@ -132,7 +128,7 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) PyObject * PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame) { - return PyStackRef_AsPyObjectNew(frame->f_executable); + return Py_NewRef(frame->f_executable); } // NOTE: We allow racy accesses to the instruction pointer from other threads diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 4e36189580bbf87..f7d2c47bdd952d2 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -253,8 +253,6 @@ frame_disable_deferred_refcounting(_PyInterpreterFrame *frame) assert(frame->owner == FRAME_OWNED_BY_FRAME_OBJECT || frame->owner == FRAME_OWNED_BY_GENERATOR); - frame->f_executable = PyStackRef_AsStrongReference(frame->f_executable); - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); if (gen->gi_frame_state == FRAME_CLEARED) { @@ -467,7 +465,6 @@ gc_visit_thread_stacks(PyInterpreterState *interp, struct collection_state *stat continue; } - gc_visit_stackref(f->f_executable); while (top != f->localsplus) { --top; gc_visit_stackref(*top); @@ -863,7 +860,7 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar } _PyStackRef *top = f->stackpointer; - if (gc_visit_stackref_mark_alive(args, f->f_executable) < 0) { + if (gc_mark_enqueue(f->f_executable, args) < 0) { err = -1; goto exit; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index cc95179ccaab17c..b658b54302790e9 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8339,7 +8339,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; @@ -13242,7 +13242,7 @@ } tracer->prev_state.recorded_count = 0; tracer->prev_state.instr = next_instr; - PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyObject *prev_code = frame->f_executable; if (tracer->prev_state.instr_code != (PyCodeObject *)prev_code) { assert(stack_pointer == _PyFrame_GetStackPointer(frame)); _PyFrame_StackPointerValidate(frame); @@ -13251,7 +13251,7 @@ } tracer->prev_state.instr_frame = frame; tracer->prev_state.instr_oparg = oparg; - tracer->prev_state.instr_stacklevel = PyStackRef_IsNone(frame->f_executable) ? 2 : STACK_LEVEL(); + tracer->prev_state.instr_stacklevel = frame->f_executable == NULL ? 2 : STACK_LEVEL(); if (_PyOpcode_Caches[_PyOpcode_Deopt[opcode]] // Branch opcodes use the cache for branch history, not // specialization counters. Don't reset it. @@ -13704,7 +13704,7 @@ FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg); assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER); #if TIER_ONE && defined(Py_DEBUG) - if (!PyStackRef_IsNone(frame->f_executable)) { + if (frame->f_executable != NULL) { Py_ssize_t i = frame->instr_ptr - _PyFrame_GetBytecode(frame); assert(i >= 0 && i <= INT_MAX); int opcode = _Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), (int)i).op.code; diff --git a/Python/optimizer.c b/Python/optimizer.c index c9f6ebdb62f07b2..d8caeb0a31b2100 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1072,8 +1072,7 @@ _PyJit_translate_single_bytecode_to_trace( DPRINTF(1, "Unknown uop needing guard ip %s\n", _PyOpcode_uop_name[last_opcode]); Py_UNREACHABLE(); } - PyObject *code = PyStackRef_AsPyObjectBorrow(frame->f_executable); - Py_INCREF(code); + PyObject *code = Py_NewRef(frame->f_executable); ADD_TO_TRACE(_RECORD_CODE, 0, (uintptr_t)code, 0); ADD_TO_TRACE(guard_ip, 0, (uintptr_t)next_instr, 0); if (PyCode_Check(code)) { diff --git a/Python/pystate.c b/Python/pystate.c index e90642fa882db72..8e84983b9554383 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1588,7 +1588,7 @@ init_threadstate(_PyThreadStateImpl *_tstate, // Initialize the embedded base frame - sentinel at the bottom of the frame stack _tstate->base_frame.previous = NULL; - _tstate->base_frame.f_executable = PyStackRef_None; + _tstate->base_frame.f_executable = NULL; _tstate->base_frame.f_funcobj = PyStackRef_NULL; _tstate->base_frame.f_globals = NULL; _tstate->base_frame.f_builtins = NULL; diff --git a/Python/specialize.c b/Python/specialize.c index 6aaae6f383d22f5..ea256165ec9db45 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2946,7 +2946,7 @@ _Py_Specialize_Resume(_Py_CODEUNIT *instr, PyThreadState *tstate, _PyInterpreter { if (tstate->tracing == 0 && instr->op.code == RESUME) { if (tstate->interp->jit) { - PyCodeObject *co = (PyCodeObject *)PyStackRef_AsPyObjectBorrow(frame->f_executable); + PyCodeObject *co = (PyCodeObject *)frame->f_executable; if (co != NULL && PyCode_Check(co) && (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) == 0) { diff --git a/Python/tracemalloc.c b/Python/tracemalloc.c index 0afc84e021817cf..012a73d19067443 100644 --- a/Python/tracemalloc.c +++ b/Python/tracemalloc.c @@ -221,7 +221,7 @@ hashtable_compare_traceback(const void *key1, const void *key2) static void tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame) { - assert(PyStackRef_CodeCheck(pyframe->f_executable)); + assert(PyCode_Check(pyframe->f_executable)); frame->filename = &_Py_STR(anon_unknown); int lineno = -1;