]> granicus.if.org Git - vim/commitdiff
patch 8.1.0798: changing a blob while iterating over it works strangely v8.1.0798
authorBram Moolenaar <Bram@vim.org>
Wed, 23 Jan 2019 20:56:21 +0000 (21:56 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 23 Jan 2019 20:56:21 +0000 (21:56 +0100)
Problem:    Changing a blob while iterating over it works strangely.
Solution:   Make a copy of the Blob before iterating.

src/blob.c
src/eval.c
src/proto/blob.pro
src/testdir/test_blob.vim
src/version.c

index bba9989136b0a79725f8375c32947e74b2699b8c..a95403033006de7a967c1b5996f710bf7142041f 100644 (file)
@@ -57,6 +57,28 @@ rettv_blob_set(typval_T *rettv, blob_T *b)
        ++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)
 {
index d1a7fd37dd32a04df92594dd15bd536ecd8998ce..dac086f8567a1fd915fc9faa4c3faf498aba2aeb 100644 (file)
@@ -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
+               fi->fi_bi = 0;
+               if (tv.vval.v_blob != NULL)
                {
-                   // 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;
+                   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, int use_gettext)
 /*
  * 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;
index 1c645248634dcae8e6c6fda7f438893f9d118872..b8e48deeec411af365219d1d4cf6cc689a565690 100644 (file)
@@ -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);
index 964ed22799cad75b5552db673e672650b61b4344..9eb2057740c325484c211d4312fcc02e6cca900f 100644 (file)
@@ -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()
index e22ebcb375d1ade948afc36ec5b7279712ae3a99..a260844d25b632098f697a40e832455168838a99 100644 (file)
@@ -791,6 +791,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    798,
 /**/
     797,
 /**/