# HG changeset patch # User Christian Brabandt # Date 1458165625 -3600 # Node ID ce37bbedcb653ebd256e4d522350c2d004f4f787 # Parent af8fed1a84e71ddf8b9978ca2ffc57a6e9e75cc9 commit https://github.com/vim/vim/commit/6f2e4b36c9d9908e1cace2b1b96e2c154a837bc2 Author: Bram Moolenaar 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. diff --git a/src/eval.c b/src/eval.c --- 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 name() and s:name() to K_SNR 123_name(). + * Change 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)) /* "" 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 name() and s:name() to K_SNR 123_name(). - * Change 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)) /* "" 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)) diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim --- 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 diff --git a/src/version.c b/src/version.c --- 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,