@@ -19,11 +19,13 @@ static PyObject* pop(PyObject* s){
1919 return NULL ;
2020 }
2121
22+ Py_INCREF (item );
23+
2224 if (PyList_SetSlice (s , size - 1 , size , NULL )){
25+ Py_DECREF (item );
2326 return NULL ;
2427 }
2528
26- Py_INCREF (item );
2729 return item ;
2830}
2931
@@ -44,12 +46,14 @@ static bool is_c_wrapper(PyObject* obj){
4446 * Special function for walking the reachable graph of a function object.
4547 *
4648 * This is necessary because the function object has a pointer to the global
47- * object , and this is problematic because freezing any function will make the
48- * global object immutable, which is not always the desired behaviour .
49+ * dictionary , and this is problematic because freezing any function directly
50+ * (as we do with other objects) would make all globals immutable .
4951 *
50- * This function attempts to find the globals that a function will use, and freeze
51- * just those, and prevent those keys from being updated in the global dictionary
52- * from this point onwards.
52+ * Instead, we walk the function and find any places where it references
53+ * global variables or builtins, and then freeze just those objects. The globals
54+ * and builtins dictionaries for the function are then replaced with frozen
55+ * copies containing just those globals and builtins we were able to determine
56+ * the function uses.
5357 */
5458static PyObject * walk_function (PyObject * op , PyObject * frontier )
5559{
@@ -129,15 +133,15 @@ static PyObject* walk_function(PyObject* op, PyObject* frontier)
129133 }
130134
131135 while (PyList_Size (f_stack ) != 0 ){
132- f_ptr = pop (f_stack ); // fp.rc = x + 1
136+ f_ptr = pop (f_stack );
133137 _PyObject_ASSERT (f_ptr , PyCode_Check (f_ptr ));
134138 f_code = (PyCodeObject * )f_ptr ;
135139
136140 size = 0 ;
137141 if (f_code -> co_names != NULL )
138142 size = PySequence_Fast_GET_SIZE (f_code -> co_names );
139143 for (Py_ssize_t i = 0 ; i < size ; i ++ ){
140- PyObject * name = PySequence_Fast_GET_ITEM (f_code -> co_names , i ); // name.rc = x
144+ PyObject * name = PySequence_Fast_GET_ITEM (f_code -> co_names , i );
141145
142146 if (PyUnicode_CompareWithASCIIString (name , "globals" ) == 0 ){
143147 // if the code calls the globals() builtin, then any
@@ -165,7 +169,7 @@ static PyObject* walk_function(PyObject* op, PyObject* frontier)
165169 return NULL ;
166170 }
167171 }else if (PyDict_Contains (module_dict , name )){
168- PyObject * value = PyDict_GetItem (module_dict , name ); // value.rc = x
172+ PyObject * value = PyDict_GetItem (module_dict , name );
169173
170174 _PyDict_SetKeyImmutable ((PyDictObject * )module_dict , name );
171175
@@ -179,7 +183,7 @@ static PyObject* walk_function(PyObject* op, PyObject* frontier)
179183
180184 size = PySequence_Fast_GET_SIZE (f_code -> co_consts );
181185 for (Py_ssize_t i = 0 ; i < size ; i ++ ){
182- PyObject * value = PySequence_Fast_GET_ITEM (f_code -> co_consts , i ); // value.rc = x
186+ PyObject * value = PySequence_Fast_GET_ITEM (f_code -> co_consts , i );
183187 if (!_Py_IsImmutable (value )){
184188 if (PyCode_Check (value )){
185189 _Py_SetImmutable (value );
@@ -195,6 +199,11 @@ static PyObject* walk_function(PyObject* op, PyObject* frontier)
195199 }
196200
197201 if (check_globals && PyUnicode_Check (value )){
202+ // if the code calls the globals() builtin, then any
203+ // cellvar or const in the function could, potentially, refer to
204+ // a global variable. As such, we need to check if the globals
205+ // dictionary contains that key and then make it immutable
206+ // from this point forwards.
198207 PyObject * name = value ;
199208 if (PyDict_Contains (globals , name )){
200209 value = PyDict_GetItem (globals , name );
@@ -212,13 +221,20 @@ static PyObject* walk_function(PyObject* op, PyObject* frontier)
212221 Py_DECREF (f_stack );
213222
214223 if (check_globals ){
224+ // if the code calls the globals() builtin, then any
225+ // cellvar or const in the function could, potentially, refer to
226+ // a global variable. As such, we need to check if the globals
227+ // dictionary contains that key and then make it immutable
228+ // from this point forwards.
229+ // we need to check the closure for any cellvars that are not
230+ // referenced in the code object, but are still used in the function
215231 size = 0 ;
216232 if (f -> func_closure != NULL )
217233 size = PySequence_Fast_GET_SIZE (f -> func_closure );
218234
219235 for (Py_ssize_t i = 0 ; i < size ; ++ i ){
220- PyObject * cellvar = PySequence_Fast_GET_ITEM (f -> func_closure , i ); // cellvar.rc = x
221- PyObject * value = PyCell_GET (cellvar ); // value.rc = x
236+ PyObject * cellvar = PySequence_Fast_GET_ITEM (f -> func_closure , i );
237+ PyObject * value = PyCell_GET (cellvar );
222238
223239 if (PyUnicode_Check (value )){
224240 PyObject * name = value ;
@@ -271,7 +287,7 @@ static int freeze_visit(PyObject* obj, void* frontier)
271287
272288PyObject * _Py_Freeze (PyObject * obj )
273289{
274- if (_Py_IsImmutable (obj ) && _Py_IsImmutable ( Py_TYPE ( obj )) ){
290+ if (_Py_IsImmutable (obj )){
275291 Py_RETURN_NONE ;
276292 }
277293
@@ -309,7 +325,7 @@ PyObject* _Py_Freeze(PyObject* obj)
309325 Py_DECREF (frozen_importlib );
310326
311327 while (PyList_Size (frontier ) != 0 ){
312- PyObject * item = pop (frontier ); // item.rc = x + 1
328+ PyObject * item = pop (frontier );
313329
314330 if (item == blocking_on ||
315331 item == module_locks ){
@@ -323,12 +339,11 @@ PyObject* _Py_Freeze(PyObject* obj)
323339 PyObject * type_op = NULL ;
324340
325341 if (_Py_IsImmutable (item )){
326- goto handle_type ;
342+ continue ;
327343 }
328344
329345 _Py_SetImmutable (item );
330346
331-
332347 if (is_c_wrapper (item )) {
333348 // C functions are not mutable, so we can skip them.
334349 continue ;
@@ -342,20 +357,20 @@ PyObject* _Py_Freeze(PyObject* obj)
342357 Py_DECREF (frontier );
343358 return err ;
344359 }
345- goto handle_type ;
346360 }
347-
348- traverse = type -> tp_traverse ;
349- if (traverse != NULL ){
350- if (traverse (item , (visitproc )freeze_visit , frontier )){
351- Py_DECREF (blocking_on );
352- Py_DECREF (module_locks );
353- Py_DECREF (frontier );
354- return NULL ;
361+ else
362+ {
363+ traverse = type -> tp_traverse ;
364+ if (traverse != NULL ){
365+ if (traverse (item , (visitproc )freeze_visit , frontier )){
366+ Py_DECREF (blocking_on );
367+ Py_DECREF (module_locks );
368+ Py_DECREF (frontier );
369+ return NULL ;
370+ }
355371 }
356372 }
357373
358- handle_type :
359374 type_op = _PyObject_CAST (item -> ob_type );
360375 if (!_Py_IsImmutable (type_op )){
361376 if (PyList_Append (frontier , type_op ))
0 commit comments