patch 8.2.2188: Vim9: crash when calling global function from :def function
Problem: Vim9: crash when calling global function from :def function.
Solution: Set the outer context. Define the partial for the context on the
original function. Use a refcount to keep track of which ufunc is
using a dfunc. (closes #7525)
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 2a305ee..8e07968 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -7336,6 +7336,8 @@
dfunc->df_idx = def_functions.ga_len;
ufunc->uf_dfunc_idx = dfunc->df_idx;
dfunc->df_ufunc = ufunc;
+ dfunc->df_name = vim_strsave(ufunc->uf_name);
+ ++dfunc->df_refcount;
++def_functions.ga_len;
return OK;
}
@@ -7928,6 +7930,7 @@
for (idx = 0; idx < instr->ga_len; ++idx)
delete_instr(((isn_T *)instr->ga_data) + idx);
ga_clear(instr);
+ VIM_CLEAR(dfunc->df_name);
// If using the last entry in the table and it was added above, we
// might as well remove it.
@@ -8102,9 +8105,7 @@
if (ufunc != NULL)
{
- // Clear uf_dfunc_idx so that the function is deleted.
- clear_def_function(ufunc);
- ufunc->uf_dfunc_idx = 0;
+ unlink_def_function(ufunc);
func_ptr_unref(ufunc);
}
@@ -8206,7 +8207,7 @@
}
/*
- * Free all instructions for "dfunc".
+ * Free all instructions for "dfunc" except df_name.
*/
static void
delete_def_function_contents(dfunc_T *dfunc)
@@ -8227,31 +8228,39 @@
/*
* When a user function is deleted, clear the contents of any associated def
- * function. The position in def_functions can be re-used.
+ * function, unless another user function still uses it.
+ * The position in def_functions can be re-used.
*/
void
-clear_def_function(ufunc_T *ufunc)
+unlink_def_function(ufunc_T *ufunc)
{
if (ufunc->uf_dfunc_idx > 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
- delete_def_function_contents(dfunc);
+ if (--dfunc->df_refcount <= 0)
+ delete_def_function_contents(dfunc);
ufunc->uf_def_status = UF_NOT_COMPILED;
+ ufunc->uf_dfunc_idx = 0;
+ if (dfunc->df_ufunc == ufunc)
+ dfunc->df_ufunc = NULL;
}
}
/*
- * Used when a user function is about to be deleted: remove the pointer to it.
- * The entry in def_functions is then unused.
+ * Used when a user function refers to an existing dfunc.
*/
void
-unlink_def_function(ufunc_T *ufunc)
+link_def_function(ufunc_T *ufunc)
{
- dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
+ if (ufunc->uf_dfunc_idx > 0)
+ {
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ + ufunc->uf_dfunc_idx;
- dfunc->df_ufunc = NULL;
+ ++dfunc->df_refcount;
+ }
}
#if defined(EXITFREE) || defined(PROTO)
@@ -8268,6 +8277,7 @@
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + idx;
delete_def_function_contents(dfunc);
+ vim_free(dfunc->df_name);
}
ga_clear(&def_functions);