diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-01-23 21:56:21 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-01-23 21:56:21 +0100 |
commit | dd29ea18050284526174b5685781469240f5bc4a (patch) | |
tree | 2dd4aa32dab187e6a6973303da6fcb13c4ba5d38 /src | |
parent | bf821bccf18453b01d25bee53e4954b02a5dd0e6 (diff) | |
download | vim-git-dd29ea18050284526174b5685781469240f5bc4a.tar.gz |
patch 8.1.0798: changing a blob while iterating over it works strangelyv8.1.0798
Problem: Changing a blob while iterating over it works strangely.
Solution: Make a copy of the Blob before iterating.
Diffstat (limited to 'src')
-rw-r--r-- | src/blob.c | 22 | ||||
-rw-r--r-- | src/eval.c | 34 | ||||
-rw-r--r-- | src/proto/blob.pro | 1 | ||||
-rw-r--r-- | src/testdir/test_blob.vim | 14 | ||||
-rw-r--r-- | src/version.c | 2 |
5 files changed, 50 insertions, 23 deletions
diff --git a/src/blob.c b/src/blob.c index bba998913..a95403033 100644 --- a/src/blob.c +++ b/src/blob.c @@ -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) { diff --git a/src/eval.c b/src/eval.c index d1a7fd37d..dac086f85 100644 --- 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 + 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; diff --git a/src/proto/blob.pro b/src/proto/blob.pro index 1c6452486..b8e48deee 100644 --- 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); diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim index 964ed2279..9eb205774 100644 --- 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() diff --git a/src/version.c b/src/version.c index e22ebcb37..a260844d2 100644 --- 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, |