summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2020-09-19 18:19:19 +0200
committerBram Moolenaar <Bram@vim.org>2020-09-19 18:19:19 +0200
commita05e524f3aa8eadc2dbd0ad8ff6db9407ac7ac7e (patch)
tree23a248c68456d043903f47c7d545f387a5db2420
parentfdeab65db60929e28640fd740c333f9bcfea0e15 (diff)
downloadvim-git-a05e524f3aa8eadc2dbd0ad8ff6db9407ac7ac7e.tar.gz
patch 8.2.1712: Vim9: leaking memory when calling a lambdav8.2.1712
Problem: Vim9: leaking memory when calling a lambda. Solution: Decrement function reference from ISN_DCALL.
-rw-r--r--src/proto/userfunc.pro1
-rw-r--r--src/userfunc.c17
-rw-r--r--src/version.c2
-rw-r--r--src/vim9compile.c21
4 files changed, 31 insertions, 10 deletions
diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro
index 4e3f2dbf4..aefedf778 100644
--- a/src/proto/userfunc.pro
+++ b/src/proto/userfunc.pro
@@ -12,6 +12,7 @@ char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *e
ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
int func_is_global(ufunc_T *ufunc);
+int func_name_refcount(char_u *name);
void copy_func(char_u *lambda, char_u *global);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
diff --git a/src/userfunc.c b/src/userfunc.c
index d6a7e5d16..595963d62 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1058,7 +1058,7 @@ cleanup_function_call(funccall_T *fc)
* using function() does not count as a reference, because the function is
* looked up by name.
*/
- static int
+ int
func_name_refcount(char_u *name)
{
return isdigit(*name) || *name == '<';
@@ -1176,8 +1176,9 @@ func_clear(ufunc_T *fp, int force)
* Free a function and remove it from the list of functions. Does not free
* what a function contains, call func_clear() first.
* When "force" is TRUE we are exiting.
+ * Returns OK when the function was actually freed.
*/
- static void
+ static int
func_free(ufunc_T *fp, int force)
{
// Only remove it when not done already, otherwise we would remove a newer
@@ -1191,7 +1192,9 @@ func_free(ufunc_T *fp, int force)
unlink_def_function(fp);
VIM_CLEAR(fp->uf_name_exp);
vim_free(fp);
+ return OK;
}
+ return FAIL;
}
/*
@@ -1890,9 +1893,13 @@ free_all_functions(void)
++skipped;
else
{
- func_free(fp, FALSE);
- skipped = 0;
- break;
+ if (func_free(fp, FALSE) == OK)
+ {
+ skipped = 0;
+ break;
+ }
+ // did not actually free it
+ ++skipped;
}
}
}
diff --git a/src/version.c b/src/version.c
index aa09b70d2..615abc74e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1712,
+/**/
1711,
/**/
1710,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index a6b4ba409..0db516274 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1452,7 +1452,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
: ISN_UCALL)) == NULL)
return FAIL;
- if (ufunc->uf_def_status != UF_NOT_COMPILED)
+ if (isn->isn_type == ISN_DCALL)
{
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
isn->isn_arg.dfunc.cdf_argcount = argcount;
@@ -2634,8 +2634,8 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
clear_tv(&rettv);
ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
- // The function will have one line: "return {expr}".
- // Compile it into instructions.
+ // The function will have one line: "return {expr}". Compile it into
+ // instructions so that we get any errors right now.
compile_def_function(ufunc, TRUE, cctx);
// compile the arguments
@@ -7285,7 +7285,19 @@ delete_instr(isn_T *isn)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.funcref.fr_func;
- func_ptr_unref(dfunc->df_ufunc);
+
+ if (func_name_refcount(dfunc->df_ufunc->uf_name))
+ func_ptr_unref(dfunc->df_ufunc);
+ }
+ break;
+
+ case ISN_DCALL:
+ {
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ + isn->isn_arg.dfunc.cdf_idx;
+
+ if (func_name_refcount(dfunc->df_ufunc->uf_name))
+ func_ptr_unref(dfunc->df_ufunc);
}
break;
@@ -7333,7 +7345,6 @@ delete_instr(isn_T *isn)
case ISN_COMPARESPECIAL:
case ISN_COMPARESTRING:
case ISN_CONCAT:
- case ISN_DCALL:
case ISN_DROP:
case ISN_ECHO:
case ISN_ECHOERR: