changeset 19864:8288884fdfe1 v8.2.0488

patch 8.2.0488: Vim9: compiling can break when using a lambda inside :def Commit: https://github.com/vim/vim/commit/05afceeddc4afbbca60e4e6a729a50d33d4b19f7 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Mar 31 23:32:31 2020 +0200 patch 8.2.0488: Vim9: compiling can break when using a lambda inside :def Problem: Vim9: Compiling can break when using a lambda inside :def. Solution: Do not keep a pointer to the dfunc_T for longer time.
author Bram Moolenaar <Bram@vim.org>
date Tue, 31 Mar 2020 23:45:04 +0200
parents b6f8f6d22e8e
children cbe06eef8c71
files src/version.c src/vim9.h src/vim9compile.c
diffstat 3 files changed, 37 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    488,
+/**/
     487,
 /**/
     486,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -257,7 +257,7 @@ struct dfunc_S {
 // Functions defined with :def are stored in this growarray.
 // They are never removed, so that they can be found by index.
 // Deleted functions have the df_deleted flag set.
-garray_T def_functions = {0, 0, sizeof(dfunc_T), 200, NULL};
+garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL};
 #else
 extern garray_T def_functions;
 #endif
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -5029,11 +5029,12 @@ compile_execute(char_u *arg, cctx_T *cct
  * Adds the function to "def_functions".
  * When "set_return_type" is set then set ufunc->uf_ret_type to the type of the
  * return statement (used for lambda).
+ * This can be used recursively through compile_lambda(), which may reallocate
+ * "def_functions".
  */
     void
 compile_def_function(ufunc_T *ufunc, int set_return_type)
 {
-    dfunc_T	*dfunc;
     char_u	*line = NULL;
     char_u	*p;
     exarg_T	ea;
@@ -5046,25 +5047,29 @@ compile_def_function(ufunc_T *ufunc, int
     sctx_T	save_current_sctx = current_sctx;
     int		emsg_before = called_emsg;
 
-    if (ufunc->uf_dfunc_idx >= 0)
     {
-	// Redefining a function that was compiled before.
-	dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
-
-	// Free old instructions.
-	delete_def_function_contents(dfunc);
-    }
-    else
-    {
-	// Add the function to "def_functions".
-	if (ga_grow(&def_functions, 1) == FAIL)
-	    return;
-	dfunc = ((dfunc_T *)def_functions.ga_data) + def_functions.ga_len;
-	vim_memset(dfunc, 0, sizeof(dfunc_T));
-	dfunc->df_idx = def_functions.ga_len;
-	ufunc->uf_dfunc_idx = dfunc->df_idx;
-	dfunc->df_ufunc = ufunc;
-	++def_functions.ga_len;
+	dfunc_T	*dfunc;  // may be invalidated by compile_lambda()
+
+	if (ufunc->uf_dfunc_idx >= 0)
+	{
+	    // Redefining a function that was compiled before.
+	    dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
+
+	    // Free old instructions.
+	    delete_def_function_contents(dfunc);
+	}
+	else
+	{
+	    // Add the function to "def_functions".
+	    if (ga_grow(&def_functions, 1) == FAIL)
+		return;
+	    dfunc = ((dfunc_T *)def_functions.ga_data) + def_functions.ga_len;
+	    vim_memset(dfunc, 0, sizeof(dfunc_T));
+	    dfunc->df_idx = def_functions.ga_len;
+	    ufunc->uf_dfunc_idx = dfunc->df_idx;
+	    dfunc->df_ufunc = ufunc;
+	    ++def_functions.ga_len;
+	}
     }
 
     vim_memset(&cctx, 0, sizeof(cctx));
@@ -5414,10 +5419,14 @@ compile_def_function(ufunc_T *ufunc, int
 	generate_instr(&cctx, ISN_RETURN);
     }
 
-    dfunc->df_deleted = FALSE;
-    dfunc->df_instr = instr->ga_data;
-    dfunc->df_instr_count = instr->ga_len;
-    dfunc->df_varcount = cctx.ctx_max_local;
+    {
+	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
+							 + ufunc->uf_dfunc_idx;
+	dfunc->df_deleted = FALSE;
+	dfunc->df_instr = instr->ga_data;
+	dfunc->df_instr_count = instr->ga_len;
+	dfunc->df_varcount = cctx.ctx_max_local;
+    }
 
     ret = OK;
 
@@ -5425,6 +5434,8 @@ erret:
     if (ret == FAIL)
     {
 	int idx;
+	dfunc_T	*dfunc = ((dfunc_T *)def_functions.ga_data)
+							 + ufunc->uf_dfunc_idx;
 
 	for (idx = 0; idx < instr->ga_len; ++idx)
 	    delete_instr(((isn_T *)instr->ga_data) + idx);