forked from aniani/vim
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.
This commit is contained in:
161
src/eval.c
161
src/eval.c
@@ -110,7 +110,6 @@ static char *e_illvar = N_("E461: Illegal variable name: %s");
|
|||||||
#ifdef FEAT_FLOAT
|
#ifdef FEAT_FLOAT
|
||||||
static char *e_float_as_string = N_("E806: using Float as a String");
|
static char *e_float_as_string = N_("E806: using Float as a String");
|
||||||
#endif
|
#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"
|
#define NAMESPACE_CHAR (char_u *)"abglstvw"
|
||||||
|
|
||||||
@@ -8678,6 +8677,67 @@ get_func_tv(
|
|||||||
return ret;
|
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
|
* Call a function with its resolved parameters
|
||||||
@@ -8700,20 +8760,11 @@ call_func(
|
|||||||
dict_T *selfdict_in) /* Dictionary for "self" */
|
dict_T *selfdict_in) /* Dictionary for "self" */
|
||||||
{
|
{
|
||||||
int ret = FAIL;
|
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 error = ERROR_NONE;
|
||||||
int i;
|
int i;
|
||||||
int llen;
|
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
#define FLEN_FIXED 40
|
|
||||||
char_u fname_buf[FLEN_FIXED + 1];
|
char_u fname_buf[FLEN_FIXED + 1];
|
||||||
|
char_u *tofree = NULL;
|
||||||
char_u *fname;
|
char_u *fname;
|
||||||
char_u *name;
|
char_u *name;
|
||||||
int argcount = argcount_in;
|
int argcount = argcount_in;
|
||||||
@@ -8728,47 +8779,7 @@ call_func(
|
|||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/*
|
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
|
||||||
* 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;
|
|
||||||
|
|
||||||
*doesrange = FALSE;
|
*doesrange = FALSE;
|
||||||
|
|
||||||
@@ -8776,9 +8787,11 @@ call_func(
|
|||||||
{
|
{
|
||||||
if (partial->pt_dict != NULL)
|
if (partial->pt_dict != NULL)
|
||||||
{
|
{
|
||||||
if (selfdict_in != NULL)
|
/* When the function has a partial with a dict and there is a dict
|
||||||
error = ERROR_BOTH;
|
* argument, use the dict argument. That is backwards compatible.
|
||||||
selfdict = partial->pt_dict;
|
*/
|
||||||
|
if (selfdict_in == NULL)
|
||||||
|
selfdict = partial->pt_dict;
|
||||||
}
|
}
|
||||||
if (error == ERROR_NONE && partial->pt_argc > 0)
|
if (error == ERROR_NONE && partial->pt_argc > 0)
|
||||||
{
|
{
|
||||||
@@ -8934,16 +8947,12 @@ call_func(
|
|||||||
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
|
emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
|
||||||
name);
|
name);
|
||||||
break;
|
break;
|
||||||
case ERROR_BOTH:
|
|
||||||
emsg_funcname(e_dict_both, name);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (argv_clear > 0)
|
while (argv_clear > 0)
|
||||||
clear_tv(&argv[--argv_clear]);
|
clear_tv(&argv[--argv_clear]);
|
||||||
if (fname != name && fname != fname_buf)
|
vim_free(tofree);
|
||||||
vim_free(fname);
|
|
||||||
vim_free(name);
|
vim_free(name);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -11876,12 +11885,6 @@ f_function(typval_T *argvars, typval_T *rettv)
|
|||||||
vim_free(name);
|
vim_free(name);
|
||||||
return;
|
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)
|
if (argvars[dict_idx].vval.v_dict == NULL)
|
||||||
dict_idx = 0;
|
dict_idx = 0;
|
||||||
}
|
}
|
||||||
@@ -11925,16 +11928,18 @@ 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. */
|
||||||
pt->pt_dict = argvars[0].vval.v_partial->pt_dict;
|
if (dict_idx > 0)
|
||||||
++pt->pt_dict->dv_refcount;
|
|
||||||
}
|
|
||||||
else if (dict_idx > 0)
|
|
||||||
{
|
{
|
||||||
pt->pt_dict = argvars[dict_idx].vval.v_dict;
|
pt->pt_dict = argvars[dict_idx].vval.v_dict;
|
||||||
++pt->pt_dict->dv_refcount;
|
++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;
|
||||||
|
}
|
||||||
|
|
||||||
pt->pt_refcount = 1;
|
pt->pt_refcount = 1;
|
||||||
pt->pt_name = name;
|
pt->pt_name = name;
|
||||||
@@ -21714,7 +21719,17 @@ handle_subscript(
|
|||||||
|
|
||||||
if (rettv->v_type == VAR_FUNC && selfdict != NULL)
|
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". */
|
/* Turn "dict.Func" into a partial for "Func" with "dict". */
|
||||||
if (fp != NULL && (fp->uf_flags & FC_DICT))
|
if (fp != NULL && (fp->uf_flags & FC_DICT))
|
||||||
|
@@ -70,8 +70,6 @@ func Test_partial_implicit()
|
|||||||
|
|
||||||
let Func = function(dict.MyFunc, ['bbb'])
|
let Func = function(dict.MyFunc, ['bbb'])
|
||||||
call assert_equal('foo/bbb', Func())
|
call assert_equal('foo/bbb', Func())
|
||||||
|
|
||||||
call assert_fails('call function(dict.MyFunc, ["bbb"], dict)', 'E924:')
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
fun InnerCall(funcref)
|
fun InnerCall(funcref)
|
||||||
@@ -87,3 +85,24 @@ func Test_function_in_dict()
|
|||||||
call OuterCall()
|
call OuterCall()
|
||||||
endfunc
|
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
|
||||||
|
@@ -748,6 +748,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1582,
|
||||||
/**/
|
/**/
|
||||||
1581,
|
1581,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user