# HG changeset patch # User Bram Moolenaar # Date 1591539303 -7200 # Node ID 298ef749e5fb6129198d2fae4118b08a37fee844 # Parent 88e438717510973c46a7e4258b68e4a7caf25767 patch 8.2.0920: writing viminfo fails with a circular reference Commit: https://github.com/vim/vim/commit/5b157fe2edfdce5f77080aeac2b4a03f39eb1c1a Author: Bram Moolenaar 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) diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim --- 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) diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/viminfo.c b/src/viminfo.c --- 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;