patch 7.4.1582 v7.4.1582
authorBram Moolenaar <Bram@vim.org>
Wed, 16 Mar 2016 21:52:12 +0000 (22:52 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 16 Mar 2016 21:52:12 +0000 (22:52 +0100)
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.

src/eval.c
src/testdir/test_partial.vim
src/version.c

index 891cbad738892eac6feddc4eddb3f241b5302bae..6280323aa5027fd19001d846331071511c2e2766 100644 (file)
@@ -110,7 +110,6 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
 #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,28 +8677,6 @@ get_func_tv(
     return ret;
 }
 
-
-/*
- * Call a function with its resolved parameters
- * Return FAIL when the function can't be called,  OK otherwise.
- * Also returns OK when an error was encountered while executing the function.
- */
-    int
-call_func(
-    char_u     *funcname,      /* name of the function */
-    int                len,            /* length of "name" */
-    typval_T   *rettv,         /* return value goes here */
-    int                argcount_in,    /* number of "argvars" */
-    typval_T   *argvars_in,    /* vars for arguments, must have "argcount"
-                                  PLUS ONE elements! */
-    linenr_T   firstline,      /* first line of range */
-    linenr_T   lastline,       /* last line of range */
-    int                *doesrange,     /* return: function handled range */
-    int                evaluate,
-    partial_T  *partial,       /* optional, can be NULL */
-    dict_T     *selfdict_in)   /* Dictionary for "self" */
-{
-    int                ret = FAIL;
 #define ERROR_UNKNOWN  0
 #define ERROR_TOOMANY  1
 #define ERROR_TOOFEW   2
@@ -8707,32 +8684,21 @@ call_func(
 #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     *fname;
-    char_u     *name;
-    int                argcount = argcount_in;
-    typval_T   *argvars = argvars_in;
-    dict_T     *selfdict = selfdict_in;
-    typval_T   argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
-    int                argv_clear = 0;
 
-    /* Make a copy of the name, if it comes from a funcref variable it could
-     * be changed or deleted in the called function. */
-    name = vim_strnsave(funcname, len);
-    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[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;
 
-    /*
-     * 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)
     {
@@ -8743,7 +8709,7 @@ call_func(
        if (eval_fname_sid(name))       /* "<SID>" or "s:" */
        {
            if (current_SID <= 0)
-               error = ERROR_SCRIPT;
+               *error = ERROR_SCRIPT;
            else
            {
                sprintf((char *)fname_buf + 3, "%ld_", (long)current_SID);
@@ -8759,9 +8725,10 @@ call_func(
        {
            fname = alloc((unsigned)(i + STRLEN(name + llen) + 1));
            if (fname == NULL)
-               error = ERROR_OTHER;
+               *error = ERROR_OTHER;
            else
            {
+               *tofree = fname;
                mch_memmove(fname, fname_buf, (size_t)i);
                STRCPY(fname + i, name + llen);
            }
@@ -8769,6 +8736,50 @@ call_func(
     }
     else
        fname = name;
+    return fname;
+}
+
+/*
+ * Call a function with its resolved parameters
+ * Return FAIL when the function can't be called,  OK otherwise.
+ * Also returns OK when an error was encountered while executing the function.
+ */
+    int
+call_func(
+    char_u     *funcname,      /* name of the function */
+    int                len,            /* length of "name" */
+    typval_T   *rettv,         /* return value goes here */
+    int                argcount_in,    /* number of "argvars" */
+    typval_T   *argvars_in,    /* vars for arguments, must have "argcount"
+                                  PLUS ONE elements! */
+    linenr_T   firstline,      /* first line of range */
+    linenr_T   lastline,       /* last line of range */
+    int                *doesrange,     /* return: function handled range */
+    int                evaluate,
+    partial_T  *partial,       /* optional, can be NULL */
+    dict_T     *selfdict_in)   /* Dictionary for "self" */
+{
+    int                ret = FAIL;
+    int                error = ERROR_NONE;
+    int                i;
+    ufunc_T    *fp;
+    char_u     fname_buf[FLEN_FIXED + 1];
+    char_u     *tofree = NULL;
+    char_u     *fname;
+    char_u     *name;
+    int                argcount = argcount_in;
+    typval_T   *argvars = argvars_in;
+    dict_T     *selfdict = selfdict_in;
+    typval_T   argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
+    int                argv_clear = 0;
+
+    /* Make a copy of the name, if it comes from a funcref variable it could
+     * be changed or deleted in the called function. */
+    name = vim_strnsave(funcname, len);
+    if (name == NULL)
+       return ret;
+
+    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 *rettv)
                    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,14 +11928,16 @@ f_function(typval_T *argvars, typval_T *rettv)
                    }
                }
 
-               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[0].vval.v_partial->pt_dict;
+                   pt->pt_dict = argvars[dict_idx].vval.v_dict;
                    ++pt->pt_dict->dv_refcount;
                }
-               else if (dict_idx > 0)
+               else if (argvars[0].v_type == VAR_PARTIAL)
                {
-                   pt->pt_dict = argvars[dict_idx].vval.v_dict;
+                   pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
                    ++pt->pt_dict->dv_refcount;
                }
 
@@ -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))
index e9d9a32267e8604298098a290a5d4e8806b1db2e..fe451cd8bed49dabf3927a5363b73668b0cfa046 100644 (file)
@@ -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
index 8af175ef116b5c0e2e5a2ea96bf59f038d4c2161..6e195360fcaf5d2618e1bf72798966ddc0598963 100644 (file)
@@ -748,6 +748,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1582,
 /**/
     1581,
 /**/