# HG changeset patch # User Bram Moolenaar # Date 1600533004 -7200 # Node ID fb69b43d73f3d45623b45761f418a14be27fe35d # Parent 543812d79fae5c4e6abd44723d31b0109082f495 patch 8.2.1712: Vim9: leaking memory when calling a lambda Commit: https://github.com/vim/vim/commit/a05e524f3aa8eadc2dbd0ad8ff6db9407ac7ac7e Author: Bram Moolenaar Date: Sat Sep 19 18:19:19 2020 +0200 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. diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -12,6 +12,7 @@ char_u *fname_trans_sid(char_u *name, ch 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 --- 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 --- 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 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1452,7 +1452,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu 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 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: