@@ -111,18 +111,46 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame)
111111 // GH-99729: Clearing this frame can expose the stack (via finalizers). It's
112112 // crucial that this frame has been unlinked, and is no longer visible:
113113 assert (_PyThreadState_GET ()-> current_frame != frame );
114+
115+ // Clear the frame object first
116+ PyFrameObject * f = NULL ;
114117 if (frame -> frame_obj ) {
115- PyFrameObject * f = frame -> frame_obj ;
118+ f = frame -> frame_obj ;
116119 frame -> frame_obj = NULL ;
117120 if (!_PyObject_IsUniquelyReferenced ((PyObject * )f )) {
118121 take_ownership (f , frame );
119122 Py_DECREF (f );
120123 return ;
121124 }
122- Py_DECREF (f );
123125 }
124- _PyFrame_ClearLocals (frame );
126+
127+ // Inline _PyFrame_ClearLocals for better performance
128+ if (frame -> stackpointer != NULL ) {
129+ _PyStackRef * sp = frame -> stackpointer ;
130+ _PyStackRef * locals = frame -> localsplus ;
131+
132+ // Fast path for empty stack
133+ if (sp <= locals ) {
134+ frame -> stackpointer = locals ;
135+ } else {
136+ // Set stackpointer to locals first to avoid reprocessing in case of exception
137+ frame -> stackpointer = locals ;
138+ // Process all stack items at once
139+ do {
140+ sp -- ;
141+ PyStackRef_XCLOSE (* sp );
142+ } while (sp > locals );
143+ }
144+ }
145+
146+ // Clear locals dict and function object
147+ Py_CLEAR (frame -> f_locals );
125148 PyStackRef_CLEAR (frame -> f_funcobj );
149+
150+ // Now it's safe to decref the frame object
151+ if (f ) {
152+ Py_DECREF (f );
153+ }
126154}
127155
128156/* Unstable API functions */
0 commit comments