diff src/vim9compile.c @ 20247:e46e72aaff74 v8.2.0679

patch 8.2.0679: Vim9: incomplete support for closures Commit: https://github.com/vim/vim/commit/bf67ea1af05cbb30cd8f0b665fb567c0dca79796 Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 2 17:52:42 2020 +0200 patch 8.2.0679: Vim9: incomplete support for closures Problem: Vim9: incomplete support for closures. Solution: At the end of a function copy arguments and local variables if they are still used by a referenced closure.
author Bram Moolenaar <Bram@vim.org>
date Sat, 02 May 2020 18:00:04 +0200
parents 23d75968ca5e
children 6f9010b6f7f9
line wrap: on
line diff
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -116,6 +116,9 @@ struct cctx_S {
     garray_T	ctx_locals;	    // currently visible local variables
     int		ctx_locals_count;   // total number of local variables
 
+    int		ctx_closure_count;  // number of closures created in the
+				    // function
+
     garray_T	ctx_imports;	    // imported items
 
     int		ctx_skip;	    // when TRUE skip commands, when FALSE skip
@@ -1254,7 +1257,8 @@ generate_FUNCREF(cctx_T *cctx, int dfunc
     RETURN_OK_IF_SKIP(cctx);
     if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
 	return FAIL;
-    isn->isn_arg.number = dfunc_idx;
+    isn->isn_arg.funcref.fr_func = dfunc_idx;
+    isn->isn_arg.funcref.fr_var_idx = cctx->ctx_closure_count++;
 
     if (ga_grow(stack, 1) == FAIL)
 	return FAIL;
@@ -6395,6 +6399,7 @@ compile_def_function(ufunc_T *ufunc, int
 	dfunc->df_instr = instr->ga_data;
 	dfunc->df_instr_count = instr->ga_len;
 	dfunc->df_varcount = cctx.ctx_locals_count;
+	dfunc->df_closure_count = cctx.ctx_closure_count;
 	if (cctx.ctx_outer_used)
 	    ufunc->uf_flags |= FC_CLOSURE;
     }
@@ -6620,6 +6625,23 @@ delete_def_function_contents(dfunc_T *df
 	    delete_instr(dfunc->df_instr + idx);
 	VIM_CLEAR(dfunc->df_instr);
     }
+    if (dfunc->df_funcstack != NULL)
+    {
+	// Decrease the reference count for the context of a closure.  If down
+	// to zero free it and clear the variables on the stack.
+	if (--dfunc->df_funcstack->fs_refcount == 0)
+	{
+	    garray_T	*gap = &dfunc->df_funcstack->fs_ga;
+	    typval_T	*stack = gap->ga_data;
+	    int		i;
+
+	    for (i = 0; i < gap->ga_len; ++i)
+		clear_tv(stack + i);
+	    ga_clear(gap);
+	    vim_free(dfunc->df_funcstack);
+	}
+	dfunc->df_funcstack = NULL;
+    }
 
     dfunc->df_deleted = TRUE;
 }