diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-09-16 21:08:28 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-09-16 21:08:28 +0200 |
commit | a187c43cfe8863d48b2159d695fedcb71f8525c1 (patch) | |
tree | 40cd63746c55f16dadb674da3d1ffab7f23f291b | |
parent | 7707228aace9aff16434edf5377a354c6ad07316 (diff) | |
download | vim-git-a187c43cfe8863d48b2159d695fedcb71f8525c1.tar.gz |
patch 8.2.1698: cannot lock a variable in legacy Vim script like in Vim9v8.2.1698
Problem: Cannot lock a variable in legacy Vim script like in Vim9.
Solution: Make ":lockvar 0" work.
-rw-r--r-- | runtime/doc/eval.txt | 15 | ||||
-rw-r--r-- | src/dict.c | 4 | ||||
-rw-r--r-- | src/eval.c | 9 | ||||
-rw-r--r-- | src/evalvars.c | 49 | ||||
-rw-r--r-- | src/list.c | 26 | ||||
-rw-r--r-- | src/proto/evalvars.pro | 3 | ||||
-rw-r--r-- | src/testdir/test_const.vim | 8 | ||||
-rw-r--r-- | src/testdir/test_listdict.vim | 13 | ||||
-rw-r--r-- | src/typval.c | 4 | ||||
-rw-r--r-- | src/userfunc.c | 4 | ||||
-rw-r--r-- | src/version.c | 2 |
11 files changed, 93 insertions, 44 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 68884ba1e..7dbf7772a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -12364,7 +12364,9 @@ text... < is equivalent to: > :let x = 1 :lockvar! x -< This is useful if you want to make sure the variable +< NOTE: in Vim9 script `:const` works differently, see + |vim9-const| + This is useful if you want to make sure the variable is not modified. If the value is a List or Dictionary literal then the items also cannot be changed: > const ll = [1, 2, 3] @@ -12404,6 +12406,8 @@ text... [depth] is relevant when locking a |List| or |Dictionary|. It specifies how deep the locking goes: + 0 Lock the variable {name} but not its + value. 1 Lock the |List| or |Dictionary| itself, cannot add or remove items, but can still change their values. @@ -12417,7 +12421,14 @@ text... |Dictionary|, one level deeper. The default [depth] is 2, thus when {name} is a |List| or |Dictionary| the values cannot be changed. - *E743* + + Example with [depth] 0: > + let mylist = [1, 2, 3] + lockvar 0 mylist + let mylist[0] = 77 " OK + call add(mylist, 4] " OK + let mylist = [7, 8, 9] " Error! +< *E743* For unlimited depth use [!] and omit [depth]. However, there is a maximum depth of 100 to catch loops. diff --git a/src/dict.c b/src/dict.c index 73190c451..05281cb70 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1009,7 +1009,7 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action) } else if (*action == 'f' && HI2DI(hi2) != di1) { - if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) + if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) break; clear_tv(&di1->di_tv); @@ -1227,7 +1227,7 @@ dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) if (argvars[2].v_type != VAR_UNKNOWN) semsg(_(e_toomanyarg), "remove()"); else if ((d = argvars[0].vval.v_dict) != NULL - && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) + && !value_check_lock(d->dv_lock, arg_errmsg, TRUE)) { key = tv_get_string_chk(&argvars[1]); if (key != NULL) diff --git a/src/eval.c b/src/eval.c index 79c548809..9b9b7f47c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1055,7 +1055,8 @@ get_lval( } // existing variable, need to check if it can be changed else if ((flags & GLV_READ_ONLY) == 0 - && var_check_ro(lp->ll_di->di_flags, name, FALSE)) + && (var_check_ro(lp->ll_di->di_flags, name, FALSE) + || var_check_lock(lp->ll_di->di_flags, name, FALSE))) { clear_tv(&var1); return NULL; @@ -1220,7 +1221,7 @@ set_var_lval( semsg(_(e_letwrong), op); return; } - if (var_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE)) + if (value_check_lock(lp->ll_blob->bv_lock, lp->ll_name, FALSE)) return; if (lp->ll_range && rettv->v_type == VAR_BLOB) @@ -1297,7 +1298,7 @@ set_var_lval( } *endp = cc; } - else if (var_check_lock(lp->ll_newkey == NULL + else if (value_check_lock(lp->ll_newkey == NULL ? lp->ll_tv->v_lock : lp->ll_tv->vval.v_dict->dv_lock, lp->ll_name, FALSE)) ; @@ -1317,7 +1318,7 @@ set_var_lval( */ for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return; ri = ri->li_next; if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) diff --git a/src/evalvars.c b/src/evalvars.c index 1c40f4111..5ceaca48a 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1560,9 +1560,9 @@ do_unlet_var( *name_end = cc; } else if ((lp->ll_list != NULL - && var_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) + && value_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) || (lp->ll_dict != NULL - && var_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) + && value_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) return FAIL; else if (lp->ll_range) { @@ -1573,7 +1573,7 @@ do_unlet_var( while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (value_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return FAIL; ll_li = li; ++ll_n1; @@ -1646,7 +1646,7 @@ do_unlet(char_u *name, int forceit) di = HI2DI(hi); if (var_check_fixed(di->di_flags, name, FALSE) || var_check_ro(di->di_flags, name, FALSE) - || var_check_lock(d->dv_lock, name, FALSE)) + || value_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); @@ -1677,9 +1677,6 @@ do_lock_var( int cc; dictitem_T *di; - if (deep == 0) // nothing to do - return OK; - if (lp->ll_tv == NULL) { cc = *name_end; @@ -1710,11 +1707,16 @@ do_lock_var( di->di_flags |= DI_FLAGS_LOCK; else di->di_flags &= ~DI_FLAGS_LOCK; - item_lock(&di->di_tv, deep, lock, FALSE); + if (deep != 0) + item_lock(&di->di_tv, deep, lock, FALSE); } } *name_end = cc; } + else if (deep == 0) + { + // nothing to do + } else if (lp->ll_range) { listitem_T *li = lp->ll_li; @@ -3007,8 +3009,13 @@ set_var_const( goto failed; } + // Check in this order for backwards compatibility: + // - Whether the variable is read-only + // - Whether the variable value is locked + // - Whether the variable is locked if (var_check_ro(di->di_flags, name, FALSE) - || var_check_lock(di->di_tv.v_lock, name, FALSE)) + || value_check_lock(di->di_tv.v_lock, name, FALSE) + || var_check_lock(di->di_flags, name, FALSE)) goto failed; } else @@ -3158,6 +3165,22 @@ var_check_ro(int flags, char_u *name, int use_gettext) } /* + * Return TRUE if di_flags "flags" indicates variable "name" is locked. + * Also give an error message. + */ + int +var_check_lock(int flags, char_u *name, int use_gettext) +{ + if (flags & DI_FLAGS_LOCK) + { + semsg(_(e_variable_is_locked_str), + use_gettext ? (char_u *)_(name) : name); + return TRUE; + } + return FALSE; +} + +/* * Return TRUE if di_flags "flags" indicates variable "name" is fixed. * Also give an error message. */ @@ -3204,12 +3227,12 @@ var_wrong_func_name( } /* - * Return TRUE if "flags" indicates variable "name" is locked (immutable). - * Also give an error message, using "name" or _("name") when use_gettext is - * TRUE. + * Return TRUE if "flags" indicates variable "name" has a locked (immutable) + * value. Also give an error message, using "name" or _("name") when + * "use_gettext" is TRUE. */ int -var_check_lock(int lock, char_u *name, int use_gettext) +value_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) { diff --git a/src/list.c b/src/list.c index fa9843ac8..e4f05e52a 100644 --- a/src/list.c +++ b/src/list.c @@ -817,7 +817,7 @@ f_flatten(typval_T *argvars, typval_T *rettv) } l = argvars[0].vval.v_list; - if (l != NULL && !var_check_lock(l->lv_lock, + if (l != NULL && !value_check_lock(l->lv_lock, (char_u *)N_("flatten() argument"), TRUE) && list_flatten(l, maxdepth) == OK) copy_tv(&argvars[0], rettv); @@ -1439,7 +1439,7 @@ list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) int idx; if ((l = argvars[0].vval.v_list) == NULL - || var_check_lock(l->lv_lock, arg_errmsg, TRUE)) + || value_check_lock(l->lv_lock, arg_errmsg, TRUE)) return; idx = (long)tv_get_number_chk(&argvars[1], &error); @@ -1687,7 +1687,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) else { l = argvars[0].vval.v_list; - if (l == NULL || var_check_lock(l->lv_lock, + if (l == NULL || value_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) goto theend; @@ -1955,13 +1955,13 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && var_check_lock(l->lv_lock, arg_errmsg, TRUE))) + || (!map && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) return; } else if (argvars[0].v_type == VAR_DICT) { if ((d = argvars[0].vval.v_dict) == NULL - || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE))) + || (!map && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; } else @@ -2004,7 +2004,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) --todo; di = HI2DI(hi); - if (map && (var_check_lock(di->di_tv.v_lock, + if (map && (value_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) || var_check_ro(di->di_flags, arg_errmsg, TRUE))) @@ -2077,7 +2077,7 @@ filter_map(typval_T *argvars, typval_T *rettv, int map) l->lv_lock = VAR_LOCKED; for (li = l->lv_first; li != NULL; li = nli) { - if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + if (map && value_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; set_vim_var_nr(VV_KEY, idx); @@ -2131,7 +2131,7 @@ f_add(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("add() argument"), TRUE) && list_append_tv(l, &argvars[1]) == OK) copy_tv(&argvars[0], rettv); @@ -2139,7 +2139,7 @@ f_add(typval_T *argvars, typval_T *rettv) else if (argvars[0].v_type == VAR_BLOB) { if ((b = argvars[0].vval.v_blob) != NULL - && !var_check_lock(b->bv_lock, + && !value_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) { int error = FALSE; @@ -2282,7 +2282,7 @@ f_extend(typval_T *argvars, typval_T *rettv) l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE) + if (l1 != NULL && !value_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) @@ -2318,7 +2318,7 @@ f_extend(typval_T *argvars, typval_T *rettv) d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE) + if (d1 != NULL && !value_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) { // Check the third argument. @@ -2402,7 +2402,7 @@ f_insert(typval_T *argvars, typval_T *rettv) else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "insert()"); else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("insert() argument"), TRUE)) { if (argvars[2].v_type != VAR_UNKNOWN) @@ -2475,7 +2475,7 @@ f_reverse(typval_T *argvars, typval_T *rettv) if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "reverse()"); else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, + && !value_check_lock(l->lv_lock, (char_u *)N_("reverse() argument"), TRUE)) { if (l->lv_first == &range_list_item) diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index 010e630d7..569a73ae2 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -68,9 +68,10 @@ void vars_clear_ext(hashtab_T *ht, int free_val); void set_var(char_u *name, typval_T *tv, int copy); void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags); int var_check_ro(int flags, char_u *name, int use_gettext); +int var_check_lock(int flags, char_u *name, int use_gettext); int var_check_fixed(int flags, char_u *name, int use_gettext); int var_wrong_func_name(char_u *name, int new_var); -int var_check_lock(int lock, char_u *name, int use_gettext); +int value_check_lock(int lock, char_u *name, int use_gettext); int valid_varname(char_u *varname); void reset_v_option_vars(void); void assert_error(garray_T *gap); diff --git a/src/testdir/test_const.vim b/src/testdir/test_const.vim index 5eb2f6994..c88efa632 100644 --- a/src/testdir/test_const.vim +++ b/src/testdir/test_const.vim @@ -215,6 +215,14 @@ func Test_lockvar() if 0 | lockvar x | endif let x = 'again' + + let val = [1, 2, 3] + lockvar 0 val + let val[0] = 9 + call assert_equal([9, 2, 3], val) + call add(val, 4) + call assert_equal([9, 2, 3, 4], val) + call assert_fails('let val = [4, 5, 6]', 'E1122:') endfunc diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 694ba0a41..66b84060a 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -354,7 +354,7 @@ endfunc " Locked variables func Test_list_locked_var() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppF'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppppppF'], @@ -381,7 +381,7 @@ func Test_list_locked_var() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try let l[1][1][0] = 99 @@ -425,7 +425,7 @@ func Test_list_locked_var() catch let ps .= 'F' endtry - call assert_equal(expected[depth][u][1], ps) + call assert_equal(expected[depth][u][1], ps, 'depth: ' .. depth) endfor endfor call assert_fails("let x=islocked('a b')", 'E488:') @@ -438,7 +438,7 @@ endfunc " Unletting locked variables func Test_list_locked_var_unlet() let expected = [ - \ [['0000-000', 'ppppppp'], + \ [['1000-000', 'ppppppp'], \ ['0000-000', 'ppppppp'], \ ['0000-000', 'ppppppp']], \ [['1000-000', 'ppFppFp'], @@ -466,7 +466,7 @@ func Test_list_locked_var_unlet() exe "unlockvar " . depth . " l" endif let ps = islocked("l").islocked("l[1]").islocked("l[1][1]").islocked("l[1][1][0]").'-'.islocked("l[2]").islocked("l[2]['6']").islocked("l[2]['6'][7]") - call assert_equal(expected[depth][u][0], ps) + call assert_equal(expected[depth][u][0], ps, 'depth: ' .. depth) let ps = '' try unlet l[2]['6'][7] @@ -666,6 +666,9 @@ func Test_func_arg_list() call s:arg_list_test(1, 2, [3, 4], {5: 6}) endfunc +func Test_dict_item_locked() +endfunc + " Tests for reverse(), sort(), uniq() func Test_reverse_sort_uniq() let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] diff --git a/src/typval.c b/src/typval.c index db52d5973..1db0d77c4 100644 --- a/src/typval.c +++ b/src/typval.c @@ -512,8 +512,8 @@ tv_check_lock(typval_T *tv, char_u *name, int use_gettext) default: break; } - return var_check_lock(tv->v_lock, name, use_gettext) - || (lock != 0 && var_check_lock(lock, name, use_gettext)); + return value_check_lock(tv->v_lock, name, use_gettext) + || (lock != 0 && value_check_lock(lock, name, use_gettext)); } /* diff --git a/src/userfunc.c b/src/userfunc.c index 53514de1d..017098d0f 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3346,11 +3346,11 @@ def_function(exarg_T *eap, char_u *name_arg) if (fudi.fd_di == NULL) { // Can't add a function to a locked dictionary - if (var_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) + if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) goto erret; } // Can't change an existing function if it is locked - else if (var_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) + else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) goto erret; // Give the function a sequential number. Can only be used with a diff --git a/src/version.c b/src/version.c index 4418790c3..ea9a50acb 100644 --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1698, +/**/ 1697, /**/ 1696, |