# HG changeset patch # User Bram Moolenaar # Date 1549919707 -3600 # Node ID 5b6c3c7feba8335fed7eb5da584fad1a39d84176 # Parent 503f872f0ec3f88a62db3a52c2779ad0aa36b066 patch 8.1.0897: can modify a:000 when using a reference commit https://github.com/vim/vim/commit/05c00c038bc16e862e17f9e5c8d5a72af6cf7788 Author: Bram Moolenaar Date: Mon Feb 11 22:00:11 2019 +0100 patch 8.1.0897: can modify a:000 when using a reference Problem: Can modify a:000 when using a reference. Solution: Make check for locked variable stricter. (Ozaki Kiichi, closes #3930) diff --git a/src/dict.c b/src/dict.c --- a/src/dict.c +++ b/src/dict.c @@ -758,8 +758,8 @@ dict_extend(dict_T *d1, dict_T *d2, char } else if (*action == 'f' && HI2DI(hi2) != di1) { - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) - || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) + if (var_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); copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -247,6 +247,7 @@ static typval_T *alloc_string_tv(char_u static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); +static int tv_check_lock(typval_T *tv, char_u *name, int use_gettext); static char_u *find_option_end(char_u **arg, int *opt_flags); /* for VIM_VERSION_ defines */ @@ -2332,9 +2333,8 @@ set_var_lval( &tv, &di, TRUE, FALSE) == OK) { if ((di == NULL - || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) - && !tv_check_lock(di->di_tv.v_lock, lp->ll_name, - FALSE))) + || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) + && !tv_check_lock(&di->di_tv, lp->ll_name, FALSE))) && tv_op(&tv, rettv, op) == OK) set_var(lp->ll_name, &tv, FALSE); clear_tv(&tv); @@ -2344,7 +2344,7 @@ set_var_lval( set_var(lp->ll_name, rettv, copy); *endp = cc; } - else if (tv_check_lock(lp->ll_newkey == NULL + 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, FALSE)) ; @@ -2358,7 +2358,7 @@ set_var_lval( */ for (ri = rettv->vval.v_list->lv_first; ri != NULL && ll_li != NULL; ) { - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (var_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)) @@ -2951,9 +2951,9 @@ do_unlet_var( *name_end = cc; } else if ((lp->ll_list != NULL - && tv_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) + && var_check_lock(lp->ll_list->lv_lock, lp->ll_name, FALSE)) || (lp->ll_dict != NULL - && tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) + && var_check_lock(lp->ll_dict->dv_lock, lp->ll_name, FALSE))) return FAIL; else if (lp->ll_range) { @@ -2964,7 +2964,7 @@ do_unlet_var( while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { li = ll_li->li_next; - if (tv_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) + if (var_check_lock(ll_li->li_tv.v_lock, lp->ll_name, FALSE)) return FAIL; ll_li = li; ++ll_n1; @@ -3034,7 +3034,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) - || tv_check_lock(d->dv_lock, name, FALSE)) + || var_check_lock(d->dv_lock, name, FALSE)) return FAIL; delete_var(ht, hi); @@ -7866,7 +7866,7 @@ set_var( { /* existing variable, need to clear the value */ if (var_check_ro(v->di_flags, name, FALSE) - || tv_check_lock(v->di_tv.v_lock, name, FALSE)) + || var_check_lock(v->di_tv.v_lock, name, FALSE)) return; /* @@ -8021,31 +8021,12 @@ var_check_func_name( } /* - * Check if a variable name is valid. - * Return FALSE and give an error if not. - */ - int -valid_varname(char_u *varname) -{ - char_u *p; - - for (p = varname; *p != NUL; ++p) - if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p)) - && *p != AUTOLOAD_CHAR) - { - semsg(_(e_illvar), varname); - return FALSE; - } - return TRUE; -} - -/* - * Return TRUE if typeval "tv" is set to be locked (immutable). + * Return TRUE if "flags" indicates variable "name" is locked (immutable). * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ int -tv_check_lock(int lock, char_u *name, int use_gettext) +var_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) { @@ -8067,6 +8048,56 @@ tv_check_lock(int lock, char_u *name, in } /* + * Return TRUE if typeval "tv" and its value are set to be locked (immutable). + * Also give an error message, using "name" or _("name") when use_gettext is + * TRUE. + */ + static int +tv_check_lock(typval_T *tv, char_u *name, int use_gettext) +{ + int lock = 0; + + 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, use_gettext) + || (lock != 0 && var_check_lock(lock, name, use_gettext)); +} + +/* + * Check if a variable name is valid. + * Return FALSE and give an error if not. + */ + int +valid_varname(char_u *varname) +{ + char_u *p; + + for (p = varname; *p != NUL; ++p) + if (!eval_isnamec1(*p) && (p == varname || !VIM_ISDIGIT(*p)) + && *p != AUTOLOAD_CHAR) + { + semsg(_(e_illvar), varname); + return FALSE; + } + return TRUE; +} + +/* * Copy the values from typval_T "from" to typval_T "to". * When needed allocates string or increases reference count. * Does not make a copy of a list, blob or dict but copies the reference! @@ -10711,13 +10742,13 @@ filter_map(typval_T *argvars, typval_T * else if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) == NULL - || (!map && tv_check_lock(l->lv_lock, arg_errmsg, TRUE))) + || (!map && var_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 && tv_check_lock(d->dv_lock, arg_errmsg, TRUE))) + || (!map && var_check_lock(d->dv_lock, arg_errmsg, TRUE))) return; } else @@ -10755,9 +10786,10 @@ filter_map(typval_T *argvars, typval_T * --todo; di = HI2DI(hi); - if (map && - (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TRUE) - || var_check_ro(di->di_flags, arg_errmsg, TRUE))) + if (map && (var_check_lock(di->di_tv.v_lock, + arg_errmsg, TRUE) + || var_check_ro(di->di_flags, + arg_errmsg, TRUE))) break; vimvars[VV_KEY].vv_str = vim_strsave(di->di_key); r = filter_map_one(&di->di_tv, expr, map, &rem); @@ -10813,7 +10845,7 @@ filter_map(typval_T *argvars, typval_T * for (li = l->lv_first; li != NULL; li = nli) { - if (map && tv_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) + if (map && var_check_lock(li->li_tv.v_lock, arg_errmsg, TRUE)) break; nli = li->li_next; vimvars[VV_KEY].vv_nr = idx; diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1248,7 +1248,7 @@ f_add(typval_T *argvars, typval_T *rettv if (argvars[0].v_type == VAR_LIST) { if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, + && !var_check_lock(l->lv_lock, (char_u *)N_("add() argument"), TRUE) && list_append_tv(l, &argvars[1]) == OK) copy_tv(&argvars[0], rettv); @@ -1256,7 +1256,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 - && !tv_check_lock(b->bv_lock, + && !var_check_lock(b->bv_lock, (char_u *)N_("add() argument"), TRUE)) { int error = FALSE; @@ -3579,7 +3579,7 @@ f_extend(typval_T *argvars, typval_T *re l1 = argvars[0].vval.v_list; l2 = argvars[1].vval.v_list; - if (l1 != NULL && !tv_check_lock(l1->lv_lock, arg_errmsg, TRUE) + if (l1 != NULL && !var_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) { if (argvars[2].v_type != VAR_UNKNOWN) @@ -3615,7 +3615,7 @@ f_extend(typval_T *argvars, typval_T *re d1 = argvars[0].vval.v_dict; d2 = argvars[1].vval.v_dict; - if (d1 != NULL && !tv_check_lock(d1->dv_lock, arg_errmsg, TRUE) + if (d1 != NULL && !var_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) { /* Check the third argument. */ @@ -7266,8 +7266,9 @@ f_insert(typval_T *argvars, typval_T *re } else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "insert()"); - else if ((l = argvars[0].vval.v_list) != NULL && !tv_check_lock(l->lv_lock, - (char_u *)N_("insert() argument"), TRUE)) + else if ((l = argvars[0].vval.v_list) != NULL + && !var_check_lock(l->lv_lock, + (char_u *)N_("insert() argument"), TRUE)) { if (argvars[2].v_type != VAR_UNKNOWN) before = (long)tv_get_number_chk(&argvars[2], &error); @@ -9698,7 +9699,7 @@ f_remove(typval_T *argvars, typval_T *re if (argvars[2].v_type != VAR_UNKNOWN) semsg(_(e_toomanyarg), "remove()"); else if ((d = argvars[0].vval.v_dict) != NULL - && !tv_check_lock(d->dv_lock, arg_errmsg, TRUE)) + && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) { key = tv_get_string_chk(&argvars[1]); if (key != NULL) @@ -9781,7 +9782,7 @@ f_remove(typval_T *argvars, typval_T *re else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listdictblobarg), "remove()"); else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, arg_errmsg, TRUE)) + && !var_check_lock(l->lv_lock, arg_errmsg, TRUE)) { idx = (long)tv_get_number_chk(&argvars[1], &error); if (error) @@ -10128,7 +10129,7 @@ f_reverse(typval_T *argvars, typval_T *r if (argvars[0].v_type != VAR_LIST) semsg(_(e_listblobarg), "reverse()"); else if ((l = argvars[0].vval.v_list) != NULL - && !tv_check_lock(l->lv_lock, + && !var_check_lock(l->lv_lock, (char_u *)N_("reverse() argument"), TRUE)) { li = l->lv_last; @@ -12112,7 +12113,7 @@ do_sort_uniq(typval_T *argvars, typval_T else { l = argvars[0].vval.v_list; - if (l == NULL || tv_check_lock(l->lv_lock, + if (l == NULL || var_check_lock(l->lv_lock, (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), TRUE)) goto theend; diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -103,8 +103,8 @@ void set_var(char_u *name, typval_T *tv, int var_check_ro(int flags, char_u *name, int use_gettext); int var_check_fixed(int flags, char_u *name, int use_gettext); int var_check_func_name(char_u *name, int new_var); +int var_check_lock(int lock, char_u *name, int use_gettext); int valid_varname(char_u *varname); -int tv_check_lock(int lock, char_u *name, int use_gettext); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2062,9 +2062,9 @@ func Test_job_tty_in_out() for in_opt in in_opts let x = copy(in_opt) for out_opt in out_opts - call extend(x, out_opt) + let x = extend(copy(x), out_opt) for err_opt in err_opts - call extend(x, err_opt) + let x = extend(copy(x), err_opt) let opts += [extend({'pty': 1}, x)] endfor endfor diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim --- a/src/testdir/test_let.vim +++ b/src/testdir/test_let.vim @@ -142,7 +142,7 @@ func Test_let_varg_fail() call assert_fails('call s:set_varg4(1)', 'E742:') call s:set_varg5([0]) call assert_fails('call s:set_varg6(1)', 'E742:') - " call assert_fails('call s:set_varg7(1)', 'E46:') + call assert_fails('call s:set_varg7(1)', 'E742:') call assert_fails('call s:set_varg8(1)', 'E742:') call s:set_varg9([0]) endfunction diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -2394,11 +2394,11 @@ ex_function(exarg_T *eap) if (fudi.fd_di == NULL) { /* Can't add a function to a locked dictionary */ - if (tv_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) + if (var_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE)) goto erret; } /* Can't change an existing function if it is locked */ - else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE)) + else if (var_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 --- a/src/version.c +++ b/src/version.c @@ -784,6 +784,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 897, +/**/ 896, /**/ 895,