]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0920: writing viminfo fails with a circular reference v8.2.0920
authorBram Moolenaar <Bram@vim.org>
Sun, 7 Jun 2020 14:08:08 +0000 (16:08 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 7 Jun 2020 14:08:08 +0000 (16:08 +0200)
Problem:    Writing viminfo fails with a circular reference.
Solution:   Use copyID to detect the cycle. (closes #6217)

src/testdir/test_viminfo.vim
src/version.c
src/viminfo.c

index b4708f5f893f08b1d6f73f3092ea3e317e9ebf3d..8650598216b1689f9e6dd7bfa5131e971b2fc041 100644 (file)
@@ -91,6 +91,28 @@ func Test_global_vars()
   set viminfo-=!
 endfunc
 
+func Test_global_vars_with_circular_reference()
+  let g:MY_GLOBAL_LIST = []
+  call add(g:MY_GLOBAL_LIST, g:MY_GLOBAL_LIST)
+  let g:MY_GLOBAL_DICT = {}
+  let g:MY_GLOBAL_DICT['self'] = g:MY_GLOBAL_DICT
+
+  set viminfo='100,<50,s10,h,!,nviminfo
+  wv! Xviminfo
+  call assert_equal(v:errmsg, '')
+
+  unlet g:MY_GLOBAL_LIST
+  unlet g:MY_GLOBAL_DICT
+
+  rv! Xviminfo
+  call assert_equal(v:errmsg, '')
+  call assert_true(!exists('g:MY_GLOBAL_LIST'))
+  call assert_true(!exists('g:MY_GLOBAL_DICT'))
+
+  call delete('Xviminfo')
+  set viminfo-=!
+endfunc
+
 func Test_cmdline_history()
   call histdel(':')
   call test_settime(11)
index 36207450c2a14c01d38cebff68f4b58e8d949501..c400df423f81439ef4635e171d4308dc9708fe60 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    920,
 /**/
     919,
 /**/
index b014d7f21562002897d288f650ebdfe731e48f03..4f26348a226ab14873d4af668314f8f7be5725e7 100644 (file)
@@ -1337,8 +1337,34 @@ write_viminfo_varlist(FILE *fp)
                    case VAR_STRING:  s = "STR"; break;
                    case VAR_NUMBER:  s = "NUM"; break;
                    case VAR_FLOAT:   s = "FLO"; break;
-                   case VAR_DICT:    s = "DIC"; break;
-                   case VAR_LIST:    s = "LIS"; break;
+                   case VAR_DICT:
+                         {
+                             dict_T    *di = this_var->di_tv.vval.v_dict;
+                             int       copyID = get_copyID();
+
+                             s = "DIC";
+                             if (di != NULL && !set_ref_in_ht(
+                                                &di->dv_hashtab, copyID, NULL)
+                                     && di->dv_copyID == copyID)
+                                 // has a circular reference, can't turn the
+                                 // value into a string
+                                 continue;
+                             break;
+                         }
+                   case VAR_LIST:
+                         {
+                             list_T    *l = this_var->di_tv.vval.v_list;
+                             int       copyID = get_copyID();
+
+                             s = "LIS";
+                             if (l != NULL && !set_ref_in_list_items(
+                                                              l, copyID, NULL)
+                                     && l->lv_copyID == copyID)
+                                 // has a circular reference, can't turn the
+                                 // value into a string
+                                 continue;
+                             break;
+                         }
                    case VAR_BLOB:    s = "BLO"; break;
                    case VAR_BOOL:    s = "XPL"; break;  // backwards compat.
                    case VAR_SPECIAL: s = "XPL"; break;