Skip to content

Commit d7ff57f

Browse files
brammooldouglaskayama
authored andcommitted
patch 7.4.698 Problem: Various problems with locked and fixed lists and dictionaries. Solution: Disallow changing locked items, fix a crash, add tests. (Olaf Dabrunz)
1 parent c90ac66 commit d7ff57f

5 files changed

Lines changed: 272 additions & 18 deletions

File tree

src/eval.c

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3658,7 +3658,10 @@ do_unlet_var(lp, name_end, forceit)
36583658
ret = FAIL;
36593659
*name_end = cc;
36603660
}
3661-
else if (tv_check_lock(lp->ll_tv->v_lock, lp->ll_name))
3661+
else if ((lp->ll_list != NULL
3662+
&& tv_check_lock(lp->ll_list->lv_lock, lp->ll_name))
3663+
|| (lp->ll_dict != NULL
3664+
&& tv_check_lock(lp->ll_dict->dv_lock, lp->ll_name)))
36623665
return FAIL;
36633666
else if (lp->ll_range)
36643667
{
@@ -3709,17 +3712,29 @@ do_unlet(name, forceit)
37093712
hashtab_T *ht;
37103713
hashitem_T *hi;
37113714
char_u *varname;
3715+
dict_T *d;
37123716
dictitem_T *di;
37133717

37143718
ht = find_var_ht(name, &varname);
37153719
if (ht != NULL && *varname != NUL)
37163720
{
3721+
if (ht == &globvarht)
3722+
d = &globvardict;
3723+
else if (current_funccal != NULL
3724+
&& ht == &current_funccal->l_vars.dv_hashtab)
3725+
d = &current_funccal->l_vars;
3726+
else
3727+
{
3728+
di = find_var_in_ht(ht, *name, (char_u *)"", FALSE);
3729+
d = di->di_tv.vval.v_dict;
3730+
}
37173731
hi = hash_find(ht, varname);
37183732
if (!HASHITEM_EMPTY(hi))
37193733
{
37203734
di = HI2DI(hi);
37213735
if (var_check_fixed(di->di_flags, name)
3722-
|| var_check_ro(di->di_flags, name))
3736+
|| var_check_ro(di->di_flags, name)
3737+
|| tv_check_lock(d->dv_lock, name))
37233738
return FAIL;
37243739
delete_var(ht, hi);
37253740
return OK;
@@ -7270,7 +7285,7 @@ dictitem_alloc(key)
72707285
if (di != NULL)
72717286
{
72727287
STRCPY(di->di_key, key);
7273-
di->di_flags = 0;
7288+
di->di_flags = DI_FLAGS_ALLOC;
72747289
}
72757290
return di;
72767291
}
@@ -7289,7 +7304,7 @@ dictitem_copy(org)
72897304
if (di != NULL)
72907305
{
72917306
STRCPY(di->di_key, org->di_key);
7292-
di->di_flags = 0;
7307+
di->di_flags = DI_FLAGS_ALLOC;
72937308
copy_tv(&org->di_tv, &di->di_tv);
72947309
}
72957310
return di;
@@ -7321,7 +7336,8 @@ dictitem_free(item)
73217336
dictitem_T *item;
73227337
{
73237338
clear_tv(&item->di_tv);
7324-
vim_free(item);
7339+
if (item->di_flags & DI_FLAGS_ALLOC)
7340+
vim_free(item);
73257341
}
73267342

73277343
/*
@@ -10482,6 +10498,7 @@ dict_extend(d1, d2, action)
1048210498
dictitem_T *di1;
1048310499
hashitem_T *hi2;
1048410500
int todo;
10501+
char *arg_errmsg = N_("extend() argument");
1048510502

1048610503
todo = (int)d2->dv_hashtab.ht_used;
1048710504
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
@@ -10516,6 +10533,9 @@ dict_extend(d1, d2, action)
1051610533
}
1051710534
else if (*action == 'f' && HI2DI(hi2) != di1)
1051810535
{
10536+
if (tv_check_lock(di1->di_tv.v_lock, (char_u *)_(arg_errmsg))
10537+
|| var_check_ro(di1->di_flags, (char_u *)_(arg_errmsg)))
10538+
break;
1051910539
clear_tv(&di1->di_tv);
1052010540
copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1052110541
}
@@ -10806,13 +10826,13 @@ filter_map(argvars, rettv, map)
1080610826
if (argvars[0].v_type == VAR_LIST)
1080710827
{
1080810828
if ((l = argvars[0].vval.v_list) == NULL
10809-
|| tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg)))
10829+
|| (!map && tv_check_lock(l->lv_lock, (char_u *)_(arg_errmsg))))
1081010830
return;
1081110831
}
1081210832
else if (argvars[0].v_type == VAR_DICT)
1081310833
{
1081410834
if ((d = argvars[0].vval.v_dict) == NULL
10815-
|| tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg)))
10835+
|| (!map && tv_check_lock(d->dv_lock, (char_u *)_(arg_errmsg))))
1081610836
return;
1081710837
}
1081810838
else
@@ -10851,16 +10871,26 @@ filter_map(argvars, rettv, map)
1085110871

1085210872
--todo;
1085310873
di = HI2DI(hi);
10854-
if (tv_check_lock(di->di_tv.v_lock,
10855-
(char_u *)_(arg_errmsg)))
10874+
if (map &&
10875+
(tv_check_lock(di->di_tv.v_lock,
10876+
(char_u *)_(arg_errmsg))
10877+
|| var_check_ro(di->di_flags,
10878+
(char_u *)_(arg_errmsg))))
1085610879
break;
1085710880
vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
1085810881
r = filter_map_one(&di->di_tv, expr, map, &rem);
1085910882
clear_tv(&vimvars[VV_KEY].vv_tv);
1086010883
if (r == FAIL || did_emsg)
1086110884
break;
1086210885
if (!map && rem)
10886+
{
10887+
if (var_check_fixed(di->di_flags,
10888+
(char_u *)_(arg_errmsg))
10889+
|| var_check_ro(di->di_flags,
10890+
(char_u *)_(arg_errmsg)))
10891+
break;
1086310892
dictitem_remove(d, di);
10893+
}
1086410894
}
1086510895
}
1086610896
hash_unlock(ht);
@@ -10871,7 +10901,8 @@ filter_map(argvars, rettv, map)
1087110901

1087210902
for (li = l->lv_first; li != NULL; li = nli)
1087310903
{
10874-
if (tv_check_lock(li->li_tv.v_lock, (char_u *)_(arg_errmsg)))
10904+
if (map && tv_check_lock(li->li_tv.v_lock,
10905+
(char_u *)_(arg_errmsg)))
1087510906
break;
1087610907
nli = li->li_next;
1087710908
vimvars[VV_KEY].vv_nr = idx;
@@ -15847,7 +15878,9 @@ f_remove(argvars, rettv)
1584715878
di = dict_find(d, key, -1);
1584815879
if (di == NULL)
1584915880
EMSG2(_(e_dictkey), key);
15850-
else
15881+
else if (!var_check_fixed(di->di_flags, (char_u *)_(arg_errmsg))
15882+
&& !var_check_ro(di->di_flags,
15883+
(char_u *)_(arg_errmsg)))
1585115884
{
1585215885
*rettv = di->di_tv;
1585315886
init_tv(&di->di_tv);
@@ -21331,7 +21364,7 @@ vars_clear_ext(ht, free_val)
2133121364
v = HI2DI(hi);
2133221365
if (free_val)
2133321366
clear_tv(&v->di_tv);
21334-
if ((v->di_flags & DI_FLAGS_FIX) == 0)
21367+
if (v->di_flags & DI_FLAGS_ALLOC)
2133521368
vim_free(v);
2133621369
}
2133721370
}
@@ -21530,7 +21563,7 @@ set_var(name, tv, copy)
2153021563
vim_free(v);
2153121564
return;
2153221565
}
21533-
v->di_flags = 0;
21566+
v->di_flags = DI_FLAGS_ALLOC;
2153421567
}
2153521568

2153621569
if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT)
@@ -23684,7 +23717,7 @@ call_user_func(fp, argcount, argvars, rettv, firstline, lastline, selfdict)
2368423717
+ STRLEN(name)));
2368523718
if (v == NULL)
2368623719
break;
23687-
v->di_flags = DI_FLAGS_RO;
23720+
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
2368823721
}
2368923722
STRCPY(v->di_key, name);
2369023723
hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));

src/structs.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,10 +1203,11 @@ struct dictitem_S
12031203

12041204
typedef struct dictitem_S dictitem_T;
12051205

1206-
#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
1207-
#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */
1208-
#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed variable, not allocated */
1209-
#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */
1206+
#define DI_FLAGS_RO 1 /* "di_flags" value: read-only variable */
1207+
#define DI_FLAGS_RO_SBX 2 /* "di_flags" value: read-only in the sandbox */
1208+
#define DI_FLAGS_FIX 4 /* "di_flags" value: fixed: no :unlet or remove() */
1209+
#define DI_FLAGS_LOCK 8 /* "di_flags" value: locked variable */
1210+
#define DI_FLAGS_ALLOC 16 /* "di_flags" value: separately allocated */
12101211

12111212
/*
12121213
* Structure to hold info about a Dictionary.

src/testdir/test55.in

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,166 @@ let l = [0, 1, 2, 3]
282282
: $put =ps
283283
: endfor
284284
:endfor
285+
:"
286+
:" Unletting locked variables
287+
:$put ='Unletting:'
288+
:for depth in range(5)
289+
: $put ='depth is ' . depth
290+
: for u in range(3)
291+
: unlet l
292+
: let l = [0, [1, [2, 3]], {4: 5, 6: {7: 8}}]
293+
: exe "lockvar " . depth . " l"
294+
: if u == 1
295+
: exe "unlockvar l"
296+
: elseif u == 2
297+
: exe "unlockvar " . depth . " l"
298+
: endif
299+
: 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]")
300+
: $put =ps
301+
: let ps = ''
302+
: try
303+
: unlet l[2]['6'][7]
304+
: let ps .= 'p'
305+
: catch
306+
: let ps .= 'F'
307+
: endtry
308+
: try
309+
: unlet l[2][6]
310+
: let ps .= 'p'
311+
: catch
312+
: let ps .= 'F'
313+
: endtry
314+
: try
315+
: unlet l[2]
316+
: let ps .= 'p'
317+
: catch
318+
: let ps .= 'F'
319+
: endtry
320+
: try
321+
: unlet l[1][1][0]
322+
: let ps .= 'p'
323+
: catch
324+
: let ps .= 'F'
325+
: endtry
326+
: try
327+
: unlet l[1][1]
328+
: let ps .= 'p'
329+
: catch
330+
: let ps .= 'F'
331+
: endtry
332+
: try
333+
: unlet l[1]
334+
: let ps .= 'p'
335+
: catch
336+
: let ps .= 'F'
337+
: endtry
338+
: try
339+
: unlet l
340+
: let ps .= 'p'
341+
: catch
342+
: let ps .= 'F'
343+
: endtry
344+
: $put =ps
345+
: endfor
346+
:endfor
347+
:"
348+
:" Locked variables and :unlet or list / dict functions
349+
:$put ='Locks and commands or functions:'
350+
:"
351+
:$put ='No :unlet after lock on dict:'
352+
:unlet! d
353+
:let d = {'a': 99, 'b': 100}
354+
:lockvar 1 d
355+
:try
356+
: unlet d.a
357+
: $put ='did :unlet'
358+
:catch
359+
: $put =v:exception[:16]
360+
:endtry
361+
:$put =string(d)
362+
:"
363+
:$put =':unlet after lock on dict item:'
364+
:unlet! d
365+
:let d = {'a': 99, 'b': 100}
366+
:lockvar d.a
367+
:try
368+
: unlet d.a
369+
: $put ='did :unlet'
370+
:catch
371+
: $put =v:exception[:16]
372+
:endtry
373+
:$put =string(d)
374+
:"
375+
:$put ='filter() after lock on dict item:'
376+
:unlet! d
377+
:let d = {'a': 99, 'b': 100}
378+
:lockvar d.a
379+
:try
380+
: call filter(d, 'v:key != "a"')
381+
: $put ='did filter()'
382+
:catch
383+
: $put =v:exception[:16]
384+
:endtry
385+
:$put =string(d)
386+
:"
387+
:$put ='map() after lock on dict:'
388+
:unlet! d
389+
:let d = {'a': 99, 'b': 100}
390+
:lockvar 1 d
391+
:try
392+
: call map(d, 'v:val + 200')
393+
: $put ='did map()'
394+
:catch
395+
: $put =v:exception[:16]
396+
:endtry
397+
:$put =string(d)
398+
:"
399+
:$put ='No extend() after lock on dict item:'
400+
:unlet! d
401+
:let d = {'a': 99, 'b': 100}
402+
:lockvar d.a
403+
:try
404+
: $put =string(extend(d, {'a': 123}))
405+
: $put ='did extend()'
406+
:catch
407+
: $put =v:exception[:14]
408+
:endtry
409+
:$put =string(d)
410+
:"
411+
:$put ='No remove() of write-protected scope-level variable:'
412+
:fun! Tfunc(this_is_a_loooooooooong_parameter_name)
413+
: try
414+
: $put =string(remove(a:, 'this_is_a_loooooooooong_parameter_name'))
415+
: $put ='did remove()'
416+
: catch
417+
: $put =v:exception[:14]
418+
: endtry
419+
:endfun
420+
:call Tfunc('testval')
421+
:"
422+
:$put ='No extend() of write-protected scope-level variable:'
423+
:fun! Tfunc(this_is_a_loooooooooong_parameter_name)
424+
: try
425+
: $put =string(extend(a:, {'this_is_a_loooooooooong_parameter_name': 1234}))
426+
: $put ='did extend()'
427+
: catch
428+
: $put =v:exception[:14]
429+
: endtry
430+
:endfun
431+
:call Tfunc('testval')
432+
:"
433+
:$put ='No :unlet of variable in locked scope:'
434+
:let b:testvar = 123
435+
:lockvar 1 b:
436+
:try
437+
: unlet b:testvar
438+
: $put ='b:testvar was :unlet: '. (!exists('b:testvar'))
439+
:catch
440+
: $put =v:exception[:16]
441+
:endtry
442+
:unlockvar 1 b:
443+
:unlet! b:testvar
444+
:"
285445
:unlet l
286446
:let l = [1, 2, 3, 4]
287447
:lockvar! l

0 commit comments

Comments
 (0)