Mercurial > vim
diff src/userfunc.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 | 35583da6397e |
children | c76240efdf02 |
line wrap: on
line diff
--- a/src/userfunc.c +++ b/src/userfunc.c @@ -1260,8 +1260,7 @@ func_clear(ufunc_T *fp, int force) // clear this function func_clear_items(fp); funccal_unref(fp->uf_scoped, fp, force); - if ((fp->uf_flags & FC_COPY) == 0) - clear_def_function(fp); + unlink_def_function(fp); } /* @@ -1307,75 +1306,98 @@ func_clear_free(ufunc_T *fp, int force) /* * Copy already defined function "lambda" to a new function with name "global". * This is for when a compiled function defines a global function. - * Caller should take care of adding a partial for a closure. */ - ufunc_T * -copy_func(char_u *lambda, char_u *global) + int +copy_func(char_u *lambda, char_u *global, ectx_T *ectx) { ufunc_T *ufunc = find_func_even_dead(lambda, TRUE, NULL); ufunc_T *fp = NULL; if (ufunc == NULL) + { semsg(_(e_lambda_function_not_found_str), lambda); - else + return FAIL; + } + + // TODO: handle ! to overwrite + fp = find_func(global, TRUE, NULL); + if (fp != NULL) { - // TODO: handle ! to overwrite - fp = find_func(global, TRUE, NULL); - if (fp != NULL) - { - semsg(_(e_funcexts), global); - return NULL; - } - - fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1); - if (fp == NULL) - return NULL; - - fp->uf_varargs = ufunc->uf_varargs; - fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY; - fp->uf_def_status = ufunc->uf_def_status; - fp->uf_dfunc_idx = ufunc->uf_dfunc_idx; - if (ga_copy_strings(&ufunc->uf_args, &fp->uf_args) == FAIL - || ga_copy_strings(&ufunc->uf_def_args, &fp->uf_def_args) - == FAIL - || ga_copy_strings(&ufunc->uf_lines, &fp->uf_lines) == FAIL) + semsg(_(e_funcexts), global); + return FAIL; + } + + fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1); + if (fp == NULL) + return FAIL; + + fp->uf_varargs = ufunc->uf_varargs; + fp->uf_flags = (ufunc->uf_flags & ~FC_VIM9) | FC_COPY; + fp->uf_def_status = ufunc->uf_def_status; + fp->uf_dfunc_idx = ufunc->uf_dfunc_idx; + if (ga_copy_strings(&ufunc->uf_args, &fp->uf_args) == FAIL + || ga_copy_strings(&ufunc->uf_def_args, &fp->uf_def_args) + == FAIL + || ga_copy_strings(&ufunc->uf_lines, &fp->uf_lines) == FAIL) + goto failed; + + fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL + : vim_strsave(ufunc->uf_name_exp); + if (ufunc->uf_arg_types != NULL) + { + fp->uf_arg_types = ALLOC_MULT(type_T *, fp->uf_args.ga_len); + if (fp->uf_arg_types == NULL) goto failed; - - fp->uf_name_exp = ufunc->uf_name_exp == NULL ? NULL - : vim_strsave(ufunc->uf_name_exp); - if (ufunc->uf_arg_types != NULL) - { - fp->uf_arg_types = ALLOC_MULT(type_T *, fp->uf_args.ga_len); - if (fp->uf_arg_types == NULL) - goto failed; - mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types, - sizeof(type_T *) * fp->uf_args.ga_len); - } - if (ufunc->uf_def_arg_idx != NULL) - { - fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1); - if (fp->uf_def_arg_idx == NULL) - goto failed; - mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx, - sizeof(int) * fp->uf_def_args.ga_len + 1); - } - if (ufunc->uf_va_name != NULL) - { - fp->uf_va_name = vim_strsave(ufunc->uf_va_name); - if (fp->uf_va_name == NULL) - goto failed; - } - fp->uf_ret_type = ufunc->uf_ret_type; - - fp->uf_refcount = 1; - STRCPY(fp->uf_name, global); - hash_add(&func_hashtab, UF2HIKEY(fp)); + mch_memmove(fp->uf_arg_types, ufunc->uf_arg_types, + sizeof(type_T *) * fp->uf_args.ga_len); + } + if (ufunc->uf_def_arg_idx != NULL) + { + fp->uf_def_arg_idx = ALLOC_MULT(int, fp->uf_def_args.ga_len + 1); + if (fp->uf_def_arg_idx == NULL) + goto failed; + mch_memmove(fp->uf_def_arg_idx, ufunc->uf_def_arg_idx, + sizeof(int) * fp->uf_def_args.ga_len + 1); + } + if (ufunc->uf_va_name != NULL) + { + fp->uf_va_name = vim_strsave(ufunc->uf_va_name); + if (fp->uf_va_name == NULL) + goto failed; } - return fp; + fp->uf_ret_type = ufunc->uf_ret_type; + + fp->uf_refcount = 1; + STRCPY(fp->uf_name, global); + hash_add(&func_hashtab, UF2HIKEY(fp)); + + // the referenced dfunc_T is now used one more time + link_def_function(fp); + + // Create a partial to store the context of the function, if not done + // already. + if ((ufunc->uf_flags & FC_CLOSURE) && ufunc->uf_partial == NULL) + { + partial_T *pt = ALLOC_CLEAR_ONE(partial_T); + + if (pt == NULL) + goto failed; + if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL) + goto failed; + ufunc->uf_partial = pt; + --pt->pt_refcount; // not referenced here yet + } + if (ufunc->uf_partial != NULL) + { + fp->uf_partial = ufunc->uf_partial; + ++fp->uf_partial->pt_refcount; + } + + return OK; failed: func_clear_free(fp, TRUE); - return NULL; + return FAIL; } static int funcdepth = 0; @@ -3515,7 +3537,7 @@ define_function(exarg_T *eap, char_u *na fp->uf_profiling = FALSE; fp->uf_prof_initialized = FALSE; #endif - clear_def_function(fp); + unlink_def_function(fp); } } }