diff src/userfunc.c @ 21558:1c4d4aa22b37 v8.2.1329

patch 8.2.1329: Vim9: cannot define global function inside :def function Commit: https://github.com/vim/vim/commit/38ddf333f6b2806b0ea2dd052ee1cd50dd7f4525 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Jul 31 22:05:04 2020 +0200 patch 8.2.1329: Vim9: cannot define global function inside :def function Problem: Vim9: cannot define global function inside :def function. Solution: Assign to global variable instead of local. (closes https://github.com/vim/vim/issues/6584)
author Bram Moolenaar <Bram@vim.org>
date Fri, 31 Jul 2020 22:15:04 +0200
parents cbc570e66d11
children 30a997217524
line wrap: on
line diff
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -366,7 +366,7 @@ register_cfunc(cfunc_T cb, cfunc_free_T 
     if (fp == NULL)
 	return NULL;
 
-    fp->uf_dfunc_idx = UF_NOT_COMPILED;
+    fp->uf_def_status = UF_NOT_COMPILED;
     fp->uf_refcount = 1;
     fp->uf_varargs = TRUE;
     fp->uf_flags = FC_CFUNC;
@@ -1069,7 +1069,8 @@ func_remove(ufunc_T *fp)
     {
 	// When there is a def-function index do not actually remove the
 	// function, so we can find the index when defining the function again.
-	if (fp->uf_def_status == UF_COMPILED)
+	// Do remove it when it's a copy.
+	if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0)
 	    fp->uf_flags |= FC_DEAD;
 	else
 	    hash_remove(&func_hashtab, hi);
@@ -1122,7 +1123,8 @@ func_clear(ufunc_T *fp, int force)
     // clear this function
     func_clear_items(fp);
     funccal_unref(fp->uf_scoped, fp, force);
-    clear_def_function(fp);
+    if ((fp->uf_flags & FC_COPY) == 0)
+	clear_def_function(fp);
 }
 
 /*
@@ -1150,12 +1152,83 @@ func_free(ufunc_T *fp, int force)
 func_clear_free(ufunc_T *fp, int force)
 {
     func_clear(fp, force);
-    if (force || fp->uf_dfunc_idx == 0)
+    if (force || fp->uf_dfunc_idx == 0 || (fp->uf_flags & FC_COPY))
 	func_free(fp, force);
     else
 	fp->uf_flags |= FC_DEAD;
 }
 
+/*
+ * Copy already defined function "lambda" to a new function with name "global".
+ * This is for when a compiled function defines a global function.
+ */
+    void
+copy_func(char_u *lambda, char_u *global)
+{
+    ufunc_T *ufunc = find_func_even_dead(lambda, TRUE, NULL);
+    ufunc_T *fp;
+
+    if (ufunc == NULL)
+	semsg(_("E1102: lambda function not found: %s"), lambda);
+    else
+    {
+	// TODO: handle ! to overwrite
+	fp = find_func(global, TRUE, NULL);
+	if (fp != NULL)
+	{
+	    semsg(_(e_funcexts), global);
+	    return;
+	}
+
+	fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(global) + 1);
+	if (fp == NULL)
+	    return;
+
+	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(&fp->uf_args, &ufunc->uf_args) == FAIL
+		|| ga_copy_strings(&fp->uf_def_args, &ufunc->uf_def_args)
+									== FAIL
+		|| ga_copy_strings(&fp->uf_lines, &ufunc->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_refcount = 1;
+	STRCPY(fp->uf_name, global);
+	hash_add(&func_hashtab, UF2HIKEY(fp));
+    }
+    return;
+
+failed:
+    func_clear_free(fp, TRUE);
+}
+
 
 /*
  * Call a user function.
@@ -2521,6 +2594,8 @@ list_functions(regmatch_T *regmatch)
 
 /*
  * ":function" also supporting nested ":def".
+ * When "name_arg" is not NULL this is a nested function, using "name_arg" for
+ * the function name.
  * Returns a pointer to the function or NULL if no function defined.
  */
     ufunc_T *