diff options
author | Bram Moolenaar <Bram@vim.org> | 2020-09-19 15:16:50 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2020-09-19 15:16:50 +0200 |
commit | fdeab65db60929e28640fd740c333f9bcfea0e15 (patch) | |
tree | 12b2d052ab2a3824579cf2b1937a5b25eb528df4 | |
parent | 77b20977dc31ecf753dddad7a7c7b8f7b6e0c244 (diff) | |
download | vim-git-fdeab65db60929e28640fd740c333f9bcfea0e15.tar.gz |
patch 8.2.1711: Vim9: leaking memory when using partialv8.2.1711
Problem: Vim9: leaking memory when using partial.
Solution: Do delete the function even when it was compiled.
-rw-r--r-- | src/proto/vim9compile.pro | 1 | ||||
-rw-r--r-- | src/userfunc.c | 34 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9compile.c | 15 | ||||
-rw-r--r-- | src/vim9execute.c | 10 |
5 files changed, 45 insertions, 17 deletions
diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index 80d0b25a2..1844e1728 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -17,5 +17,6 @@ int compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx void set_function_type(ufunc_T *ufunc); void delete_instr(isn_T *isn); void clear_def_function(ufunc_T *ufunc); +void unlink_def_function(ufunc_T *ufunc); void free_def_functions(void); /* vim: set ft=c : */ diff --git a/src/userfunc.c b/src/userfunc.c index 017098d0f..d6a7e5d16 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1049,6 +1049,21 @@ cleanup_function_call(funccall_T *fc) } } } + +/* + * There are two kinds of function names: + * 1. ordinary names, function defined with :function or :def + * 2. numbered functions and lambdas + * For the first we only count the name stored in func_hashtab as a reference, + * using function() does not count as a reference, because the function is + * looked up by name. + */ + static int +func_name_refcount(char_u *name) +{ + return isdigit(*name) || *name == '<'; +} + /* * Unreference "fc": decrement the reference count and free it when it * becomes zero. "fp" is detached from "fc". @@ -1172,6 +1187,8 @@ func_free(ufunc_T *fp, int force) if ((fp->uf_flags & FC_DEAD) == 0 || force) { + if (fp->uf_dfunc_idx > 0) + unlink_def_function(fp); VIM_CLEAR(fp->uf_name_exp); vim_free(fp); } @@ -1185,7 +1202,8 @@ func_free(ufunc_T *fp, int force) func_clear_free(ufunc_T *fp, int force) { func_clear(fp, force); - if (force || fp->uf_dfunc_idx == 0 || (fp->uf_flags & FC_COPY)) + if (force || fp->uf_dfunc_idx == 0 || func_name_refcount(fp->uf_name) + || (fp->uf_flags & FC_COPY)) func_free(fp, force); else fp->uf_flags |= FC_DEAD; @@ -1730,20 +1748,6 @@ call_user_func_check( return error; } -/* - * There are two kinds of function names: - * 1. ordinary names, function defined with :function - * 2. numbered functions and lambdas - * For the first we only count the name stored in func_hashtab as a reference, - * using function() does not count as a reference, because the function is - * looked up by name. - */ - static int -func_name_refcount(char_u *name) -{ - return isdigit(*name) || *name == '<'; -} - static funccal_entry_T *funccal_stack = NULL; /* diff --git a/src/version.c b/src/version.c index 3b1f963bb..aa09b70d2 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 */ /**/ + 1711, +/**/ 1710, /**/ 1709, diff --git a/src/vim9compile.c b/src/vim9compile.c index a70ed5a44..a6b4ba409 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2593,6 +2593,9 @@ compile_lambda(char_u **arg, cctx_T *cctx) // The return type will now be known. set_function_type(ufunc); + // The function reference count will be 1. When the ISN_FUNCREF + // instruction is deleted the reference count is decremented and the + // function is freed. return generate_FUNCREF(cctx, ufunc); } @@ -7424,6 +7427,18 @@ clear_def_function(ufunc_T *ufunc) } } +/* + * Used when a user function is about to be deleted: remove the pointer to it. + * The entry in def_functions is then unused. + */ + void +unlink_def_function(ufunc_T *ufunc) +{ + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; + + dfunc->df_ufunc = NULL; +} + #if defined(EXITFREE) || defined(PROTO) /* * Free all functions defined with ":def". diff --git a/src/vim9execute.c b/src/vim9execute.c index e4ccaaa8a..cd6eff56c 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -270,12 +270,18 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx; - int argcount = ufunc_argcount(dfunc->df_ufunc); - int top = ectx->ec_frame_idx - argcount; + int argcount; + int top; int idx; typval_T *tv; int closure_in_use = FALSE; + if (dfunc->df_ufunc == NULL) + // function was freed + return OK; + argcount = ufunc_argcount(dfunc->df_ufunc); + top = ectx->ec_frame_idx - argcount; + // Check if any created closure is still in use. for (idx = 0; idx < dfunc->df_closure_count; ++idx) { |