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/userfunc.c b/src/userfunc.c
index f7ad9f3..ff19afd 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1260,8 +1260,7 @@
// 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 @@
/*
* 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
{
- // 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)
- 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));
+ semsg(_(e_lambda_function_not_found_str), lambda);
+ return FAIL;
}
- return fp;
+
+ // TODO: handle ! to overwrite
+ fp = find_func(global, TRUE, NULL);
+ if (fp != NULL)
+ {
+ 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;
+ 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));
+
+ // 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 @@
fp->uf_profiling = FALSE;
fp->uf_prof_initialized = FALSE;
#endif
- clear_def_function(fp);
+ unlink_def_function(fp);
}
}
}