changeset 22324:a4ed0de125d9 v8.2.1711

patch 8.2.1711: Vim9: leaking memory when using partial Commit: https://github.com/vim/vim/commit/fdeab65db60929e28640fd740c333f9bcfea0e15 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 19 15:16:50 2020 +0200 patch 8.2.1711: Vim9: leaking memory when using partial Problem: Vim9: leaking memory when using partial. Solution: Do delete the function even when it was compiled.
author Bram Moolenaar <Bram@vim.org>
date Sat, 19 Sep 2020 15:30:04 +0200
parents bf31e05d3fec
children 543812d79fae
files src/proto/vim9compile.pro src/userfunc.c src/version.c src/vim9compile.c src/vim9execute.c
diffstat 5 files changed, 45 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/proto/vim9compile.pro
+++ b/src/proto/vim9compile.pro
@@ -17,5 +17,6 @@ int compile_def_function(ufunc_T *ufunc,
 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 : */
--- 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;
 
 /*
--- 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,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2593,6 +2593,9 @@ compile_lambda(char_u **arg, cctx_T *cct
 	// 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".
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -270,12 +270,18 @@ handle_closure_in_use(ectx_T *ectx, int 
 {
     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)
     {