Mercurial > vim
diff src/vim9compile.c @ 23285:112fa621b127 v8.2.2188
patch 8.2.2188: Vim9: crash when calling global function from :def function
Commit: https://github.com/vim/vim/commit/cd45ed03bfdd7fac53d562ad402df74bd26e7754
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Dec 22 17:35:54 2020 +0100
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)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Tue, 22 Dec 2020 17:45:03 +0100 |
parents | 00f7cd9b6033 |
children | ac701146c708 |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7336,6 +7336,8 @@ add_def_function(ufunc_T *ufunc) 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 @@ erret: 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 @@ delete_instr(isn_T *isn) 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 @@ delete_instr(isn_T *isn) } /* - * 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 @@ delete_def_function_contents(dfunc_T *df /* * 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; - } -} - -/* - * Used when a user function is about to be deleted: remove the pointer to it. - * The entry in def_functions is then unused. + ufunc->uf_dfunc_idx = 0; + if (dfunc->df_ufunc == ufunc) + dfunc->df_ufunc = NULL; + } +} + +/* + * Used when a user function refers to an existing dfunc. */ void -unlink_def_function(ufunc_T *ufunc) -{ - dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; - - dfunc->df_ufunc = NULL; +link_def_function(ufunc_T *ufunc) +{ + if (ufunc->uf_dfunc_idx > 0) + { + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + + ufunc->uf_dfunc_idx; + + ++dfunc->df_refcount; + } } #if defined(EXITFREE) || defined(PROTO) @@ -8268,6 +8277,7 @@ free_def_functions(void) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + idx; delete_def_function_contents(dfunc); + vim_free(dfunc->df_name); } ga_clear(&def_functions);