diff src/vim9execute.c @ 23699:317018f62643 v8.2.2391

patch 8.2.2391: memory leak when creating a global function with closure Commit: https://github.com/vim/vim/commit/0d3de8cb590aed90be71d96eb3dbc6addf80bb11 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Jan 22 20:46:27 2021 +0100 patch 8.2.2391: memory leak when creating a global function with closure Problem: Memory leak when creating a global function with closure. Solution: Create a separate partial for every instantiated function.
author Bram Moolenaar <Bram@vim.org>
date Fri, 22 Jan 2021 21:00:05 +0100
parents 19073a768852
children e3720756acdc
line wrap: on
line diff
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -263,7 +263,8 @@ call_dfunc(int cdf_idx, partial_T *pt, i
     }
     ectx->ec_stack.ga_len += STACK_FRAME_SIZE + varcount;
 
-    if (pt != NULL || ufunc->uf_partial != NULL || ufunc->uf_flags & FC_CLOSURE)
+    if (pt != NULL || ufunc->uf_partial != NULL
+					     || (ufunc->uf_flags & FC_CLOSURE))
     {
 	outer_T *outer = ALLOC_CLEAR_ONE(outer_T);
 
@@ -1062,7 +1063,7 @@ fill_partial_and_closure(partial_T *pt, 
     pt->pt_func = ufunc;
     pt->pt_refcount = 1;
 
-    if (pt->pt_func->uf_flags & FC_CLOSURE)
+    if (ufunc->uf_flags & FC_CLOSURE)
     {
 	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
 							  + ectx->ec_dfunc_idx;
@@ -1093,7 +1094,7 @@ fill_partial_and_closure(partial_T *pt, 
 	++pt->pt_refcount;
 	++ectx->ec_funcrefs.ga_len;
     }
-    ++pt->pt_func->uf_refcount;
+    ++ufunc->uf_refcount;
     return OK;
 }
 
@@ -1243,24 +1244,32 @@ call_def_function(
     ectx.ec_frame_idx = ectx.ec_stack.ga_len;
     initial_frame_idx = ectx.ec_frame_idx;
 
-    if (partial != NULL || ufunc->uf_partial != NULL)
     {
-	ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
-	if (ectx.ec_outer == NULL)
-	    goto failed_early;
-	if (partial != NULL)
+	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
+							 + ufunc->uf_dfunc_idx;
+	ufunc_T *base_ufunc = dfunc->df_ufunc;
+
+	// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
+	// by copy_func().
+	if (partial != NULL || base_ufunc->uf_partial != NULL)
 	{
-	    if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
+	    ectx.ec_outer = ALLOC_CLEAR_ONE(outer_T);
+	    if (ectx.ec_outer == NULL)
+		goto failed_early;
+	    if (partial != NULL)
 	    {
-		if (current_ectx->ec_outer != NULL)
-		    *ectx.ec_outer = *current_ectx->ec_outer;
+		if (partial->pt_outer.out_stack == NULL && current_ectx != NULL)
+		{
+		    if (current_ectx->ec_outer != NULL)
+			*ectx.ec_outer = *current_ectx->ec_outer;
+		}
+		else
+		    *ectx.ec_outer = partial->pt_outer;
 	    }
 	    else
-		*ectx.ec_outer = partial->pt_outer;
+		*ectx.ec_outer = base_ufunc->uf_partial->pt_outer;
+	    ectx.ec_outer->out_up_is_copy = TRUE;
 	}
-	else
-	    *ectx.ec_outer = ufunc->uf_partial->pt_outer;
-	ectx.ec_outer->out_up_is_copy = TRUE;
     }
 
     // dummy frame entries