changeset 20735:298ef749e5fb v8.2.0920

patch 8.2.0920: writing viminfo fails with a circular reference Commit: https://github.com/vim/vim/commit/5b157fe2edfdce5f77080aeac2b4a03f39eb1c1a Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jun 7 16:08:08 2020 +0200 patch 8.2.0920: writing viminfo fails with a circular reference Problem: Writing viminfo fails with a circular reference. Solution: Use copyID to detect the cycle. (closes https://github.com/vim/vim/issues/6217)
author Bram Moolenaar <Bram@vim.org>
date Sun, 07 Jun 2020 16:15:03 +0200
parents 88e438717510
children a1503031ffe6
files src/testdir/test_viminfo.vim src/version.c src/viminfo.c
diffstat 3 files changed, 52 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_viminfo.vim
+++ b/src/testdir/test_viminfo.vim
@@ -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)
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    920,
+/**/
     919,
 /**/
     918,
--- a/src/viminfo.c
+++ b/src/viminfo.c
@@ -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;