aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/eval/funcs.c11
-rw-r--r--src/nvim/testdir/test_listdict.vim9
2 files changed, 17 insertions, 3 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 997096aad4..c80ff8f36a 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -7885,7 +7885,7 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
typval_T initial;
typval_T argv[3];
if (argvars[0].v_type == VAR_LIST) {
- const list_T *const l = argvars[0].vval.v_list;
+ list_T *const l = argvars[0].vval.v_list;
const listitem_T *li;
if (argvars[2].v_type == VAR_UNKNOWN) {
@@ -7901,6 +7901,10 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
li = tv_list_first(l);
}
+ const VarLockStatus prev_locked = tv_list_locked(l);
+ const int called_emsg_start = called_emsg;
+
+ tv_list_set_lock(l, VAR_FIXED); // disallow the list changing here
tv_copy(&initial, rettv);
for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) {
argv[0] = *rettv;
@@ -7908,10 +7912,11 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_UNKNOWN;
const int r = call_func(func_name, -1, rettv, 2, argv, &funcexe);
tv_clear(&argv[0]);
- if (r == FAIL) {
- return;
+ if (r == FAIL || called_emsg != called_emsg_start) {
+ break;
}
}
+ tv_list_set_lock(l, prev_locked);
} else {
const blob_T *const b = argvars[0].vval.v_blob;
int i;
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index 42b46fc76c..fdf9123d82 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -649,6 +649,15 @@ func Test_reduce()
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
+
+ let g:lut = [1, 2, 3, 4]
+ func EvilRemove()
+ call remove(g:lut, 1)
+ return 1
+ endfunc
+ call assert_fails("call reduce(g:lut, { acc, val -> EvilRemove() }, 1)", 'E742:')
+ unlet g:lut
+ delfunc EvilRemove
endfunc
" splitting a string to a List