Skip to content

Commit c970e42

Browse files
committed
patch 8.2.2614: Vim9: function is deleted while executing
Problem: Vim9: function is deleted while executing. Solution: increment the call count, when more than zero do not delete the function but mark it as dead. (closes #7977)
1 parent 6ccfd99 commit c970e42

File tree

4 files changed

+65
-9
lines changed

4 files changed

+65
-9
lines changed

src/testdir/test_vim9_script.vim

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,35 @@ def Test_script_reload_change_type()
15611561
delete('Xreload.vim')
15621562
enddef
15631563

1564+
" Define CallFunc so that the test can be compiled
1565+
command CallFunc echo 'nop'
1566+
1567+
def Test_script_reload_from_function()
1568+
var lines =<< trim END
1569+
vim9script
1570+
1571+
if exists('g:loaded')
1572+
finish
1573+
endif
1574+
g:loaded = 1
1575+
delcommand CallFunc
1576+
command CallFunc Func()
1577+
def Func()
1578+
so /tmp/test.vim
1579+
g:didTheFunc = 1
1580+
enddef
1581+
END
1582+
writefile(lines, 'XreloadFunc.vim')
1583+
source XreloadFunc.vim
1584+
CallFunc
1585+
assert_equal(1, g:didTheFunc)
1586+
1587+
delete('XreloadFunc.vim')
1588+
delcommand CallFunc
1589+
unlet g:loaded
1590+
unlet g:didTheFunc
1591+
enddef
1592+
15641593
def Test_script_var_shadows_function()
15651594
var lines =<< trim END
15661595
vim9script

src/userfunc.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,9 +1359,12 @@ func_remove(ufunc_T *fp)
13591359
// function, so we can find the index when defining the function again.
13601360
// Do remove it when it's a copy.
13611361
if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0)
1362+
{
13621363
fp->uf_flags |= FC_DEAD;
1363-
else
1364-
hash_remove(&func_hashtab, hi);
1364+
return FALSE;
1365+
}
1366+
hash_remove(&func_hashtab, hi);
1367+
fp->uf_flags |= FC_DELETED;
13651368
return TRUE;
13661369
}
13671370
return FALSE;
@@ -2134,11 +2137,23 @@ delete_script_functions(int sid)
21342137
int changed = func_hashtab.ht_changed;
21352138

21362139
fp->uf_flags |= FC_DEAD;
2137-
func_clear(fp, TRUE);
2138-
// When clearing a function another function can be cleared
2139-
// as a side effect. When that happens start over.
2140-
if (changed != func_hashtab.ht_changed)
2141-
break;
2140+
2141+
if (fp->uf_calls > 0)
2142+
{
2143+
// Function is executing, don't free it but do remove
2144+
// it from the hashtable.
2145+
if (func_remove(fp))
2146+
fp->uf_refcount--;
2147+
}
2148+
else
2149+
{
2150+
func_clear(fp, TRUE);
2151+
// When clearing a function another function can be
2152+
// cleared as a side effect. When that happens start
2153+
// over.
2154+
if (changed != func_hashtab.ht_changed)
2155+
break;
2156+
}
21422157
}
21432158
--todo;
21442159
}
@@ -4251,7 +4266,6 @@ ex_delfunction(exarg_T *eap)
42514266
// do remove it from the hashtable.
42524267
if (func_remove(fp))
42534268
fp->uf_refcount--;
4254-
fp->uf_flags |= FC_DELETED;
42554269
}
42564270
else
42574271
func_clear_free(fp, FALSE);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ static char *(features[]) =
750750

751751
static int included_patches[] =
752752
{ /* Add new patch number below this line */
753+
/**/
754+
2614,
753755
/**/
754756
2613,
755757
/**/

src/vim9execute.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
323323
else
324324
ectx->ec_outer = NULL;
325325

326+
++ufunc->uf_calls;
327+
326328
// Set execution state to the start of the called function.
327329
ectx->ec_dfunc_idx = cdf_idx;
328330
ectx->ec_instr = INSTRUCTIONS(dfunc);
@@ -556,6 +558,9 @@ func_return(ectx_T *ectx)
556558
}
557559
}
558560
#endif
561+
// TODO: when is it safe to delete the function when it is no longer used?
562+
--dfunc->df_ufunc->uf_calls;
563+
559564
// execution context goes one level up
560565
entry = estack_pop();
561566
if (entry != NULL)
@@ -1334,7 +1339,7 @@ call_def_function(
13341339
++ectx.ec_stack.ga_len;
13351340
}
13361341
if (ufunc->uf_va_name != NULL)
1337-
++ectx.ec_stack.ga_len;
1342+
++ectx.ec_stack.ga_len;
13381343

13391344
// Frame pointer points to just after arguments.
13401345
ectx.ec_frame_idx = ectx.ec_stack.ga_len;
@@ -1407,6 +1412,9 @@ call_def_function(
14071412
// Do turn errors into exceptions.
14081413
suppress_errthrow = FALSE;
14091414

1415+
// Do not delete the function while executing it.
1416+
++ufunc->uf_calls;
1417+
14101418
// When ":silent!" was used before calling then we still abort the
14111419
// function. If ":silent!" is used in the function then we don't.
14121420
emsg_silent_def = emsg_silent;
@@ -3838,6 +3846,9 @@ call_def_function(
38383846
estack_pop();
38393847
current_sctx = save_current_sctx;
38403848

3849+
// TODO: when is it safe to delete the function if it is no longer used?
3850+
--ufunc->uf_calls;
3851+
38413852
if (*msg_list != NULL && saved_msg_list != NULL)
38423853
{
38433854
msglist_T **plist = saved_msg_list;

0 commit comments

Comments
 (0)