diff options
author | Jan Edmund Lazo <jan.lazo@mail.utoronto.ca> | 2021-06-26 10:19:09 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-26 10:19:09 -0400 |
commit | 20dc3f1989dca8aa9b64970f3799e4f48ac080c8 (patch) | |
tree | f851d279fd6730e55e7c751dc319d35286581476 /src | |
parent | ad9c2c069a96b694956fcae3614c637aeb5fa6fe (diff) | |
download | rneovim-20dc3f1989dca8aa9b64970f3799e4f48ac080c8.tar.gz rneovim-20dc3f1989dca8aa9b64970f3799e4f48ac080c8.tar.bz2 rneovim-20dc3f1989dca8aa9b64970f3799e4f48ac080c8.zip |
vim-patch:8.1.0897: can modify a:000 when using a reference (#14902)
Problem: Can modify a:000 when using a reference.
Solution: Make check for locked variable stricter. (Ozaki Kiichi,
closes vim/vim#3930)
https://github.com/vim/vim/commit/05c00c038bc16e862e17f9e5c8d5a72af6cf7788
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 55 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 31 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 33 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_let.vim | 10 |
5 files changed, 84 insertions, 53 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 1b78147ec2..ff019d1e07 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2340,10 +2340,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, true, false) == OK) { if ((di == NULL - || (!var_check_ro(di->di_flags, (const char *)lp->ll_name, - TV_CSTRING) - && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, - TV_CSTRING))) + || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING) + && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING))) && eexe_mod_op(&tv, rettv, op) == OK) { set_var(lp->ll_name, lp->ll_name_len, &tv, false); } @@ -2353,10 +2351,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const); } *endp = cc; - } else if (tv_check_lock(lp->ll_newkey == NULL - ? lp->ll_tv->v_lock - : lp->ll_tv->vval.v_dict->dv_lock, - (const char *)lp->ll_name, TV_CSTRING)) { + } else if (var_check_lock(lp->ll_newkey == NULL + ? lp->ll_tv->v_lock + : lp->ll_tv->vval.v_dict->dv_lock, + lp->ll_name, TV_CSTRING)) { } else if (lp->ll_range) { listitem_T *ll_li = lp->ll_li; int ll_n1 = lp->ll_n1; @@ -2369,9 +2367,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, // Check whether any of the list items is locked for (ri = tv_list_first(rettv->vval.v_list); ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, - (const char *)lp->ll_name, - TV_CSTRING)) { + if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name, + TV_CSTRING)) { return; } ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); @@ -2795,13 +2792,13 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, } else if ((lp->ll_list != NULL // ll_list is not NULL when lvalue is not in a list, NULL lists // yield E689. - && tv_check_lock(tv_list_locked(lp->ll_list), - (const char *)lp->ll_name, - lp->ll_name_len)) + && var_check_lock(tv_list_locked(lp->ll_list), + lp->ll_name, + lp->ll_name_len)) || (lp->ll_dict != NULL - && tv_check_lock(lp->ll_dict->dv_lock, - (const char *)lp->ll_name, - lp->ll_name_len))) { + && var_check_lock(lp->ll_dict->dv_lock, + lp->ll_name, + lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { assert(lp->ll_list != NULL); @@ -2810,9 +2807,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap, listitem_T *last_li = first_li; for (;;) { listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - (const char *)lp->ll_name, - lp->ll_name_len)) { + if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, + lp->ll_name, + lp->ll_name_len)) { return false; } lp->ll_li = li; @@ -2897,11 +2894,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit) dictitem_T *const di = TV_DICT_HI2DI(hi); if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING) || var_check_ro(di->di_flags, (const char *)name, TV_CSTRING) - || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { + || var_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } - if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) { + if (var_check_lock(d->dv_lock, name, TV_CSTRING)) { return FAIL; } @@ -5962,14 +5959,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_LIST) { tv_copy(&argvars[0], rettv); if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg, - TV_TRANSLATE))) { + || (!map + && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) { return; } } else if (argvars[0].v_type == VAR_DICT) { tv_copy(&argvars[0], rettv); if ((d = argvars[0].vval.v_dict) == NULL - || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) { return; } } else { @@ -6002,7 +5999,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) di = TV_DICT_HI2DI(hi); if (map - && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) + && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE) || var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) { break; } @@ -6029,8 +6026,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map - && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, - TV_TRANSLATE)) { + && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, + TV_TRANSLATE)) { break; } vimvars[VV_KEY].vv_nr = idx; @@ -8947,7 +8944,7 @@ static void set_var_const(const char *name, const size_t name_len, // existing variable, need to clear the value if (var_check_ro(v->di_flags, name, name_len) - || tv_check_lock(v->di_tv.v_lock, name, name_len)) { + || var_check_lock(v->di_tv.v_lock, name, name_len)) { return; } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4f9a9fcd68..1ba31bfe68 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -268,7 +268,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; // Default: failed. if (argvars[0].v_type == VAR_LIST) { list_T *const l = argvars[0].vval.v_list; - if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) { + if (!var_check_lock(tv_list_locked(l), N_("add() argument"), + TV_TRANSLATE)) { tv_list_append_tv(l, &argvars[1]); tv_copy(&argvars[0], rettv); } @@ -2277,9 +2278,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr) list = argvars[0].vval.v_list; if (list != NULL - && !tv_check_lock(tv_list_locked(list), - N_("flatten() argument"), - TV_TRANSLATE) + && !var_check_lock(tv_list_locked(list), + N_("flatten() argument"), + TV_TRANSLATE) && tv_list_flatten(list, maxdepth) == OK) { tv_copy(&argvars[0], rettv); } @@ -2299,7 +2300,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *const l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { listitem_T *item; if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -2328,13 +2329,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr) dict_T *const d1 = argvars[0].vval.v_dict; dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { - const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); + const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); } else if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *action = "force"; // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -4845,8 +4846,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "insert()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("insert() argument"), TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("insert() argument"), TV_TRANSLATE)) { long before = 0; if (argvars[2].v_type != VAR_UNKNOWN) { before = tv_get_number_chk(&argvars[2], &error); @@ -7079,7 +7080,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (argvars[2].v_type != VAR_UNKNOWN) { EMSG2(_(e_toomanyarg), "remove()"); } else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { + && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) { const char *key = tv_get_string_chk(&argvars[1]); if (key != NULL) { di = tv_dict_find(d, key, -1); @@ -7098,8 +7099,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } else if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listdictarg), "remove()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - arg_errmsg, TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + arg_errmsg, TV_TRANSLATE)) { bool error = false; idx = tv_get_number_chk(&argvars[1], &error); @@ -7374,8 +7375,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_T *l; if (argvars[0].v_type != VAR_LIST) { EMSG2(_(e_listarg), "reverse()"); - } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), - N_("reverse() argument"), TV_TRANSLATE)) { + } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)), + N_("reverse() argument"), TV_TRANSLATE)) { tv_list_reverse(l); tv_list_set_ret(rettv, l); } @@ -9462,7 +9463,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) EMSG2(_(e_listarg), sort ? "sort()" : "uniq()"); } else { list_T *const l = argvars[0].vval.v_list; - if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { + if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) { goto theend; } tv_list_set_ret(rettv, l); diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4275ff7c61..7221dc8bc9 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1994,7 +1994,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, } else if (*action == 'f' && di2 != di1) { typval_T oldtv; - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) + if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { break; } @@ -2625,7 +2625,7 @@ bool tv_islocked(const typval_T *const tv) /// /// Also gives an error message when typval is locked. /// -/// @param[in] lock Lock status. +/// @param[in] tv Typval. /// @param[in] name Variable name, used in the error message. /// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate /// variable name and compute the length. Use #TV_CSTRING @@ -2639,10 +2639,37 @@ bool tv_islocked(const typval_T *const tv) /// gettext. /// /// @return true if variable is locked, false otherwise. -bool tv_check_lock(const VarLockStatus lock, const char *name, +bool tv_check_lock(const typval_T *tv, const char *name, size_t name_len) FUNC_ATTR_WARN_UNUSED_RESULT { + VarLockStatus lock = VAR_UNLOCKED; + + switch (tv->v_type) { + // case VAR_BLOB: + // if (tv->vval.v_blob != NULL) + // lock = tv->vval.v_blob->bv_lock; + // break; + case VAR_LIST: + if (tv->vval.v_list != NULL) { + lock = tv->vval.v_list->lv_lock; + } + break; + case VAR_DICT: + if (tv->vval.v_dict != NULL) { + lock = tv->vval.v_dict->dv_lock; + } + break; + default: + break; + } + return var_check_lock(tv->v_lock, name, name_len) + || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len)); +} + +/// @return true if variable "name" is locked (immutable) +bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len) +{ const char *error_message = NULL; switch (lock) { case VAR_UNLOCKED: { diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index f5d1b1e870..5ffc06ec44 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2455,13 +2455,13 @@ void ex_function(exarg_T *eap) goto erret; } if (fudi.fd_di == NULL) { - if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, - TV_CSTRING)) { + if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't add a function to a locked dictionary goto erret; } - } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, - TV_CSTRING)) { + } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg, + TV_CSTRING)) { // Can't change an existing function if it is locked goto erret; } diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim index a5cbd8f6a6..6cb736a38a 100644 --- a/src/nvim/testdir/test_let.vim +++ b/src/nvim/testdir/test_let.vim @@ -126,11 +126,16 @@ endfunction func s:set_varg7(...) abort let b = a:000 - call add(b, 1) + let b += [1] endfunction func s:set_varg8(...) abort let b = a:000 + call add(b, 1) +endfunction + +func s:set_varg9(...) abort + let b = a:000 let b[0][0] = 1 endfunction @@ -142,7 +147,8 @@ func Test_let_varg_fail() call s:set_varg5([0]) call assert_fails('call s:set_varg6(1)', 'E742:') call assert_fails('call s:set_varg7(1)', 'E742:') - call s:set_varg8([0]) + call assert_fails('call s:set_varg8(1)', 'E742:') + call s:set_varg9([0]) endfunction func Test_let_utf8_environment() |