changeset 27394:69a48bcd1d80 v8.2.4225

patch 8.2.4225: Vim9: depth argument of :lockvar not parsed in :def function Commit: https://github.com/vim/vim/commit/70c43d84be98ab54d3723155dcc4232dc5a5f081 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Jan 26 21:01:15 2022 +0000 patch 8.2.4225: Vim9: depth argument of :lockvar not parsed in :def function Problem: Vim9: depth argument of :lockvar not parsed in :def function. Solution: Parse the optional depth argument. (closes https://github.com/vim/vim/issues/9629) Fix that locking doesn't work for a non-materialize list.
author Bram Moolenaar <Bram@vim.org>
date Wed, 26 Jan 2022 22:15:03 +0100
parents db7fa549029b
children 69ac9f95b436
files src/errors.h src/evalfunc.c src/evalvars.c src/structs.h src/testdir/test_vim9_cmd.vim src/testdir/test_vim9_disassemble.vim src/version.c src/vim9cmds.c src/vim9execute.c
diffstat 9 files changed, 82 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/errors.h
+++ b/src/errors.h
@@ -2859,10 +2859,10 @@ EXTERN char e_assert_fails_fifth_argumen
 	INIT(= N_("E1116: \"assert_fails()\" fifth argument must be a string"));
 EXTERN char e_cannot_use_bang_with_nested_def[]
 	INIT(= N_("E1117: Cannot use ! with nested :def"));
