From 7a5c46a9df7ef01a4f6a620861c35400d5ad28d9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Mar 2016 20:41:21 +0100 Subject: [PATCH 1/4] patch 7.4.1580 Problem: Crash when using function reference. (Luchr) Solution: Set initial refcount. (Ken Takata, closes #690) --- src/eval.c | 1 + src/testdir/test_partial.vim | 14 ++++++++++++++ src/version.c | 2 ++ 3 files changed, 17 insertions(+) diff --git a/src/eval.c b/src/eval.c index 506932403a..85f209221a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -21705,6 +21705,7 @@ handle_subscript( /* Turn "dict.Func" into a partial for "Func" with "dict". */ if (pt != NULL) { + pt->pt_refcount = 1; pt->pt_dict = selfdict; selfdict = NULL; pt->pt_name = rettv->vval.v_string; diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim index ddc85e0ef9..acba887cbf 100644 --- a/src/testdir/test_partial.vim +++ b/src/testdir/test_partial.vim @@ -68,3 +68,17 @@ func Test_partial_implicit() call assert_fails('call function(dict.MyFunc, ["bbb"], dict)', 'E924:') endfunc + +fun InnerCall(funcref) + return a:funcref +endfu + +fun OuterCall() + let opt = { 'func' : function('sin') } + call InnerCall(opt.func) +endfu + +func Test_function_in_dict() + call OuterCall() +endfunc + diff --git a/src/version.c b/src/version.c index e4c3412d77..cb291581d2 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1580, /**/ 1579, /**/ From 65639032bb7b17996cd255d1508a1df4ad528a1f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Mar 2016 21:40:30 +0100 Subject: [PATCH 2/4] patch 7.4.1581 Problem: Using ":call dict.func()" where the function is a partial does not work. Using "dict.func()" where the function does not take a Dictionary does not work. Solution: Handle partial properly in ":call". (Yasuhiro Matsumoto) --- src/eval.c | 77 ++++++++++++++++++++++-------------- src/testdir/test55.ok | 2 +- src/testdir/test_partial.vim | 5 +++ src/version.c | 2 + 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/eval.c b/src/eval.c index 85f209221a..891cbad738 100644 --- a/src/eval.c +++ b/src/eval.c @@ -867,7 +867,7 @@ static int valid_varname(char_u *varname); static int tv_check_lock(int lock, char_u *name, int use_gettext); static int item_copy(typval_T *from, typval_T *to, int deep, int copyID); static char_u *find_option_end(char_u **arg, int *opt_flags); -static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd); +static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd, partial_T **partial); static int eval_fname_script(char_u *p); static int eval_fname_sid(char_u *p); static void list_func_head(ufunc_T *fp, int indent); @@ -3476,7 +3476,7 @@ ex_call(exarg_T *eap) return; } - tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi); + tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial); if (fudi.fd_newkey != NULL) { /* Still need to give an error message for missing key. */ @@ -3491,9 +3491,18 @@ ex_call(exarg_T *eap) if (fudi.fd_dict != NULL) ++fudi.fd_dict->dv_refcount; - /* If it is the name of a variable of type VAR_FUNC use its contents. */ + /* If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its + * contents. For VAR_PARTIAL get its partial, unless we already have one + * from trans_function_name(). */ len = (int)STRLEN(tofree); - name = deref_func_name(tofree, &len, &partial, FALSE); + name = deref_func_name(tofree, &len, + partial != NULL ? NULL : &partial, FALSE); + + /* When calling fdict.func(), where "func" is a partial, use "fdict" + * instead of the dict in the partial, for backwards compatibility. + * TODO: Do use the arguments in the partial? */ + if (fudi.fd_dict != NULL) + partial = NULL; /* Skip white space to allow ":call func ()". Not good, but required for * backward compatibility. */ @@ -8561,15 +8570,17 @@ find_internal_func( /* * Check if "name" is a variable of type VAR_FUNC. If so, return the function * name it contains, otherwise return "name". - * If "name" is of type VAR_PARTIAL also return "partial" + * If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set + * "partialp". */ static char_u * -deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload) +deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload) { dictitem_T *v; int cc; - *partial = NULL; + if (partialp != NULL) + *partialp = NULL; cc = name[*lenp]; name[*lenp] = NUL; @@ -8588,14 +8599,17 @@ deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload) if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) { - *partial = v->di_tv.vval.v_partial; - if (*partial == NULL) + partial_T *pt = v->di_tv.vval.v_partial; + + if (pt == NULL) { *lenp = 0; return (char_u *)""; /* just in case */ } - *lenp = (int)STRLEN((*partial)->pt_name); - return (*partial)->pt_name; + if (partialp != NULL) + *partialp = pt; + *lenp = (int)STRLEN(pt->pt_name); + return pt->pt_name; } return name; @@ -21700,18 +21714,23 @@ handle_subscript( if (rettv->v_type == VAR_FUNC && selfdict != NULL) { - partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + ufunc_T *fp = find_func(rettv->vval.v_string); /* Turn "dict.Func" into a partial for "Func" with "dict". */ - if (pt != NULL) + if (fp != NULL && (fp->uf_flags & FC_DICT)) { - pt->pt_refcount = 1; - pt->pt_dict = selfdict; - selfdict = NULL; - pt->pt_name = rettv->vval.v_string; - func_ref(pt->pt_name); - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; + partial_T *pt = (partial_T *)alloc_clear(sizeof(partial_T)); + + if (pt != NULL) + { + pt->pt_refcount = 1; + pt->pt_dict = selfdict; + selfdict = NULL; + pt->pt_name = rettv->vval.v_string; + func_ref(pt->pt_name); + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } } } @@ -23220,7 +23239,7 @@ ex_function(exarg_T *eap) * g:func global function name, same as "func" */ p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi); + name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); paren = (vim_strchr(p, '(') != NULL); if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) { @@ -23533,7 +23552,7 @@ ex_function(exarg_T *eap) if (*p == '!') p = skipwhite(p + 1); p += eval_fname_script(p); - vim_free(trans_function_name(&p, TRUE, 0, NULL)); + vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL)); if (*skipwhite(p) == '(') { ++nesting; @@ -23788,7 +23807,8 @@ trans_function_name( char_u **pp, int skip, /* only find the end, don't evaluate */ int flags, - funcdict_T *fdp) /* return: info about dictionary used */ + funcdict_T *fdp, /* return: info about dictionary used */ + partial_T **partial) /* return: partial of a FuncRef */ { char_u *name = NULL; char_u *start; @@ -23797,7 +23817,6 @@ trans_function_name( char_u sid_buf[20]; int len; lval_T lv; - partial_T *partial; if (fdp != NULL) vim_memset(fdp, 0, sizeof(funcdict_T)); @@ -23882,7 +23901,7 @@ trans_function_name( if (lv.ll_exp_name != NULL) { len = (int)STRLEN(lv.ll_exp_name); - name = deref_func_name(lv.ll_exp_name, &len, &partial, + name = deref_func_name(lv.ll_exp_name, &len, partial, flags & TFN_NO_AUTOLOAD); if (name == lv.ll_exp_name) name = NULL; @@ -23890,7 +23909,7 @@ trans_function_name( else { len = (int)(end - *pp); - name = deref_func_name(*pp, &len, &partial, flags & TFN_NO_AUTOLOAD); + name = deref_func_name(*pp, &len, partial, flags & TFN_NO_AUTOLOAD); if (name == *pp) name = NULL; } @@ -24115,7 +24134,7 @@ function_exists(char_u *name) int n = FALSE; p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET|TFN_NO_AUTOLOAD, - NULL); + NULL, NULL); nm = skipwhite(nm); /* Only accept "funcname", "funcname ", "funcname (..." and @@ -24132,7 +24151,7 @@ get_expanded_name(char_u *name, int check) char_u *nm = name; char_u *p; - p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL); + p = trans_function_name(&nm, FALSE, TFN_INT|TFN_QUIET, NULL, NULL); if (p != NULL && *nm == NUL) if (!check || translated_function_exists(p)) @@ -24488,7 +24507,7 @@ ex_delfunction(exarg_T *eap) funcdict_T fudi; p = eap->arg; - name = trans_function_name(&p, eap->skip, 0, &fudi); + name = trans_function_name(&p, eap->skip, 0, &fudi, NULL); vim_free(fudi.fd_newkey); if (name == NULL) { diff --git a/src/testdir/test55.ok b/src/testdir/test55.ok index ee6d3a0800..70ee24a1bb 100644 --- a/src/testdir/test55.ok +++ b/src/testdir/test55.ok @@ -44,7 +44,7 @@ NONE 2999 {'33': 999} len: 3 again: 3 -Vim(call):E725: +xxx3 g:dict.func-4 a:function('3') Vim(let):E698: diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim index acba887cbf..e9d9a32267 100644 --- a/src/testdir/test_partial.vim +++ b/src/testdir/test_partial.vim @@ -16,6 +16,8 @@ endfunc func Test_partial_args() let Cb = function('MyFunc', ["foo", "bar"]) + + call Cb("zzz") call assert_equal("foo/bar/xxx", Cb("xxx")) call assert_equal("foo/bar/yyy", call(Cb, ["yyy"])) @@ -49,6 +51,9 @@ func Test_partial_dict() let Cb = function('MyDictFunc', dict) call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy")) call assert_fails('Cb("fff")', 'E492:') + + let dict = {"tr": function('tr', ['hello', 'h', 'H'])} + call assert_equal("Hello", dict.tr()) endfunc func Test_partial_implicit() diff --git a/src/version.c b/src/version.c index cb291581d2..8af175ef11 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1581, /**/ 1580, /**/ From 6f2e4b36c9d9908e1cace2b1b96e2c154a837bc2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Mar 2016 22:52:12 +0100 Subject: [PATCH 3/4] 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. --- src/eval.c | 161 +++++++++++++++++++---------------- src/testdir/test_partial.vim | 23 ++++- src/version.c | 2 + 3 files changed, 111 insertions(+), 75 deletions(-) diff --git a/src/eval.c b/src/eval.c index 891cbad738..6280323aa5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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,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 *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,16 +11928,18 @@ f_function(typval_T *argvars, typval_T *rettv) } } - 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) + /* 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; + } 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 index e9d9a32267..fe451cd8be 100644 --- 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 index 8af175ef11..6e195360fc 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1582, /**/ 1581, /**/ From 597385ab43093ba27adcb86cdc1b46aba86a0093 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 16 Mar 2016 23:24:43 +0100 Subject: [PATCH 4/4] patch 7.4.1583 Problem: Warning for unitinialized variable. Solution: Initialize it. (Dominique) --- src/ex_cmds2.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 018f44cc29..19210f30bd 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -1179,7 +1179,7 @@ check_due_timer() { timer_T *timer; long this_due; - long next_due; + long next_due = -1; proftime_T now; int did_one = FALSE; # ifdef WIN3264 diff --git a/src/version.c b/src/version.c index 6e195360fc..20f3e071c0 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1583, /**/ 1582, /**/