Skip to content

Commit a05e524

Browse files
committed
patch 8.2.1712: Vim9: leaking memory when calling a lambda
Problem: Vim9: leaking memory when calling a lambda. Solution: Decrement function reference from ISN_DCALL.
1 parent fdeab65 commit a05e524

4 files changed

Lines changed: 31 additions & 10 deletions

File tree

src/proto/userfunc.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *e
1212
ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
1313
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
1414
int func_is_global(ufunc_T *ufunc);
15+
int func_name_refcount(char_u *name);
1516
void copy_func(char_u *lambda, char_u *global);
1617
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
1718
void save_funccal(funccal_entry_T *entry);

src/userfunc.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ cleanup_function_call(funccall_T *fc)
10581058
* using function() does not count as a reference, because the function is
10591059
* looked up by name.
10601060
*/
1061-
static int
1061+
int
10621062
func_name_refcount(char_u *name)
10631063
{
10641064
return isdigit(*name) || *name == '<';
@@ -1176,8 +1176,9 @@ func_clear(ufunc_T *fp, int force)
11761176
* Free a function and remove it from the list of functions. Does not free
11771177
* what a function contains, call func_clear() first.
11781178
* When "force" is TRUE we are exiting.
1179+
* Returns OK when the function was actually freed.
11791180
*/
1180-
static void
1181+
static int
11811182
func_free(ufunc_T *fp, int force)
11821183
{
11831184
// Only remove it when not done already, otherwise we would remove a newer
@@ -1191,7 +1192,9 @@ func_free(ufunc_T *fp, int force)
11911192
unlink_def_function(fp);
11921193
VIM_CLEAR(fp->uf_name_exp);
11931194
vim_free(fp);
1195+
return OK;
11941196
}
1197+
return FAIL;
11951198
}
11961199

11971200
/*
@@ -1890,9 +1893,13 @@ free_all_functions(void)
18901893
++skipped;
18911894
else
18921895
{
1893-
func_free(fp, FALSE);
1894-
skipped = 0;
1895-
break;
1896+
if (func_free(fp, FALSE) == OK)
1897+
{
1898+
skipped = 0;
1899+
break;
1900+
}
1901+
// did not actually free it
1902+
++skipped;
18961903
}
18971904
}
18981905
}

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+
1712,
753755
/**/
754756
1711,
755757
/**/

src/vim9compile.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,7 +1452,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
14521452
ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
14531453
: ISN_UCALL)) == NULL)
14541454
return FAIL;
1455-
if (ufunc->uf_def_status != UF_NOT_COMPILED)
1455+
if (isn->isn_type == ISN_DCALL)
14561456
{
14571457
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
14581458
isn->isn_arg.dfunc.cdf_argcount = argcount;
@@ -2634,8 +2634,8 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
26342634
clear_tv(&rettv);
26352635
ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
26362636

2637-
// The function will have one line: "return {expr}".
2638-
// Compile it into instructions.
2637+
// The function will have one line: "return {expr}". Compile it into
2638+
// instructions so that we get any errors right now.
26392639
compile_def_function(ufunc, TRUE, cctx);
26402640

26412641
// compile the arguments
@@ -7285,7 +7285,19 @@ delete_instr(isn_T *isn)
72857285
{
72867286
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
72877287
+ isn->isn_arg.funcref.fr_func;
7288-
func_ptr_unref(dfunc->df_ufunc);
7288+
7289+
if (func_name_refcount(dfunc->df_ufunc->uf_name))
7290+
func_ptr_unref(dfunc->df_ufunc);
7291+
}
7292+
break;
7293+
7294+
case ISN_DCALL:
7295+
{
7296+
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
7297+
+ isn->isn_arg.dfunc.cdf_idx;
7298+
7299+
if (func_name_refcount(dfunc->df_ufunc->uf_name))
7300+
func_ptr_unref(dfunc->df_ufunc);
72897301
}
72907302
break;
72917303

@@ -7333,7 +7345,6 @@ delete_instr(isn_T *isn)
73337345
case ISN_COMPARESPECIAL:
73347346
case ISN_COMPARESTRING:
73357347
case ISN_CONCAT:
7336-
case ISN_DCALL:
73377348
case ISN_DROP:
73387349
case ISN_ECHO:
73397350
case ISN_ECHOERR:

0 commit comments

Comments
 (0)