-EXTERN char e_cannot_change_list[]
-	INIT(= N_("E1118: Cannot change list"));
-EXTERN char e_cannot_change_list_item[]
-	INIT(= N_("E1119: Cannot change list item"));
+EXTERN char e_cannot_change_locked_list[]
+	INIT(= N_("E1118: Cannot change locked list"));
+EXTERN char e_cannot_change_locked_list_item[]
+	INIT(= N_("E1119: Cannot change locked list item"));
 EXTERN char e_cannot_change_dict[]
 	INIT(= N_("E1120: Cannot change dict"));
 EXTERN char e_cannot_change_dict_item[]
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -7922,7 +7922,7 @@ range_list_materialize(list_T *list)
 {
     varnumber_T start = list->lv_u.nonmat.lv_start;
     varnumber_T end = list->lv_u.nonmat.lv_end;
-    int	    stride = list->lv_u.nonmat.lv_stride;
+    int		stride = list->lv_u.nonmat.lv_stride;
     varnumber_T i;
 
     list->lv_first = NULL;
@@ -7930,8 +7930,13 @@ range_list_materialize(list_T *list)
     list->lv_len = 0;
     list->lv_u.mat.lv_idx_item = NULL;
     for (i = start; stride > 0 ? i <= end : i >= end; i += stride)
+    {
 	if (list_append_number(list, (varnumber_T)i) == FAIL)
 	    break;
+	if (list->lv_lock & VAR_ITEMS_LOCKED)
+	    list->lv_u.mat.lv_last->li_tv.v_lock = VAR_LOCKED;
+    }
+    list->lv_lock &= ~VAR_ITEMS_LOCKED;
 }
 
 /*
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -2060,10 +2060,18 @@ item_lock(typval_T *tv, int deep, int lo
 		    l->lv_lock |= VAR_LOCKED;
 		else
 		    l->lv_lock &= ~VAR_LOCKED;
-		if ((deep < 0 || deep > 1) && l->lv_first != &range_list_item)
-		    // recursive: lock/unlock the items the List contains
-		    FOR_ALL_LIST_ITEMS(l, li)
-			item_lock(&li->li_tv, deep - 1, lock, check_refcount);
+		if (deep < 0 || deep > 1)
+		{
+		    if (l->lv_first == &range_list_item)
+			l->lv_lock |= VAR_ITEMS_LOCKED;
+		    else
+		    {
+			// recursive: lock/unlock the items the List contains
+			CHECK_LIST_MATERIALIZE(l);
+			FOR_ALL_LIST_ITEMS(l, li) item_lock(&li->li_tv,
+					       deep - 1, lock, check_refcount);
+		    }
+		}
 	    }
 	    break;
 	case VAR_DICT:
--- a/src/structs.h
+++ b/src/structs.h
@@ -1464,8 +1464,9 @@ typedef struct
 			// 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
+#define VAR_LOCKED	    1	// locked with lock(), can use unlock()
+#define VAR_FIXED	    2	// locked forever
+#define VAR_ITEMS_LOCKED    4	// items of non-materialized list locked
 
 /*
  * Structure to hold an item of a list: an internal variable without a name.
@@ -1497,7 +1498,8 @@ struct listwatch_S
  */
 struct listvar_S
 {
-    listitem_T	*lv_first;	// first item, NULL if none
+    listitem_T	*lv_first;	// first item, NULL if none, &range_list_item
+				// for a non-materialized list
     listwatch_T	*lv_watch;	// first watcher, NULL if none
     union {
 	struct {	// used for non-materialized range list:
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1396,6 +1396,35 @@ def Test_lockvar()
     lockvar whatever
   endif
 
+  g:lockme = [1, 2, 3]
+  lockvar 1 g:lockme
+  g:lockme[1] = 77
+  assert_equal([1, 77, 3], g:lockme)
+
+  lockvar 2 g:lockme
+  var caught = false
+  try
+    g:lockme[1] = 99
+  catch /E1119:/
+    caught = true
+  endtry
+  assert_true(caught)
+  assert_equal([1, 77, 3], g:lockme)
+  unlet g:lockme
+
+  # also for non-materialized list
+  g:therange = range(3)
+  lockvar 2 g:therange
+  caught = false
+  try
+    g:therange[1] = 99
+  catch /E1119:/
+    caught = true
+  endtry
+  assert_true(caught)
+  assert_equal([0, 1, 2], g:therange)
+  unlet g:therange
+
   var d = {a: 1, b: 2}
   d.a = 3
   d.b = 4
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -625,7 +625,7 @@ def Test_disassemble_locl_local()
         '\d STORE $0\_s*' ..
         'lockvar d.a\_s*' ..
         '\d LOAD $0\_s*' ..
-        '\d LOCKUNLOCK lockvar d.a\_s*',
+        '\d LOCKUNLOCK lockvar 2 d.a\_s*',
         res)
 enddef
 
--- 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 */
 /**/
+    4225,
+/**/
     4224,
 /**/
     4223,
--- a/src/vim9cmds.c
+++ b/src/vim9cmds.c
@@ -178,7 +178,7 @@ compile_lock_unlock(
     lval_T  *lvp,
     char_u  *name_end,
     exarg_T *eap,
-    int	    deep UNUSED,
+    int	    deep,
     void    *coookie)
 {
     cctx_T	*cctx = coookie;
@@ -223,8 +223,9 @@ compile_lock_unlock(
 	ret = FAIL;
     else
     {
-	vim_snprintf((char *)buf, len, "%s %s",
+	vim_snprintf((char *)buf, len, "%s %d %s",
 		eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
+		deep,
 		p);
 	ret = generate_EXEC_copy(cctx, isn, buf);
 
@@ -241,7 +242,23 @@ compile_lock_unlock(
     char_u *
 compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx)
 {
-    ex_unletlock(eap, arg, 0, GLV_NO_AUTOLOAD | GLV_COMPILING,
+    int	    deep = 0;
+    char_u  *p = arg;
+
+    if (eap->cmdidx != CMD_unlet)
+    {
+	if (eap->forceit)
+	    deep = -1;
+	else if (vim_isdigit(*p))
+	{
+	    deep = getdigits(&p);
+	    p = skipwhite(p);
+	}
+	else
+	    deep = 2;
+    }
+
+    ex_unletlock(eap, p, deep, GLV_NO_AUTOLOAD | GLV_COMPILING,
 	    eap->cmdidx == CMD_unlet ? compile_unlet : compile_lock_unlock,
 	    cctx);
     return eap->nextcmd == NULL ? (char_u *)"" : eap->nextcmd;
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1754,7 +1754,8 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	{
 	    listitem_T *li = list_find(list, lidx);
 
-	    if (error_if_locked(li->li_tv.v_lock, e_cannot_change_list_item))
+	    if (error_if_locked(li->li_tv.v_lock,
+					     e_cannot_change_locked_list_item))
 		return FAIL;
 	    // overwrite existing list item
 	    clear_tv(&li->li_tv);
@@ -1762,7 +1763,7 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	}
 	else
 	{
-	    if (error_if_locked(list->lv_lock, e_cannot_change_list))
+	    if (error_if_locked(list->lv_lock, e_cannot_change_locked_list))
 		return FAIL;
 	    // append to list, only fails when out of memory
 	    if (list_append_tv(list, tv) == FAIL)