@@ -66,6 +66,171 @@ _Py3kWarn_NextOpcode(void)
6666 return -1 ;
6767}
6868
69+ static PyObject * exec_local_writeback_map = NULL ;
70+
71+ static PyObject *
72+ _get_exec_local_writeback_map (void )
73+ {
74+ if (exec_local_writeback_map == NULL ) {
75+ exec_local_writeback_map = PyDict_New ();
76+ }
77+ return exec_local_writeback_map ;
78+ }
79+
80+ static void
81+ _clear_exec_local_writeback_for_frame (PyFrameObject * f )
82+ {
83+ PyObject * frame_key ;
84+ PyObject * exc_type , * exc_value , * exc_tb ;
85+
86+ if (exec_local_writeback_map == NULL ) {
87+ return ;
88+ }
89+ PyErr_Fetch (& exc_type , & exc_value , & exc_tb );
90+ frame_key = PyLong_FromVoidPtr (f );
91+ if (frame_key == NULL ) {
92+ PyErr_Clear ();
93+ PyErr_Restore (exc_type , exc_value , exc_tb );
94+ return ;
95+ }
96+ if (PyDict_GetItem (exec_local_writeback_map , frame_key ) != NULL &&
97+ PyDict_DelItem (exec_local_writeback_map , frame_key ) < 0 ) {
98+ PyErr_Clear ();
99+ }
100+ Py_DECREF (frame_key );
101+ PyErr_Restore (exc_type , exc_value , exc_tb );
102+ }
103+
104+ static int
105+ _warn_exec_local_writeback (PyFrameObject * f , int oparg )
106+ {
107+ PyObject * frame_key = NULL ;
108+ PyObject * frame_dict = NULL ;
109+ PyObject * entry = NULL ;
110+ PyObject * idx = NULL ;
111+ PyObject * msg = NULL ;
112+ PyObject * name_obj = NULL ;
113+ const char * local_name = NULL ;
114+ int read_lineno = 0 ;
115+ int warn_result ;
116+
117+ if (exec_local_writeback_map == NULL ) {
118+ return 0 ;
119+ }
120+ frame_key = PyLong_FromVoidPtr (f );
121+ if (frame_key == NULL ) {
122+ PyErr_Clear ();
123+ return 0 ;
124+ }
125+ frame_dict = PyDict_GetItem (exec_local_writeback_map , frame_key );
126+ if (frame_dict == NULL ) {
127+ Py_DECREF (frame_key );
128+ return 0 ;
129+ }
130+ idx = PyInt_FromLong (oparg );
131+ if (idx == NULL ) {
132+ Py_DECREF (frame_key );
133+ PyErr_Clear ();
134+ return 0 ;
135+ }
136+ entry = PyDict_GetItem (frame_dict , idx );
137+ if (entry == NULL ) {
138+ Py_DECREF (idx );
139+ Py_DECREF (frame_key );
140+ return 0 ;
141+ }
142+ read_lineno = PyCode_Addr2Line (f -> f_code , f -> f_lasti );
143+ name_obj = PyTuple_GetItem (f -> f_code -> co_varnames , oparg );
144+ if (name_obj != NULL ) {
145+ local_name = PyString_AsString (name_obj );
146+ }
147+ else if (local_name == NULL ) {
148+ local_name = "<local>" ;
149+ }
150+ msg = PyString_FromFormat (
151+ "exec() modified local '%s' which is read later in the enclosing function; "
152+ "in 3.x exec() does not reliably write back to function locals without an explicit locals mapping" ,
153+ local_name );
154+ if (msg == NULL ) {
155+ Py_DECREF (idx );
156+ Py_DECREF (frame_key );
157+ PyErr_Clear ();
158+ return 0 ;
159+ }
160+
161+ warn_result = PyErr_WarnExplicit_WithFix (
162+ PyExc_Py3xWarning ,
163+ PyString_AsString (msg ),
164+ "use exec(code, globals, locals) with an explicit locals mapping" ,
165+ PyString_AsString (f -> f_code -> co_filename ),
166+ read_lineno ,
167+ NULL ,
168+ NULL );
169+
170+ Py_DECREF (msg );
171+ if (warn_result < 0 ) {
172+ Py_DECREF (idx );
173+ Py_DECREF (frame_key );
174+ return -1 ;
175+ }
176+
177+ if (PyDict_DelItem (frame_dict , idx ) < 0 ) {
178+ PyErr_Clear ();
179+ }
180+ Py_DECREF (idx );
181+
182+ if (PyDict_Size (frame_dict ) <= 0 &&
183+ PyDict_DelItem (exec_local_writeback_map , frame_key ) < 0 ) {
184+ PyErr_Clear ();
185+ }
186+ Py_DECREF (frame_key );
187+ return 0 ;
188+ }
189+
190+ static void
191+ _clear_exec_local_writeback_for_local (PyFrameObject * f , int oparg )
192+ {
193+ PyObject * frame_key = NULL ;
194+ PyObject * frame_dict = NULL ;
195+ PyObject * idx = NULL ;
196+ PyObject * exc_type , * exc_value , * exc_tb ;
197+
198+ if (exec_local_writeback_map == NULL ) {
199+ return ;
200+ }
201+ PyErr_Fetch (& exc_type , & exc_value , & exc_tb );
202+ frame_key = PyLong_FromVoidPtr (f );
203+ if (frame_key == NULL ) {
204+ PyErr_Clear ();
205+ PyErr_Restore (exc_type , exc_value , exc_tb );
206+ return ;
207+ }
208+ frame_dict = PyDict_GetItem (exec_local_writeback_map , frame_key );
209+ if (frame_dict == NULL ) {
210+ Py_DECREF (frame_key );
211+ PyErr_Restore (exc_type , exc_value , exc_tb );
212+ return ;
213+ }
214+ idx = PyInt_FromLong (oparg );
215+ if (idx == NULL ) {
216+ Py_DECREF (frame_key );
217+ PyErr_Clear ();
218+ PyErr_Restore (exc_type , exc_value , exc_tb );
219+ return ;
220+ }
221+ if (PyDict_GetItem (frame_dict , idx ) != NULL &&
222+ PyDict_DelItem (frame_dict , idx ) < 0 ) {
223+ PyErr_Clear ();
224+ }
225+ Py_DECREF (idx );
226+ if (PyDict_Size (frame_dict ) <= 0 &&
227+ PyDict_DelItem (exec_local_writeback_map , frame_key ) < 0 ) {
228+ PyErr_Clear ();
229+ }
230+ Py_DECREF (frame_key );
231+ PyErr_Restore (exc_type , exc_value , exc_tb );
232+ }
233+
69234#ifndef WITH_TSC
70235
71236#define READ_TIMESTAMP (var )
@@ -1273,6 +1438,11 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12731438 {
12741439 x = GETLOCAL (oparg );
12751440 if (x != NULL ) {
1441+ if (Py_Py3kWarningFlag &&
1442+ _warn_exec_local_writeback (f , oparg ) < 0 ) {
1443+ err = -1 ;
1444+ break ;
1445+ }
12761446 Py_INCREF (x );
12771447 PUSH (x );
12781448 FAST_DISPATCH ();
@@ -1296,6 +1466,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12961466 {
12971467 v = POP ();
12981468 SETLOCAL (oparg , v );
1469+ if (Py_Py3kWarningFlag ) {
1470+ _clear_exec_local_writeback_for_local (f , oparg );
1471+ }
12991472 FAST_DISPATCH ();
13001473 }
13011474
@@ -2454,6 +2627,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
24542627 x = GETLOCAL (oparg );
24552628 if (x != NULL ) {
24562629 SETLOCAL (oparg , NULL );
2630+ if (Py_Py3kWarningFlag ) {
2631+ _clear_exec_local_writeback_for_local (f , oparg );
2632+ }
24572633 DISPATCH ();
24582634 }
24592635 format_exc_check_arg (
@@ -3404,6 +3580,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
34043580
34053581 /* pop frame */
34063582exit_eval_frame :
3583+ _clear_exec_local_writeback_for_frame (f );
34073584 Py_LeaveRecursiveCall ();
34083585 tstate -> frame = f -> f_back ;
34093586
@@ -5086,6 +5263,12 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
50865263 int n ;
50875264 PyObject * v ;
50885265 int plain = 0 ;
5266+ int track_exec_writeback = 0 ;
5267+ int exec_lineno = 0 ;
5268+ int exec_offset = 0 ;
5269+ int nlocals = 0 ;
5270+ PyObject * * before = NULL ;
5271+ int i ;
50895272
50905273 if (PyTuple_Check (prog ) && globals == Py_None && locals == Py_None &&
50915274 ((n = PyTuple_Size (prog )) == 2 || n == 3 )) {
@@ -5131,6 +5314,25 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
51315314 }
51325315 if (PyDict_GetItemString (globals , "__builtins__" ) == NULL )
51335316 PyDict_SetItemString (globals , "__builtins__" , f -> f_builtins );
5317+
5318+ /* Snapshot fast locals for plain exec so later LOAD_FAST warns only
5319+ about bindings that exec actually wrote back. */
5320+ if (plain && Py_Py3kWarningFlag &&
5321+ (f -> f_code -> co_flags & CO_NEWLOCALS ) &&
5322+ f -> f_code -> co_nlocals > 0 &&
5323+ f -> f_localsplus != NULL ) {
5324+ nlocals = f -> f_code -> co_nlocals ;
5325+ before = PyMem_New (PyObject * , nlocals );
5326+ if (before != NULL ) {
5327+ for (i = 0 ; i < nlocals ; i ++ ) {
5328+ before [i ] = f -> f_localsplus [i ];
5329+ Py_XINCREF (before [i ]);
5330+ }
5331+ track_exec_writeback = 1 ;
5332+ exec_offset = f -> f_lasti ;
5333+ exec_lineno = PyCode_Addr2Line (f -> f_code , exec_offset );
5334+ }
5335+ }
51345336 if (PyCode_Check (prog )) {
51355337 if (PyCode_GetNumFree ((PyCodeObject * )prog ) > 0 ) {
51365338 PyErr_SetString (PyExc_TypeError ,
@@ -5178,6 +5380,67 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
51785380 }
51795381 if (plain )
51805382 PyFrame_LocalsToFast (f , 0 );
5383+ if (track_exec_writeback && v != NULL ) {
5384+ PyObject * frame_key = NULL ;
5385+ PyObject * frame_dict = NULL ;
5386+ PyObject * map = NULL ;
5387+ for (i = 0 ; i < nlocals ; i ++ ) {
5388+ PyObject * before_obj = before [i ];
5389+ PyObject * after_obj = f -> f_localsplus [i ];
5390+ if (before_obj == after_obj ) {
5391+ continue ;
5392+ }
5393+ if (frame_key == NULL ) {
5394+ frame_key = PyLong_FromVoidPtr (f );
5395+ if (frame_key == NULL ) {
5396+ PyErr_Clear ();
5397+ break ;
5398+ }
5399+ }
5400+ if (map == NULL ) {
5401+ map = _get_exec_local_writeback_map ();
5402+ if (map == NULL ) {
5403+ PyErr_Clear ();
5404+ break ;
5405+ }
5406+ }
5407+ if (frame_dict == NULL ) {
5408+ frame_dict = PyDict_GetItem (map , frame_key );
5409+ if (frame_dict == NULL ) {
5410+ frame_dict = PyDict_New ();
5411+ if (frame_dict == NULL ) {
5412+ PyErr_Clear ();
5413+ break ;
5414+ }
5415+ if (PyDict_SetItem (map , frame_key , frame_dict ) < 0 ) {
5416+ PyErr_Clear ();
5417+ Py_DECREF (frame_dict );
5418+ frame_dict = NULL ;
5419+ break ;
5420+ }
5421+ Py_DECREF (frame_dict );
5422+ frame_dict = PyDict_GetItem (map , frame_key );
5423+ }
5424+ }
5425+ if (frame_dict != NULL ) {
5426+ PyObject * idx = PyInt_FromLong (i );
5427+ PyObject * val = Py_BuildValue ("ii" , exec_lineno , exec_offset );
5428+ if (idx == NULL || val == NULL ||
5429+ PyDict_SetItem (frame_dict , idx , val ) < 0 ) {
5430+ PyErr_Clear ();
5431+ }
5432+ Py_XDECREF (idx );
5433+ Py_XDECREF (val );
5434+ }
5435+ }
5436+ Py_XDECREF (frame_key );
5437+ }
5438+ if (before != NULL ) {
5439+ for (i = 0 ; i < nlocals ; i ++ ) {
5440+ Py_XDECREF (before [i ]);
5441+ }
5442+ PyMem_Free (before );
5443+ }
51815444 if (v == NULL )
51825445 return -1 ;
51835446 Py_DECREF (v );
0 commit comments