changeset 20283:934657e365e5 v8.2.0697

patch 8.2.0697: Vim9: memory leak when using nested function Commit: https://github.com/vim/vim/commit/221fcc741a6660bfc3fd0d64937d0c15bb71f51d Author: Bram Moolenaar <Bram@vim.org> Date: Tue May 5 19:46:20 2020 +0200 patch 8.2.0697: Vim9: memory leak when using nested function Problem: Vim9: memory leak when using nested function. Solution: Unreference function when deleting instructions. Adjust reference count for local variables.
author Bram Moolenaar <Bram@vim.org>
date Tue, 05 May 2020 20:00:04 +0200
parents a461511dc68f
children 7b7c9d3abf7c
files src/version.c src/vim9compile.c src/vim9execute.c
diffstat 3 files changed, 30 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    697,
+/**/
     696,
 /**/
     695,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6629,6 +6629,14 @@ delete_instr(isn_T *isn)
 	    vim_free(isn->isn_arg.ufunc.cuf_name);
 	    break;
 
+	case ISN_FUNCREF:
+	    {
+		dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+					       + isn->isn_arg.funcref.fr_func;
+		func_ptr_unref(dfunc->df_ufunc);
+	    }
+	    break;
+
 	case ISN_2BOOL:
 	case ISN_2STRING:
 	case ISN_ADDBLOB:
@@ -6657,7 +6665,6 @@ delete_instr(isn_T *isn)
 	case ISN_EXECCONCAT:
 	case ISN_EXECUTE:
 	case ISN_FOR:
-	case ISN_FUNCREF:
 	case ISN_INDEX:
 	case ISN_JUMP:
 	case ISN_LOAD:
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -264,10 +264,27 @@ handle_closure_in_use(ectx_T *ectx, int 
     {
 	tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
 						   + dfunc->df_varcount + idx);
-	if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial->pt_refcount > 1)
+	if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL
+					&& tv->vval.v_partial->pt_refcount > 1)
 	{
-	    closure_in_use = TRUE;
-	    break;
+	    int refcount = tv->vval.v_partial->pt_refcount;
+	    int i;
+
+	    // A Reference in a local variables doesn't count, its get
+	    // unreferenced on return.
+	    for (i = 0; i < dfunc->df_varcount; ++i)
+	    {
+		typval_T *stv = STACK_TV(ectx->ec_frame_idx
+						       + STACK_FRAME_SIZE + i);
+		if (stv->v_type == VAR_PARTIAL
+				  && tv->vval.v_partial == stv->vval.v_partial)
+		    --refcount;
+	    }
+	    if (refcount > 1)
+	    {
+		closure_in_use = TRUE;
+		break;
+	    }
 	}
     }