diff src/evalfunc.c @ 9723:80ac9cf77c9b v7.4.2137

commit https://github.com/vim/vim/commit/437bafe4c8a83ed71ee006eda7f54b65a90f0d4c Author: Bram Moolenaar <Bram@vim.org> Date: Mon Aug 1 15:40:54 2016 +0200 patch 7.4.2137 Problem: Using function() with a name will find another function when it is redefined. Solution: Add funcref(). Refer to lambda using a partial. Fix several reference counting issues.
author Christian Brabandt <cb@256bit.org>
date Mon, 01 Aug 2016 15:45:07 +0200
parents 6226de5f8137
children a990e7ed260b
line wrap: on
line diff
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -148,6 +148,7 @@ static void f_foldlevel(typval_T *argvar
 static void f_foldtext(typval_T *argvars, typval_T *rettv);
 static void f_foldtextresult(typval_T *argvars, typval_T *rettv);
 static void f_foreground(typval_T *argvars, typval_T *rettv);
+static void f_funcref(typval_T *argvars, typval_T *rettv);
 static void f_function(typval_T *argvars, typval_T *rettv);
 static void f_garbagecollect(typval_T *argvars, typval_T *rettv);
 static void f_get(typval_T *argvars, typval_T *rettv);
@@ -563,6 +564,7 @@ static struct fst
     {"foldtext",	0, 0, f_foldtext},
     {"foldtextresult",	1, 1, f_foldtextresult},
     {"foreground",	0, 0, f_foreground},
+    {"funcref",		1, 3, f_funcref},
     {"function",	1, 3, f_function},
     {"garbagecollect",	0, 1, f_garbagecollect},
     {"get",		2, 3, f_get},
@@ -1723,7 +1725,7 @@ f_call(typval_T *argvars, typval_T *rett
     else if (argvars[0].v_type == VAR_PARTIAL)
     {
 	partial = argvars[0].vval.v_partial;
-	func = partial->pt_name;
+	func = partial_name(partial);
     }
     else
 	func = get_tv_string(&argvars[0]);
@@ -3543,16 +3545,14 @@ f_foreground(typval_T *argvars UNUSED, t
 #endif
 }
 
-/*
- * "function()" function
- */
-    static void
-f_function(typval_T *argvars, typval_T *rettv)
+    static void
+common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
 {
     char_u	*s;
     char_u	*name;
     int		use_string = FALSE;
     partial_T   *arg_pt = NULL;
+    char_u	*trans_name = NULL;
 
     if (argvars[0].v_type == VAR_FUNC)
     {
@@ -3564,7 +3564,7 @@ f_function(typval_T *argvars, typval_T *
     {
 	/* function(dict.MyFunc, [arg]) */
 	arg_pt = argvars[0].vval.v_partial;
-	s = arg_pt->pt_name;
+	s = partial_name(arg_pt);
     }
     else
     {
@@ -3573,11 +3573,22 @@ f_function(typval_T *argvars, typval_T *
 	use_string = TRUE;
     }
 
+    if (((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL)
+				   || is_funcref))
+    {
+	name = s;
+	trans_name = trans_function_name(&name, FALSE,
+	     TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL);
+	if (*name != NUL)
+	    s = NULL;
+    }
+
     if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s)))
 	EMSG2(_(e_invarg2), s);
     /* Don't check an autoload name for existence here. */
-    else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL
-						&& !function_exists(s, TRUE))
+    else if (trans_name != NULL && (is_funcref
+				? find_func(trans_name) == NULL
+				: !translated_function_exists(trans_name)))
 	EMSG2(_("E700: Unknown function: %s"), s);
     else
     {
@@ -3625,7 +3636,7 @@ f_function(typval_T *argvars, typval_T *
 		{
 		    EMSG(_("E922: expected a dict"));
 		    vim_free(name);
-		    return;
+		    goto theend;
 		}
 		if (argvars[dict_idx].vval.v_dict == NULL)
 		    dict_idx = 0;
@@ -3636,14 +3647,14 @@ f_function(typval_T *argvars, typval_T *
 		{
 		    EMSG(_("E923: Second argument of function() must be a list or a dict"));
 		    vim_free(name);
-		    return;
+		    goto theend;
 		}
 		list = argvars[arg_idx].vval.v_list;
 		if (list == NULL || list->lv_len == 0)
 		    arg_idx = 0;
 	    }
 	}
-	if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL)
+	if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref)
 	{
 	    partial_T	*pt = (partial_T *)alloc_clear(sizeof(partial_T));
 
@@ -3670,17 +3681,14 @@ f_function(typval_T *argvars, typval_T *
 		    {
 			vim_free(pt);
 			vim_free(name);
-			return;
+			goto theend;
 		    }
-		    else
-		    {
-			for (i = 0; i < arg_len; i++)
-			    copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
-			if (lv_len > 0)
-			    for (li = list->lv_first; li != NULL;
-							     li = li->li_next)
-				copy_tv(&li->li_tv, &pt->pt_argv[i++]);
-		    }
+		    for (i = 0; i < arg_len; i++)
+			copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
+		    if (lv_len > 0)
+			for (li = list->lv_first; li != NULL;
+							 li = li->li_next)
+			    copy_tv(&li->li_tv, &pt->pt_argv[i++]);
 		}
 
 		/* For "function(dict.func, [], dict)" and "func" is a partial
@@ -3702,8 +3710,23 @@ f_function(typval_T *argvars, typval_T *
 		}
 
 		pt->pt_refcount = 1;
-		pt->pt_name = name;
-		func_ref(pt->pt_name);
+		if (arg_pt != NULL && arg_pt->pt_func != NULL)
+		{
+		    pt->pt_func = arg_pt->pt_func;
+		    func_ptr_ref(pt->pt_func);
+		    vim_free(name);
+		}
+		else if (is_funcref)
+		{
+		    pt->pt_func = find_func(trans_name);
+		    func_ptr_ref(pt->pt_func);
+		    vim_free(name);
+		}
+		else
+		{
+		    pt->pt_name = name;
+		    func_ref(name);
+		}
 	    }
 	    rettv->v_type = VAR_PARTIAL;
 	    rettv->vval.v_partial = pt;
@@ -3716,6 +3739,26 @@ f_function(typval_T *argvars, typval_T *
 	    func_ref(name);
 	}
     }
+theend:
+    vim_free(trans_name);
+}
+
+/*
+ * "funcref()" function
+ */
+    static void
+f_funcref(typval_T *argvars, typval_T *rettv)
+{
+    common_function(argvars, rettv, TRUE);
+}
+
+/*
+ * "function()" function
+ */
+    static void
+f_function(typval_T *argvars, typval_T *rettv)
+{
+    common_function(argvars, rettv, FALSE);
 }
 
 /*
@@ -3781,14 +3824,20 @@ f_get(typval_T *argvars, typval_T *rettv
 	if (pt != NULL)
 	{
 	    char_u *what = get_tv_string(&argvars[1]);
+	    char_u *n;
 
 	    if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0)
 	    {
 		rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
-		if (pt->pt_name == NULL)
+		n = partial_name(pt);
+		if (n == NULL)
 		    rettv->vval.v_string = NULL;
 		else
-		    rettv->vval.v_string = vim_strsave(pt->pt_name);
+		{
+		    rettv->vval.v_string = vim_strsave(n);
+		    if (rettv->v_type == VAR_FUNC)
+			func_ref(rettv->vval.v_string);
+		}
 	    }
 	    else if (STRCMP(what, "dict") == 0)
 	    {
@@ -10104,7 +10153,7 @@ item_compare2(const void *s1, const void
     if (partial == NULL)
 	func_name = sortinfo->item_compare_func;
     else
-	func_name = partial->pt_name;
+	func_name = partial_name(partial);
 
     /* Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
      * in the copy without changing the original list items. */
@@ -11863,16 +11912,14 @@ get_callback(typval_T *arg, partial_T **
     {
 	*pp = arg->vval.v_partial;
 	++(*pp)->pt_refcount;
-	return (*pp)->pt_name;
+	return partial_name(*pp);
     }
     *pp = NULL;
-    if (arg->v_type == VAR_FUNC)
+    if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
     {
 	func_ref(arg->vval.v_string);
 	return arg->vval.v_string;
     }
-    if (arg->v_type == VAR_STRING)
-	return arg->vval.v_string;
     if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
 	return (char_u *)"";
     EMSG(_("E921: Invalid callback argument"));