changeset 15581:c2382f0d1279 v8.1.0798

patch 8.1.0798: changing a blob while iterating over it works strangely commit https://github.com/vim/vim/commit/dd29ea18050284526174b5685781469240f5bc4a Author: Bram Moolenaar <Bram@vim.org> Date: Wed Jan 23 21:56:21 2019 +0100 patch 8.1.0798: changing a blob while iterating over it works strangely Problem: Changing a blob while iterating over it works strangely. Solution: Make a copy of the Blob before iterating.
author Bram Moolenaar <Bram@vim.org>
date Wed, 23 Jan 2019 22:00:06 +0100
parents 0bf6ce525d42
children 01eb15a644b6
files src/blob.c src/eval.c src/proto/blob.pro src/testdir/test_blob.vim src/version.c
diffstat 5 files changed, 52 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/blob.c
+++ b/src/blob.c
@@ -57,6 +57,28 @@ rettv_blob_set(typval_T *rettv, blob_T *
 	++b->bv_refcount;
 }
 
+    int
+blob_copy(typval_T *from, typval_T *to)
+{
+    int	    ret = OK;
+
+    to->v_type = VAR_BLOB;
+    if (from->vval.v_blob == NULL)
+	to->vval.v_blob = NULL;
+    else if (rettv_blob_alloc(to) == FAIL)
+	ret = FAIL;
+    else
+    {
+	int  len = from->vval.v_blob->bv_ga.ga_len;
+
+	if (len > 0)
+	    to->vval.v_blob->bv_ga.ga_data =
+			    vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
+	to->vval.v_blob->bv_ga.ga_len = len;
+    }
+    return ret;
+}
+
     void
 blob_free(blob_T *b)
 {
--- a/src/eval.c
+++ b/src/eval.c
@@ -2587,7 +2587,6 @@ eval_for_line(
     char_u	*expr;
     typval_T	tv;
     list_T	*l;
-    blob_T	*b;
 
     *errp = TRUE;	/* default: there is an error */
 
@@ -2632,16 +2631,17 @@ eval_for_line(
 	    }
 	    else if (tv.v_type == VAR_BLOB)
 	    {
-		b = tv.vval.v_blob;
-		if (b == NULL)
-		    clear_tv(&tv);
-		else
-		{
-		    // No need to increment the refcount, it's already set for
-		    // the blob being used in "tv".
-		    fi->fi_blob = b;
-		    fi->fi_bi = 0;
-		}
+		fi->fi_bi = 0;
+		if (tv.vval.v_blob != NULL)
+		{
+		    typval_T btv;
+
+		    // Make a copy, so that the iteration still works when the
+		    // blob is changed.
+		    blob_copy(&tv, &btv);
+		    fi->fi_blob = btv.vval.v_blob;
+		}
+		clear_tv(&tv);
 	    }
 	    else
 	    {
@@ -8076,7 +8076,7 @@ tv_check_lock(int lock, char_u *name, in
 /*
  * 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 or dict but copies the reference!
+ * Does not make a copy of a list, blob or dict but copies the reference!
  * It is OK for "from" and "to" to point to the same item.  This is used to
  * make a copy later.
  */
@@ -8216,19 +8216,7 @@ item_copy(
 		ret = FAIL;
 	    break;
 	case VAR_BLOB:
-	    to->v_type = VAR_BLOB;
-	    if (from->vval.v_blob == NULL)
-		to->vval.v_blob = NULL;
-	    else if (rettv_blob_alloc(to) == FAIL)
-		ret = FAIL;
-	    else
-	    {
-		int  len = from->vval.v_blob->bv_ga.ga_len;
-
-		to->vval.v_blob->bv_ga.ga_data =
-			    vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
-		to->vval.v_blob->bv_ga.ga_len = len;
-	    }
+	    ret = blob_copy(from, to);
 	    break;
 	case VAR_DICT:
 	    to->v_type = VAR_DICT;
--- a/src/proto/blob.pro
+++ b/src/proto/blob.pro
@@ -2,6 +2,7 @@
 blob_T *blob_alloc(void);
 int rettv_blob_alloc(typval_T *rettv);
 void rettv_blob_set(typval_T *rettv, blob_T *b);
+int blob_copy(typval_T *from, typval_T *to);
 void blob_free(blob_T *b);
 void blob_unref(blob_T *b);
 long blob_len(blob_T *b);
--- a/src/testdir/test_blob.vim
+++ b/src/testdir/test_blob.vim
@@ -154,6 +154,7 @@ func Test_blob_for_loop()
     call assert_equal(i, byte)
     let i += 1
   endfor
+    call assert_equal(4, i)
 
   let blob = 0z00
   call remove(blob, 0)
@@ -161,6 +162,19 @@ func Test_blob_for_loop()
   for byte in blob
     call assert_error('loop over empty blob')
   endfor
+
+  let blob = 0z0001020304
+  let i = 0
+  for byte in blob
+    call assert_equal(i, byte)
+    if i == 1
+      call remove(blob, 0)
+    elseif i == 3
+      call remove(blob, 3)
+    endif
+    let i += 1
+  endfor
+  call assert_equal(5, i)
 endfunc
 
 func Test_blob_concatenate()
--- a/src/version.c
+++ b/src/version.c
@@ -792,6 +792,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    798,
+/**/
     797,
 /**/
     796,