changeset 8585:ce37bbedcb65 v7.4.1582

commit https://github.com/vim/vim/commit/6f2e4b36c9d9908e1cace2b1b96e2c154a837bc2 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Mar 16 22:52:12 2016 +0100 patch 7.4.1582 Problem: Get E923 when using function(dict.func, [], dict). (Kent Sibilev) Storing a function with a dict in a variable drops the dict if the function is script-local. Solution: Translate the function name. Use dict arg if present.
author Christian Brabandt <cb@256bit.org>
date Wed, 16 Mar 2016 23:00:25 +0100
parents af8fed1a84e7
children 6d28aebf96df
files src/eval.c src/testdir/test_partial.vim src/version.c
diffstat 3 files changed, 111 insertions(+), 75 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -110,7 +110,6 @@ static char *e_illvar = N_("E461: Illega
 #ifdef FEAT_FLOAT
 static char *e_float_as_string = N_("E806: using Float as a String");
 #endif
-static char *e_dict_both = N_("E924: can't have both a \"self\" dict and a partial: %s");
 
 #define NAMESPACE_CHAR	(char_u *)"abglstvw"
 
@@ -8678,6 +8677,67 @@ get_func_tv(
     return ret;
 }
 
+#define ERROR_UNKNOWN	0
+#define ERROR_TOOMANY	1
+#define ERROR_TOOFEW	2
+#define ERROR_SCRIPT	3
+#define ERROR_DICT	4
+#define ERROR_NONE	5
+#define ERROR_OTHER	6
+#define FLEN_FIXED 40
+
+/*
+ * In a script change <SID>name() and s:name() to K_SNR 123_name().
+ * Change <SNR>123_name() to K_SNR 123_name().
+ * Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory
+ * (slow).
+ */
+    static char_u *
+fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error)
+{
+    int		llen;
+    char_u	*fname;
+    int		i;
+
+    llen = eval_fname_script(name);
+    if (llen > 0)
+    {
+	fname_buf[0] = K_SPECIAL;
+	fname_buf[1] = KS_EXTRA;
+	fname_buf[2] = (int)KE_SNR;
+	i = 3;
+	if (eval_fname_sid(name))	/* "<SID>" or "s:" */
+	{
+	    if (current_SID <= 0)
+		*error = ERROR_SCRIPT;
+	    else
+	    {
+		sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID);
+		i = (int)STRLEN(fname_buf);
+	    }
+	}
+	if (i + STRLEN(name + llen) < FLEN_FIXED)
+	{
+	    STRCPY(fname_buf + i, name + llen);
+	    fname = fname_buf;
+	}
+	else
+	{
+	    fname = alloc((unsigned)(i + STRLEN(name + llen) + 1));
+	    if (fname == NULL)
+		*error = ERROR_OTHER;
+	    else
+	    {
+		*tofree = fname;
+		mch_memmove(fname, fname_buf, (size_t)i);
+		STRCPY(fname + i, name + llen);
+	    }
+	}
+    }
+    else
+	fname = name;
+    return fname;
+}
 
 /*
  * Call a function with its resolved parameters
@@ -8700,20 +8760,11 @@ call_func(
     dict_T	*selfdict_in)	/* Dictionary for "self" */
 {
     int		ret = FAIL;
-#define ERROR_UNKNOWN	0
-#define ERROR_TOOMANY	1
-#define ERROR_TOOFEW	2
-#define ERROR_SCRIPT	3
-#define ERROR_DICT	4
-#define ERROR_NONE	5
-#define ERROR_OTHER	6
-#define ERROR_BOTH	7
     int		error = ERROR_NONE;
     int		i;
-    int		llen;
     ufunc_T	*fp;
-#define FLEN_FIXED 40
     char_u	fname_buf[FLEN_FIXED + 1];
+    char_u	*tofree = NULL;
     char_u	*fname;
     char_u	*name;
     int		argcount = argcount_in;
@@ -8728,47 +8779,7 @@ call_func(
     if (name == NULL)
 	return ret;
 
-    /*
-     * In a script change <SID>name() and s:name() to K_SNR 123_name().
-     * Change <SNR>123_name() to K_SNR 123_name().
-     * Use fname_buf[] when it fits, otherwise allocate memory (slow).
-     */
-    llen = eval_fname_script(name);
-    if (llen > 0)
-    {
-	fname_buf[0] = K_SPECIAL;
-	fname_buf[1] = KS_EXTRA;
-	fname_buf[2] = (int)KE_SNR;
-	i = 3;
-	if (eval_fname_sid(name))	/* "<SID>" or "s:" */
-	{
-	    if (current_SID <= 0)
-		error = ERROR_SCRIPT;
-	    else
-	    {
-		sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID);
-		i = (int)STRLEN(fname_buf);
-	    }
-	}
-	if (i + STRLEN(name + llen) < FLEN_FIXED)
-	{
-	    STRCPY(fname_buf + i, name + llen);
-	    fname = fname_buf;
-	}
-	else
-	{
-	    fname = alloc((unsigned)(i + STRLEN(name + llen) + 1));
-	    if (fname == NULL)
-		error = ERROR_OTHER;
-	    else
-	    {
-		mch_memmove(fname, fname_buf, (size_t)i);
-		STRCPY(fname + i, name + llen);
-	    }
-	}
-    }
-    else
-	fname = name;
+    fname = fname_trans_sid(name, fname_buf, &tofree, &error);
 
     *doesrange = FALSE;
 
@@ -8776,9 +8787,11 @@ call_func(
     {
 	if (partial->pt_dict != NULL)
 	{
-	    if (selfdict_in != NULL)
-		error = ERROR_BOTH;
-	    selfdict = partial->pt_dict;
+	    /* When the function has a partial with a dict and there is a dict
+	     * argument, use the dict argument.  That is backwards compatible.
+	     */
+	    if (selfdict_in == NULL)
+		selfdict = partial->pt_dict;
 	}
 	if (error == ERROR_NONE && partial->pt_argc > 0)
 	{
@@ -8934,16 +8947,12 @@ call_func(
 		    emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
 									name);
 		    break;
-	    case ERROR_BOTH:
-		    emsg_funcname(e_dict_both, name);
-		    break;
 	}
     }
 
     while (argv_clear > 0)
 	clear_tv(&argv[--argv_clear]);
-    if (fname != name && fname != fname_buf)
-	vim_free(fname);
+    vim_free(tofree);
     vim_free(name);
 
     return ret;
@@ -11876,12 +11885,6 @@ f_function(typval_T *argvars, typval_T *
 		    vim_free(name);
 		    return;
 		}
-		if (argvars[0].v_type == VAR_PARTIAL)
-		{
-		    EMSG2(_(e_dict_both), name);
-		    vim_free(name);
-		    return;
-		}
 		if (argvars[dict_idx].vval.v_dict == NULL)
 		    dict_idx = 0;
 	    }
@@ -11925,16 +11928,18 @@ f_function(typval_T *argvars, typval_T *
 		    }
 		}
 
