# HG changeset patch # User Bram Moolenaar # Date 1342452713 -7200 # Node ID 085f14642fe828b1cbae5706ca87f2932cebeb75 # Parent 612cfb8dc62877bc2ce46a080f2f5564fc1c8a45 updated for version 7.3.603 Problem: It is possible to add replace builtin functions by calling extend() on g:. Solution: Add a flag to a dict to indicate it is a scope. Check for existing functions. (ZyX) diff --git a/src/buffer.c b/src/buffer.c --- a/src/buffer.c +++ b/src/buffer.c @@ -1747,7 +1747,8 @@ buflist_new(ffname, sfname, lnum, flags) buf->b_wininfo->wi_win = curwin; #ifdef FEAT_EVAL - init_var_dict(&buf->b_vars, &buf->b_bufvar); /* init b: variables */ + /* init b: variables */ + init_var_dict(&buf->b_vars, &buf->b_bufvar, VAR_SCOPE); #endif #ifdef FEAT_SYN_HL hash_init(&buf->b_s.b_keywtab); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -850,8 +850,8 @@ eval_init() int i; struct vimvar *p; - init_var_dict(&globvardict, &globvars_var); - init_var_dict(&vimvardict, &vimvars_var); + init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE); + init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE); vimvardict.dv_lock = VAR_FIXED; hash_init(&compat_hashtab); hash_init(&func_hashtab); @@ -2725,14 +2725,26 @@ get_lval(name, rettv, lp, unlet, skip, q lp->ll_dict = lp->ll_tv->vval.v_dict; lp->ll_di = dict_find(lp->ll_dict, key, len); - /* When assigning to g: check that a function and variable name is - * valid. */ - if (rettv != NULL && lp->ll_dict == &globvardict) - { - if (rettv->v_type == VAR_FUNC + /* When assigning to a scope dictionary check that a function and + * variable name is valid (only variable name unless it is l: or + * g: dictionary). Disallow overwriting a builtin function. */ + if (rettv != NULL && lp->ll_dict->dv_scope != 0) + { + int prevval; + int wrong; + + if (len != -1) + { + prevval = key[len]; + key[len] = NUL; + } + wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE + && rettv->v_type == VAR_FUNC && var_check_func_name(key, lp->ll_di == NULL)) - return NULL; - if (!valid_varname(key)) + || !valid_varname(key); + if (len != -1) + key[len] = prevval; + if (wrong) return NULL; } @@ -6951,7 +6963,7 @@ dict_alloc() d = (dict_T *)alloc(sizeof(dict_T)); if (d != NULL) { - /* Add the list to the list of dicts for garbage collection. */ + /* Add the dict to the list of dicts for garbage collection. */ if (first_dict != NULL) first_dict->dv_used_prev = d; d->dv_used_next = first_dict; @@ -6960,6 +6972,7 @@ dict_alloc() hash_init(&d->dv_hashtab); d->dv_lock = 0; + d->dv_scope = 0; d->dv_refcount = 0; d->dv_copyID = 0; } @@ -10203,6 +10216,19 @@ f_extend(argvars, rettv) { --todo; di1 = dict_find(d1, hi2->hi_key, -1); + if (d1->dv_scope != 0) + { + /* Disallow replacing a builtin function in l: and g:. + * Check the key to be valid when adding to any + * scope. */ + if (d1->dv_scope == VAR_DEF_SCOPE + && HI2DI(hi2)->di_tv.v_type == VAR_FUNC + && var_check_func_name(hi2->hi_key, + di1 == NULL)) + break; + if (!valid_varname(hi2->hi_key)) + break; + } if (di1 == NULL) { di1 = dictitem_copy(HI2DI(hi2)); @@ -20027,7 +20053,7 @@ new_script_vars(id) { sv = SCRIPT_SV(ga_scripts.ga_len + 1) = (scriptvar_T *)alloc_clear(sizeof(scriptvar_T)); - init_var_dict(&sv->sv_dict, &sv->sv_var); + init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE); ++ga_scripts.ga_len; } } @@ -20038,12 +20064,14 @@ new_script_vars(id) * point to it. */ void -init_var_dict(dict, dict_var) +init_var_dict(dict, dict_var, scope) dict_T *dict; dictitem_T *dict_var; + int scope; { hash_init(&dict->dv_hashtab); dict->dv_lock = 0; + dict->dv_scope = scope; dict->dv_refcount = DO_NOT_FREE_CNT; dict->dv_copyID = 0; dict_var->di_tv.vval.v_dict = dict; @@ -22304,7 +22332,7 @@ call_user_func(fp, argcount, argvars, re /* * Init l: variables. */ - init_var_dict(&fc->l_vars, &fc->l_vars_var); + init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE); if (selfdict != NULL) { /* Set l:self to "selfdict". Use "name" to avoid a warning from @@ -22325,7 +22353,7 @@ call_user_func(fp, argcount, argvars, re * Set a:0 to "argcount". * Set a:000 to a list with room for the "..." arguments. */ - init_var_dict(&fc->l_avars, &fc->l_avars_var); + init_var_dict(&fc->l_avars, &fc->l_avars_var, VAR_SCOPE); add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "0", (varnumber_T)(argcount - fp->uf_args.ga_len)); /* Use "name" to avoid a warning from some compiler that checks the diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -93,7 +93,7 @@ long get_tv_number_chk __ARGS((typval_T char_u *get_tv_string_chk __ARGS((typval_T *varp)); char_u *get_var_value __ARGS((char_u *name)); void new_script_vars __ARGS((scid_T id)); -void init_var_dict __ARGS((dict_T *dict, dictitem_T *dict_var)); +void init_var_dict __ARGS((dict_T *dict, dictitem_T *dict_var, int scope)); void vars_clear __ARGS((hashtab_T *ht)); void copy_tv __ARGS((typval_T *from, typval_T *to)); void ex_echo __ARGS((exarg_T *eap)); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1106,6 +1106,11 @@ typedef struct #define VAR_DICT 5 /* "v_dict" is used */ #define VAR_FLOAT 6 /* "v_float" is used */ +/* Values for "dv_scope". */ +#define VAR_SCOPE 1 /* a:, v:, s:, etc. scope dictionaries */ +#define VAR_DEF_SCOPE 2 /* l:, g: scope dictionaries: here funcrefs are not + allowed to mask existing functions */ + /* Values for "v_lock". */ #define VAR_LOCKED 1 /* locked with lock(), can use unlock() */ #define VAR_FIXED 2 /* locked forever */ @@ -1181,6 +1186,7 @@ struct dictvar_S int dv_copyID; /* ID used by deepcopy() */ dict_T *dv_copydict; /* copied dict used by deepcopy() */ char dv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ + char dv_scope; /* zero, VAR_SCOPE, VAR_DEF_SCOPE */ dict_T *dv_used_next; /* next dict in used dicts list */ dict_T *dv_used_prev; /* previous dict in used dicts list */ }; diff --git a/src/testdir/test34.in b/src/testdir/test34.in --- a/src/testdir/test34.in +++ b/src/testdir/test34.in @@ -1,5 +1,6 @@ Test for user functions. Also test an mapping calling a function. +Also test that a builtin function cannot be replaced. STARTTEST :so small.vim @@ -58,7 +59,10 @@ XX+-XX ---*--- (one (two -[(one again:$-5,$w! test.out +[(one again:call append(line('$'), max([1, 2, 3])) +:call extend(g:, {'max': function('min')}) +:call append(line('$'), max([1, 2, 3])) +:$-7,$w! test.out :delfunc Table :delfunc Compute :delfunc Expr1 diff --git a/src/testdir/test34.ok b/src/testdir/test34.ok --- a/src/testdir/test34.ok +++ b/src/testdir/test34.ok @@ -4,3 +4,5 @@ XX111-XX 1. one 2. two 1. one again +3 +3 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -715,6 +715,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 603, +/**/ 602, /**/ 601, diff --git a/src/window.c b/src/window.c --- a/src/window.c +++ b/src/window.c @@ -3468,7 +3468,7 @@ alloc_tabpage() # endif #ifdef FEAT_EVAL /* init t: variables */ - init_var_dict(&tp->tp_vars, &tp->tp_winvar); + init_var_dict(&tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); #endif tp->tp_ch_used = p_ch; } @@ -4410,7 +4410,7 @@ win_alloc(after, hidden) #endif #ifdef FEAT_EVAL /* init w: variables */ - init_var_dict(&new_wp->w_vars, &new_wp->w_winvar); + init_var_dict(&new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); #endif #ifdef FEAT_FOLDING foldInitWin(new_wp);