-		if (argvars[0].v_type == VAR_PARTIAL)
+		/* For "function(dict.func, [], dict)" and "func" is a partial
+		 * use "dict".  That is backwards compatible. */
+		if (dict_idx > 0)
+		{
+		    pt->pt_dict = argvars[dict_idx].vval.v_dict;
+		    ++pt->pt_dict->dv_refcount;
+		}
+		else if (argvars[0].v_type == VAR_PARTIAL)
 		{
 		    pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
 		    ++pt->pt_dict->dv_refcount;
 		}
-		else if (dict_idx > 0)
-		{
-		    pt->pt_dict = argvars[dict_idx].vval.v_dict;
-		    ++pt->pt_dict->dv_refcount;
-		}
 
 		pt->pt_refcount = 1;
 		pt->pt_name = name;
@@ -21714,7 +21719,17 @@ handle_subscript(
 
     if (rettv->v_type == VAR_FUNC && selfdict != NULL)
     {
-	ufunc_T	*fp = find_func(rettv->vval.v_string);
+	char_u	    *fname;
+	char_u	    *tofree = NULL;
+	ufunc_T	    *fp;
+	char_u	    fname_buf[FLEN_FIXED + 1];
+	int	    error;
+
+	/* Translate "s:func" to the stored function name. */
+	fname = fname_trans_sid(rettv->vval.v_string, fname_buf,
+							     &tofree, &error);
+	fp = find_func(fname);
+	vim_free(tofree);
 
 	/* Turn "dict.Func" into a partial for "Func" with "dict". */
 	if (fp != NULL && (fp->uf_flags & FC_DICT))
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -70,8 +70,6 @@ func Test_partial_implicit()
 
   let Func = function(dict.MyFunc, ['bbb'])
   call assert_equal('foo/bbb', Func())
-
-  call assert_fails('call function(dict.MyFunc, ["bbb"], dict)', 'E924:')
 endfunc
 
 fun InnerCall(funcref)
@@ -87,3 +85,24 @@ func Test_function_in_dict()
   call OuterCall()
 endfunc
 
+function! s:cache_clear() dict
+  return self.name
+endfunction
+
+func Test_script_function_in_dict()
+  let s:obj = {'name': 'foo'}
+  let s:obj2 = {'name': 'bar'}
+
+  let s:obj['clear'] = function('s:cache_clear')
+
+  call assert_equal('foo', s:obj.clear())
+  let F = s:obj.clear
+  call assert_equal('foo', F())
+  call assert_equal('foo', call(s:obj.clear, [], s:obj))
+  call assert_equal('bar', call(s:obj.clear, [], s:obj2))
+
+  let s:obj2['clear'] = function('s:cache_clear')
+  call assert_equal('bar', s:obj2.clear())
+  let B = s:obj2.clear
+  call assert_equal('bar', B())
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -749,6 +749,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1582,
+/**/
     1581,
 /**/
     1580,